From 2b1dc7c266afe18b00da43ed43f72f414809f3a2 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Thu, 24 Aug 2017 12:46:23 +0200 Subject: 0.10.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a534bc0e0..4c8eb20f4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "0x.js", - "version": "0.10.3", + "version": "0.10.4", "description": "A javascript library for interacting with the 0x protocol", "keywords": [ "0x.js", -- cgit v1.2.3 From 6d6688dddaecac94eb87f42dc5e4861b6ad6b4b5 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Thu, 24 Aug 2017 13:07:55 +0200 Subject: Rename build:commonjs:dev to build:commonjs --- package.json | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 4c8eb20f4..83931611d 100644 --- a/package.json +++ b/package.json @@ -13,8 +13,8 @@ "types": "lib/src/index.d.ts", "scripts": { "prebuild": "npm run clean", - "build": "run-p build:*:prod", - "prepublishOnly": "run-p build:umd:prod build:commonjs:dev", + "build": "run-p build:umd:prod build:commonjs", + "prepublishOnly": "run-p build", "postpublish": "run-s release docs:json upload_docs_json", "release": "publish-release --assets _bundles/index.js,_bundles/index.min.js --tag $(git describe --tags) --owner 0xProject --repo 0x.js", "upload_docs_json": "aws s3 cp docs/index.json s3://0xjs-docs-jsons/$(git describe --tags).json --profile 0xproject --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers --content-type aplication/json", @@ -29,10 +29,9 @@ "docs:generate": "typedoc --out docs .", "docs:open": "opn docs/index.html", "clean": "shx rm -rf _bundles lib test_temp", - "build:dev": "npm run clean && run-p build:*:dev", "build:umd:dev": "webpack", "build:umd:prod": "NODE_ENV=production webpack", - "build:commonjs:dev": "tsc; copyfiles -u 2 './src/artifacts/**/*.json' ./lib/src/artifacts;", + "build:commonjs": "tsc; copyfiles -u 2 './src/artifacts/**/*.json' ./lib/src/artifacts;", "test:commonjs": "run-s build:commonjs:dev run_mocha", "pretest:umd": "run-s clean build:*:dev", "substitute_umd_bundle": "npm run remove_src_files_not_used_by_tests; shx mv _bundles/* lib/src", -- cgit v1.2.3 From 9e76b2dd98365adb96d89ddaea8133691b0f3cfc Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Thu, 24 Aug 2017 13:13:34 +0200 Subject: Fix test:commonjs --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 83931611d..23c1fe4aa 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "build:umd:dev": "webpack", "build:umd:prod": "NODE_ENV=production webpack", "build:commonjs": "tsc; copyfiles -u 2 './src/artifacts/**/*.json' ./lib/src/artifacts;", - "test:commonjs": "run-s build:commonjs:dev run_mocha", + "test:commonjs": "run-s build:commonjs run_mocha", "pretest:umd": "run-s clean build:*:dev", "substitute_umd_bundle": "npm run remove_src_files_not_used_by_tests; shx mv _bundles/* lib/src", "remove_src_files_not_used_by_tests": "find ./lib/src \\( -path ./lib/src/utils -o -path ./lib/src/subproviders -o -path ./lib/src/schemas -o -path \"./lib/src/types.*\" \\) -prune -o -type f -print | xargs rm", -- cgit v1.2.3 From 9b66b168edd9c1dce90475ef7b5a65c572454bb7 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Thu, 24 Aug 2017 14:50:27 +0200 Subject: Add setUnlimitedProxyAllowanceAsync and setUnlimitedAllowanceAsync --- src/contract_wrappers/token_wrapper.ts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/contract_wrappers/token_wrapper.ts b/src/contract_wrappers/token_wrapper.ts index 8c1bf6b52..a9abb4f09 100644 --- a/src/contract_wrappers/token_wrapper.ts +++ b/src/contract_wrappers/token_wrapper.ts @@ -29,6 +29,7 @@ const ALLOWANCE_TO_ZERO_GAS_AMOUNT = 45730; * to the 0x Proxy smart contract. */ export class TokenWrapper extends ContractWrapper { + public UNLIMITED_ALLOWANCE_IN_BASE_UNITS = new BigNumber(2).pow(256).minus(1); private _tokenContractsByAddress: {[address: string]: TokenContract}; private _tokenLogEventEmitters: ContractEventEmitter[]; constructor(web3Wrapper: Web3Wrapper) { @@ -79,6 +80,20 @@ export class TokenWrapper extends ContractWrapper { gas, }); } + /** + * Sets the spender's allowance to an unlimited number of baseUnits on behalf of the owner address. + * Equivalent to the ERC20 spec method `approve`. + * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed. + * @param ownerAddress The hex encoded user Ethereum address who would like to set an allowance + * for spenderAddress. + * @param spenderAddress The hex encoded user Ethereum address who will be able to spend the set allowance. + */ + public async setUnlimitedAllowanceAsync(tokenAddress: string, ownerAddress: string, + spenderAddress: string): Promise { + await this.setAllowanceAsync( + tokenAddress, ownerAddress, spenderAddress, this.UNLIMITED_ALLOWANCE_IN_BASE_UNITS, + ); + } /** * Retrieves the owners allowance in baseUnits set to the spender's address. * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed. @@ -126,6 +141,16 @@ export class TokenWrapper extends ContractWrapper { const proxyAddress = await this._getProxyAddressAsync(); await this.setAllowanceAsync(tokenAddress, ownerAddress, proxyAddress, amountInBaseUnits); } + /** + * Sets the 0x proxy contract's allowance to a unlimited number of a tokens' baseUnits on behalf + * of an owner address. + * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed. + * @param ownerAddress The hex encoded user Ethereum address who is setting an allowance + * for the Proxy contract. + */ + public async setUnlimitedProxyAllowanceAsync(tokenAddress: string, ownerAddress: string): Promise { + await this.setProxyAllowanceAsync(tokenAddress, ownerAddress, this.UNLIMITED_ALLOWANCE_IN_BASE_UNITS); + } /** * Transfers `amountInBaseUnits` ERC20 tokens from `fromAddress` to `toAddress`. * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed. -- cgit v1.2.3 From af242278c35eba00ad48fdb7699800ac28f54059 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Thu, 24 Aug 2017 15:06:24 +0200 Subject: Update CHANGELOG --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index df161b3a2..ad72b3d04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # CHANGELOG +v0.11.0 - _TBD_ +------------------------ + * Added `zeroEx.token.setUnlimitedProxyAllowanceAsync` (#137) + * Added `zeroEx.token.setUnlimitedAllowanceAsync` (#137) + * Added `zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS` (#137) + v0.10.4 - _Aug 24, 2017_ ------------------------ * Fixed a bug where checksummed addresses were being pulled from artifacts and not lower-cased. (#135) -- cgit v1.2.3 From 6f227c152d49b6266fab3c8eb054c16e3c9cef45 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Thu, 24 Aug 2017 15:26:59 +0200 Subject: Add tests for unlimited allowance --- src/contract_wrappers/token_wrapper.ts | 2 +- test/token_wrapper_test.ts | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/contract_wrappers/token_wrapper.ts b/src/contract_wrappers/token_wrapper.ts index a9abb4f09..472ff909a 100644 --- a/src/contract_wrappers/token_wrapper.ts +++ b/src/contract_wrappers/token_wrapper.ts @@ -21,7 +21,7 @@ import { ContractEventObj, } from '../types'; -const ALLOWANCE_TO_ZERO_GAS_AMOUNT = 45730; +const ALLOWANCE_TO_ZERO_GAS_AMOUNT = 47155; /** * This class includes all the functionality related to interacting with ERC20 token contracts. diff --git a/test/token_wrapper_test.ts b/test/token_wrapper_test.ts index 8adaa6351..e466cc0ad 100644 --- a/test/token_wrapper_test.ts +++ b/test/token_wrapper_test.ts @@ -206,6 +206,17 @@ describe('TokenWrapper', () => { return expect(allowanceAfterSet).to.be.bignumber.equal(expectedAllowanceAfterAllowanceSet); }); }); + describe('#setUnlimitedAllowanceAsync', () => { + it('should set the unlimited spender\'s allowance', async () => { + const token = tokens[0]; + const ownerAddress = coinbase; + const spenderAddress = addressWithoutFunds; + + await zeroEx.token.setUnlimitedAllowanceAsync(token.address, ownerAddress, spenderAddress); + const allowance = await zeroEx.token.getAllowanceAsync(token.address, ownerAddress, spenderAddress); + return expect(allowance).to.be.bignumber.equal(zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS); + }); + }); describe('#getAllowanceAsync', () => { describe('With web3 provider with accounts', () => { it('should get the proxy allowance', async () => { @@ -282,6 +293,16 @@ describe('TokenWrapper', () => { return expect(allowanceAfterSet).to.be.bignumber.equal(expectedAllowanceAfterAllowanceSet); }); }); + describe('#setUnlimitedProxyAllowanceAsync', () => { + it('should set the unlimited proxy allowance', async () => { + const token = tokens[0]; + const ownerAddress = coinbase; + + await zeroEx.token.setUnlimitedProxyAllowanceAsync(token.address, ownerAddress); + const allowance = await zeroEx.token.getProxyAllowanceAsync(token.address, ownerAddress); + return expect(allowance).to.be.bignumber.equal(zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS); + }); + }); describe('#subscribeAsync', () => { const indexFilterValues = {}; const shouldThrowOnInsufficientBalanceOrAllowance = true; -- cgit v1.2.3 From 79dcc1660e5201a6c8738ed6117ba80e96d7c225 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Thu, 24 Aug 2017 15:58:22 +0200 Subject: Add a test that unlimited allowance reduces gas cost --- test/token_wrapper_test.ts | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/test/token_wrapper_test.ts b/test/token_wrapper_test.ts index e466cc0ad..22e1ff12d 100644 --- a/test/token_wrapper_test.ts +++ b/test/token_wrapper_test.ts @@ -16,6 +16,7 @@ import { ApprovalContractEventArgs, } from '../src'; import {BlockchainLifecycle} from './utils/blockchain_lifecycle'; +import {TokenUtils} from './utils/token_utils'; import {DoneCallback} from '../src/types'; chaiSetup.configure(); @@ -27,6 +28,7 @@ describe('TokenWrapper', () => { let zeroEx: ZeroEx; let userAddresses: string[]; let tokens: Token[]; + let tokenUtils: TokenUtils; let coinbase: string; let addressWithoutFunds: string; before(async () => { @@ -34,6 +36,7 @@ describe('TokenWrapper', () => { zeroEx = new ZeroEx(web3.currentProvider); userAddresses = await promisify(web3.eth.getAccounts)(); tokens = await zeroEx.tokenRegistry.getTokensAsync(); + tokenUtils = new TokenUtils(tokens); coinbase = userAddresses[0]; addressWithoutFunds = userAddresses[1]; }); @@ -216,6 +219,31 @@ describe('TokenWrapper', () => { const allowance = await zeroEx.token.getAllowanceAsync(token.address, ownerAddress, spenderAddress); return expect(allowance).to.be.bignumber.equal(zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS); }); + it('should reduce the gas cost for transfers including tokens with unlimited allowance support', async () => { + const transferAmount = new BigNumber(5); + const zrx = tokenUtils.getProtocolTokenOrThrow(); + const [, userWithNormalAllowance, userWithUnlimitedAllowance] = userAddresses; + await zeroEx.token.setAllowanceAsync(zrx.address, coinbase, userWithNormalAllowance, transferAmount); + await zeroEx.token.setUnlimitedAllowanceAsync(zrx.address, coinbase, userWithUnlimitedAllowance); + + const initBalanceWithNormalAllowance = await promisify(web3.eth.getBalance)(userWithNormalAllowance); + const initBalanceWithUnlimitedAllowance = await promisify(web3.eth.getBalance)(userWithUnlimitedAllowance); + + await zeroEx.token.transferFromAsync( + zrx.address, coinbase, userWithNormalAllowance, userWithNormalAllowance, transferAmount, + ); + await zeroEx.token.transferFromAsync( + zrx.address, coinbase, userWithUnlimitedAllowance, userWithUnlimitedAllowance, transferAmount, + ); + + const finalBalanceWithNormalAllowance = await promisify(web3.eth.getBalance)(userWithNormalAllowance); + const finalBalanceWithUnlimitedAllowance = await promisify(web3.eth.getBalance)(userWithUnlimitedAllowance); + + const normalGasCost = initBalanceWithNormalAllowance.minus(finalBalanceWithNormalAllowance); + const unlimitedGasCost = initBalanceWithUnlimitedAllowance.minus(finalBalanceWithUnlimitedAllowance); + + expect(normalGasCost.toNumber()).to.be.gt(unlimitedGasCost.toNumber()); + }); }); describe('#getAllowanceAsync', () => { describe('With web3 provider with accounts', () => { -- cgit v1.2.3 From a2d579b201b9e06ede1bff0aaf7536266c145963 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Thu, 24 Aug 2017 18:07:29 +0200 Subject: Add an explanatory comment --- test/token_wrapper_test.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/token_wrapper_test.ts b/test/token_wrapper_test.ts index 22e1ff12d..f4653e432 100644 --- a/test/token_wrapper_test.ts +++ b/test/token_wrapper_test.ts @@ -242,7 +242,10 @@ describe('TokenWrapper', () => { const normalGasCost = initBalanceWithNormalAllowance.minus(finalBalanceWithNormalAllowance); const unlimitedGasCost = initBalanceWithUnlimitedAllowance.minus(finalBalanceWithUnlimitedAllowance); - expect(normalGasCost.toNumber()).to.be.gt(unlimitedGasCost.toNumber()); + // In theory the gas cost with unlimited allowance should be smaller, but with testrpc it's actually bigger. + // This needs to be investigated in ethereumjs-vm. This test is essentially a repro. + // TODO: Make this test pass with inverted assertion. + expect(unlimitedGasCost.toNumber()).to.be.gt(normalGasCost.toNumber()); }); }); describe('#getAllowanceAsync', () => { -- cgit v1.2.3 From 5a3a8ae7d5867a0304fbb1368dc1f2d018e14ac5 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Thu, 24 Aug 2017 20:34:19 +0200 Subject: Refactor UNLIMITED_ALLOWANCE_IN_BASE_UNITS to constants --- src/contract_wrappers/token_wrapper.ts | 2 +- src/utils/constants.ts | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/contract_wrappers/token_wrapper.ts b/src/contract_wrappers/token_wrapper.ts index 472ff909a..0c89d4e9b 100644 --- a/src/contract_wrappers/token_wrapper.ts +++ b/src/contract_wrappers/token_wrapper.ts @@ -29,7 +29,7 @@ const ALLOWANCE_TO_ZERO_GAS_AMOUNT = 47155; * to the 0x Proxy smart contract. */ export class TokenWrapper extends ContractWrapper { - public UNLIMITED_ALLOWANCE_IN_BASE_UNITS = new BigNumber(2).pow(256).minus(1); + public UNLIMITED_ALLOWANCE_IN_BASE_UNITS = constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS; private _tokenContractsByAddress: {[address: string]: TokenContract}; private _tokenLogEventEmitters: ContractEventEmitter[]; constructor(web3Wrapper: Web3Wrapper) { diff --git a/src/utils/constants.ts b/src/utils/constants.ts index d56ee16c5..1b11d7055 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -1,7 +1,10 @@ +import * as BigNumber from 'bignumber.js'; + export const constants = { NULL_ADDRESS: '0x0000000000000000000000000000000000000000', TESTRPC_NETWORK_ID: 50, MAX_DIGITS_IN_UNSIGNED_256_INT: 78, INVALID_JUMP_PATTERN: 'invalid JUMP at', OUT_OF_GAS_PATTERN: 'out of gas', + UNLIMITED_ALLOWANCE_IN_BASE_UNITS: new BigNumber(2).pow(256).minus(1), }; -- cgit v1.2.3 From bd67cf0f9f9bf321848d2dd929fcd474e236cee7 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Thu, 24 Aug 2017 20:35:27 +0200 Subject: Improve comments --- src/contract_wrappers/token_wrapper.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/contract_wrappers/token_wrapper.ts b/src/contract_wrappers/token_wrapper.ts index 0c89d4e9b..a2812ccdb 100644 --- a/src/contract_wrappers/token_wrapper.ts +++ b/src/contract_wrappers/token_wrapper.ts @@ -83,6 +83,8 @@ export class TokenWrapper extends ContractWrapper { /** * Sets the spender's allowance to an unlimited number of baseUnits on behalf of the owner address. * Equivalent to the ERC20 spec method `approve`. + * Setting an unlimited allowance will lower the gas cost for filling orders involving tokens that forego updating + * allowances set to the max amount (e.g ZRX, WETH) * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed. * @param ownerAddress The hex encoded user Ethereum address who would like to set an allowance * for spenderAddress. @@ -144,6 +146,8 @@ export class TokenWrapper extends ContractWrapper { /** * Sets the 0x proxy contract's allowance to a unlimited number of a tokens' baseUnits on behalf * of an owner address. + * Setting an unlimited allowance will lower the gas cost for filling orders involving tokens that forego updating + * allowances set to the max amount (e.g ZRX, WETH) * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed. * @param ownerAddress The hex encoded user Ethereum address who is setting an allowance * for the Proxy contract. -- cgit v1.2.3