Compare commits

..

546 Commits

Author SHA1 Message Date
Leonid Logvinov
62452db5d8 0.12.1 2017-09-02 05:18:31 +02:00
Leonid Logvinov
792646888a Update CHANGELOG version 2017-09-02 05:18:24 +02:00
Leonid Logvinov
cac36e781e 0.12.0 2017-09-02 05:09:38 +02:00
Leonid Logvinov
02f736ac06 Update yarn.lock 2017-09-02 05:08:07 +02:00
Leonid Logvinov
05296b7f48 Update CHANGELOG.md 2017-09-02 05:01:21 +02:00
Leonid
edeed527c2 Merge pull request #148 from BMillman19/bmillman_json_content_type
Add 'content-type' header with value 'application/json' for http requ…
2017-09-01 12:03:34 +02:00
Brandon Millman
4c40b60a2d Add 'content-type' header with value 'application/json' for http requests made JSONRPC endpoints 2017-09-01 00:58:05 -07:00
Leonid
134b9dfb23 Merge pull request #147 from BMillman19/bmillman_contract_types
Fix incorrect types related to contract calls in types.ts
2017-08-31 18:14:36 +02:00
Brandon Millman
18a5f7485c Fix type of ExchangeContract.getOrderHash.call 2017-08-31 08:33:34 -07:00
Brandon Millman
0ff6cc1997 Fix incorrect types related to contract interactions in types.ts 2017-08-31 08:16:28 -07:00
Fabio Berger
4620c1c818 Merge branch 'development' of github.com:0xProject/0x.js into development
* 'development' of github.com:0xProject/0x.js:
  Improve the comment
  Add assert.isWeb3Provider
  Use more concise dep pointing
  Don't create whole web3 object in beta tests
  Improve the comment
  Add tests for web3@1.0
  Use zeroEx.getAvailableAddressesAsync instead of web3.eth.getAccounts
  Add web3@1.0 to web3Factory
  Support web3@1.0 providers
  Define web3@1.0 types ;)
  Install web3@1.0.0 as web3_beta
2017-08-30 19:47:14 +02:00
Fabio Berger
4370e19880 update 0x-json-schema 2017-08-30 19:47:04 +02:00
Leonid
5f44b5f711 Merge pull request #142 from 0xProject/feature/support_web3@1.0_provider
Feature/support web3@1.0 provider
2017-08-30 11:24:15 +02:00
Leonid Logvinov
c83e1d57fc Improve the comment 2017-08-30 11:14:01 +02:00
Leonid Logvinov
60ad5024ce Add assert.isWeb3Provider 2017-08-30 11:12:28 +02:00
Leonid Logvinov
5149fcdd74 Use more concise dep pointing 2017-08-30 11:05:23 +02:00
Leonid Logvinov
130a7967aa Don't create whole web3 object in beta tests 2017-08-30 10:56:46 +02:00
Leonid Logvinov
6e34cf2c3b Move CONTRIBUTING and PULL_REQUEST_TEMPLATE to top level 2017-08-30 10:22:47 +02:00
Leonid Logvinov
a98bb1f7ac Improve the comment 2017-08-29 18:45:26 +02:00
Leonid Logvinov
da2661cf33 Add tests for web3@1.0 2017-08-29 18:42:13 +02:00
Leonid Logvinov
7d82d14a7d Use zeroEx.getAvailableAddressesAsync instead of web3.eth.getAccounts 2017-08-29 18:41:29 +02:00
Leonid Logvinov
e5bb3bc75e Add web3@1.0 to web3Factory 2017-08-29 18:41:14 +02:00
Leonid Logvinov
3f99281309 Support web3@1.0 providers 2017-08-29 18:40:49 +02:00
Leonid Logvinov
28b4ff42ea Define web3@1.0 types ;) 2017-08-29 18:39:06 +02:00
Leonid Logvinov
372fc39a6b Install web3@1.0.0 as web3_beta 2017-08-29 17:04:57 +02:00
Leonid
c6b99fcca0 Merge pull request #141 from 0xProject/feature/CONTRIBUTING.md
Add a CONTRIBUTING.md and PULL_REQUEST_TEMPLATE.md
2017-08-29 13:21:22 +02:00
Leonid Logvinov
b4a95428c1 Add a CONTRIBUTING.md and PULL_REQUEST_TEMPLATE.md 2017-08-29 11:11:09 +02:00
Leonid
07a872f802 Merge pull request #139 from 0xProject/feature/gas-price-config
Gas price config
2017-08-29 10:03:00 +02:00
Leonid Logvinov
9516a50f64 Document changes in CHANGELOG 2017-08-29 10:01:53 +02:00
Leonid Logvinov
bfac021085 Use zeroExConfig in tests 2017-08-29 10:01:53 +02:00
Leonid Logvinov
a19b40b051 Pass config object to a constructor instead of gasPrice 2017-08-29 10:01:53 +02:00
Leonid Logvinov
836cea6fd7 Document gasPrice in ZeroExConfig 2017-08-29 10:01:13 +02:00
Leonid Logvinov
66dd659a2f Add ZeroExConfig type to public interface 2017-08-29 10:01:13 +02:00
Leonid Logvinov
c765f115ae Add ZeroExConfig type 2017-08-29 10:01:13 +02:00
Leonid Logvinov
e372b0c61f Fix CHANGELOG message 2017-08-29 10:01:13 +02:00
Leonid Logvinov
0f646da970 Update CHANGELOG 2017-08-29 10:01:13 +02:00
Leonid Logvinov
8eb8037f9b Revert "Add a unit parameter to web3Wrapper.toWei"
This reverts commit 4503c6a09dd9b97fb3b13f0222954b8f1f0db62e.
2017-08-29 10:01:13 +02:00
Leonid Logvinov
5b60d9f0f4 Remove default value for gasPrice 2017-08-29 10:01:13 +02:00
Leonid Logvinov
dcfc0ecac6 Specify gasPrice in WETH tests 2017-08-29 10:01:13 +02:00
Leonid Logvinov
a6a1601799 Allow user to specify the gas price 2017-08-29 10:01:13 +02:00
Leonid Logvinov
a8fd3f30cf Add a unit parameter to web3Wrapper.toWei 2017-08-29 10:01:13 +02:00
Leonid
05ce9733da Merge pull request #140 from 0xProject/0x-json-schemas
Use 0x-json-schemas
2017-08-28 18:01:39 +02:00
Leonid Logvinov
96da2c26dc Use 0x-json-schemas 2017-08-28 16:49:57 +02:00
Leonid Logvinov
0afc95982b Fix UMD tests 2017-08-24 23:23:50 +02:00
Leonid Logvinov
b132860f1f 0.11.0 2017-08-24 22:55:30 +02:00
Leonid Logvinov
f9a8392d2c Add release date for 0.11.0 2017-08-24 22:55:02 +02:00
Leonid
1e590ce4ba Merge pull request #137 from 0xProject/unlimited-allowance
Add setUnlimitedProxyAllowanceAsync and setUnlimitedAllowanceAsync
2017-08-24 22:49:11 +02:00
Leonid Logvinov
bd67cf0f9f Improve comments 2017-08-24 22:48:51 +02:00
Leonid Logvinov
5a3a8ae7d5 Refactor UNLIMITED_ALLOWANCE_IN_BASE_UNITS to constants 2017-08-24 22:48:51 +02:00
Leonid Logvinov
a2d579b201 Add an explanatory comment 2017-08-24 22:48:51 +02:00
Leonid Logvinov
79dcc1660e Add a test that unlimited allowance reduces gas cost 2017-08-24 22:48:51 +02:00
Leonid Logvinov
6f227c152d Add tests for unlimited allowance 2017-08-24 22:48:51 +02:00
Leonid Logvinov
af242278c3 Update CHANGELOG 2017-08-24 22:48:51 +02:00
Leonid Logvinov
9b66b168ed Add setUnlimitedProxyAllowanceAsync and setUnlimitedAllowanceAsync 2017-08-24 22:48:51 +02:00
Leonid Logvinov
9e76b2dd98 Fix test:commonjs 2017-08-24 22:48:51 +02:00
Leonid Logvinov
6d6688ddda Rename build:commonjs:dev to build:commonjs 2017-08-24 22:48:51 +02:00
Leonid Logvinov
2b1dc7c266 0.10.4 2017-08-24 22:48:51 +02:00
Leonid
3e1c436fa6 Merge pull request #136 from 0xProject/commonjs-build
Rename build:commonjs:dev to build:commonjs
2017-08-24 14:06:02 +02:00
Leonid Logvinov
02ae853718 Fix test:commonjs 2017-08-24 13:13:34 +02:00
Leonid Logvinov
b9f9581db9 Rename build:commonjs:dev to build:commonjs 2017-08-24 13:07:55 +02:00
Leonid Logvinov
31ce891beb Merge branch 'master' into development 2017-08-24 12:50:45 +02:00
Leonid Logvinov
6f29239211 0.10.4 2017-08-24 12:46:23 +02:00
Leonid
ea7c6be128 Merge pull request #135 from 0xProject/artifacts-addresses
Artifacts addresses
2017-08-24 12:46:08 +02:00
Leonid Logvinov
b4970d5bb9 Update CHANGELOG 2017-08-24 12:45:44 +02:00
Leonid Logvinov
73b2f59caa Update CHANGELOG 2017-08-24 12:40:16 +02:00
Leonid Logvinov
d92340903f Convert artifacts addresses lo lower case before using 2017-08-24 12:37:21 +02:00
Leonid Logvinov
44ec05de1f 0.10.3 2017-08-24 12:27:20 +02:00
Leonid Logvinov
2356c0e30e Bump the version 2017-08-24 12:24:52 +02:00
Leonid Logvinov
4e517b32f8 0.10.2 2017-08-24 12:01:46 +02:00
Leonid Logvinov
c21a0512af Add v0.10.2 CHANGELOG 2017-08-24 12:01:37 +02:00
Leonid Logvinov
ee4182c149 Hot fix the checksummed addresses in artifacts 2017-08-24 12:00:28 +02:00
Leonid
2d714d33c6 Merge pull request #134 from 0xProject/release-script
Specify tag and other values in the release script
2017-08-24 11:27:23 +02:00
Leonid Logvinov
92cbb48e15 Specify tag and other values in the release script 2017-08-24 11:15:10 +02:00
Leonid Logvinov
f3fe3fb0c5 0.10.1 2017-08-24 11:04:23 +02:00
Leonid Logvinov
dd75f8bf96 Bump patch version due to the publishing issue with 0.10.0 2017-08-24 11:04:13 +02:00
Leonid Logvinov
dcc4b894e0 0.10.0 2017-08-24 10:51:36 +02:00
Leonid
640825d18d Merge pull request #132 from 0xProject/token-registry
Add public Token Registry getters
2017-08-24 10:45:31 +02:00
Leonid Logvinov
933242bd9d Change the unregistered token in tests 2017-08-24 10:05:53 +02:00
Leonid Logvinov
5dce4c3b3b Rename _getTokenByMetadata to _createTokenFromMetadata 2017-08-24 10:04:56 +02:00
Leonid Logvinov
0ba0d2f8d1 Remove redundant else's 2017-08-24 10:02:45 +02:00
Leonid Logvinov
846eec6269 Document changes in a CHANGELOG 2017-08-24 10:00:29 +02:00
Leonid Logvinov
a41ea299e5 Add tests for tokenRegistry public getters 2017-08-24 09:59:10 +02:00
Leonid Logvinov
e4f5b9cdb3 Add all public tokenRegistry functions 2017-08-24 09:59:10 +02:00
Leonid Logvinov
0d7b75801a Add test for getTokenAddressesAsync 2017-08-24 09:59:10 +02:00
Leonid Logvinov
82ef8b19f0 Add zeroEx.tokenRegistry.getTokenAddressesAsync() 2017-08-24 09:59:10 +02:00
Leonid
e216f2ff6d Merge pull request #128 from 0xProject/verifyOrder
Add implementation for public order validation functions
2017-08-24 09:56:34 +02:00
Leonid Logvinov
8d6045c1d5 Add validation for the case where the order is fully filled or canceled and a test 2017-08-24 09:55:02 +02:00
Leonid Logvinov
dc3756bc99 Simplify the validation check for ExchangeContractErrs.OrderAlreadyCancelledOrFilled 2017-08-24 09:55:02 +02:00
Leonid Logvinov
32b2141794 Remove unused var 2017-08-24 09:55:02 +02:00
Leonid Logvinov
1e7a87e2a3 Add newline 2017-08-24 09:55:02 +02:00
Leonid Logvinov
c6e35fbc9d Fix comments 2017-08-24 09:55:02 +02:00
Leonid Logvinov
c7ce966516 Update CHANGELOG 2017-08-24 09:55:02 +02:00
Leonid Logvinov
dd9561797f Fix order validation tests 2017-08-24 09:55:02 +02:00
Leonid Logvinov
5dc32c32a6 Add a comment for isRoundingErrorAsync 2017-08-24 09:55:02 +02:00
Leonid Logvinov
202d619435 Fix isRoundingError name in CHANGELOG 2017-08-24 09:55:01 +02:00
Leonid Logvinov
37676d338e Remove And's from names 2017-08-24 09:55:01 +02:00
Leonid Logvinov
ae1cd90b9a Separate order validation errors tests from exchange tests 2017-08-24 09:55:01 +02:00
Leonid Logvinov
8695f64322 Add the changes to CHANGELOG 2017-08-24 09:55:01 +02:00
Leonid Logvinov
f58c203653 Move order validation functions to orderValidationUtils and make isRoundingError public 2017-08-24 09:55:01 +02:00
Leonid Logvinov
e0a92419e4 Add initial implementation for public order validation functions 2017-08-24 09:55:01 +02:00
Fabio Berger
18f2a93950 Merge pull request #131 from 0xProject/addTokenRegistryMethod
Add public method to TokenRegistry
2017-08-23 18:22:50 +02:00
Fabio Berger
98be786764 rename method for clarity since we return a Token and not tokenMetadata 2017-08-23 18:14:19 +02:00
Fabio Berger
c12e48d28a Add assertion to public method 2017-08-23 18:13:50 +02:00
Fabio Berger
2a7da4fc4f use non-checksummed address 2017-08-23 18:13:26 +02:00
Fabio Berger
bd7102efbe Merge pull request #130 from 0xProject/addProxyAddressMethod
Add public TokenTransferProxy address getter
2017-08-23 17:37:31 +02:00
Fabio Berger
f7010dfa3a Return variable instead of expression 2017-08-23 17:32:10 +02:00
Fabio Berger
8ba3d608c3 Add PR numbers to changelog items 2017-08-23 17:30:53 +02:00
Fabio Berger
fedded3ec1 Add public method getTokenMetadataIfExistsAsync to TokenRegistry wrapper, refactor getTokensAsync to use getTokenMetadataIfExistsAsync under the hood and added unit tests 2017-08-23 17:27:54 +02:00
Fabio Berger
e4393fdd09 Add note about the added public method in CHANGELOG 2017-08-23 17:02:48 +02:00
Fabio Berger
e3f7b18deb Add getContractAddressAsync public method to proxy instance 2017-08-23 17:01:45 +02:00
Fabio Berger
9dca3b76b5 rename proxy_wrapper_test to token_transfer_proxy_wrapper_test 2017-08-23 16:58:56 +02:00
Leonid
d837e27739 Merge pull request #127 from 0xProject/shouldThrowOnInsufficientBalanceOrAllowance
Fix the docs for shouldThrowOnInsufficientBalanceOrAllowance
2017-08-23 14:42:45 +02:00
Leonid Logvinov
894ade168d Rename shouldCheckTransfer to shouldThrowOnInsufficientBalanceOrAllowance in tests 2017-08-22 16:15:10 +02:00
Leonid Logvinov
df274591f7 Update CHANGELOG 2017-08-22 15:20:10 +02:00
Leonid Logvinov
b809839ed4 Fix the docs for shouldThrowOnInsufficientBalanceOrAllowance 2017-08-22 15:17:36 +02:00
Leonid
51f2c46ed0 Merge pull request #126 from 0xProject/token-transfer-proxy-rename
Rename internally Proxy to TokenTransferProxy
2017-08-22 13:38:41 +02:00
Leonid Logvinov
b2b5abadb2 Rename internally Proxy to TokenTransferProxy 2017-08-22 13:28:05 +02:00
Leonid
0bc9083bff Merge pull request #124 from 0xProject/reject-checksummed-addresses
Reject checksummed addresses
2017-08-22 13:27:40 +02:00
Leonid Logvinov
4207400f8a Fix merge conflicts in a changelog 2017-08-22 11:11:33 +02:00
Leonid Logvinov
40f4ccd888 Include variableName in error message for checksummed address 2017-08-22 11:10:54 +02:00
Leonid Logvinov
c10a2d4fe4 Put the last test address on it's own line 2017-08-22 11:08:45 +02:00
Leonid Logvinov
45aa9ef542 Change CHANGELOG text 2017-08-22 11:02:28 +02:00
Leonid Logvinov
f77cc87271 Use lowercase address in tests 2017-08-22 11:02:13 +02:00
Leonid Logvinov
40ce3a9c06 Update CHANGELOG.md 2017-08-22 11:02:13 +02:00
Leonid Logvinov
52ac8c2251 Reject checksummed addresses 2017-08-22 11:00:44 +02:00
Leonid Logvinov
e376189bc6 0.9.3 2017-08-22 10:42:32 +02:00
Leonid
2a8c42688e Merge pull request #125 from 0xProject/0.9.3
Update CHANGELOG.md
2017-08-22 10:41:25 +02:00
Leonid Logvinov
b2fc0abdfb Update CHANGELOG.md 2017-08-22 10:40:00 +02:00
Fabio Berger
ac03cbbd48 Merge branch 'master' of github.com:0xProject/0x.js
* 'master' of github.com:0xProject/0x.js:
  Add subproviders to the list of folders required by UMD tests
2017-08-21 18:21:52 +02:00
Leonid Logvinov
87e0fe43f7 Add subproviders to the list of folders required by UMD tests 2017-08-21 18:10:18 +02:00
Fabio Berger
b2f5fc218d 0.9.2 2017-08-21 17:32:37 +02:00
Fabio Berger
984f9e1201 Merge branch 'master' of github.com:0xProject/0x.js
* 'master' of github.com:0xProject/0x.js:
  try 4000 timeout
  try longer timeout
2017-08-21 17:32:21 +02:00
Fabio Berger
be354d703a Update changelog 2017-08-21 17:31:52 +02:00
Fabio Berger
1ff24f0338 Merge pull request #123 from 0xProject/fixCi
Increase test timeout
2017-08-21 17:26:24 +02:00
Fabio Berger
798620d0ef try 4000 timeout 2017-08-21 17:19:13 +02:00
Fabio Berger
13d7037e8a try longer timeout 2017-08-21 17:14:49 +02:00
Fabio Berger
01eed19abd Update testrpc snapshot and testrpc artifacts 2017-08-21 14:44:41 +02:00
Fabio Berger
1311dfe7f5 Update artifacts and rename proxy to tokenTransferProxy 2017-08-21 07:20:06 -05:00
Fabio Berger
dd6997423f Merge pull request #118 from 0xProject/artifacts-update
Update artifacts with the latest mainnet version
2017-08-21 07:02:50 -05:00
Leonid Logvinov
69668d07c1 0.9.1 2017-08-16 23:26:44 -05:00
Leonid Logvinov
f2ff14f500 Update CHANGELOG.md before v0.9.1 2017-08-16 23:26:21 -05:00
Leonid
c394073653 Merge pull request #120 from 0xProject/addTestWithWeb3WithoutAddresses
Remove unnecessary assertions & add regression tests
2017-08-16 23:23:32 -05:00
Leonid Logvinov
b95d93a43b Add JSONRPCPayload type and remove unused import 2017-08-16 23:22:24 -05:00
Fabio Berger
b5bfcc772a set default value to hasAddresses boolean 2017-08-16 21:06:41 -07:00
Fabio Berger
20045a438a remove unnecessary assertion and add regression test 2017-08-16 15:44:39 -07:00
Fabio Berger
2e668a771a remove unneeded assertion from _isRoundingErrorAsync since one can make static calls without an available address 2017-08-16 15:44:20 -07:00
Fabio Berger
a32b94bac4 Remove isUserAddressAvailable assertion from getBalanceAsync and add regression test 2017-08-16 15:34:09 -07:00
Fabio Berger
6ec3c8728e Update yarn.lock 2017-08-16 15:18:11 -07:00
Leonid Logvinov
9099ef4ee5 Remove url from token registry 2017-08-14 19:32:41 -05:00
Leonid Logvinov
c4dfda9485 Update testrpc artifacts 2017-08-14 19:32:03 -05:00
Leonid Logvinov
9979e6796b Update contracts snapshot hash 2017-08-14 19:08:43 -05:00
Leonid Logvinov
6f93583d41 --no-edit 2017-08-14 18:55:42 -05:00
Leonid Logvinov
a7924cef04 Update Exchange artifacts and adjust ExchangeWrapper 2017-08-14 18:46:38 -05:00
Leonid Logvinov
23e3546073 Update TokenRegistry artifacts 2017-08-14 18:24:12 -05:00
Leonid Logvinov
52e88a63b9 Update EtherToken artifacts 2017-08-14 18:09:52 -05:00
Leonid Logvinov
a44375a6fc Update Token artifacts 2017-08-14 18:08:40 -05:00
Leonid Logvinov
fa65bcc115 Remove Mintable artifacts 2017-08-14 18:08:00 -05:00
Leonid
1c553cb6b7 Merge pull request #117 from 0xProject/greenkeeper/sinon-3.0.0
Update sinon to the latest version 🚀
2017-08-04 16:39:00 +02:00
greenkeeper[bot]
36d5510d07 chore(package): update sinon to version 3.0.0 2017-08-03 14:40:25 +00:00
Leonid
617ca799c5 Merge pull request #116 from 0xProject/greenkeeper/typedoc-0.8.0
Update typedoc to the latest version 🚀
2017-07-28 09:35:40 +02:00
greenkeeper[bot]
42a5de1ec1 chore(package): update typedoc to version 0.8.0 2017-07-27 17:22:34 +00:00
Leonid Logvinov
d175009d55 Add PR links to CHANGELOG.md 2017-07-26 17:53:20 +02:00
Leonid Logvinov
7f58a778d8 0.9.0 2017-07-26 17:26:16 +02:00
Leonid Logvinov
b3601fc7bc Add release date for 0.9.0 2017-07-26 17:26:07 +02:00
Leonid
ac92ebc54f Merge pull request #115 from 0xProject/pre-0.9.0-CHANGELOG
Update CHANGELOG.md
2017-07-26 17:24:53 +02:00
Leonid Logvinov
ea2317be11 Update CHANGELOG.md 2017-07-26 17:14:09 +02:00
Leonid Logvinov
a89e9461b6 Merge branch 'greenkeeper/web3-typescript-typings-0.3.0' 2017-07-26 17:05:54 +02:00
Leonid Logvinov
fffc807823 Update yarn.lock 2017-07-26 17:04:08 +02:00
Leonid
da97be645b Merge pull request #114 from 0xProject/greenkeeper/web3-typescript-typings-0.3.0
Update web3-typescript-typings to the latest version 🚀
2017-07-26 15:42:22 +02:00
greenkeeper[bot]
028a9a4880 chore(package): update web3-typescript-typings to version 0.3.0 2017-07-26 13:31:18 +00:00
Leonid
89aa19f6e4 Merge pull request #109 from 0xProject/fees-balance-allowance-validation
Fees balance allowance validation
2017-07-25 22:26:14 +02:00
Leonid Logvinov
660aa224ca Indent callbacks 2017-07-25 22:25:57 +02:00
Leonid Logvinov
c6e0acdb04 Rearrange imports 2017-07-25 22:25:57 +02:00
Leonid Logvinov
1690aae1cf Simplify order checks 2017-07-25 22:25:56 +02:00
Leonid Logvinov
4d27b89fe3 Store tokenWrapper inside of OrdervalidationUtils 2017-07-25 22:25:56 +02:00
Leonid Logvinov
31d068b83f Remove and from names 2017-07-25 22:25:56 +02:00
Leonid Logvinov
73e8f890b5 Remove unused imports 2017-07-25 22:25:56 +02:00
Leonid Logvinov
c11ba988c7 Fix tests 2017-07-25 22:25:56 +02:00
Leonid Logvinov
4dda6c0949 Factor out order validation tests 2017-07-25 22:25:56 +02:00
Leonid Logvinov
defd09459d Cover all possible branches of order validation errors with tests 2017-07-25 22:25:56 +02:00
Leonid Logvinov
58d2b799d6 Refactor OrderValidationUtils to check for the case when ZRX is one of the tokens traded 2017-07-25 22:25:56 +02:00
Leonid Logvinov
d9a8b96154 Pass tokenWrapper to validateFillOrderBalancesAndAllowancesAndThrowIfInvalidAsync 2017-07-25 22:25:56 +02:00
Leonid Logvinov
c7d89d98f3 Move _validateFillOrderBalancesAndAllowancesAndThrowIfInvalidAsync to orderValidationUtils 2017-07-25 22:25:56 +02:00
Leonid Logvinov
bdbd01f965 Add a test: should throw when maker has balance to cover fees or transfer but not both 2017-07-25 22:25:56 +02:00
Leonid
97e680aba1 Merge pull request #113 from 0xProject/ethereumjs-util-types
Add ethereumjs-utils types
2017-07-21 15:48:14 -07:00
Leonid Logvinov
64f4a276ff Add ethereumjs-utils types 2017-07-21 15:42:42 -07:00
Leonid
5d31d43cf8 Merge pull request #112 from 0xProject/bn.js-types
Add bn.js types
2017-07-21 15:39:23 -07:00
Leonid Logvinov
f7fac34e03 Add bn.js types 2017-07-21 15:34:47 -07:00
Leonid
569cff01f3 Merge pull request #108 from 0xProject/greenkeeper/web3-0.20.0
Update web3 to the latest version 🚀
2017-07-18 15:02:06 -07:00
Leonid
d741fe5f1b Merge pull request #110 from 0xProject/greenkeeper/web3-typescript-typings-0.2.1
Update web3-typescript-typings to the latest version 🚀
2017-07-18 10:22:08 -07:00
greenkeeper[bot]
106e096304 chore(package): update web3-typescript-typings to version 0.2.1 2017-07-18 16:42:09 +00:00
greenkeeper[bot]
745bdb6c3f fix(package): update web3 to version 0.20.0 2017-07-17 10:01:33 +00:00
Leonid
5fe128ccf6 Merge pull request #104 from 0xProject/ts-2.4
Update to typescript@2.4
2017-07-11 18:22:22 -07:00
Leonid Logvinov
41f0be48f1 Use actual exception values in test names 2017-07-11 18:21:12 -07:00
Leonid Logvinov
b376f03102 Add a comment explaining any 2017-07-11 18:12:35 -07:00
Leonid Logvinov
468c95a4c2 Add forgotten meger conflict resolution 2017-07-11 18:00:17 -07:00
Leonid Logvinov
6033c1a5d5 Use PascalCase names as string enum keys 2017-07-11 17:54:57 -07:00
Leonid Logvinov
38fbf028a6 Migrate to using native string enums 2017-07-11 17:46:24 -07:00
Leonid Logvinov
ef88c71b27 Update to typescript@2.4 2017-07-11 17:46:24 -07:00
Leonid
89236fff41 Merge pull request #107 from 0xProject/static-get-order-hash-hex
Static get order hash hex
2017-07-11 17:45:46 -07:00
Leonid Logvinov
6c62c92f0c Reformat CHANGELOG to use past tense 2017-07-11 17:45:35 -07:00
Leonid Logvinov
c05737e7a6 Update CHANGELOG.md 2017-07-11 17:45:35 -07:00
Leonid Logvinov
fbf89aea1c Make getOrderHashHex static 2017-07-11 17:45:35 -07:00
Leonid
98e8a6dd70 Merge pull request #106 from 0xProject/single-exchange
Single exchange
2017-07-11 17:44:24 -07:00
Leonid Logvinov
4efba2a4bc Fix the comment 2017-07-11 17:40:37 -07:00
Leonid Logvinov
d8fb58379e Update CHANGELOG.md 2017-07-11 16:15:08 -07:00
Leonid Logvinov
8052625e76 Migrate to using a single Exchange contract 2017-07-11 16:15:08 -07:00
Leonid Logvinov
2787bdc46b Remove exchange artifacts by name 2017-07-11 16:15:08 -07:00
Leonid Logvinov
ba46a2558d Flatten artifacts 2017-07-11 16:15:08 -07:00
Leonid
9bb14a1d69 Merge pull request #105 from 0xProject/chai-bignumber
Stop using custom chai-bignumber fork
2017-07-11 13:17:44 -07:00
Leonid Logvinov
aa344d268c Stop using custom chai-bignumber fork 2017-07-11 13:17:27 -07:00
Leonid
2ab2ad5902 Merge pull request #103 from 0xProject/jsonschema-types
Jsonschema types
2017-07-11 13:16:02 -07:00
Leonid
5506f7a240 Add the source for the schema 2017-07-11 13:14:23 -07:00
Leonid Logvinov
c832cc35cc Use custom Schema types 2017-07-11 10:51:22 -07:00
Leonid Logvinov
232bf22af6 Add jsonschema Schema declaration 2017-07-11 10:49:29 -07:00
Fabio Berger
2a15fe0ffe Merge pull request #101 from 0xProject/new-contracts
Migrate to using a new Exchange contract
2017-07-11 00:33:54 -07:00
Fabio Berger
56bedf724f Merge branch 'new-contracts'
* new-contracts: (29 commits)
  Fix a typo
  Fix a typo in comment
  Rename shouldCheckTransfer to shouldThrowOnInsufficientBalanceOrAllowance
  Simplify BigNumber hack
  use yarn on CI
  Upgrade to a new node version
  Make CONTRACTS_COMMIT_HASH a string
  Use never testrpc snapshot
  Migrate the rest of the artifacts
  Change arguments order at isRoundingError
  Migrate events
  Migrate constructor arguments
  Migrate fillOrKillOrder
  Migrate ZRX_TOKEN_AMOUNT
  Migrate getUnavailableTakerTokenAmount
  Migrate PROXY_CONTRACT
  Migrate batchFillOrKillOrders
  Migrate batchFillOrders
  Migrate fillOrder
  Migrate fillOrdersUpTo and remove min
  ...
2017-07-11 00:30:55 -07:00
Leonid Logvinov
054843d599 Fix a typo 2017-07-11 00:30:14 -07:00
Leonid Logvinov
e54e9dfa2c Fix a typo in comment 2017-07-11 00:30:14 -07:00
Leonid Logvinov
987d7a7d8a Rename shouldCheckTransfer to shouldThrowOnInsufficientBalanceOrAllowance 2017-07-11 00:30:14 -07:00
Leonid Logvinov
183ea1b33d Simplify BigNumber hack 2017-07-11 00:30:14 -07:00
Leonid Logvinov
994b51590f use yarn on CI 2017-07-11 00:30:14 -07:00
Leonid Logvinov
4fb2b5cf2b Upgrade to a new node version 2017-07-11 00:30:14 -07:00
Leonid Logvinov
e7bb280d67 Make CONTRACTS_COMMIT_HASH a string 2017-07-11 00:30:14 -07:00
Leonid Logvinov
3d974854bc Use never testrpc snapshot 2017-07-11 00:30:14 -07:00
Leonid Logvinov
48314941bd Migrate the rest of the artifacts 2017-07-11 00:30:14 -07:00
Leonid Logvinov
23ca4047f3 Change arguments order at isRoundingError 2017-07-11 00:30:14 -07:00
Leonid Logvinov
a8c9945a0f Migrate events 2017-07-11 00:30:14 -07:00
Leonid Logvinov
be1f7db2df Migrate constructor arguments 2017-07-11 00:30:14 -07:00
Leonid Logvinov
722c24d127 Migrate fillOrKillOrder 2017-07-11 00:30:14 -07:00
Leonid Logvinov
720b2a2506 Migrate ZRX_TOKEN_AMOUNT 2017-07-11 00:30:14 -07:00
Leonid Logvinov
79762a2e38 Migrate getUnavailableTakerTokenAmount 2017-07-11 00:30:14 -07:00
Leonid Logvinov
89167c8297 Migrate PROXY_CONTRACT 2017-07-11 00:30:14 -07:00
Leonid Logvinov
dffc047f56 Migrate batchFillOrKillOrders 2017-07-11 00:30:14 -07:00
Leonid Logvinov
7cbd408c24 Migrate batchFillOrders 2017-07-11 00:30:14 -07:00
Leonid Logvinov
b774a9f91c Migrate fillOrder 2017-07-11 00:30:14 -07:00
Leonid Logvinov
5a8297649f Migrate fillOrdersUpTo and remove min 2017-07-11 00:30:14 -07:00
Leonid Logvinov
540ac54dee Migrate batchCancelOrders 2017-07-11 00:30:14 -07:00
Leonid Logvinov
caaad46991 Migrate getPartialAmount 2017-07-11 00:30:14 -07:00
Leonid Logvinov
4ecfac6cb4 Migrate cencelOrder 2017-07-11 00:30:14 -07:00
Leonid Logvinov
00c1198b34 Revert "Migrate isRoundingError parameter changes"
This reverts commit 9a200c67210599faf5eb735893abae99f72a4d1f.
2017-07-11 00:30:14 -07:00
Leonid Logvinov
15f68f9b54 Revert "Rename ZRX to ZRX_TOKEN_CONTRACT"
This reverts commit 6f0b8a185b6fcf58b7427fb907599dbc82c10eaf.
2017-07-11 00:30:14 -07:00
Leonid Logvinov
dce13796ac Update binaries and timestamps 2017-07-11 00:30:14 -07:00
Leonid Logvinov
197e38adae Migrate isRoundingError parameter changes 2017-07-11 00:30:14 -07:00
Leonid Logvinov
c21c322056 Rename ZRX to ZRX_TOKEN_CONTRACT 2017-07-11 00:30:14 -07:00
Leonid Logvinov
15fe8afd1e Add contracts artifacts for contracts that didn't change 2017-07-11 00:30:14 -07:00
Leonid Logvinov
dc70e3522a Fix a typo 2017-07-10 19:24:09 -07:00
Leonid Logvinov
2b7b9c8f8b Fix a typo in comment 2017-07-10 18:58:05 -07:00
Leonid Logvinov
8180225d1f Rename shouldCheckTransfer to shouldThrowOnInsufficientBalanceOrAllowance 2017-07-10 18:55:01 -07:00
Leonid
b98f3fc094 Merge pull request #102 from 0xProject/greenkeeper/dirty-chai-2.0.1
Update dirty-chai to the latest version 🚀
2017-07-10 10:28:17 -07:00
greenkeeper[bot]
18ab31814f chore(package): update dirty-chai to version 2.0.1 2017-07-10 14:09:09 +00:00
Leonid Logvinov
643a7e6b3a Simplify BigNumber hack 2017-07-07 18:35:07 -07:00
Leonid Logvinov
baa7668cda use yarn on CI 2017-07-07 18:34:56 -07:00
Leonid Logvinov
48a79a8488 Upgrade to a new node version 2017-07-07 18:34:42 -07:00
Leonid Logvinov
c975094df0 Make CONTRACTS_COMMIT_HASH a string 2017-07-07 17:45:41 -07:00
Leonid Logvinov
6e598792cd Use never testrpc snapshot 2017-07-07 17:45:41 -07:00
Leonid Logvinov
b0eb6efaae Migrate the rest of the artifacts 2017-07-07 17:45:40 -07:00
Leonid Logvinov
9bfe0c2090 Change arguments order at isRoundingError 2017-07-07 17:45:40 -07:00
Leonid Logvinov
c522cd5020 Migrate events 2017-07-07 17:45:40 -07:00
Leonid Logvinov
1441d1098f Migrate constructor arguments 2017-07-07 17:45:40 -07:00
Leonid Logvinov
1f9710590a Migrate fillOrKillOrder 2017-07-07 17:45:40 -07:00
Leonid Logvinov
cf6efc6596 Migrate ZRX_TOKEN_AMOUNT 2017-07-07 17:45:40 -07:00
Leonid Logvinov
8d53c78661 Migrate getUnavailableTakerTokenAmount 2017-07-07 17:45:40 -07:00
Leonid Logvinov
d21ef4db82 Migrate PROXY_CONTRACT 2017-07-07 17:45:40 -07:00
Leonid Logvinov
1aaa0020d0 Migrate batchFillOrKillOrders 2017-07-07 17:45:40 -07:00
Leonid Logvinov
53b38105dd Migrate batchFillOrders 2017-07-07 17:45:40 -07:00
Leonid Logvinov
6f9a14929b Migrate fillOrder 2017-07-07 17:45:39 -07:00
Leonid Logvinov
f92b0e918e Migrate fillOrdersUpTo and remove min 2017-07-07 17:45:39 -07:00
Leonid Logvinov
e2696c259b Migrate batchCancelOrders 2017-07-07 17:45:39 -07:00
Leonid Logvinov
406763ff61 Migrate getPartialAmount 2017-07-07 17:45:39 -07:00
Leonid Logvinov
f3e66110be Migrate cencelOrder 2017-07-07 17:45:39 -07:00
Leonid Logvinov
bc300a3797 Revert "Migrate isRoundingError parameter changes"
This reverts commit 9a200c67210599faf5eb735893abae99f72a4d1f.
2017-07-07 17:45:39 -07:00
Leonid Logvinov
049a3be102 Revert "Rename ZRX to ZRX_TOKEN_CONTRACT"
This reverts commit 6f0b8a185b6fcf58b7427fb907599dbc82c10eaf.
2017-07-07 17:45:39 -07:00
Leonid Logvinov
b21a02424c Update binaries and timestamps 2017-07-07 17:45:39 -07:00
Leonid Logvinov
1e54dec280 Migrate isRoundingError parameter changes 2017-07-07 17:45:39 -07:00
Leonid Logvinov
1d8ca6c4f3 Rename ZRX to ZRX_TOKEN_CONTRACT 2017-07-07 17:45:38 -07:00
Leonid Logvinov
db888912d0 Add contracts artifacts for contracts that didn't change 2017-07-07 17:45:38 -07:00
Leonid Logvinov
305b98ee21 Merge branch 'master' of github.com:0xProject/0x.js 2017-07-07 17:43:52 -07:00
Leonid Logvinov
39daf6f963 Update yarn.lock to include latest webpack version 2017-07-07 17:43:44 -07:00
Fabio Berger
6b80134f48 Merge pull request #100 from 0xProject/improveSignOrder
Improve signOrderHashAsync
2017-07-07 17:43:31 -07:00
Leonid Logvinov
d5b4032b25 Add --bail option to mocha run command 2017-07-07 16:34:11 -07:00
Fabio Berger
68120ad1da Move private helper methods into signatureUtils so that they don't show up in the ZeroEx classes auto-complete list 2017-07-07 14:21:47 -07:00
Leonid
3eb21a76c1 Merge pull request #95 from 0xProject/cache_net_version
Cache `net_version` requests
2017-07-07 14:21:25 -07:00
Leonid Logvinov
73a48ddb0d Refactor net_version caching logic 2017-07-07 14:21:00 -07:00
Fabio Berger
bdfbfb829b Remove duplication of 27, 28 v values 2017-07-07 14:20:59 -07:00
Leonid
0876fc6cab Merge branch 'master' into cache_net_version 2017-07-07 14:08:34 -07:00
Leonid
6593ddf35c Merge pull request #97 from 0xProject/speedup-tests
Paralellize fill scenarios
2017-07-07 14:07:41 -07:00
Leonid
23f32b6bba Add noop comment 2017-07-07 14:07:26 -07:00
Leonid
f7515b489d Merge pull request #98 from 0xProject/typos
Typos
2017-07-07 13:58:26 -07:00
Leonid
33f713e0cc Merge pull request #99 from 0xProject/greenkeeper/web3-typescript-typings-0.0.11
Update web3-typescript-typings to the latest version 🚀
2017-07-07 13:58:14 -07:00
Fabio Berger
712a1ba36e Modify signOrderHashAsync to parse the signatureHex string as V + R + S AND R + S + V and check both for a valid signature in order to fix the issue of different nodes returning it differently 2017-07-07 13:49:02 -07:00
Fabio Berger
e9509b4ff3 Remove space after it keywork in tests 2017-07-07 11:50:23 -07:00
greenkeeper[bot]
014a458525 chore(package): update web3-typescript-typings to version 0.0.11 2017-07-07 18:10:54 +00:00
Leonid Logvinov
b46d7f9cca Update CHANGELOG.md 2017-07-07 10:56:47 -07:00
Leonid Logvinov
96596b1c2b Rename batchCancelOrderAsync to batchCancelOrdersAsync 2017-07-07 10:31:22 -07:00
Leonid Logvinov
c0a90fe84e Rename batchFillOrderAsync to batchFillOrdersAsync 2017-07-07 10:31:02 -07:00
Leonid Logvinov
198cd364db Paralellize fill scenarios 2017-07-07 09:58:56 -07:00
Leonid
03a6e1dac5 Merge pull request #96 from 0xProject/greenkeeper/webpack-3.1.0
Update webpack to the latest version 🚀
2017-07-07 09:56:41 -07:00
Leonid Logvinov
90ddfe58a9 Rename networkId to networkIdIfExists 2017-07-06 15:43:35 -07:00
Leonid Logvinov
0f3f557e0b Fix a typo in a filename 2017-07-06 15:42:00 -07:00
Leonid Logvinov
21cb428b38 Rename RPC_NETWORK_ID to TESTRPC_NETWORK_ID 2017-07-06 15:41:27 -07:00
Leonid Logvinov
de76ed478f Fix CHANGELOG.md comment 2017-07-06 15:40:32 -07:00
Leonid Logvinov
2ad6a64664 Remove only 2017-07-06 15:23:55 -07:00
Leonid Logvinov
092e0c4273 Update CHANGELOG.md 2017-07-06 15:20:39 -07:00
Leonid Logvinov
e19c222f30 Add tests for networkId caching and invalidation 2017-07-06 15:18:56 -07:00
Leonid Logvinov
8b88ad835c Cache networkId in web3Wrapper 2017-07-06 15:18:56 -07:00
greenkeeper[bot]
afc12ac497 chore(package): update webpack to version 3.1.0 2017-07-06 22:13:48 +00:00
Leonid
18050b7ea3 Merge pull request #94 from 0xProject/moveExchangeAddressGetters
Moves exchange contract address getters to zeroEx top level
2017-07-06 14:51:44 -07:00
Leonid Logvinov
9f89dbc8e4 Inline _isExchangeContractAddressProxyAuthorizedAsync 2017-07-06 10:19:22 -07:00
Leonid Logvinov
b1667cdd89 Fix comments to use web3 provider instead of web3 instance 2017-07-06 10:18:38 -07:00
Leonid Logvinov
87f2658fc9 Update CHANGELOG.md 2017-07-05 14:22:23 -07:00
Leonid Logvinov
9a9fd7d926 Move zeroEx.exchange.getAvailableContractAddressesAsync to zeroEx.getAvailableExchangeContractAddressesAsync and zeroEx.exchange.getProxyAuthorizedContractAddressesAsync to zeroEx.getProxyAuthorizedExchangeContractAddressesAsync 2017-07-05 14:18:46 -07:00
Leonid Logvinov
f2611d5b2b 0.8.0 2017-07-04 19:00:55 -07:00
Leonid Logvinov
370b82ee18 Add publish date for 0.8.0 in CHANGELOG.md 2017-07-04 19:00:12 -07:00
Leonid
74b2308488 Merge pull request #90 from 0xProject/subscribe-token
Add implementation and tests for zeroEx.token.subscribeAsync
2017-07-04 18:17:57 -07:00
Leonid Logvinov
371acc0ba1 Merge branch 'master' into subscribe-token 2017-07-04 17:55:08 -07:00
Leonid Logvinov
3302d18f6e Run umd tests only on master 2017-07-04 17:54:31 -07:00
Leonid Logvinov
d6595f171a Add order hash schema 2017-07-04 17:49:25 -07:00
Leonid Logvinov
61a6040e74 Merge branch 'master' into subscribe-token 2017-07-04 17:44:32 -07:00
Leonid
c81855e119 Merge pull request #93 from 0xProject/testrpc-snapshot
Testrpc snapshots
2017-07-04 17:43:42 -07:00
Leonid Logvinov
db53c5a41f Rename CONTRACTS_COMMIT to CONTRACTS_COMMIT_HASH 2017-07-04 17:43:30 -07:00
Leonid Logvinov
9c4f3b2cba Rearrange methods in event utils 2017-07-04 17:37:02 -07:00
Leonid Logvinov
4cb544f4f5 Add underscore in front of _getBigNumberWrappingEventCallback 2017-07-04 17:36:33 -07:00
Leonid Logvinov
6f73589d40 Use string template 2017-07-04 17:35:51 -07:00
Leonid Logvinov
ea6583d47e Make all fields of subscriptionOpts schema optional 2017-07-04 17:35:02 -07:00
Leonid Logvinov
464c16be73 move order hash schema to a separate file 2017-07-04 17:33:35 -07:00
Leonid Logvinov
4d522db474 Fix CHANGELOG entry 2017-07-04 17:21:14 -07:00
Leonid Logvinov
33494c2206 Change prepublish to prepublishOnly 2017-07-04 16:38:57 -07:00
Leonid Logvinov
ea2dbaabeb Use testrpc snapshots in tests 2017-07-04 16:30:55 -07:00
Leonid
8f8b678e7c Change gitter link badge to 0xproject/Lobby 2017-07-04 13:51:44 -07:00
Leonid
5f62aed80a Add npm version badge 2017-07-04 12:47:13 -07:00
Leonid Logvinov
82c1d7ca7c Add exchangeContractAddress to an order in schema tests 2017-07-04 12:29:14 -07:00
Leonid Logvinov
e570ad28dd Fix order schema (add exchangeContractAddress) 2017-07-04 11:59:08 -07:00
Leonid Logvinov
a45f6ff4af Fix the bug when didn't invalidate etherToken contract instance 2017-07-04 11:48:41 -07:00
Leonid Logvinov
74e991db94 Update CHANGELOG.md 2017-07-04 11:36:22 -07:00
Leonid Logvinov
33216ea53f Make this.proxy.invalidateContractInstance and this.tokenRegistry.invalidateContractInstance private 2017-07-04 11:22:12 -07:00
Leonid Logvinov
4ebf046cea Make invalidateContractInstancesAsync private on exchange and token 2017-07-04 11:12:56 -07:00
Leonid Logvinov
43bebf6d61 Add parameter validation for subscribeAsync 2017-07-04 11:06:13 -07:00
Leonid Logvinov
40d1d30b58 Add assert.doesBelongToStringEnum 2017-07-04 10:59:11 -07:00
Leonid Logvinov
196194c04a Add StringEnum type 2017-07-04 10:58:36 -07:00
Leonid Logvinov
c5dca95f89 Use .bignumber for BigNumber comparation 2017-07-04 10:31:07 -07:00
Leonid Logvinov
b1c7291d3c Use orderHashSchema to validate order hash 2017-07-04 10:27:31 -07:00
Leonid Logvinov
5611df82f9 Refactor event tests 2017-07-04 10:26:43 -07:00
Leonid Logvinov
f39a2134b9 Add tests for order hash Schema 2017-07-04 10:05:45 -07:00
Leonid Logvinov
551b6db35e Add orderHashSchema 2017-07-03 22:31:59 -07:00
Leonid Logvinov
64cf47f297 Add tests for subscriptionOptsSchema and blockParamSchema 2017-07-03 21:59:42 -07:00
Leonid Logvinov
7d7bef2b1a Handle errors from pseudo-async subscription tests 2017-07-03 18:26:31 -07:00
Leonid Logvinov
7b24809698 Register new schemas within schema validator 2017-07-03 18:24:11 -07:00
Leonid Logvinov
ca92502d34 Add index_filter_values_schema.ts and subscription_opts_schema.ts 2017-07-03 18:23:35 -07:00
Leonid Logvinov
12b71b20f0 Make IndexedFilterValues type scricter 2017-07-03 16:54:08 -07:00
Leonid Logvinov
90f99e48e1 Add CHANGELOG.md entry 2017-07-03 16:50:24 -07:00
Leonid Logvinov
4fae4b4493 Fix comments 2017-07-03 16:49:37 -07:00
Leonid
6d234c09af Merge branch 'master' into subscribe-token 2017-07-03 16:37:15 -07:00
Leonid
2892f45ab7 Merge pull request #89 from 0xProject/proxy
Add zeroEx.proxy
2017-07-03 16:24:22 -07:00
Leonid Logvinov
e6fcf9cdbf Use call instead of use in the comments 2017-07-03 16:24:10 -07:00
Leonid
997964f3e2 Merge pull request #87 from 0xProject/wrap-log-bignumber
Wrap all event args in a newer version of BigNumber and test it
2017-07-03 16:13:16 -07:00
Leonid Logvinov
4633637efe Fix the comment 2017-07-03 16:11:37 -07:00
Leonid Logvinov
5a8eb77ff0 Add initial implementation and tests for zeroEx.token.subscribeAsync 2017-07-03 15:54:05 -07:00
Leonid Logvinov
99051bdfdf Bump the minor version in CHANGELOG.md 2017-07-03 15:13:35 -07:00
Leonid Logvinov
20f630d1ae Update CHANGELOG.md 2017-07-03 15:05:21 -07:00
Leonid Logvinov
1275f243a3 Add zeroEx.proxy.getAuthorizedAddressesAsync and tests 2017-07-03 15:03:33 -07:00
Leonid Logvinov
dacf19ecae Make proxy wrapper public on zeroEx instance 2017-07-03 14:48:11 -07:00
Leonid Logvinov
c9edeae6d8 Rename _wrapEventCallback to _getBigNumberWrappingEventCallback 2017-07-03 14:41:50 -07:00
Leonid Logvinov
3a4a3b9aa1 Fix merge problems 2017-07-03 14:35:13 -07:00
Leonid
86c742cb11 Merge branch 'master' into wrap-log-bignumber 2017-07-03 14:31:40 -07:00
Leonid Logvinov
92c6144b6a Check for a BigNumber instance 2017-07-03 14:26:48 -07:00
Leonid
d4cef89587 Merge pull request #82 from 0xProject/add-exchange-address-to-order-struct
Allow multiple Exchange contracts and multiple artifacts
2017-07-03 12:28:34 -07:00
Leonid Logvinov
8151523d33 Use _.isString instead of instanceof 2017-07-03 12:08:14 -07:00
Leonid
8204409c6d Merge branch 'master' into add-exchange-address-to-order-struct 2017-07-03 12:05:46 -07:00
Leonid Logvinov
4bcad26024 Stop passing exchangeContractAddress to utils.getOrderHashHex 2017-07-03 12:04:07 -07:00
Leonid Logvinov
15bd31e629 Await ZRX call 2017-07-03 12:00:43 -07:00
Leonid Logvinov
287e9c7c85 Fix the comment 2017-07-03 11:58:58 -07:00
Leonid Logvinov
f09a0fca75 Rename invalidateContractInstanceAsync to invalidateContractInstancesAsync 2017-07-03 11:57:05 -07:00
Leonid Logvinov
649d55f0a2 Fix _getProxyContractAsync function name 2017-07-03 11:43:36 -07:00
Leonid Logvinov
b7705f0871 Fix comment 2017-07-03 11:42:58 -07:00
Leonid
d506a1f985 Merge pull request #88 from 0xProject/greenkeeper/chai-as-promised-7.1.0
Update chai-as-promised to the latest version 🚀
2017-07-03 11:38:42 -07:00
Leonid
8e6fbc214b Merge pull request #86 from 0xProject/subscribe-async-event-names
Assert correct event names in subscribeAsync tests
2017-07-03 08:18:47 -07:00
Leonid
5cb1e5853e Merge pull request #85 from 0xProject/greenkeeper/ethereumjs-testrpc-4.0.1
Greenkeeper/ethereumjs testrpc 4.0.1
2017-07-03 08:18:28 -07:00
greenkeeper[bot]
86e38f45fc chore(package): update chai-as-promised to version 7.1.0 2017-07-02 18:25:13 +00:00
Leonid Logvinov
0e54418dbb Wrap all event args in a newer version of BigNumber and test it 2017-07-01 19:44:17 -07:00
Leonid Logvinov
6e0edb8d8e Fix contracts repo commit in circle.yml 2017-07-01 19:40:13 -07:00
Leonid Logvinov
c0d4b30828 Assert correct event names in subscribeAsync tests 2017-07-01 08:39:57 -07:00
Leonid Logvinov
74ef0458f5 Fix test failures caused by testrpc new eth_sign behaviour 2017-06-30 19:08:07 -07:00
Leonid Logvinov
c5a93b0945 Fix comment 2017-06-30 14:21:58 -07:00
Leonid Logvinov
24e2ee831d Fix comment 2017-06-30 14:21:28 -07:00
Leonid Logvinov
cc9f0f17af Remove constructor 2017-06-30 14:20:52 -07:00
Leonid Logvinov
8f90ce2cf7 Fix proxy comment 2017-06-30 14:20:34 -07:00
Leonid Logvinov
7cedeb0be9 Fix naming 2017-06-30 14:19:54 -07:00
Leonid Logvinov
43848f45ec Fix a typo in authorized 2017-06-30 14:15:49 -07:00
Leonid Logvinov
fc3b0ce553 Fix a bug in getProxyAuthorizedContractAddressesAsync 2017-06-30 14:14:34 -07:00
Leonid Logvinov
a85c2b61ce Move schema assertion up to prevent the _.map failure 2017-06-30 14:04:24 -07:00
Leonid Logvinov
0c07490ae5 Change 'changes' to 'latest changes' for no good reason in CHANGELOG.md 2017-06-30 14:03:29 -07:00
Leonid Logvinov
6aa37ad3e4 Add tests for getProxyAuthorizedContractAddressesAsync 2017-06-29 13:41:35 -07:00
Leonid Logvinov
1321ea5beb Remove unused code from test/proxy_wrapper_test.ts 2017-06-29 13:15:20 -07:00
Leonid Logvinov
7ca113b59b Add a test for a ProxyWrapper 2017-06-29 13:11:50 -07:00
Leonid Logvinov
fd242a1e06 Fix a typo in authorized 2017-06-29 13:11:22 -07:00
Leonid Logvinov
83eeeb85b2 Rename _proxy to _proxyWrapper 2017-06-29 13:11:06 -07:00
Leonid Logvinov
fa3963199e Make zeroEx.getOrderHashHex non-async 2017-06-29 12:54:41 -07:00
Leonid Logvinov
e1e79519b9 Fix typo in getAvailableContractAddressesAsync 2017-06-29 12:54:03 -07:00
Leonid Logvinov
b1573fc6a7 Implement getProxyAuthorizedContractAddressesAsync 2017-06-29 12:20:41 -07:00
Leonid Logvinov
cbe17aa44b Store ProxyWrapper instance in ExchangeWrapper 2017-06-29 12:17:51 -07:00
Leonid Logvinov
d009b9b525 Add ProxyWrapper instance to zeroEx 2017-06-29 12:16:50 -07:00
Leonid Logvinov
889410d605 Add Proxy wrapper 2017-06-29 12:14:49 -07:00
Leonid Logvinov
82cceb8605 Add type for a ProxyContract 2017-06-29 12:14:00 -07:00
Leonid Logvinov
83bc740911 Make it clear that we are using a fake address in tests 2017-06-29 11:25:58 -07:00
Leonid Logvinov
bc92968c5e Rename Exchange.json to Exchange_v1.json 2017-06-29 11:21:04 -07:00
Leonid Logvinov
d0830d55ad Factor out ZRXtokenAddress variable 2017-06-29 11:19:51 -07:00
Leonid Logvinov
9395b53af4 Rename ZeroExError.CONTRACT_DOES_NOT_EXIST to ZeroExError.EXCHANGE_CONTRACT_DOES_NOT_EXIST 2017-06-29 11:18:15 -07:00
Leonid Logvinov
f6ec388916 Fix a typo in a name 2017-06-29 11:08:33 -07:00
Leonid Logvinov
3f7bdfa67a Factor out exchangeArtifacts variable 2017-06-29 11:05:56 -07:00
Leonid Logvinov
260def1aef Check that artefacts by networkId are not undefined before getting the address 2017-06-29 11:02:52 -07:00
Leonid Logvinov
330a1cff11 Fix a typo in a name 2017-06-29 10:54:20 -07:00
Leonid Logvinov
91e2857645 Fix a typo in a name 2017-06-29 10:53:11 -07:00
Leonid Logvinov
5e5acdc56e Move schema assertion up to prevent the _.map failure 2017-06-29 10:51:24 -07:00
Leonid Logvinov
b896a723c2 Move schema assertion up to prevent the _.map failure 2017-06-29 10:50:22 -07:00
Leonid Logvinov
3acdeaf518 Mve schema assertion up to prevent the _.map failure 2017-06-29 10:49:44 -07:00
Leonid Logvinov
e9db532727 Make getOrderHashHex a non-async function 2017-06-29 10:34:49 -07:00
Leonid Logvinov
dd590ca79e Improve CHANGELOG comments 2017-06-29 10:30:14 -07:00
greenkeeper[bot]
6a8c54e8de chore(package): update ethereumjs-testrpc to version 4.0.1
Closes #83
2017-06-28 16:07:40 +00:00
Leonid Logvinov
70c7914c4c Update CHANGELOG.md 2017-06-27 11:36:18 -07:00
Leonid Logvinov
a4ce7ed21e Update contract artefacts 2017-06-27 11:32:06 -07:00
Leonid Logvinov
6bf594bb4b Remove update_contracts call from circleci.yml 2017-06-27 11:16:00 -07:00
Leonid Logvinov
75711f348a Remove custom Schema type and use one from jsonschema 2017-06-27 11:04:27 -07:00
Leonid Logvinov
56cfb41d54 Add CHANGELOG.md entry 2017-06-26 19:48:24 -07:00
Leonid Logvinov
3d55f97c0a Remove _getExchangeAddressAsync 2017-06-26 19:44:21 -07:00
Leonid Logvinov
de0e436aad Allow multiple exchange versions t be functional at the same time 2017-06-26 19:44:21 -07:00
Leonid Logvinov
2c5f221073 Update copyfiles to copy nested artifacts 2017-06-26 19:44:21 -07:00
Leonid Logvinov
df751a2af5 Move exchange artifacts to an exchange subfolder 2017-06-26 19:44:21 -07:00
Fabio Berger
79b79a5795 Add the 0xproject profile flag to the AWS cli command so that devs can have their personal AWS creds be the default in .aws/credentials 2017-06-26 15:31:15 -07:00
Fabio Berger
540faac79c 0.7.1 2017-06-26 15:26:51 -07:00
Fabio Berger
dfff718972 Add 0.7.1 release notes 2017-06-26 15:25:30 -07:00
Fabio Berger
40636f1609 Change all the dates from July to June in changelog 2017-06-26 15:22:34 -07:00
Fabio Berger
6c48e6c74f Additional EtherToken comment changes 2017-06-26 14:54:04 -07:00
Fabio Berger
718b801f48 Improve EtherToken comments 2017-06-26 14:14:03 -07:00
Leonid
73eb78d793 Merge pull request #81 from 0xProject/addWrappedETHSupport
Add Wrapped ETH Support
2017-06-26 13:23:02 -07:00
Fabio Berger
f15451ebfd Merge branch 'addWrappedETHSupport' of github.com:0xProject/0x.js into addWrappedETHSupport
# Conflicts:
#	test/ether_token_wrapper_test.ts
2017-06-26 11:21:09 -07:00
Fabio Berger
41098c6a35 refactor getBalanceInEthAsync to getBalanceInWeiAsync and change the test assertions to check if the discrepancy between the existing ETH balance and expected balance is small enough to simply be the gas cost used by the transaction. 2017-06-26 11:20:16 -07:00
Leonid Logvinov
e780664b40 Remove .only from describe 2017-06-26 10:34:45 -07:00
Leonid Logvinov
efb5556e4e Rename INSUFFICIENT_WETH_BALANCE_FOR_WITHDRAWL to INSUFFICIENT_WETH_BALANCE_FOR_WITHDRAWAL 2017-06-26 10:33:53 -07:00
Fabio Berger
0f413febd3 Fix typo 2017-06-26 10:30:09 -07:00
Fabio Berger
60b3f3e6dd Implement EtherTokenWrapper and tests, with deposit and withdraw methods 2017-06-25 14:50:11 -07:00
Fabio Berger
7d001240c1 Add missing return comment 2017-06-25 14:49:12 -07:00
Fabio Berger
a2c495a983 Re-wrap BigNumber returned from web3 in getBalanceInEthAsync 2017-06-25 14:48:48 -07:00
Leonid
d5ed5865ca Merge pull request #79 from 0xProject/integration-tests
Add kovan integration tests
2017-06-23 17:17:06 +02:00
Leonid Logvinov
75ae305a6e Rename KOVAN_URL to KOVAN_RPC_URL 2017-06-23 17:10:43 +02:00
Leonid
0c5ba2a68e Merge pull request #80 from 0xProject/greenkeeper/web3-typescript-typings-0.0.10
Update web3-typescript-typings to the latest version 🚀
2017-06-23 14:25:33 +02:00
greenkeeper[bot]
d21b716cb3 chore(package): update web3-typescript-typings to version 0.0.10 2017-06-23 11:26:40 +00:00
Leonid Logvinov
845aee35d3 Simplify integration tests 2017-06-23 12:12:16 +02:00
Leonid Logvinov
9a6a9fc28b Add comment before declarations 2017-06-23 12:05:48 +02:00
Leonid Logvinov
3f962585e9 Refactor KOVAN_URL to constants and fetch mnemonic from package.json 2017-06-23 12:03:40 +02:00
Fabio Berger
13dfc263b6 Fix version in CHANGELOG 2017-06-22 16:18:49 -07:00
Fabio Berger
4eb050967f 0.7.0 2017-06-22 16:18:27 -07:00
Fabio Berger
cb3127c568 Add changelog for v0.7.2 2017-06-22 16:16:50 -07:00
Leonid Logvinov
dcd08e40e6 Add kovan integration tests 2017-06-22 23:36:43 +02:00
Leonid Logvinov
438b821835 Fix incorrect merge consequences 2017-06-22 23:15:25 +02:00
Leonid
c52d6753ce Merge pull request #72 from 0xProject/fill-order-amuont
Return filledAmount from fillOrderAsync
2017-06-22 22:50:35 +02:00
Leonid
e5785532ed Merge branch 'master' into fill-order-amuont 2017-06-22 22:50:16 +02:00
Fabio Berger
49e43c9876 Merge pull request #78 from 0xProject/update-contracts
Update contracts
2017-06-22 12:04:25 -07:00
Leonid Logvinov
0cd9875369 Update contracts 2017-06-22 17:47:56 +02:00
Leonid
1375126e4c Merge pull request #77 from 0xProject/revert-71-lodash-tree-shake
Revert "Use different lodash import syntax which allows to include only used functions"
2017-06-22 16:22:20 +02:00
Leonid
94cab6f694 Revert "Use different lodash import syntax which allows to include only used functions" 2017-06-22 16:21:56 +02:00
Leonid Logvinov
bd9fa3d335 Add test for return amount from fillOrdersUpToAsync 2017-06-22 14:15:35 +02:00
Leonid Logvinov
9c2a332b69 Return filledTakerTokenAmount from fillOrdersUpToAsync 2017-06-22 14:12:17 +02:00
Leonid Logvinov
b28b0fad7a Add test for cancelOrderAsync return value 2017-06-22 13:45:43 +02:00
Leonid Logvinov
0dbad86d23 Return cancelledAmount from cancelOrderAsync 2017-06-22 13:41:07 +02:00
Leonid
25a9ba90f5 Merge pull request #73 from 0xProject/json-docs
Auto-upload json docs on release
2017-06-22 13:32:14 +02:00
Leonid Logvinov
5001e5fc70 Set content type 2017-06-22 13:29:39 +02:00
Leonid Logvinov
d088dcdd36 Add simple test checking that fillOrderAsync return filled amount 2017-06-22 00:16:35 +02:00
Leonid Logvinov
7b471858fa Fix return comment 2017-06-22 00:16:15 +02:00
Leonid Logvinov
e7152ed58c Grant read permissions to all users 2017-06-22 00:07:32 +02:00
Leonid Logvinov
c5139bca26 Auto-upload json docs on release 2017-06-21 18:07:04 +02:00
Leonid Logvinov
42c61ecda5 Return filledAmount from fillOrderAsync 2017-06-21 17:55:21 +02:00
Leonid Logvinov
656d74518d Rename Log*Args to Log*ContractEventArgs 2017-06-21 16:38:20 +02:00
Leonid Logvinov
8d2b67bce2 0.6.2 2017-06-21 15:35:02 +02:00
Leonid Logvinov
de29c23ad7 Revert "Temporarily use forked version of truffle-contract to reduce the bundle"
This reverts commit ef96c58b7f.
truffle-contract is not compatible with web3 0.19.0 cause it checks the
number of arguments and they pass incorrect number of arguments
2017-06-21 15:15:42 +02:00
Leonid Logvinov
41e62d078f Updat CHANGELOG.md with v0.6.2 2017-06-21 15:15:42 +02:00
Leonid
d49057dbfb Merge pull request #70 from 0xProject/greenkeeper/@types/node-8.0.1
Update @types/node to the latest version 🚀
2017-06-21 15:08:44 +02:00
Leonid
8af7d2303e Merge pull request #71 from 0xProject/lodash-tree-shake
Use different lodash import syntax which allows to include only used functions
2017-06-21 15:04:13 +02:00
Leonid Logvinov
ef96c58b7f Temporarily use forked version of truffle-contract to reduce the bundle
truffle-contract uses web3 0.18.0. We use web3 0.19.0 which results in 2
versions of web3 being bundled with the package
This commit temporarily switches to custom truffle-contract fork
We'll switch back when the PR would be accepted
2017-06-21 15:02:09 +02:00
Leonid Logvinov
a1c363a8af Fall back to import x = require() syntax 2017-06-21 14:11:06 +02:00
Leonid Logvinov
40a7be0690 Use different lodash import syntax which allows to include only used functions 2017-06-21 13:56:14 +02:00
greenkeeper[bot]
060ee5e9d3 chore(package): update @types/node to version 8.0.1 2017-06-20 20:57:09 +00:00
Leonid Logvinov
096d3bdb26 Connect to github over https so that it works without authentication 2017-06-20 16:50:15 +02:00
Leonid
6b044a966a Add Greenkeeper badge 2017-06-20 12:24:16 +02:00
Leonid Logvinov
9fe042733d Move out of @0xproject scope 2017-06-20 09:05:41 +02:00
Fabio Berger
6aaa7a0856 Merge pull request #69 from 0xProject/improveReadme
Improve README
2017-06-19 17:51:03 -07:00
Fabio Berger
8135f98029 Add documentation section 2017-06-19 17:48:29 -07:00
Fabio Berger
6386c43e01 Include two links to the documentation 2017-06-19 17:46:11 -07:00
Fabio Berger
f86d177cd4 Add the installation instructions to the README 2017-06-19 17:44:22 -07:00
Fabio Berger
20f59d556d Add missing semi-colon 2017-06-19 17:38:34 -07:00
Fabio Berger
1ad54ba742 Add newline between type defs 2017-06-19 17:38:21 -07:00
Fabio Berger
dc7c2082df Improve comment 2017-06-19 17:38:08 -07:00
Leonid Logvinov
06dc1adb4a 0.6.1 2017-06-19 19:40:57 +02:00
Leonid Logvinov
1b01251e4c Update CHANGELOG.md 2017-06-19 19:40:43 +02:00
Fabio Berger
7f32cdf29d Fix tslint issue 2017-06-19 08:59:04 -07:00
Fabio Berger
999cf1be7c Improve the README 2017-06-19 08:57:27 -07:00
Fabio Berger
e8574a3948 Update version in package.json 2017-06-19 08:45:18 -07:00
Fabio Berger
a8f706c79f Merge branch 'master' of github.com:0xProject/0x.js 2017-06-19 08:42:25 -07:00
Leonid
36fe4c01aa Merge pull request #68 from 0xProject/changelog
Add CHANGELOG.md
2017-06-19 16:32:07 +02:00
Leonid Logvinov
ea8cff4ce0 Remove coveralls integration from CHANGELOG.md 2017-06-19 16:31:47 +02:00
Leonid Logvinov
a177760593 Add CHANGELOG.md 2017-06-19 10:32:02 +02:00
Leonid
ed1e2e5347 Merge pull request #66 from 0xProject/event-types
Add actual type for contract event arguments
2017-06-19 09:48:08 +02:00
Leonid Logvinov
26f20fa3e3 Rename LogErrorArgs to LogErrorEventArgs and EventArgs to ContractEventArgs 2017-06-19 09:47:29 +02:00
Fabio Berger
fe63a81f6a Add and improve comments 2017-06-16 16:30:35 +02:00
Leonid
f2d08ce3d9 Merge branch 'master' into event-types 2017-06-15 17:59:13 +02:00
Fabio Berger
5932ebb52a Merge pull request #67 from 0xProject/web3-provider
Make ZeroEx constructor accept Web3Provider instead of Web3 instance
2017-06-15 17:58:07 +02:00
Leonid Logvinov
38170a24ed Fix bug in 0x.js tests when passing Web3 instead of Web3 Provider 2017-06-15 17:34:54 +02:00
Leonid Logvinov
04698313a3 Switch back to using Web3.Provider, not Web3Provider in internal classes 2017-06-15 17:27:58 +02:00
Leonid Logvinov
209ca30460 Add explaining comment for the web3Provider type 2017-06-15 17:27:21 +02:00
Leonid Logvinov
d789aa24aa Make ZeroEx constructor accept Web3Provider instead of Web3 instance 2017-06-15 17:05:03 +02:00
Leonid Logvinov
76d6e6a748 Add actual type for contract event arguments 2017-06-15 16:21:27 +02:00
Leonid
424912040a Merge pull request #65 from 0xProject/coveralls
Coveralls integration
2017-06-15 15:31:53 +02:00
Leonid Logvinov
1ff8e850a5 Add badge to README 2017-06-15 15:31:21 +02:00
Leonid Logvinov
e01a19a591 Report test coverage on CI 2017-06-15 15:17:25 +02:00
Leonid Logvinov
8c585e733c Add node script to report coverage 2017-06-15 15:17:09 +02:00
Leonid Logvinov
56bfbbbecc Add coveralls as a dependency 2017-06-15 15:16:54 +02:00
Leonid Logvinov
501cec1fac Remove unused import 2017-06-15 14:48:38 +02:00
Leonid Logvinov
022d6699c2 0.5.2 2017-06-15 14:32:17 +02:00
Leonid Logvinov
9bdca7a5bd Fix bug in postinstall script 2017-06-15 14:31:49 +02:00
Leonid Logvinov
fcc3616bbd 0.5.1 2017-06-15 14:27:42 +02:00
Leonid Logvinov
b966e9a691 Add postrelease script to publish assets to github 2017-06-15 14:27:31 +02:00
Leonid Logvinov
29be4a60af Set NODE_ENV to production for production builds and don't rely on webpack minification (we use uglifyJS) 2017-06-15 13:25:15 +02:00
Leonid Logvinov
5deeca4b68 Fix lodash import bug in webpack config 2017-06-15 13:23:40 +02:00
Leonid Logvinov
6790ca136e Update web3-provider-engine cause the console.log issue was fixed and the new version was released 2017-06-15 12:07:04 +02:00
Leonid Logvinov
225ec50645 0.5.0 2017-06-14 11:41:42 +02:00
Leonid Logvinov
ba289c2843 Remove types that are not used in public interface from export and rename EventEmitter to ContractEventEmitter and IndexFilterValues to IndexedFilterValues 2017-06-14 11:27:33 +02:00
Leonid Logvinov
aea8c7f7df Merge branch 'master' of github.com:0xProject/0x.js 2017-06-14 11:18:05 +02:00
Fabio Berger
b24afe2653 Update doc:json command to exclude privates and externals 2017-06-14 11:05:23 +02:00
Leonid Logvinov
53fcdf13be 0.4.0 2017-06-13 18:08:48 +02:00
Leonid
2a56ab0846 Merge pull request #64 from 0xProject/events
Refactor events API
2017-06-13 18:08:30 +02:00
Leonid Logvinov
ac8116bbe4 Address feedback 2017-06-13 18:08:18 +02:00
Leonid Logvinov
5ebc3a4660 Rename ZeroExEvent to EventEmitter 2017-06-13 17:52:06 +02:00
Leonid Logvinov
55f4c45af1 Revert "0.4.0"
This reverts commit 870f9838d6.
2017-06-13 17:49:55 +02:00
Leonid Logvinov
96a93c4b35 Add tests for stopWatchingAsync 2017-06-13 17:39:43 +02:00
Leonid Logvinov
870f9838d6 0.4.0 2017-06-13 17:33:57 +02:00
Leonid Logvinov
048302625f Bind watch function on an event 2017-06-13 17:26:08 +02:00
Leonid Logvinov
6ad1900ada Refactor subscription to return zeroExSignature object 2017-06-13 15:52:50 +02:00
Leonid Logvinov
a045eb7bbb 0.3.0 2017-06-13 13:29:36 +02:00
Leonid Logvinov
52dc6b7710 Add getContractAddressAsync and tests 2017-06-13 13:29:02 +02:00
Leonid Logvinov
72318314c6 0.2.3 2017-06-12 19:08:15 +02:00
Leonid
2cac659f8b Merge pull request #63 from 0xProject/api-refactor
Api refactor
2017-06-12 19:07:40 +02:00
Leonid Logvinov
6cdd90e18b Fix test 2017-06-12 19:03:16 +02:00
Leonid Logvinov
7cf31db20e Fix test 2017-06-12 18:57:54 +02:00
Leonid Logvinov
2248514584 Change UMD bundle name 2017-06-12 18:49:57 +02:00
Leonid Logvinov
a4b431349d Export DoneCallback 2017-06-12 18:27:35 +02:00
Leonid Logvinov
365548e126 Introduce index.ts file which defines public interface 2017-06-12 18:20:43 +02:00
62 changed files with 5981 additions and 2746 deletions

101
CHANGELOG.md Normal file
View File

@@ -0,0 +1,101 @@
# CHANGELOG
v0.12.1 - _September 2, 2017_
* Added the support for web3@1.x.x provider (#142)
* Added the optional `zeroExConfig` parameter to the constructor of `ZeroEx` (#139)
* Added the ability to specify `gasPrice` when instantiating `ZeroEx` (#139)
v0.11.0 - _August 24, 2017_
------------------------
* 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)
v0.10.1 - _Aug 24, 2017_
------------------------
* Added `zeroEx.exchange.validateFillOrderThrowIfInvalidAsync` (#128)
* Added `zeroEx.exchange.validateFillOrKillOrderThrowIfInvalidAsync` (#128)
* Added `zeroEx.exchange.validateCancelOrderThrowIfInvalidAsync` (#128)
* Added `zeroEx.exchange.isRoundingErrorAsync` (#128)
* Added `zeroEx.proxy.getContractAddressAsync` (#130)
* Added `zeroEx.tokenRegistry.getTokenAddressesAsync` (#132)
* Added `zeroEx.tokenRegistry.getTokenAddressBySymbolIfExistsAsync` (#132)
* Added `zeroEx.tokenRegistry.getTokenAddressByNameIfExistsAsync` (#132)
* Added `zeroEx.tokenRegistry.getTokenBySymbolIfExistsAsync` (#132)
* Added `zeroEx.tokenRegistry.getTokenByNameIfExistsAsync` (#132)
* Added clear error message when checksummed address is passed to a public method (#124)
* Fixes the description of `shouldThrowOnInsufficientBalanceOrAllowance` in docs (#127)
v0.9.3 - _Aug 22, 2017_
------------------------
* Update contract artifacts to include latest Kovan and Mainnet deploys (#118)
v0.9.2 - _Aug 21, 2017_
------------------------
* *This version was unpublished because of a publishing issue.*
* Update contract artifacts to include latest Kovan and Mainnet deploys (#118)
v0.9.1 - _Aug. 16, 2017_
------------------------
* Fixed the bug causing `zeroEx.token.getBalanceAsync()` to fail if no addresses available (#120)
v0.9.0 - _Jul. 26, 2017_
------------------------
* Migrated to the new version of smart contracts (#101)
* Removed the ability to call methods on multiple authorized Exchange smart contracts (#106)
* Made `zeroEx.getOrderHashHex` a static method (#107)
* Cached `net_version` requests and invalidate the cache on calls to `setProvider` (#95)
* Renamed `zeroEx.exchange.batchCancelOrderAsync` to `zeroEx.exchange.batchCancelOrdersAsync`
* Renamed `zeroEx.exchange.batchFillOrderAsync` to `zeroEx.exchange.batchFillOrdersAsync`
* Updated to typescript v2.4 (#104)
* Fixed an issue with incorrect balance/allowance validation when ZRX is one of the tokens traded (#109)
v0.8.0 - _Jul. 4, 2017_
------------------------
* Added the ability to call methods on different authorized versions of the Exchange smart contract (#82)
* Updated contract artifacts to reflect latest changes to the smart contracts (0xproject/contracts#59)
* Added `zeroEx.proxy.isAuthorizedAsync` and `zeroEx.proxy.getAuthorizedAddressesAsync` (#89)
* Added `zeroEx.token.subscribeAsync` (#90)
* Made contract invalidation functions private (#90)
* `zeroEx.token.invalidateContractInstancesAsync`
* `zeroEx.exchange.invalidateContractInstancesAsync`
* `zeroEx.proxy.invalidateContractInstance`
* `zeroEx.tokenRegistry.invalidateContractInstance`
* Fixed the bug where `zeroEx.setProviderAsync` didn't invalidate etherToken contract's instance
v0.7.1 - _Jun. 26, 2017_
------------------------
* Added the ability to convert Ether to wrapped Ether tokens and back via `zeroEx.etherToken.depostAsync` and `zeroEx.etherToken.withdrawAsync` (#81)
v0.7.0 - _Jun. 22, 2017_
------------------------
* Added Kovan smart contract artifacts (#78)
* Started returning fillAmount from `fillOrderAsync` and `fillUpToAsync` (#72)
* Started returning cancelledAmount from `cancelOrderAsync` (#72)
* Renamed type `LogCancelArgs` to `LogCancelContractEventArgs` and `LogFillArgs` to `LogFillContractEventArgs`
v0.6.2 - _Jun. 21, 2017_
------------------------
* Reduced bundle size
* Improved documentation
v0.6.1 - _Jun. 19, 2017_
------------------------
* Improved documentation
v0.6.0 - _Jun. 19, 2017_
------------------------
* Made `ZeroEx` class accept `Web3Provider` instance instead of `Web3` instance
* Added types for contract event arguments
v0.5.2 - _Jun. 15, 2017_
------------------------
* Fixed the bug in `postpublish` script that caused that only unminified UMD bundle was uploaded to release page
v0.5.1 - _Jun. 15, 2017_
------------------------
* Added `postpublish` script to publish to Github Releases with assets.

65
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,65 @@
# 0x.js CONTRIBUTING.md
Thank you for your interest in contributing to 0x.js! We welcome contributions from anyone on the internet, and are grateful for even the smallest of fixes!
## Developer's guide
## How to contribute
If you'd like to contribute to 0x.js, please fork the repo, fix, commit and send a pull request against the `development` branch for the maintainers to review and merge into the main code base. If you wish to submit more complex changes though, please check up with a core dev first on [our gitter channel](https://gitter.im/0xProject/Lobby) or in the `#dev` channel on our [slack](https://slack.0xproject.com/) to ensure those changes are in line with the general philosophy of the project and/or to get some early feedback which can make both your efforts easier as well as our review and merge procedures quick and simple.
We encourage a “PR early” approach so create the PR as early as possible even without the fix/feature ready, so that devs and other volunteers know you have picked up the issue. These early PRs should indicate an 'in progress' status by adding the '[WIP]' prefix to the PR title. Please make sure your contributions adhere to our coding guidelines:
* Pull requests adding features or refactoring should be opened against the `development` branch
* Pull requests fixing bugs in the latest release version should be opened again the `master` branch
* Write [good commit messages](https://chris.beams.io/posts/git-commit/)
## Code quality
Because 0x.js is used by multiple relayers in production and their businesses depend on it, we strive for excellent code quality. Please follow the existing code standards and conventions. `tslint` (described below) will help you.
If you're adding functionality, please also add tests and make sure they pass. We have an automatic coverage reporting tool, so we'll see it if they are missing ;)
If you're adding a new public function/member, make sure you document it with Java doc-style comments. We use typedoc to generate [awesome documentation](https://0xproject.com/docs/0xjs) from the comments within our source code.
## Running and building
First thing to do with an unknown code base is to run the tests.
We assume that you have `npm` and `yarn` installed.
To do that:
* Install dependencies: `yarn`
* Initialize the testrpc state (migrate the contracts) by doing one of the following:
* Manual contracts migration:
* Run testrpc: `yarn testrpc`
* Clone the `[contracts](https://github.com/0xProject/contracts)` repo and run `yarn migrate`
* Use one of the existing testrpc snapshots
* Check out `circle.yml` for an example
* Run tests: `yarn test`
To build run: `yarn build`
We also recommend you read through the tests.
## Styleguide
We use `[tslint](https://palantir.github.io/tslint/)` with [custom configs](https://github.com/0xProject/tslint-config-0xproject) to keep our code style consistent.
To lint your code just run: `yarn lint`
If using the Atom text editor, we recommend you install the following packages:
* [atom-typescript](https://atom.io/packages/atom-typescript)
* [linter-tslint](https://atom.io/packages/linter-tslint)
Our CI will also run it as a part of the test run when you submit your PR.
## Branch structure & versioning
We use [semantic versioning](http://semver.org/), but before we reach v1.0.0 all breaking changes as well as new features will be minor version bumps.
We have two main branches: `master` and `development`.
`master` represents the most recent released (published on npm) version.
`development` represents the development state and is a default branch to which you will submit a PR. We use this structure so that we can push hotfixes to the currently released version without needing to publish all the changes made towards the next release. If a hotfix is implemented on `master`, it is back-ported to `development`.

2
PULL_REQUEST_TEMPLATE.md Normal file
View File

@@ -0,0 +1,2 @@
This PR:
*

View File

@@ -1,3 +1,53 @@
# 0x.js
<img src="https://github.com/0xProject/branding/blob/master/0x_Black_CMYK.png" width="200px" >
---
[0x][website-url] is an open protocol that facilitates trustless, low friction exchange of Ethereum-based assets. A full description of the protocol may be found in our [whitepaper][whitepaper-url].
This repository contains a Javascript library that makes it easy to build Relayers and other DApps that use the 0x protocol.
[![CircleCI](https://circleci.com/gh/0xProject/0x.js.svg?style=svg&circle-token=61bf7cd8c9b4e11b132089dfcffdd1be277d1e0c)](https://circleci.com/gh/0xProject/0x.js)
[![npm version](https://badge.fury.io/js/0x.js.svg)](https://badge.fury.io/js/0x.js)
[![Coverage Status](https://coveralls.io/repos/github/0xProject/0x.js/badge.svg?branch=master&t=fp0cXD)](https://coveralls.io/github/0xProject/0x.js?branch=master)
[![Slack Status](http://slack.0xProject.com/badge.svg)](http://slack.0xProject.com)
[![Join the chat at https://gitter.im/0xProject/Lobby](https://badges.gitter.im/0xProject/Lobby.svg)](https://gitter.im/0xProject/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
[![Greenkeeper badge](https://badges.greenkeeper.io/0xProject/0x.js.svg?token=7c22e5c72acf39d3ead8d29c5d9bb38f9096df3e643024dcedd53ab732847be1&ts=1496426342666)](https://greenkeeper.io/)
## Installation
0x.js ships as both a [UMD](https://github.com/umdjs/umd) module and a [CommonJS](https://en.wikipedia.org/wiki/CommonJS) package.
#### CommonJS *(recommended)*:
**Install**
```bash
npm install 0x.js --save
```
**Import**
```javascript
import {ZeroEx} from '0x.js';
```
#### UMD:
**Install**
Download the UMD module from our [releases page](https://github.com/0xProject/0x.js/releases) and add it to your project.
**Import**
```html
<script type="text/javascript" src="0x.js"></script>
```
## Documentation
Extensive documentation of 0x.js can be found on [our website][docs-url].
[website-url]: https://0xproject.com/
[whitepaper-url]: https://0xproject.com/pdfs/0x_white_paper.pdf
[docs-url]: https://0xproject.com/docs/0xjs

View File

@@ -1,14 +1,23 @@
machine:
node:
version: 6.1.0
version: 6.5.0
environment:
CONTRACTS_COMMIT_HASH: '35053f9'
PATH: "${PATH}:${HOME}/${CIRCLE_PROJECT_REPONAME}/node_modules/.bin"
dependencies:
override:
- yarn
cache_directories:
- ~/.cache/yarn
test:
override:
- npm run testrpc:
- wget https://s3.amazonaws.com/testrpc-shapshots/${CONTRACTS_COMMIT_HASH}.zip
- unzip ${CONTRACTS_COMMIT_HASH}.zip -d testrpc_snapshot
- npm run testrpc -- --db testrpc_snapshot:
background: true
- git clone git@github.com:0xProject/contracts.git ../contracts
- cd ../contracts; git checkout 38c2b4c; npm install && npm run migrate
- npm run update_contracts
- npm run test:coverage
- npm run test:umd
- npm run lint
- yarn test:coverage
- yarn report_test_coverage
- if [ $CIRCLE_BRANCH = "master" ]; then yarn test:umd; fi
- yarn lint

View File

@@ -1,6 +1,6 @@
{
"name": "@0xproject/0x.js",
"version": "0.2.2",
"name": "0x.js",
"version": "0.12.1",
"description": "A javascript library for interacting with the 0x protocol",
"keywords": [
"0x.js",
@@ -9,34 +9,37 @@
"tokens",
"exchange"
],
"main": "lib/src/0x.js",
"types": "lib/src/0x.d.ts",
"main": "lib/src/index.js",
"types": "lib/src/index.d.ts",
"scripts": {
"prebuild": "npm run clean",
"build": "run-p build:*:prod",
"prepublish": "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",
"lint": "tslint src/*.ts test/*.ts",
"test": "run-s clean test:commonjs",
"test:umd": "./scripts/test_umd.sh",
"test:coverage": "nyc npm run test --all",
"report_test_coverage": "nyc report --reporter=text-lcov | coveralls",
"update_contracts": "for i in ${npm_package_config_artifacts}; do copyfiles -u 4 ../contracts/build/contracts/$i.json ../0x.js/src/artifacts; done;",
"testrpc": "testrpc -p 8545 --networkId 50 -m \"${npm_package_config_mnemonic}\"",
"docs:json": "typedoc --json docs/index.json .",
"docs:json": "typedoc --excludePrivate --excludeExternals --target ES5 --json docs/index.json .",
"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": "webpack -p",
"build:commonjs:dev": "tsc; copyfiles -u 2 ./src/artifacts/*.json ../0x.js/lib/src/artifacts;",
"test:commonjs": "run-s build:commonjs:dev run_mocha",
"pretest:umd": "run-s clean build:*:dev",
"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 run_mocha",
"pretest:umd": "run-s clean build:umd:dev build:commonjs",
"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/schemas -o -path \"./lib/src/types.*\" \\) -prune -o -type f -print | xargs rm",
"run_mocha": "mocha lib/test/**/*_test.js --timeout 3000"
"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",
"run_mocha": "mocha lib/test/**/*_test.js --timeout 4000 --bail"
},
"config": {
"artifacts": "Proxy Exchange TokenRegistry Token Mintable EtherToken",
"artifacts": "TokenTransferProxy Exchange TokenRegistry Token EtherToken",
"mnemonic": "concert load couple harbor equip island argue ramp clarify fence smart topic"
},
"repository": {
@@ -52,18 +55,19 @@
"@types/jsonschema": "^1.1.1",
"@types/lodash": "^4.14.64",
"@types/mocha": "^2.2.41",
"@types/node": "^7.0.22",
"@types/node": "^8.0.1",
"@types/sinon": "^2.2.2",
"awesome-typescript-loader": "^3.1.3",
"bignumber.js": "^4.0.2",
"chai": "^4.0.1",
"chai-as-promised": "^6.0.0",
"chai-as-promised": "^7.1.0",
"chai-as-promised-typescript-typings": "0.0.3",
"chai-bignumber": "git+ssh://git@github.com:0xProject/chai-bignumber.git",
"chai-bignumber": "^2.0.1",
"chai-typescript-typings": "^0.0.0",
"copyfiles": "^1.2.0",
"dirty-chai": "^1.2.2",
"ethereumjs-testrpc": "3.0.5",
"coveralls": "^2.13.1",
"dirty-chai": "^2.0.1",
"ethereumjs-testrpc": "4.0.1",
"json-loader": "^0.5.4",
"mocha": "^3.4.1",
"npm-run-all": "^4.0.2",
@@ -72,26 +76,31 @@
"request": "^2.81.0",
"request-promise-native": "^1.0.4",
"shx": "^0.2.2",
"sinon": "^2.3.2",
"sinon": "^3.0.0",
"source-map-support": "^0.4.15",
"truffle-hdwallet-provider": "^0.0.3",
"tslint": "^5.3.2",
"tslint-config-0xproject": "^0.0.2",
"typedoc": "^0.7.1",
"typescript": "^2.3.3",
"web3-provider-engine": "https://github.com/0xProject/provider-engine.git",
"web3-typescript-typings": "0.0.8",
"webpack": "^2.6.0"
"typedoc": "^0.8.0",
"types-bn": "^0.0.1",
"types-ethereumjs-util": "^0.0.5",
"typescript": "^2.4.1",
"web3_beta": "ethereum/web3.js#1.0",
"web3-provider-engine": "^13.0.1",
"web3-typescript-typings": "^0.3.2",
"webpack": "^3.1.0"
},
"dependencies": {
"0x-json-schemas": "^0.3.0",
"bignumber.js": "^4.0.2",
"compare-versions": "^3.0.1",
"es6-promisify": "^5.0.0",
"ethereumjs-abi": "^0.6.4",
"ethereumjs-util": "^5.1.1",
"find-versions": "^2.0.0",
"jsonschema": "^1.1.1",
"lodash": "^4.17.4",
"publish-release": "^1.3.3",
"truffle-contract": "^2.0.1",
"web3": "^0.19.0"
"web3": "^0.20.0"
}
}

172
src/0x.ts
View File

@@ -1,27 +1,30 @@
import * as _ from 'lodash';
import * as BigNumber from 'bignumber.js';
import {SchemaValidator, schemas} from '0x-json-schemas';
import {bigNumberConfigs} from './bignumber_config';
import * as ethUtil from 'ethereumjs-util';
import contract = require('truffle-contract');
import * as Web3 from 'web3';
import findVersions = require('find-versions');
import compareVersions = require('compare-versions');
import {Web3Wrapper} from './web3_wrapper';
import {constants} from './utils/constants';
import {utils} from './utils/utils';
import {signatureUtils} from './utils/signature_utils';
import {assert} from './utils/assert';
import {ExchangeWrapper} from './contract_wrappers/exchange_wrapper';
import {TokenRegistryWrapper} from './contract_wrappers/token_registry_wrapper';
import {ecSignatureSchema} from './schemas/ec_signature_schema';
import {EtherTokenWrapper} from './contract_wrappers/ether_token_wrapper';
import {TokenWrapper} from './contract_wrappers/token_wrapper';
import {ECSignature, ZeroExError, Order, SignedOrder} from './types';
import * as ExchangeArtifacts from './artifacts/Exchange.json';
import {SchemaValidator} from './utils/schema_validator';
import {orderSchema} from './schemas/order_schemas';
import {TokenTransferProxyWrapper} from './contract_wrappers/token_transfer_proxy_wrapper';
import {ECSignature, ZeroExError, Order, SignedOrder, Web3Provider, ZeroExConfig} from './types';
// Customize our BigNumber instances
bigNumberConfigs.configure();
/**
* The ZeroEx class is the single entry-point into the 0x.js library. It contains all of the library's functionality
* and all calls to the library should be made through a ZeroEx instance.
*/
export class ZeroEx {
/**
* When creating an order without a specified taker or feeRecipient you must supply the Solidity
@@ -30,21 +33,41 @@ export class ZeroEx {
*/
public static NULL_ADDRESS = constants.NULL_ADDRESS;
/**
* An instance of the ExchangeWrapper class containing methods for interacting with the 0x Exchange smart contract.
*/
public exchange: ExchangeWrapper;
/**
* An instance of the TokenRegistryWrapper class containing methods for interacting with the 0x
* TokenRegistry smart contract.
*/
public tokenRegistry: TokenRegistryWrapper;
/**
* An instance of the TokenWrapper class containing methods for interacting with any ERC20 token smart contract.
*/
public token: TokenWrapper;
/**
* An instance of the EtherTokenWrapper class containing methods for interacting with the
* wrapped ETH ERC20 token smart contract.
*/
public etherToken: EtherTokenWrapper;
/**
* An instance of the TokenTransferProxyWrapper class containing methods for interacting with the
* tokenTransferProxy smart contract.
*/
public proxy: TokenTransferProxyWrapper;
private _web3Wrapper: Web3Wrapper;
/**
* Verifies that the elliptic curve signature `signature` was generated
* by signing `data` with the private key corresponding to the `signerAddress` address.
* @param data The hex encoded data signed by the supplied signature.
* @param signature A JS object containing the elliptic curve signature parameters.
* @param signature An object containing the elliptic curve signature parameters.
* @param signerAddress The hex encoded address that signed the data, producing the supplied signature.
* @return Whether the signature is valid for the supplied signerAddress and data.
*/
public static isValidSignature(data: string, signature: ECSignature, signerAddress: string): boolean {
assert.isHexString('data', data);
assert.doesConformToSchema('signature', signature, ecSignatureSchema);
assert.doesConformToSchema('signature', signature, schemas.ecSignatureSchema);
assert.isETHAddressHex('signerAddress', signerAddress);
const dataBuff = ethUtil.toBuffer(data);
@@ -86,7 +109,8 @@ export class ZeroEx {
// Since this method can be called to check if any arbitrary string conforms to an orderHash's
// format, we only assert that we were indeed passed a string.
assert.isString('orderHash', orderHash);
const isValidOrderHash = utils.isValidOrderHash(orderHash);
const schemaValidator = new SchemaValidator();
const isValidOrderHash = schemaValidator.validate(orderHash, schemas.orderHashSchema).valid;
return isValidOrderHash;
}
/**
@@ -121,56 +145,67 @@ export class ZeroEx {
const baseUnitAmount = amount.times(unit);
return baseUnitAmount;
}
/**
* Computes the orderHash for a supplied order.
* @param order An object that conforms to the Order or SignedOrder interface definitions.
* @return The resulting orderHash from hashing the supplied order.
*/
public static getOrderHashHex(order: Order|SignedOrder): string {
assert.doesConformToSchema('order', order, schemas.orderSchema);
const orderHashHex = utils.getOrderHashHex(order);
return orderHashHex;
}
/**
* Instantiates a new ZeroEx instance that provides the public interface to the 0x.js library.
* @param web3 The Web3.js instance you would like the 0x.js library to use for interacting with
* the Ethereum network.
* @param provider The Web3.js Provider instance you would like the 0x.js library to use for interacting with
* the Ethereum network.
* @param config The configuration object. Look up the type for the description.
* @return An instance of the 0x.js ZeroEx class.
*/
constructor(web3: Web3) {
this._web3Wrapper = new Web3Wrapper(web3);
this.token = new TokenWrapper(this._web3Wrapper);
this.exchange = new ExchangeWrapper(this._web3Wrapper, this.token);
this.tokenRegistry = new TokenRegistryWrapper(this._web3Wrapper);
constructor(provider: Web3Provider, config?: ZeroExConfig) {
assert.isWeb3Provider('provider', provider);
if (_.isUndefined((provider as any).sendAsync)) {
// Web3@1.0 provider doesn't support synchronous http requests,
// so it only has an async `send` method, instead of a `send` and `sendAsync` in web3@0.x.x`
// We re-assign the send method so that Web3@1.0 providers work with 0x.js
(provider as any).sendAsync = (provider as any).send;
}
this._web3Wrapper = new Web3Wrapper(provider);
const gasPrice = _.isUndefined(config) ? undefined : config.gasPrice;
this.token = new TokenWrapper(this._web3Wrapper, gasPrice);
this.proxy = new TokenTransferProxyWrapper(this._web3Wrapper, gasPrice);
this.exchange = new ExchangeWrapper(this._web3Wrapper, this.token, gasPrice);
this.tokenRegistry = new TokenRegistryWrapper(this._web3Wrapper, gasPrice);
this.etherToken = new EtherTokenWrapper(this._web3Wrapper, this.token, gasPrice);
}
/**
* Sets a new provider for the web3 instance used by 0x.js. Updating the provider will stop all
* Sets a new web3 provider for 0x.js. Updating the provider will stop all
* subscriptions so you will need to re-subscribe to all events relevant to your app after this call.
* @param provider The Web3.Provider you would like the 0x.js library to use from now on.
* @param provider The Web3Provider you would like the 0x.js library to use from now on.
*/
public async setProviderAsync(provider: Web3.Provider) {
public async setProviderAsync(provider: Web3Provider) {
this._web3Wrapper.setProvider(provider);
await this.exchange.invalidateContractInstanceAsync();
this.tokenRegistry.invalidateContractInstance();
this.token.invalidateContractInstances();
await (this.exchange as any)._invalidateContractInstancesAsync();
(this.tokenRegistry as any)._invalidateContractInstance();
await (this.token as any)._invalidateContractInstancesAsync();
(this.proxy as any)._invalidateContractInstance();
(this.etherToken as any)._invalidateContractInstance();
}
/**
* Get addresses available throught the supplied web3 instance available for sending transactions.
* @return An array of Ethereum addresses available.
* Get user Ethereum addresses available through the supplied web3 provider available for sending transactions.
* @return An array of available user Ethereum addresses.
*/
public async getAvailableAddressesAsync(): Promise<string[]> {
const availableAddresses = await this._web3Wrapper.getAvailableAddressesAsync();
return availableAddresses;
}
/**
* Computes the orderHash for a supplied order.
* @param order A JS object that conforms to the Order or SignedOrder interface definitions.
* @return The resulting orderHash from hashing the supplied order.
*/
public async getOrderHashHexAsync(order: Order|SignedOrder): Promise<string> {
assert.doesConformToSchema('order', order, orderSchema);
const exchangeContractAddr = await this._getExchangeAddressAsync();
const orderHashHex = utils.getOrderHashHex(order, exchangeContractAddr);
return orderHashHex;
}
/**
* Signs an orderHash and returns it's elliptic curve signature.
* This method currently supports TestRPC, Geth and Parity above and below V1.6.6
* @param orderHash Hex encoded orderHash to sign.
* @param signerAddress The hex encoded Ethereum address you wish to sign it with. This address
* must be available via the Web3.Provider supplied to 0x.js.
* @return A JS object containing the Elliptic curve signature parameters generated by signing the orderHash.
* @return An object containing the Elliptic curve signature parameters generated by signing the orderHash.
*/
public async signOrderHashAsync(orderHash: string, signerAddress: string): Promise<ECSignature> {
assert.isHexString('orderHash', orderHash);
@@ -179,8 +214,9 @@ export class ZeroEx {
let msgHashHex;
const nodeVersion = await this._web3Wrapper.getNodeVersionAsync();
const isParityNode = utils.isParityNode(nodeVersion);
if (isParityNode) {
// Parity node adds the personalMessage prefix itself
const isTestRpc = utils.isTestRpc(nodeVersion);
if (isParityNode || isTestRpc) {
// Parity and TestRpc nodes add the personalMessage prefix itself
msgHashHex = orderHash;
} else {
const orderHashBuff = ethUtil.toBuffer(orderHash);
@@ -190,49 +226,27 @@ export class ZeroEx {
const signature = await this._web3Wrapper.signTransactionAsync(signerAddress, msgHashHex);
let signatureData;
const [nodeVersionNumber] = findVersions(nodeVersion);
// Parity v1.6.6 and earlier returns the signatureData as vrs instead of rsv as Geth does
// Later versions return rsv but for the time being we still want to support version < 1.6.6
// Date: May 23rd 2017
const latestParityVersionWithVRS = '1.6.6';
const isVersionBeforeParityFix = compareVersions(nodeVersionNumber, latestParityVersionWithVRS) <= 0;
if (isParityNode && isVersionBeforeParityFix) {
const signatureBuffer = ethUtil.toBuffer(signature);
let v = signatureBuffer[0];
if (v < 27) {
v += 27;
// HACK: There is no consensus on whether the signatureHex string should be formatted as
// v + r + s OR r + s + v, and different clients (even different versions of the same client)
// return the signature params in different orders. In order to support all client implementations,
// we parse the signature in both ways, and evaluate if either one is a valid signature.
const validVParamValues = [27, 28];
const ecSignatureVRS = signatureUtils.parseSignatureHexAsVRS(signature);
if (_.includes(validVParamValues, ecSignatureVRS.v)) {
const isValidVRSSignature = ZeroEx.isValidSignature(orderHash, ecSignatureVRS, signerAddress);
if (isValidVRSSignature) {
return ecSignatureVRS;
}
signatureData = {
v,
r: signatureBuffer.slice(1, 33),
s: signatureBuffer.slice(33, 65),
};
} else {
signatureData = ethUtil.fromRpcSig(signature);
}
const {v, r, s} = signatureData;
const ecSignature: ECSignature = {
v,
r: ethUtil.bufferToHex(r),
s: ethUtil.bufferToHex(s),
};
const isValidSignature = ZeroEx.isValidSignature(orderHash, ecSignature, signerAddress);
if (!isValidSignature) {
throw new Error(ZeroExError.INVALID_SIGNATURE);
const ecSignatureRSV = signatureUtils.parseSignatureHexAsRSV(signature);
if (_.includes(validVParamValues, ecSignatureRSV.v)) {
const isValidRSVSignature = ZeroEx.isValidSignature(orderHash, ecSignatureRSV, signerAddress);
if (isValidRSVSignature) {
return ecSignatureRSV;
}
}
return ecSignature;
}
private async _getExchangeAddressAsync() {
const networkIdIfExists = await this._web3Wrapper.getNetworkIdIfExistsAsync();
const exchangeNetworkConfigsIfExists = _.isUndefined(networkIdIfExists) ?
undefined :
(ExchangeArtifacts as any).networks[networkIdIfExists];
if (_.isUndefined(exchangeNetworkConfigsIfExists)) {
throw new Error(ZeroExError.CONTRACT_NOT_DEPLOYED_ON_NETWORK);
}
const exchangeAddress = exchangeNetworkConfigsIfExists.address;
return exchangeAddress;
throw new Error(ZeroExError.InvalidSignature);
}
}

View File

@@ -184,6 +184,10 @@
"payable": false,
"type": "function"
},
{
"payable": true,
"type": "fallback"
},
{
"anonymous": false,
"inputs": [
@@ -229,8 +233,110 @@
"type": "event"
}
],
"unlinked_binary": "0x606060405234610000575b61072d806100196000396000f300606060405236156100935763ffffffff60e060020a60003504166306fdde038114610098578063095ea7b31461012557806318160ddd1461015557806323b872dd146101745780632e1a7d4d146101aa578063313ce567146101bc57806370a08231146101df57806395d89b411461020a578063a9059cbb14610297578063d0e30db0146102c7578063dd62ed3e146102d1575b610000565b34610000576100a5610302565b6040805160208082528351818301528351919283929083019185019080838382156100eb575b8051825260208311156100eb57601f1990920191602091820191016100cb565b505050905090810190601f1680156101175780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3461000057610141600160a060020a0360043516602435610339565b604080519115158252519081900360200190f35b34610000576101626103a4565b60408051918252519081900360200190f35b3461000057610141600160a060020a03600435811690602435166044356103aa565b604080519115158252519081900360200190f35b34610000576101ba6004356104a7565b005b34610000576101c9610527565b6040805160ff9092168252519081900360200190f35b3461000057610162600160a060020a036004351661052c565b60408051918252519081900360200190f35b34610000576100a561054b565b6040805160208082528351818301528351919283929083019185019080838382156100eb575b8051825260208311156100eb57601f1990920191602091820191016100cb565b505050905090810190601f1680156101175780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3461000057610141600160a060020a0360043516602435610582565b604080519115158252519081900360200190f35b6101ba610634565b005b3461000057610162600160a060020a0360043581169060243516610683565b60408051918252519081900360200190f35b60408051808201909152600b81527f457468657220546f6b656e000000000000000000000000000000000000000000602082015281565b600160a060020a03338116600081815260016020908152604080832094871680845294825280832086905580518681529051929493927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a35060015b92915050565b60025481565b600160a060020a0383166000908152602081905260408120546103cd90836106b0565b600160a060020a03808616600090815260208181526040808320949094556001815283822033909316825291909152205461040890836106b0565b600160a060020a0380861660009081526001602090815260408083203385168452825280832094909455918616815290819052205461044790836106c9565b600160a060020a038085166000818152602081815260409182902094909455805186815290519193928816927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a35060015b9392505050565b600160a060020a0333166000908152602081905260409020546104ca90826106b0565b600160a060020a0333166000908152602081905260409020556002546104f090826106b0565b600255604051600160a060020a0333169082156108fc029083906000818181858888f19350505050151561052357610000565b5b50565b601281565b600160a060020a0381166000908152602081905260409020545b919050565b60408051808201909152600481527f5745544800000000000000000000000000000000000000000000000000000000602082015281565b600160a060020a0333166000908152602081905260408120546105a590836106b0565b600160a060020a0333811660009081526020819052604080822093909355908516815220546105d490836106c9565b600160a060020a03808516600081815260208181526040918290209490945580518681529051919333909316927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a35060015b92915050565b600160a060020a03331660009081526020819052604090205461065790346106c9565b600160a060020a03331660009081526020819052604090205560025461067d90346106c9565b6002555b565b600160a060020a038083166000908152600160209081526040808320938516835292905220545b92915050565b60006106be838311156106f1565b508082035b92915050565b60008282016106e68482108015906106e15750838210155b6106f1565b8091505b5092915050565b80151561052357610000565b5b505600a165627a7a72305820dd221653234069427a90e77c21585b4e7c7f669edbf89a71a57b5bccb1ab42a50029",
"unlinked_binary": "0x6060604052341561000c57fe5b5b6107598061001c6000396000f300606060405236156100935763ffffffff60e060020a60003504166306fdde0381146100a4578063095ea7b31461013457806318160ddd1461016757806323b872dd146101895780632e1a7d4d146101c2578063313ce567146101d757806370a08231146101fd57806395d89b411461022b578063a9059cbb146102bb578063d0e30db0146102ee578063dd62ed3e146102f8575b6100a25b61009f61032c565b5b565b005b34156100ac57fe5b6100b461037b565b6040805160208082528351818301528351919283929083019185019080838382156100fa575b8051825260208311156100fa57601f1990920191602091820191016100da565b505050905090810190601f1680156101265780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561013c57fe5b610153600160a060020a03600435166024356103a3565b604080519115158252519081900360200190f35b341561016f57fe5b61017761040e565b60408051918252519081900360200190f35b341561019157fe5b610153600160a060020a0360043581169060243516604435610414565b604080519115158252519081900360200190f35b34156101ca57fe5b6100a2600435610537565b005b34156101df57fe5b6101e76105b8565b6040805160ff9092168252519081900360200190f35b341561020557fe5b610177600160a060020a03600435166105bd565b60408051918252519081900360200190f35b341561023357fe5b6100b46105dc565b6040805160208082528351818301528351919283929083019185019080838382156100fa575b8051825260208311156100fa57601f1990920191602091820191016100da565b505050905090810190601f1680156101265780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34156102c357fe5b610153600160a060020a03600435166024356105fd565b604080519115158252519081900360200190f35b6100a261032c565b005b341561030057fe5b610177600160a060020a03600435811690602435166106af565b60408051918252519081900360200190f35b600160a060020a03331660009081526020819052604090205461034f90346106dc565b600160a060020a03331660009081526020819052604090205560025461037590346106dc565b6002555b565b60408051808201909152600b815260a960020a6a22ba3432b9102a37b5b2b702602082015281565b600160a060020a03338116600081815260016020908152604080832094871680845294825280832086905580518681529051929493927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a35060015b92915050565b60025481565b600160a060020a03808416600081815260016020908152604080832033909516835293815283822054928252819052918220548390108015906104575750828110155b801561047d5750600160a060020a03841660009081526020819052604090205483810110155b1561052957600160a060020a03808516600090815260208190526040808220805487019055918716815220805484900390556000198110156104e757600160a060020a03808616600090815260016020908152604080832033909416835292905220805484900390555b83600160a060020a031685600160a060020a031660008051602061070e833981519152856040518082815260200191505060405180910390a36001915061052e565b600091505b5b509392505050565b600160a060020a03331660009081526020819052604090205461055a90826106f6565b600160a060020a03331660009081526020819052604090205560025461058090826106f6565b600255604051600160a060020a0333169082156108fc029083906000818181858888f1935050505015156105b45760006000fd5b5b50565b601281565b600160a060020a0381166000908152602081905260409020545b919050565b604080518082019091526004815260e360020a630ae8aa8902602082015281565b600160a060020a0333166000908152602081905260408120548290108015906106405750600160a060020a03831660009081526020819052604090205482810110155b156106a057600160a060020a03338116600081815260208181526040808320805488900390559387168083529184902080548701905583518681529351919360008051602061070e833981519152929081900390910190a3506001610408565b506000610408565b5b92915050565b600160a060020a038083166000908152600160209081526040808320938516835292905220545b92915050565b6000828201838110156106eb57fe5b8091505b5092915050565b60008282111561070257fe5b508082035b929150505600ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa165627a7a72305820ec42c469bb8ddd5de28c55b9cc393c812397c063a57fb88926e3f6de246318b70029",
"networks": {
"1": {
"links": {},
"events": {
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef": {
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "_from",
"type": "address"
},
{
"indexed": true,
"name": "_to",
"type": "address"
},
{
"indexed": false,
"name": "_value",
"type": "uint256"
}
],
"name": "Transfer",
"type": "event"
},
"0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925": {
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "_owner",
"type": "address"
},
{
"indexed": true,
"name": "_spender",
"type": "address"
},
{
"indexed": false,
"name": "_value",
"type": "uint256"
}
],
"name": "Approval",
"type": "event"
}
},
"updated_at": 1502488087000,
"address": "0x2956356cd2a2bf3202f771f50d3d14a367b48070"
},
"42": {
"links": {},
"events": {
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef": {
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "_from",
"type": "address"
},
{
"indexed": true,
"name": "_to",
"type": "address"
},
{
"indexed": false,
"name": "_value",
"type": "uint256"
}
],
"name": "Transfer",
"type": "event"
},
"0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925": {
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "_owner",
"type": "address"
},
{
"indexed": true,
"name": "_spender",
"type": "address"
},
{
"indexed": false,
"name": "_value",
"type": "uint256"
}
],
"name": "Approval",
"type": "event"
}
},
"updated_at": 1502391794392,
"address": "0x05d090b51c40b020eab3bfcb6a2dff130df22e9c"
},
"50": {
"links": {},
"events": {
@@ -279,10 +385,10 @@
"type": "event"
}
},
"updated_at": 1496427473670,
"updated_at": 1503318938233,
"address": "0x48bacb9266a570d521063ef5dd96e61686dbe788"
}
},
"schema_version": "0.0.5",
"updated_at": 1496427473670
"updated_at": 1503318938233
}

File diff suppressed because one or more lines are too long

View File

@@ -1,189 +0,0 @@
{
"contract_name": "Mintable",
"abi": [
{
"constant": false,
"inputs": [
{
"name": "_spender",
"type": "address"
},
{
"name": "_value",
"type": "uint256"
}
],
"name": "approve",
"outputs": [
{
"name": "success",
"type": "bool"
}
],
"payable": false,
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "totalSupply",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_from",
"type": "address"
},
{
"name": "_to",
"type": "address"
},
{
"name": "_value",
"type": "uint256"
}
],
"name": "transferFrom",
"outputs": [
{
"name": "success",
"type": "bool"
}
],
"payable": false,
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "_owner",
"type": "address"
}
],
"name": "balanceOf",
"outputs": [
{
"name": "balance",
"type": "uint256"
}
],
"payable": false,
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_value",
"type": "uint256"
}
],
"name": "mint",
"outputs": [],
"payable": false,
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_to",
"type": "address"
},
{
"name": "_value",
"type": "uint256"
}
],
"name": "transfer",
"outputs": [
{
"name": "success",
"type": "bool"
}
],
"payable": false,
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "_owner",
"type": "address"
},
{
"name": "_spender",
"type": "address"
}
],
"name": "allowance",
"outputs": [
{
"name": "remaining",
"type": "uint256"
}
],
"payable": false,
"type": "function"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "_from",
"type": "address"
},
{
"indexed": true,
"name": "_to",
"type": "address"
},
{
"indexed": false,
"name": "_value",
"type": "uint256"
}
],
"name": "Transfer",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "_owner",
"type": "address"
},
{
"indexed": true,
"name": "_spender",
"type": "address"
},
{
"indexed": false,
"name": "_value",
"type": "uint256"
}
],
"name": "Approval",
"type": "event"
}
],
"unlinked_binary": "0x606060405234610000575b6104e7806100196000396000f300606060405236156100675763ffffffff60e060020a600035041663095ea7b3811461006c57806318160ddd1461009c57806323b872dd146100bb57806370a08231146100f1578063a0712d681461011c578063a9059cbb1461012e578063dd62ed3e1461015e575b610000565b3461000057610088600160a060020a036004351660243561018f565b604080519115158252519081900360200190f35b34610000576100a96101fa565b60408051918252519081900360200190f35b3461000057610088600160a060020a0360043581169060243516604435610200565b604080519115158252519081900360200190f35b34610000576100a9600160a060020a036004351661030d565b60408051918252519081900360200190f35b346100005761012c60043561032c565b005b3461000057610088600160a060020a0360043516602435610393565b604080519115158252519081900360200190f35b34610000576100a9600160a060020a0360043581169060243516610456565b60408051918252519081900360200190f35b600160a060020a03338116600081815260016020908152604080832094871680845294825280832086905580518681529051929493927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a35060015b92915050565b60025481565b600160a060020a0383166000908152602081905260408120548290108015906102505750600160a060020a0380851660009081526001602090815260408083203390941683529290522054829010155b80156102755750600160a060020a038316600090815260208190526040902054828101115b1561030157600160a060020a0380841660008181526020818152604080832080548801905588851680845281842080548990039055600183528184203390961684529482529182902080548790039055815186815291519293927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a3506001610305565b5060005b5b9392505050565b600160a060020a0381166000908152602081905260409020545b919050565b68056bc75e2d6310000081111561034257610000565b600160a060020a033316600090815260208190526040902054610366908290610483565b600160a060020a03331660009081526020819052604090205560025461038c9082610483565b6002555b50565b600160a060020a0333166000908152602081905260408120548290108015906103d55750600160a060020a038316600090815260208190526040902054828101115b1561044757600160a060020a0333811660008181526020818152604080832080548890039055938716808352918490208054870190558351868152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a35060016101f4565b5060006101f4565b5b92915050565b600160a060020a038083166000908152600160209081526040808320938516835292905220545b92915050565b60008282016104a084821080159061049b5750838210155b6104ab565b8091505b5092915050565b80151561039057610000565b5b505600a165627a7a72305820ee38e175a277bbcf215259201687857177c3da8e0da65470b4f7c1eb1c95a21a0029",
"networks": {},
"schema_version": "0.0.5",
"updated_at": 1496427420920
}

View File

@@ -1,226 +0,0 @@
{
"contract_name": "Proxy",
"abi": [
{
"constant": false,
"inputs": [
{
"name": "token",
"type": "address"
},
{
"name": "from",
"type": "address"
},
{
"name": "to",
"type": "address"
},
{
"name": "value",
"type": "uint256"
}
],
"name": "transferFrom",
"outputs": [
{
"name": "success",
"type": "bool"
}
],
"payable": false,
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "target",
"type": "address"
}
],
"name": "addAuthorizedAddress",
"outputs": [
{
"name": "success",
"type": "bool"
}
],
"payable": false,
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "",
"type": "uint256"
}
],
"name": "authorities",
"outputs": [
{
"name": "",
"type": "address"
}
],
"payable": false,
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "target",
"type": "address"
}
],
"name": "removeAuthorizedAddress",
"outputs": [
{
"name": "success",
"type": "bool"
}
],
"payable": false,
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "owner",
"outputs": [
{
"name": "",
"type": "address"
}
],
"payable": false,
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "",
"type": "address"
}
],
"name": "authorized",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"payable": false,
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "getAuthorizedAddresses",
"outputs": [
{
"name": "",
"type": "address[]"
}
],
"payable": false,
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "newOwner",
"type": "address"
}
],
"name": "transferOwnership",
"outputs": [],
"payable": false,
"type": "function"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "target",
"type": "address"
},
{
"indexed": true,
"name": "caller",
"type": "address"
}
],
"name": "LogAuthorizedAddressAdded",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "target",
"type": "address"
},
{
"indexed": true,
"name": "caller",
"type": "address"
}
],
"name": "LogAuthorizedAddressRemoved",
"type": "event"
}
],
"unlinked_binary": "0x60606040525b60008054600160a060020a03191633600160a060020a03161790555b5b610701806100316000396000f300606060405236156100725763ffffffff60e060020a60003504166315dacbea811461007757806342f1181e146100b3578063494503d4146100e0578063707129391461010c5780638da5cb5b14610139578063b918161114610162578063d39de6e91461018f578063f2fde38b146101f7575b610000565b346100005761009f600160a060020a0360043581169060243581169060443516606435610212565b604080519115158252519081900360200190f35b346100005761009f600160a060020a03600435166102d8565b604080519115158252519081900360200190f35b34610000576100f06004356103f5565b60408051600160a060020a039092168252519081900360200190f35b346100005761009f600160a060020a0360043516610425565b604080519115158252519081900360200190f35b34610000576100f06105ee565b60408051600160a060020a039092168252519081900360200190f35b346100005761009f600160a060020a03600435166105fd565b604080519115158252519081900360200190f35b346100005761019c610612565b60408051602080825283518183015283519192839290830191858101910280838382156101e4575b8051825260208311156101e457601f1990920191602091820191016101c4565b5050509050019250505060405180910390f35b3461000057610210600160a060020a036004351661067d565b005b600160a060020a03331660009081526001602052604081205460ff16151561023957610000565b604080516000602091820181905282517f23b872dd000000000000000000000000000000000000000000000000000000008152600160a060020a0388811660048301528781166024830152604482018790529351938916936323b872dd9360648084019491938390030190829087803b156100005760325a03f11561000057505060405151151590506102cb57610000565b5060015b5b949350505050565b6000805433600160a060020a039081169116146102f457610000565b600160a060020a038216600090815260016020526040902054829060ff161561031c57610000565b600160a060020a0383166000908152600160208190526040909120805460ff191682179055600280549182018082559091908281838015829011610385576000838152602090206103859181019083015b80821115610381576000815560010161036d565b5090565b5b505050916000526020600020900160005b81546101009190910a600160a060020a0381810219909216878316918202179092556040513390911692507f94bb87f4c15c4587ff559a7584006fa01ddf9299359be6b512b94527aa961aca90600090a3600191505b5b505b919050565b600281815481101561000057906000526020600020900160005b915054906101000a9004600160a060020a031681565b60008054819033600160a060020a0390811691161461044357610000565b600160a060020a038316600090815260016020526040902054839060ff16151561046c57610000565b600160a060020a0384166000908152600160205260408120805460ff1916905591505b6002548210156105a75783600160a060020a0316600283815481101561000057906000526020600020900160005b9054906101000a9004600160a060020a0316600160a060020a0316141561059b576002805460001981019081101561000057906000526020600020900160005b9054906101000a9004600160a060020a0316600283815481101561000057906000526020600020900160005b6101000a815481600160a060020a030219169083600160a060020a031602179055506001600281818054905003915081815481835581811511610591576000838152602090206105919181019083015b80821115610381576000815560010161036d565b5090565b5b505050506105a7565b5b60019091019061048f565b604051600160a060020a0333811691908616907ff5b347a1e40749dd050f5f07fbdbeb7e3efa9756903044dd29401fd1d4bb4a1c90600090a3600192505b5b505b50919050565b600054600160a060020a031681565b60016020526000908152604090205460ff1681565b60408051602081810183526000825260028054845181840281018401909552808552929392909183018282801561067257602002820191906000526020600020905b8154600160a060020a03168152600190910190602001808311610654575b505050505090505b90565b60005433600160a060020a0390811691161461069857610000565b600160a060020a038116156106d0576000805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0383161790555b5b5b505600a165627a7a72305820f2ffcbe9ff4a73a57b4b40b848879fd6fd1de0cb036195f9541c0a579d2a8b8a0029",
"networks": {
"50": {
"links": {},
"events": {
"0x94bb87f4c15c4587ff559a7584006fa01ddf9299359be6b512b94527aa961aca": {
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "target",
"type": "address"
},
{
"indexed": true,
"name": "caller",
"type": "address"
}
],
"name": "LogAuthorizedAddressAdded",
"type": "event"
},
"0xf5b347a1e40749dd050f5f07fbdbeb7e3efa9756903044dd29401fd1d4bb4a1c": {
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "target",
"type": "address"
},
{
"indexed": true,
"name": "caller",
"type": "address"
}
],
"name": "LogAuthorizedAddressRemoved",
"type": "event"
}
},
"updated_at": 1496427473664,
"address": "0x871dd7c2b4b25e1aa18728e9d5f2af4c4e431f5c"
}
},
"schema_version": "0.0.5",
"updated_at": 1496427473664
}

View File

@@ -169,8 +169,8 @@
"type": "event"
}
],
"unlinked_binary": "0x606060405234610000575b6101d1806100196000396000f3006060604052361561005c5763ffffffff60e060020a600035041663095ea7b3811461006157806318160ddd1461009157806323b872dd146100b057806370a08231146100e6578063a9059cbb14610061578063dd62ed3e14610141575b610000565b346100005761007d600160a060020a0360043516602435610172565b604080519115158252519081900360200190f35b346100005761009e61017b565b60408051918252519081900360200190f35b346100005761007d600160a060020a0360043581169060243516604435610181565b604080519115158252519081900360200190f35b346100005761009e600160a060020a036004351661018b565b60408051918252519081900360200190f35b346100005761007d600160a060020a0360043516602435610172565b604080519115158252519081900360200190f35b346100005761009e600160a060020a0360043581169060243516610172565b60408051918252519081900360200190f35b60005b92915050565b60005b90565b60005b9392505050565b60005b919050565b60005b92915050565b60005b929150505600a165627a7a7230582091fa49296e1416a21d33862878c9ba9a9f7db53c284ddea5857ea6eb7f5c1d180029",
"unlinked_binary": "0x6060604052341561000c57fe5b5b6101e08061001c6000396000f3006060604052361561005c5763ffffffff60e060020a600035041663095ea7b3811461005e57806318160ddd1461009157806323b872dd146100b357806370a08231146100ec578063a9059cbb1461005e578063dd62ed3e1461014d575bfe5b341561006657fe5b61007d600160a060020a0360043516602435610181565b604080519115158252519081900360200190f35b341561009957fe5b6100a161018a565b60408051918252519081900360200190f35b34156100bb57fe5b61007d600160a060020a0360043581169060243516604435610190565b604080519115158252519081900360200190f35b34156100f457fe5b6100a1600160a060020a036004351661019a565b60408051918252519081900360200190f35b341561006657fe5b61007d600160a060020a0360043516602435610181565b604080519115158252519081900360200190f35b341561015557fe5b6100a1600160a060020a0360043581169060243516610181565b60408051918252519081900360200190f35b60005b92915050565b60005b90565b60005b9392505050565b60005b919050565b60005b92915050565b60005b929150505600a165627a7a723058202e3f7ac17048343c0d0ea24fccb64620577374eeeed61539e543df4025d7d0db0029",
"networks": {},
"schema_version": "0.0.5",
"updated_at": 1496427420921
"updated_at": 1503317882695
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,298 @@
{
"contract_name": "TokenTransferProxy",
"abi": [
{
"constant": false,
"inputs": [
{
"name": "token",
"type": "address"
},
{
"name": "from",
"type": "address"
},
{
"name": "to",
"type": "address"
},
{
"name": "value",
"type": "uint256"
}
],
"name": "transferFrom",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"payable": false,
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "target",
"type": "address"
}
],
"name": "addAuthorizedAddress",
"outputs": [],
"payable": false,
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "",
"type": "uint256"
}
],
"name": "authorities",
"outputs": [
{
"name": "",
"type": "address"
}
],
"payable": false,
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "target",
"type": "address"
}
],
"name": "removeAuthorizedAddress",
"outputs": [],
"payable": false,
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "owner",
"outputs": [
{
"name": "",
"type": "address"
}
],
"payable": false,
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "",
"type": "address"
}
],
"name": "authorized",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"payable": false,
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "getAuthorizedAddresses",
"outputs": [
{
"name": "",
"type": "address[]"
}
],
"payable": false,
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "newOwner",
"type": "address"
}
],
"name": "transferOwnership",
"outputs": [],
"payable": false,
"type": "function"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "target",
"type": "address"
},
{
"indexed": true,
"name": "caller",
"type": "address"
}
],
"name": "LogAuthorizedAddressAdded",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "target",
"type": "address"
},
{
"indexed": true,
"name": "caller",
"type": "address"
}
],
"name": "LogAuthorizedAddressRemoved",
"type": "event"
}
],
"unlinked_binary": "0x60606040525b60008054600160a060020a03191633600160a060020a03161790555b5b6106e6806100316000396000f300606060405236156100725763ffffffff60e060020a60003504166315dacbea811461007457806342f1181e146100b3578063494503d4146100d157806370712939146101005780638da5cb5b1461011e578063b91816111461014a578063d39de6e91461017a578063f2fde38b146101e5575bfe5b341561007c57fe5b61009f600160a060020a0360043581169060243581169060443516606435610203565b604080519115158252519081900360200190f35b34156100bb57fe5b6100cf600160a060020a03600435166102ae565b005b34156100d957fe5b6100e4600435610390565b60408051600160a060020a039092168252519081900360200190f35b341561010857fe5b6100cf600160a060020a03600435166103c2565b005b341561012657fe5b6100e461055a565b60408051600160a060020a039092168252519081900360200190f35b341561015257fe5b61009f600160a060020a0360043516610569565b604080519115158252519081900360200190f35b341561018257fe5b61018a61057e565b60408051602080825283518183015283519192839290830191858101910280838382156101d2575b8051825260208311156101d257601f1990920191602091820191016101b2565b5050509050019250505060405180910390f35b34156101ed57fe5b6100cf600160a060020a03600435166105e7565b005b600160a060020a03331660009081526001602052604081205460ff16151561022b5760006000fd5b6040805160006020918201819052825160e060020a6323b872dd028152600160a060020a0388811660048301528781166024830152604482018790529351938916936323b872dd9360648084019491938390030190829087803b151561028d57fe5b6102c65a03f1151561029b57fe5b5050604051519150505b5b949350505050565b60005433600160a060020a039081169116146102ca5760006000fd5b600160a060020a038116600090815260016020526040902054819060ff16156102f35760006000fd5b600160a060020a0382166000908152600160208190526040909120805460ff191682179055600280549091810161032a8382610633565b916000526020600020900160005b81546101009190910a600160a060020a0381810219909216868316918202179092556040513390911692507f94bb87f4c15c4587ff559a7584006fa01ddf9299359be6b512b94527aa961aca90600090a35b5b505b50565b600280548290811061039e57fe5b906000526020600020900160005b915054906101000a9004600160a060020a031681565b6000805433600160a060020a039081169116146103df5760006000fd5b600160a060020a038216600090815260016020526040902054829060ff1615156104095760006000fd5b600160a060020a0383166000908152600160205260408120805460ff1916905591505b6002548210156105195782600160a060020a031660028381548110151561044f57fe5b906000526020600020900160005b9054906101000a9004600160a060020a0316600160a060020a0316141561050d5760028054600019810190811061049057fe5b906000526020600020900160005b9054906101000a9004600160a060020a03166002838154811015156104bf57fe5b906000526020600020900160005b6101000a815481600160a060020a030219169083600160a060020a0316021790555060016002818180549050039150816105079190610633565b50610519565b5b60019091019061042c565b604051600160a060020a0333811691908516907ff5b347a1e40749dd050f5f07fbdbeb7e3efa9756903044dd29401fd1d4bb4a1c90600090a35b5b505b5050565b600054600160a060020a031681565b60016020526000908152604090205460ff1681565b610586610687565b60028054806020026020016040519081016040528092919081815260200182805480156105dc57602002820191906000526020600020905b8154600160a060020a031681526001909101906020018083116105be575b505050505090505b90565b60005433600160a060020a039081169116146106035760006000fd5b600160a060020a0381161561038d5760008054600160a060020a031916600160a060020a0383161790555b5b5b50565b81548183558181151161055357600083815260209020610553918101908301610699565b5b505050565b81548183558181151161055357600083815260209020610553918101908301610699565b5b505050565b60408051602081019091526000815290565b6105e491905b808211156106b3576000815560010161069f565b5090565b905600a165627a7a72305820d2924957bb88a128789172e164d874fe5445218fc2dde2f5eb265839a1f341a20029",
"networks": {
"1": {
"links": {},
"events": {
"0x94bb87f4c15c4587ff559a7584006fa01ddf9299359be6b512b94527aa961aca": {
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "target",
"type": "address"
},
{
"indexed": true,
"name": "caller",
"type": "address"
}
],
"name": "LogAuthorizedAddressAdded",
"type": "event"
},
"0xf5b347a1e40749dd050f5f07fbdbeb7e3efa9756903044dd29401fd1d4bb4a1c": {
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "target",
"type": "address"
},
{
"indexed": true,
"name": "caller",
"type": "address"
}
],
"name": "LogAuthorizedAddressRemoved",
"type": "event"
}
},
"updated_at": 1502478966000,
"address": "0x8da0d80f5007ef1e431dd2127178d224e32c2ef4"
},
"42": {
"links": {},
"events": {
"0x94bb87f4c15c4587ff559a7584006fa01ddf9299359be6b512b94527aa961aca": {
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "target",
"type": "address"
},
{
"indexed": true,
"name": "caller",
"type": "address"
}
],
"name": "LogAuthorizedAddressAdded",
"type": "event"
},
"0xf5b347a1e40749dd050f5f07fbdbeb7e3efa9756903044dd29401fd1d4bb4a1c": {
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "target",
"type": "address"
},
{
"indexed": true,
"name": "caller",
"type": "address"
}
],
"name": "LogAuthorizedAddressRemoved",
"type": "event"
}
},
"updated_at": 1502391794384,
"address": "0x087eed4bc1ee3de49befbd66c662b434b15d49d4"
},
"50": {
"links": {},
"events": {
"0x94bb87f4c15c4587ff559a7584006fa01ddf9299359be6b512b94527aa961aca": {
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "target",
"type": "address"
},
{
"indexed": true,
"name": "caller",
"type": "address"
}
],
"name": "LogAuthorizedAddressAdded",
"type": "event"
},
"0xf5b347a1e40749dd050f5f07fbdbeb7e3efa9756903044dd29401fd1d4bb4a1c": {
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "target",
"type": "address"
},
{
"indexed": true,
"name": "caller",
"type": "address"
}
],
"name": "LogAuthorizedAddressRemoved",
"type": "event"
}
},
"updated_at": 1503318938227,
"address": "0x871dd7c2b4b25e1aa18728e9d5f2af4c4e431f5c"
}
},
"schema_version": "0.0.5",
"updated_at": 1503318938227
}

View File

@@ -6,11 +6,16 @@ import {utils} from '../utils/utils';
export class ContractWrapper {
protected _web3Wrapper: Web3Wrapper;
constructor(web3Wrapper: Web3Wrapper) {
private _gasPrice?: BigNumber.BigNumber;
constructor(web3Wrapper: Web3Wrapper, gasPrice?: BigNumber.BigNumber) {
this._web3Wrapper = web3Wrapper;
this._gasPrice = gasPrice;
}
protected async _instantiateContractIfExistsAsync(artifact: Artifact, address?: string): Promise<ContractInstance> {
const c = await contract(artifact);
c.defaults({
gasPrice: this._gasPrice,
});
const providerObj = this._web3Wrapper.getCurrentProvider();
c.setProvider(providerObj);
@@ -22,13 +27,13 @@ export class ContractWrapper {
if (!_.isUndefined(address)) {
contractAddress = address;
} else if (!_.isUndefined(artifactNetworkConfigs)) {
contractAddress = artifactNetworkConfigs.address;
contractAddress = artifactNetworkConfigs.address.toLowerCase();
}
if (!_.isUndefined(contractAddress)) {
const doesContractExist = await this._web3Wrapper.doesContractExistAtAddressAsync(contractAddress);
if (!doesContractExist) {
throw new Error(ZeroExError.CONTRACT_DOES_NOT_EXIST);
throw new Error(ZeroExError.ContractDoesNotExist);
}
}
@@ -38,10 +43,10 @@ export class ContractWrapper {
} catch (err) {
const errMsg = `${err}`;
if (_.includes(errMsg, 'not been deployed to detected network')) {
throw new Error(ZeroExError.CONTRACT_DOES_NOT_EXIST);
throw new Error(ZeroExError.ContractDoesNotExist);
} else {
utils.consoleLog(`Notice: Error encountered: ${err} ${err.stack}`);
throw new Error(ZeroExError.UNHANDLED_ERROR);
throw new Error(ZeroExError.UnhandledError);
}
}
}

View File

@@ -0,0 +1,78 @@
import * as _ from 'lodash';
import {Web3Wrapper} from '../web3_wrapper';
import {ContractWrapper} from './contract_wrapper';
import {TokenWrapper} from './token_wrapper';
import {EtherTokenContract, ZeroExError} from '../types';
import {assert} from '../utils/assert';
import * as EtherTokenArtifacts from '../artifacts/EtherToken.json';
/**
* This class includes all the functionality related to interacting with a wrapped Ether ERC20 token contract.
* The caller can convert ETH into the equivalent number of wrapped ETH ERC20 tokens and back.
*/
export class EtherTokenWrapper extends ContractWrapper {
private _etherTokenContractIfExists?: EtherTokenContract;
private _tokenWrapper: TokenWrapper;
constructor(web3Wrapper: Web3Wrapper, tokenWrapper: TokenWrapper, gasPrice?: BigNumber.BigNumber) {
super(web3Wrapper, gasPrice);
this._tokenWrapper = tokenWrapper;
}
/**
* Deposit ETH into the Wrapped ETH smart contract and issues the equivalent number of wrapped ETH tokens
* to the depositor address. These wrapped ETH tokens can be used in 0x trades and are redeemable for 1-to-1
* for ETH.
* @param amountInWei Amount of ETH in Wei the caller wishes to deposit.
* @param depositor The hex encoded user Ethereum address that would like to make the deposit.
*/
public async depositAsync(amountInWei: BigNumber.BigNumber, depositor: string): Promise<void> {
assert.isBigNumber('amountInWei', amountInWei);
await assert.isSenderAddressAsync('depositor', depositor, this._web3Wrapper);
const ethBalanceInWei = await this._web3Wrapper.getBalanceInWeiAsync(depositor);
assert.assert(ethBalanceInWei.gte(amountInWei), ZeroExError.InsufficientEthBalanceForDeposit);
const wethContract = await this._getEtherTokenContractAsync();
await wethContract.deposit({
from: depositor,
value: amountInWei,
});
}
/**
* Withdraw ETH to the withdrawer's address from the wrapped ETH smart contract in exchange for the
* equivalent number of wrapped ETH tokens.
* @param amountInWei Amount of ETH in Wei the caller wishes to withdraw.
* @param withdrawer The hex encoded user Ethereum address that would like to make the withdrawl.
*/
public async withdrawAsync(amountInWei: BigNumber.BigNumber, withdrawer: string): Promise<void> {
assert.isBigNumber('amountInWei', amountInWei);
await assert.isSenderAddressAsync('withdrawer', withdrawer, this._web3Wrapper);
const wethContractAddress = await this.getContractAddressAsync();
const WETHBalanceInBaseUnits = await this._tokenWrapper.getBalanceAsync(wethContractAddress, withdrawer);
assert.assert(WETHBalanceInBaseUnits.gte(amountInWei), ZeroExError.InsufficientWEthBalanceForWithdrawal);
const wethContract = await this._getEtherTokenContractAsync();
await wethContract.withdraw(amountInWei, {
from: withdrawer,
});
}
/**
* Retrieves the Wrapped Ether token contract address
* @return The Wrapped Ether token contract address
*/
public async getContractAddressAsync(): Promise<string> {
const wethContract = await this._getEtherTokenContractAsync();
return wethContract.address;
}
private _invalidateContractInstance(): void {
delete this._etherTokenContractIfExists;
}
private async _getEtherTokenContractAsync(): Promise<EtherTokenContract> {
if (!_.isUndefined(this._etherTokenContractIfExists)) {
return this._etherTokenContractIfExists;
}
const contractInstance = await this._instantiateContractIfExistsAsync((EtherTokenArtifacts as any));
this._etherTokenContractIfExists = contractInstance as EtherTokenContract;
return this._etherTokenContractIfExists;
}
}

View File

@@ -1,5 +1,6 @@
import * as _ from 'lodash';
import * as BigNumber from 'bignumber.js';
import {SchemaValidator, schemas} from '0x-json-schemas';
import promisify = require('es6-promisify');
import {Web3Wrapper} from '../web3_wrapper';
import {
@@ -7,6 +8,7 @@ import {
ExchangeContract,
ExchangeContractErrCodes,
ExchangeContractErrs,
ZeroExError,
OrderValues,
OrderAddresses,
Order,
@@ -14,40 +16,44 @@ import {
SignedOrder,
ContractEvent,
ExchangeEvents,
ContractEventEmitter,
SubscriptionOpts,
IndexFilterValues,
IndexedFilterValues,
CreateContractEvent,
ContractEventObj,
EventCallback,
ContractResponse,
OrderCancellationRequest,
OrderFillRequest,
LogErrorContractEventArgs,
LogFillContractEventArgs,
LogCancelContractEventArgs,
} from '../types';
import {assert} from '../utils/assert';
import {utils} from '../utils/utils';
import {eventUtils} from '../utils/event_utils';
import {OrderValidationUtils} from '../utils/order_validation_utils';
import {ContractWrapper} from './contract_wrapper';
import * as ExchangeArtifacts from '../artifacts/Exchange.json';
import {ecSignatureSchema} from '../schemas/ec_signature_schema';
import {signedOrdersSchema} from '../schemas/signed_orders_schema';
import {orderFillRequestsSchema} from '../schemas/order_fill_requests_schema';
import {orderCancellationRequestsSchema} from '../schemas/order_cancel_schema';
import {orderFillOrKillRequestsSchema} from '../schemas/order_fill_or_kill_requests_schema';
import {signedOrderSchema, orderSchema} from '../schemas/order_schemas';
import {constants} from '../utils/constants';
import {TokenWrapper} from './token_wrapper';
import {decorators} from '../utils/decorators';
import * as ExchangeArtifacts from '../artifacts/Exchange.json';
/**
* This class includes all the functionality related to calling methods and subscribing to
* events of the 0x Exchange smart contract.
*/
export class ExchangeWrapper extends ContractWrapper {
private _exchangeContractErrCodesToMsg = {
[ExchangeContractErrCodes.ERROR_FILL_EXPIRED]: ExchangeContractErrs.ORDER_FILL_EXPIRED,
[ExchangeContractErrCodes.ERROR_CANCEL_EXPIRED]: ExchangeContractErrs.ORDER_FILL_EXPIRED,
[ExchangeContractErrCodes.ERROR_FILL_NO_VALUE]: ExchangeContractErrs.ORDER_REMAINING_FILL_AMOUNT_ZERO,
[ExchangeContractErrCodes.ERROR_CANCEL_NO_VALUE]: ExchangeContractErrs.ORDER_REMAINING_FILL_AMOUNT_ZERO,
[ExchangeContractErrCodes.ERROR_FILL_TRUNCATION]: ExchangeContractErrs.ORDER_FILL_ROUNDING_ERROR,
[ExchangeContractErrCodes.ERROR_FILL_BALANCE_ALLOWANCE]: ExchangeContractErrs.FILL_BALANCE_ALLOWANCE_ERROR,
[ExchangeContractErrCodes.ERROR_FILL_EXPIRED]: ExchangeContractErrs.OrderFillExpired,
[ExchangeContractErrCodes.ERROR_CANCEL_EXPIRED]: ExchangeContractErrs.OrderFillExpired,
[ExchangeContractErrCodes.ERROR_FILL_NO_VALUE]: ExchangeContractErrs.OrderRemainingFillAmountZero,
[ExchangeContractErrCodes.ERROR_CANCEL_NO_VALUE]: ExchangeContractErrs.OrderRemainingFillAmountZero,
[ExchangeContractErrCodes.ERROR_FILL_TRUNCATION]: ExchangeContractErrs.OrderFillRoundingError,
[ExchangeContractErrCodes.ERROR_FILL_BALANCE_ALLOWANCE]: ExchangeContractErrs.FillBalanceAllowanceError,
};
private _exchangeContractIfExists?: ExchangeContract;
private _exchangeLogEventObjs: ContractEventObj[];
private _exchangeLogEventEmitters: ContractEventEmitter[];
private _orderValidationUtils: OrderValidationUtils;
private _tokenWrapper: TokenWrapper;
private static _getOrderAddressesAndValues(order: Order): [OrderAddresses, OrderValues] {
const orderAddresses: OrderAddresses = [
@@ -67,31 +73,28 @@ export class ExchangeWrapper extends ContractWrapper {
];
return [orderAddresses, orderValues];
}
constructor(web3Wrapper: Web3Wrapper, tokenWrapper: TokenWrapper) {
super(web3Wrapper);
constructor(web3Wrapper: Web3Wrapper, tokenWrapper: TokenWrapper, gasPrice?: BigNumber.BigNumber) {
super(web3Wrapper, gasPrice);
this._tokenWrapper = tokenWrapper;
this._exchangeLogEventObjs = [];
}
public async invalidateContractInstanceAsync(): Promise<void> {
await this._stopWatchingExchangeLogEventsAsync();
delete this._exchangeContractIfExists;
this._orderValidationUtils = new OrderValidationUtils(tokenWrapper, this);
this._exchangeLogEventEmitters = [];
}
/**
* Returns the unavailable takerAmount of an order. Unavailable amount is defined as the total
* amount that has been filled or cancelled. The remaining takerAmount can be calculated by
* subtracting the unavailable amount from the total order takerAmount.
* @param orderHash The hex encoded orderHash for which you would like to retrieve the
* unavailable takerAmount.
* @param orderHash The hex encoded orderHash for which you would like to retrieve the
* unavailable takerAmount.
* @return The amount of the order (in taker tokens) that has either been filled or canceled.
*/
public async getUnavailableTakerAmountAsync(orderHash: string): Promise<BigNumber.BigNumber> {
assert.isValidOrderHash('orderHash', orderHash);
assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema);
const exchangeContract = await this._getExchangeContractAsync();
let unavailableAmountInBaseUnits = await exchangeContract.getUnavailableValueT.call(orderHash);
let unavailableTakerTokenAmount = await exchangeContract.getUnavailableTakerTokenAmount.call(orderHash);
// Wrap BigNumbers returned from web3 with our own (later) version of BigNumber
unavailableAmountInBaseUnits = new BigNumber(unavailableAmountInBaseUnits);
return unavailableAmountInBaseUnits;
unavailableTakerTokenAmount = new BigNumber(unavailableTakerTokenAmount);
return unavailableTakerTokenAmount;
}
/**
* Retrieve the takerAmount of an order that has already been filled.
@@ -99,7 +102,7 @@ export class ExchangeWrapper extends ContractWrapper {
* @return The amount of the order (in taker tokens) that has already been filled.
*/
public async getFilledTakerAmountAsync(orderHash: string): Promise<BigNumber.BigNumber> {
assert.isValidOrderHash('orderHash', orderHash);
assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema);
const exchangeContract = await this._getExchangeContractAsync();
let fillAmountInBaseUnits = await exchangeContract.filled.call(orderHash);
@@ -114,7 +117,7 @@ export class ExchangeWrapper extends ContractWrapper {
* @return The amount of the order (in taker tokens) that has been cancelled.
*/
public async getCanceledTakerAmountAsync(orderHash: string): Promise<BigNumber.BigNumber> {
assert.isValidOrderHash('orderHash', orderHash);
assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema);
const exchangeContract = await this._getExchangeContractAsync();
let cancelledAmountInBaseUnits = await exchangeContract.cancelled.call(orderHash);
@@ -126,34 +129,39 @@ export class ExchangeWrapper extends ContractWrapper {
* Fills a signed order with an amount denominated in baseUnits of the taker token.
* Since the order in which transactions are included in the next block is indeterminate, race-conditions
* could arise where a users balance or allowance changes before the fillOrder executes. Because of this,
* we allow you to specify `shouldCheckTransfer`. If true, the smart contract will not throw if while
* executing, the parties do not have sufficient balances/allowances, preserving gas costs. Setting it to
* false forgoes this check and causes the smart contract to throw (using all the gas supplied) instead.
* @param signedOrder A JS object that conforms to the SignedOrder interface.
* @param takerTokenFillAmount The amount of the order (in taker tokens baseUnits) that you wish to fill.
* @param shouldCheckTransfer Whether or not you wish for the contract call to throw if upon
* execution the tokens cannot be transferred.
* @param takerAddress The user Ethereum address who would like to fill this order.
* Must be available via the supplied Web3.Provider passed to 0x.js.
* we allow you to specify `shouldThrowOnInsufficientBalanceOrAllowance`.
* If false, the smart contract will not throw if the parties
* do not have sufficient balances/allowances, preserving gas costs. Setting it to true forgoes this check
* and causes the smart contract to throw (using all the gas supplied) instead.
* @param signedOrder An object that conforms to the SignedOrder interface.
* @param fillTakerTokenAmount The amount of the order (in taker tokens baseUnits) that
* you wish to fill.
* @param shouldThrowOnInsufficientBalanceOrAllowance Whether or not you wish for the contract call to throw
* if upon execution the tokens cannot be transferred.
* @param takerAddress The user Ethereum address who would like to fill this order.
* Must be available via the supplied Web3.Provider
* passed to 0x.js.
* @return The amount of the order that was filled (in taker token baseUnits).
*/
@decorators.contractCallErrorHandler
public async fillOrderAsync(signedOrder: SignedOrder, takerTokenFillAmount: BigNumber.BigNumber,
shouldCheckTransfer: boolean, takerAddress: string): Promise<void> {
assert.doesConformToSchema('signedOrder', signedOrder, signedOrderSchema);
assert.isBigNumber('takerTokenFillAmount', takerTokenFillAmount);
assert.isBoolean('shouldCheckTransfer', shouldCheckTransfer);
public async fillOrderAsync(signedOrder: SignedOrder, fillTakerTokenAmount: BigNumber.BigNumber,
shouldThrowOnInsufficientBalanceOrAllowance: boolean,
takerAddress: string): Promise<BigNumber.BigNumber> {
assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
assert.isBigNumber('fillTakerTokenAmount', fillTakerTokenAmount);
assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance);
await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
const exchangeInstance = await this._getExchangeContractAsync();
await this._validateFillOrderAndThrowIfInvalidAsync(signedOrder, takerTokenFillAmount, takerAddress);
await this.validateFillOrderThrowIfInvalidAsync(signedOrder, fillTakerTokenAmount, takerAddress);
const [orderAddresses, orderValues] = ExchangeWrapper._getOrderAddressesAndValues(signedOrder);
const gas = await exchangeInstance.fill.estimateGas(
const gas = await exchangeInstance.fillOrder.estimateGas(
orderAddresses,
orderValues,
takerTokenFillAmount,
shouldCheckTransfer,
fillTakerTokenAmount,
shouldThrowOnInsufficientBalanceOrAllowance,
signedOrder.ecSignature.v,
signedOrder.ecSignature.r,
signedOrder.ecSignature.s,
@@ -161,11 +169,11 @@ export class ExchangeWrapper extends ContractWrapper {
from: takerAddress,
},
);
const response: ContractResponse = await exchangeInstance.fill(
const response: ContractResponse = await exchangeInstance.fillOrder(
orderAddresses,
orderValues,
takerTokenFillAmount,
shouldCheckTransfer,
fillTakerTokenAmount,
shouldThrowOnInsufficientBalanceOrAllowance,
signedOrder.ecSignature.v,
signedOrder.ecSignature.r,
signedOrder.ecSignature.s,
@@ -175,37 +183,46 @@ export class ExchangeWrapper extends ContractWrapper {
},
);
this._throwErrorLogsAsErrors(response.logs);
const logFillArgs = response.logs[0].args as LogFillContractEventArgs;
const filledTakerTokenAmount = new BigNumber(logFillArgs.filledTakerTokenAmount);
return filledTakerTokenAmount;
}
/**
* Sequentially and atomically fills signedOrders up to the specified takerTokenFillAmount.
* If the fill amount is reached - it succeeds and does not fill the rest of the orders.
* If fill amount is not reached - it fills as much of the fill amount as possible and succeeds.
* @param signedOrders The array of signedOrders that you would like to fill until
* takerTokenFillAmount is reached.
* @param takerTokenFillAmount The total amount of the takerTokens you would like to fill.
* @param shouldCheckTransfer Whether or not you wish for the contract call to throw if upon
* execution any of the tokens cannot be transferred. If set to false,
* the call will continue to fill subsequent signedOrders even when
* some cannot be filled.
* @param takerAddress The user Ethereum address who would like to fill these orders.
* Must be available via the supplied Web3.Provider passed to 0x.js.
* @param signedOrders The array of signedOrders that you would like to fill until
* takerTokenFillAmount is reached.
* @param fillTakerTokenAmount The total amount of the takerTokens you would like to fill.
* @param shouldThrowOnInsufficientBalanceOrAllowance Whether or not you wish for the contract call to throw if
* upon execution any of the tokens cannot be transferred.
* If set to false, the call will continue to fill subsequent
* signedOrders even when some cannot be filled.
* @param takerAddress The user Ethereum address who would like to fill these
* orders. Must be available via the supplied Web3.Provider
* passed to 0x.js.
* @return The amount of the orders that was filled (in taker token baseUnits).
*/
@decorators.contractCallErrorHandler
public async fillOrdersUpToAsync(signedOrders: SignedOrder[], takerTokenFillAmount: BigNumber.BigNumber,
shouldCheckTransfer: boolean, takerAddress: string): Promise<void> {
public async fillOrdersUpToAsync(signedOrders: SignedOrder[], fillTakerTokenAmount: BigNumber.BigNumber,
shouldThrowOnInsufficientBalanceOrAllowance: boolean,
takerAddress: string): Promise<BigNumber.BigNumber> {
assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema);
const takerTokenAddresses = _.map(signedOrders, signedOrder => signedOrder.takerTokenAddress);
assert.hasAtMostOneUniqueValue(takerTokenAddresses,
ExchangeContractErrs.MULTIPLE_TAKER_TOKENS_IN_FILL_UP_TO_DISALLOWED);
assert.isBigNumber('takerTokenFillAmount', takerTokenFillAmount);
assert.isBoolean('shouldCheckTransfer', shouldCheckTransfer);
assert.doesConformToSchema('signedOrders', signedOrders, signedOrdersSchema);
ExchangeContractErrs.MultipleTakerTokensInFillUpToDisallowed);
const exchangeContractAddresses = _.map(signedOrders, signedOrder => signedOrder.exchangeContractAddress);
assert.hasAtMostOneUniqueValue(exchangeContractAddresses,
ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress);
assert.isBigNumber('fillTakerTokenAmount', fillTakerTokenAmount);
assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance);
await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
for (const signedOrder of signedOrders) {
await this._validateFillOrderAndThrowIfInvalidAsync(
signedOrder, takerTokenFillAmount, takerAddress);
await this.validateFillOrderThrowIfInvalidAsync(
signedOrder, fillTakerTokenAmount, takerAddress);
}
if (_.isEmpty(signedOrders)) {
return; // no-op
return new BigNumber(0); // no-op
}
const orderAddressesValuesAndSignatureArray = _.map(signedOrders, signedOrder => {
@@ -222,11 +239,11 @@ export class ExchangeWrapper extends ContractWrapper {
);
const exchangeInstance = await this._getExchangeContractAsync();
const gas = await exchangeInstance.fillUpTo.estimateGas(
const gas = await exchangeInstance.fillOrdersUpTo.estimateGas(
orderAddressesArray,
orderValuesArray,
takerTokenFillAmount,
shouldCheckTransfer,
fillTakerTokenAmount,
shouldThrowOnInsufficientBalanceOrAllowance,
vArray,
rArray,
sArray,
@@ -234,11 +251,11 @@ export class ExchangeWrapper extends ContractWrapper {
from: takerAddress,
},
);
const response: ContractResponse = await exchangeInstance.fillUpTo(
const response: ContractResponse = await exchangeInstance.fillOrdersUpTo(
orderAddressesArray,
orderValuesArray,
takerTokenFillAmount,
shouldCheckTransfer,
fillTakerTokenAmount,
shouldThrowOnInsufficientBalanceOrAllowance,
vArray,
rArray,
sArray,
@@ -248,28 +265,45 @@ export class ExchangeWrapper extends ContractWrapper {
},
);
this._throwErrorLogsAsErrors(response.logs);
let filledTakerTokenAmount = new BigNumber(0);
_.each(response.logs, log => {
filledTakerTokenAmount = filledTakerTokenAmount.plus(
(log.args as LogFillContractEventArgs).filledTakerTokenAmount);
});
return filledTakerTokenAmount;
}
/**
* Batch version of fillOrderAsync.
* Executes multiple fills atomically in a single transaction.
* If shouldCheckTransfer is set to true, it will continue filling subsequent orders even when earlier ones fail.
* When shouldCheckTransfer is set to false, if any fill fails, the entire batch fails.
* @param orderFillRequests An array of JS objects that conform to the OrderFillRequest interface.
* @param shouldCheckTransfer Whether or not you wish for the contract call to throw if upon
* execution any of the tokens cannot be transferred. If set to false,
* the call will continue to fill subsequent signedOrders even when some
* cannot be filled.
* @param takerAddress The user Ethereum address who would like to fill these orders.
* Must be available via the supplied Web3.Provider passed to 0x.js.
* If shouldThrowOnInsufficientBalanceOrAllowance is set to true, it will continue filling subsequent orders even
* when earlier ones fail.
* When shouldThrowOnInsufficientBalanceOrAllowance is set to false, if any fill fails, the entire batch fails.
* @param orderFillRequests An array of objects that conform to the
* OrderFillRequest interface.
* @param shouldThrowOnInsufficientBalanceOrAllowance Whether or not you wish for the contract call to throw
* if upon execution any of the tokens cannot be
* transferred. If set to false, the call will continue to
* fill subsequent signedOrders even when some
* cannot be filled.
* @param takerAddress The user Ethereum address who would like to fill
* these orders. Must be available via the supplied
* Web3.Provider passed to 0x.js.
*/
@decorators.contractCallErrorHandler
public async batchFillOrderAsync(orderFillRequests: OrderFillRequest[],
shouldCheckTransfer: boolean, takerAddress: string): Promise<void> {
assert.isBoolean('shouldCheckTransfer', shouldCheckTransfer);
public async batchFillOrdersAsync(orderFillRequests: OrderFillRequest[],
shouldThrowOnInsufficientBalanceOrAllowance: boolean,
takerAddress: string): Promise<void> {
assert.doesConformToSchema('orderFillRequests', orderFillRequests, schemas.orderFillRequestsSchema);
const exchangeContractAddresses = _.map(
orderFillRequests,
orderFillRequest => orderFillRequest.signedOrder.exchangeContractAddress,
);
assert.hasAtMostOneUniqueValue(exchangeContractAddresses,
ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress);
assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance);
await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
assert.doesConformToSchema('orderFillRequests', orderFillRequests, orderFillRequestsSchema);
for (const orderFillRequest of orderFillRequests) {
await this._validateFillOrderAndThrowIfInvalidAsync(
await this.validateFillOrderThrowIfInvalidAsync(
orderFillRequest.signedOrder, orderFillRequest.takerTokenFillAmount, takerAddress);
}
if (_.isEmpty(orderFillRequests)) {
@@ -286,16 +320,16 @@ export class ExchangeWrapper extends ContractWrapper {
];
});
// We use _.unzip<any> because _.unzip doesn't type check if values have different types :'(
const [orderAddressesArray, orderValuesArray, takerTokenFillAmountArray, vArray, rArray, sArray] = _.unzip<any>(
const [orderAddressesArray, orderValuesArray, fillTakerTokenAmounts, vArray, rArray, sArray] = _.unzip<any>(
orderAddressesValuesAmountsAndSignatureArray,
);
const exchangeInstance = await this._getExchangeContractAsync();
const gas = await exchangeInstance.batchFill.estimateGas(
const gas = await exchangeInstance.batchFillOrders.estimateGas(
orderAddressesArray,
orderValuesArray,
takerTokenFillAmountArray,
shouldCheckTransfer,
fillTakerTokenAmounts,
shouldThrowOnInsufficientBalanceOrAllowance,
vArray,
rArray,
sArray,
@@ -303,11 +337,11 @@ export class ExchangeWrapper extends ContractWrapper {
from: takerAddress,
},
);
const response: ContractResponse = await exchangeInstance.batchFill(
const response: ContractResponse = await exchangeInstance.batchFillOrders(
orderAddressesArray,
orderValuesArray,
takerTokenFillAmountArray,
shouldCheckTransfer,
fillTakerTokenAmounts,
shouldThrowOnInsufficientBalanceOrAllowance,
vArray,
rArray,
sArray,
@@ -321,31 +355,29 @@ export class ExchangeWrapper extends ContractWrapper {
/**
* Attempts to fill a specific amount of an order. If the entire amount specified cannot be filled,
* the fill order is abandoned.
* @param signedOrder A JS object that conforms to the SignedOrder interface. The
* @param signedOrder An object that conforms to the SignedOrder interface. The
* signedOrder you wish to fill.
* @param takerTokenFillAmount The total amount of the takerTokens you would like to fill.
* @param fillTakerTokenAmount The total amount of the takerTokens you would like to fill.
* @param takerAddress The user Ethereum address who would like to fill this order.
* Must be available via the supplied Web3.Provider passed to 0x.js.
*/
@decorators.contractCallErrorHandler
public async fillOrKillOrderAsync(signedOrder: SignedOrder, takerTokenFillAmount: BigNumber.BigNumber,
public async fillOrKillOrderAsync(signedOrder: SignedOrder, fillTakerTokenAmount: BigNumber.BigNumber,
takerAddress: string): Promise<void> {
assert.doesConformToSchema('signedOrder', signedOrder, signedOrderSchema);
assert.isBigNumber('takerTokenFillAmount', takerTokenFillAmount);
assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
assert.isBigNumber('fillTakerTokenAmount', fillTakerTokenAmount);
await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
const exchangeInstance = await this._getExchangeContractAsync();
await this._validateFillOrderAndThrowIfInvalidAsync(signedOrder, takerTokenFillAmount, takerAddress);
await this._validateFillOrKillOrderAndThrowIfInvalidAsync(signedOrder, exchangeInstance.address,
takerTokenFillAmount);
await this.validateFillOrKillOrderThrowIfInvalidAsync(signedOrder, fillTakerTokenAmount, takerAddress);
const [orderAddresses, orderValues] = ExchangeWrapper._getOrderAddressesAndValues(signedOrder);
const gas = await exchangeInstance.fillOrKill.estimateGas(
const gas = await exchangeInstance.fillOrKillOrder.estimateGas(
orderAddresses,
orderValues,
takerTokenFillAmount,
fillTakerTokenAmount,
signedOrder.ecSignature.v,
signedOrder.ecSignature.r,
signedOrder.ecSignature.s,
@@ -353,10 +385,10 @@ export class ExchangeWrapper extends ContractWrapper {
from: takerAddress,
},
);
const response: ContractResponse = await exchangeInstance.fillOrKill(
const response: ContractResponse = await exchangeInstance.fillOrKillOrder(
orderAddresses,
orderValues,
takerTokenFillAmount,
fillTakerTokenAmount,
signedOrder.ecSignature.v,
signedOrder.ecSignature.r,
signedOrder.ecSignature.s,
@@ -370,19 +402,29 @@ export class ExchangeWrapper extends ContractWrapper {
/**
* Batch version of fillOrKill. Allows a taker to specify a batch of orders that will either be atomically
* filled (each to the specified fillAmount) or aborted.
* @param orderFillOrKillRequests An array of JS objects that conform to the OrderFillOrKillRequest interface.
* @param orderFillOrKillRequests An array of objects that conform to the OrderFillOrKillRequest interface.
* @param takerAddress The user Ethereum address who would like to fill there orders.
* Must be available via the supplied Web3.Provider passed to 0x.js.
*/
@decorators.contractCallErrorHandler
public async batchFillOrKillAsync(orderFillOrKillRequests: OrderFillOrKillRequest[],
takerAddress: string): Promise<void> {
assert.doesConformToSchema('orderFillOrKillRequests', orderFillOrKillRequests,
schemas.orderFillOrKillRequestsSchema);
const exchangeContractAddresses = _.map(
orderFillOrKillRequests,
orderFillOrKillRequest => orderFillOrKillRequest.signedOrder.exchangeContractAddress,
);
assert.hasAtMostOneUniqueValue(exchangeContractAddresses,
ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress);
await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
assert.doesConformToSchema('orderFillOrKillRequests', orderFillOrKillRequests, orderFillOrKillRequestsSchema);
if (_.isEmpty(orderFillOrKillRequests)) {
return; // no-op
}
const exchangeInstance = await this._getExchangeContractAsync();
for (const request of orderFillOrKillRequests) {
await this._validateFillOrKillOrderAndThrowIfInvalidAsync(request.signedOrder, exchangeInstance.address,
request.fillTakerAmount);
await this.validateFillOrKillOrderThrowIfInvalidAsync(
request.signedOrder, request.fillTakerAmount, takerAddress);
}
const orderAddressesValuesAndTakerTokenFillAmounts = _.map(orderFillOrKillRequests, request => {
@@ -396,13 +438,13 @@ export class ExchangeWrapper extends ContractWrapper {
});
// We use _.unzip<any> because _.unzip doesn't type check if values have different types :'(
const [orderAddresses, orderValues, fillTakerAmounts, vParams, rParams, sParams] =
const [orderAddresses, orderValues, fillTakerTokenAmounts, vParams, rParams, sParams] =
_.unzip<any>(orderAddressesValuesAndTakerTokenFillAmounts);
const gas = await exchangeInstance.batchFillOrKill.estimateGas(
const gas = await exchangeInstance.batchFillOrKillOrders.estimateGas(
orderAddresses,
orderValues,
fillTakerAmounts,
fillTakerTokenAmounts,
vParams,
rParams,
sParams,
@@ -410,10 +452,10 @@ export class ExchangeWrapper extends ContractWrapper {
from: takerAddress,
},
);
const response: ContractResponse = await exchangeInstance.batchFillOrKill(
const response: ContractResponse = await exchangeInstance.batchFillOrKillOrders(
orderAddresses,
orderValues,
fillTakerAmounts,
fillTakerTokenAmounts,
vParams,
rParams,
sParams,
@@ -426,56 +468,66 @@ export class ExchangeWrapper extends ContractWrapper {
}
/**
* Cancel a given fill amount of an order. Cancellations are cumulative.
* @param order A JS object that conforms to the Order or SignedOrder interface.
* @param order An object that conforms to the Order or SignedOrder interface.
* The order you would like to cancel.
* @param takerTokenCancelAmount The amount (specified in taker tokens) that you would like to cancel.
* @param cancelTakerTokenAmount The amount (specified in taker tokens) that you would like to cancel.
* @return The amount of the order that was cancelled (in taker token baseUnits).
*/
@decorators.contractCallErrorHandler
public async cancelOrderAsync(
order: Order|SignedOrder, takerTokenCancelAmount: BigNumber.BigNumber): Promise<void> {
assert.doesConformToSchema('order', order, orderSchema);
assert.isBigNumber('takerTokenCancelAmount', takerTokenCancelAmount);
order: Order|SignedOrder, cancelTakerTokenAmount: BigNumber.BigNumber): Promise<BigNumber.BigNumber> {
assert.doesConformToSchema('order', order, schemas.orderSchema);
assert.isBigNumber('takerTokenCancelAmount', cancelTakerTokenAmount);
await assert.isSenderAddressAsync('order.maker', order.maker, this._web3Wrapper);
const exchangeInstance = await this._getExchangeContractAsync();
await this._validateCancelOrderAndThrowIfInvalidAsync(order, takerTokenCancelAmount);
await this.validateCancelOrderThrowIfInvalidAsync(order, cancelTakerTokenAmount);
const [orderAddresses, orderValues] = ExchangeWrapper._getOrderAddressesAndValues(order);
const gas = await exchangeInstance.cancel.estimateGas(
const gas = await exchangeInstance.cancelOrder.estimateGas(
orderAddresses,
orderValues,
takerTokenCancelAmount,
cancelTakerTokenAmount,
{
from: order.maker,
},
);
const response: ContractResponse = await exchangeInstance.cancel(
const response: ContractResponse = await exchangeInstance.cancelOrder(
orderAddresses,
orderValues,
takerTokenCancelAmount,
cancelTakerTokenAmount,
{
from: order.maker,
gas,
},
);
this._throwErrorLogsAsErrors(response.logs);
const logFillArgs = response.logs[0].args as LogCancelContractEventArgs;
const cancelledTakerTokenAmount = new BigNumber(logFillArgs.cancelledTakerTokenAmount);
return cancelledTakerTokenAmount;
}
/**
* Batch version of cancelOrderAsync. Atomically cancels multiple orders in a single transaction.
* All orders must be from the same maker.
* @param orderCancellationRequests An array of JS objects that conform to the OrderCancellationRequest
* @param orderCancellationRequests An array of objects that conform to the OrderCancellationRequest
* interface.
*/
@decorators.contractCallErrorHandler
public async batchCancelOrderAsync(orderCancellationRequests: OrderCancellationRequest[]): Promise<void> {
public async batchCancelOrdersAsync(orderCancellationRequests: OrderCancellationRequest[]): Promise<void> {
assert.doesConformToSchema('orderCancellationRequests', orderCancellationRequests,
schemas.orderCancellationRequestsSchema);
const exchangeContractAddresses = _.map(
orderCancellationRequests,
orderCancellationRequest => orderCancellationRequest.order.exchangeContractAddress,
);
assert.hasAtMostOneUniqueValue(exchangeContractAddresses,
ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress);
const makers = _.map(orderCancellationRequests, cancellationRequest => cancellationRequest.order.maker);
assert.hasAtMostOneUniqueValue(makers, ExchangeContractErrs.MULTIPLE_MAKERS_IN_SINGLE_CANCEL_BATCH_DISALLOWED);
assert.hasAtMostOneUniqueValue(makers, ExchangeContractErrs.MultipleMakersInSingleCancelBatchDisallowed);
const maker = makers[0];
await assert.isSenderAddressAsync('maker', maker, this._web3Wrapper);
assert.doesConformToSchema('orderCancellationRequests', orderCancellationRequests,
orderCancellationRequestsSchema);
for (const cancellationRequest of orderCancellationRequests) {
await this._validateCancelOrderAndThrowIfInvalidAsync(
await this.validateCancelOrderThrowIfInvalidAsync(
cancellationRequest.order, cancellationRequest.takerTokenCancelAmount,
);
}
@@ -490,20 +542,20 @@ export class ExchangeWrapper extends ContractWrapper {
];
});
// We use _.unzip<any> because _.unzip doesn't type check if values have different types :'(
const [orderAddresses, orderValues, takerTokenCancelAmounts] =
const [orderAddresses, orderValues, cancelTakerTokenAmounts] =
_.unzip<any>(orderAddressesValuesAndTakerTokenCancelAmounts);
const gas = await exchangeInstance.batchCancel.estimateGas(
const gas = await exchangeInstance.batchCancelOrders.estimateGas(
orderAddresses,
orderValues,
takerTokenCancelAmounts,
cancelTakerTokenAmounts,
{
from: maker,
},
);
const response: ContractResponse = await exchangeInstance.batchCancel(
const response: ContractResponse = await exchangeInstance.batchCancelOrders(
orderAddresses,
orderValues,
takerTokenCancelAmounts,
cancelTakerTokenAmounts,
{
from: maker,
gas,
@@ -513,15 +565,20 @@ export class ExchangeWrapper extends ContractWrapper {
}
/**
* Subscribe to an event type emitted by the Exchange smart contract
* @param eventName The exchange contract event you would like to subscribe to.
* @param subscriptionOpts Subscriptions options that let you configure the subscription.
* @param indexFilterValues A JS object where the keys are indexed args returned by the event and
* the value is the value you are interested in. E.g `{maker: aUserAddressHex}`
* @param callback The callback that will be called everytime a matching event is found.
* @param eventName The exchange contract event you would like to subscribe to.
* @param subscriptionOpts Subscriptions options that let you configure the subscription.
* @param indexFilterValues An object where the keys are indexed args returned by the event and
* the value is the value you are interested in. E.g `{maker: aUserAddressHex}`
* @param exchangeContractAddress The hex encoded address of the Exchange contract to call.
* @return ContractEventEmitter object
*/
public async subscribeAsync(eventName: ExchangeEvents, subscriptionOpts: SubscriptionOpts,
indexFilterValues: IndexFilterValues, callback: EventCallback):
Promise<void> {
indexFilterValues: IndexedFilterValues, exchangeContractAddress: string):
Promise<ContractEventEmitter> {
assert.isETHAddressHex('exchangeContractAddress', exchangeContractAddress);
assert.doesBelongToStringEnum('eventName', eventName, ExchangeEvents);
assert.doesConformToSchema('subscriptionOpts', subscriptionOpts, schemas.subscriptionOptsSchema);
assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
const exchangeContract = await this._getExchangeContractAsync();
let createLogEvent: CreateContractEvent;
switch (eventName) {
@@ -535,18 +592,113 @@ export class ExchangeWrapper extends ContractWrapper {
createLogEvent = exchangeContract.LogCancel;
break;
default:
utils.spawnSwitchErr('ExchangeEvents', eventName);
return;
throw utils.spawnSwitchErr('ExchangeEvents', eventName);
}
const logEventObj: ContractEventObj = createLogEvent(indexFilterValues, subscriptionOpts);
logEventObj.watch(callback);
this._exchangeLogEventObjs.push(logEventObj);
const eventEmitter = eventUtils.wrapEventEmitter(logEventObj);
this._exchangeLogEventEmitters.push(eventEmitter);
return eventEmitter;
}
/**
* Stops watching for all exchange events
*/
public async stopWatchingAllEventsAsync(): Promise<void> {
const stopWatchingPromises = _.map(this._exchangeLogEventEmitters,
logEventObj => logEventObj.stopWatchingAsync());
await Promise.all(stopWatchingPromises);
this._exchangeLogEventEmitters = [];
}
/**
* Retrieves the Ethereum address of the Exchange contract deployed on the network
* that the user-passed web3 provider is connected to.
* @returns The Ethereum address of the Exchange contract being used.
*/
public async getContractAddressAsync(): Promise<string> {
const exchangeInstance = await this._getExchangeContractAsync();
const exchangeAddress = exchangeInstance.address;
return exchangeAddress;
}
/**
* Checks if order fill will succeed and throws an error otherwise.
* @param signedOrder An object that conforms to the SignedOrder interface. The
* signedOrder you wish to fill.
* @param fillTakerTokenAmount The total amount of the takerTokens you would like to fill.
* @param takerAddress The user Ethereum address who would like to fill this order.
* Must be available via the supplied Web3.Provider passed to 0x.js.
*/
public async validateFillOrderThrowIfInvalidAsync(signedOrder: SignedOrder,
fillTakerTokenAmount: BigNumber.BigNumber,
takerAddress: string): Promise<void> {
assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
assert.isBigNumber('fillTakerTokenAmount', fillTakerTokenAmount);
await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
const zrxTokenAddress = await this._getZRXTokenAddressAsync();
await this._orderValidationUtils.validateFillOrderThrowIfInvalidAsync(
signedOrder, fillTakerTokenAmount, takerAddress, zrxTokenAddress);
}
/**
* Checks if cancelling a given order will succeed and throws an informative error if it won't.
* @param order An object that conforms to the Order or SignedOrder interface.
* The order you would like to cancel.
* @param cancelTakerTokenAmount The amount (specified in taker tokens) that you would like to cancel.
*/
public async validateCancelOrderThrowIfInvalidAsync(
order: Order, cancelTakerTokenAmount: BigNumber.BigNumber): Promise<void> {
assert.doesConformToSchema('order', order, schemas.orderSchema);
assert.isBigNumber('cancelTakerTokenAmount', cancelTakerTokenAmount);
const orderHash = utils.getOrderHashHex(order);
const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash);
await this._orderValidationUtils.validateCancelOrderThrowIfInvalidAsync(
order, cancelTakerTokenAmount, unavailableTakerTokenAmount);
}
/**
* Checks if calling fillOrKill on a given order will succeed and throws an informative error if it won't.
* @param signedOrder An object that conforms to the SignedOrder interface. The
* signedOrder you wish to fill.
* @param fillTakerTokenAmount The total amount of the takerTokens you would like to fill.
* @param takerAddress The user Ethereum address who would like to fill this order.
* Must be available via the supplied Web3.Provider passed to 0x.js.
*/
public async validateFillOrKillOrderThrowIfInvalidAsync(signedOrder: SignedOrder,
fillTakerTokenAmount: BigNumber.BigNumber,
takerAddress: string): Promise<void> {
assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
assert.isBigNumber('fillTakerTokenAmount', fillTakerTokenAmount);
await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
const zrxTokenAddress = await this._getZRXTokenAddressAsync();
await this._orderValidationUtils.validateFillOrKillOrderThrowIfInvalidAsync(
signedOrder, fillTakerTokenAmount, takerAddress, zrxTokenAddress);
}
/**
* Checks if rounding error will be > 0.1% when computing makerTokenAmount by doing:
* `(fillTakerTokenAmount * makerTokenAmount) / takerTokenAmount`.
* 0x Protocol does not accept any trades that result in large rounding errors. This means that tokens with few or
* no decimals can only be filled in quantities and ratios that avoid large rounding errors.
* @param fillTakerTokenAmount The amount of the order (in taker tokens baseUnits) that you wish to fill.
* @param takerTokenAmount The order size on the taker side
* @param makerTokenAmount The order size on the maker side
*/
public async isRoundingErrorAsync(fillTakerTokenAmount: BigNumber.BigNumber,
takerTokenAmount: BigNumber.BigNumber,
makerTokenAmount: BigNumber.BigNumber): Promise<boolean> {
assert.isBigNumber('fillTakerTokenAmount', fillTakerTokenAmount);
assert.isBigNumber('takerTokenAmount', takerTokenAmount);
assert.isBigNumber('makerTokenAmount', makerTokenAmount);
const exchangeInstance = await this._getExchangeContractAsync();
const isRoundingError = await exchangeInstance.isRoundingError.call(
fillTakerTokenAmount, takerTokenAmount, makerTokenAmount,
);
return isRoundingError;
}
private async _invalidateContractInstancesAsync(): Promise<void> {
await this.stopWatchingAllEventsAsync();
delete this._exchangeContractIfExists;
}
private async _isValidSignatureUsingContractCallAsync(dataHex: string, ecSignature: ECSignature,
signerAddressHex: string): Promise<boolean> {
assert.isHexString('dataHex', dataHex);
assert.doesConformToSchema('ecSignature', ecSignature, ecSignatureSchema);
assert.doesConformToSchema('ecSignature', ecSignature, schemas.ecSignatureSchema);
assert.isETHAddressHex('signerAddressHex', signerAddressHex);
const exchangeInstance = await this._getExchangeContractAsync();
@@ -560,153 +712,20 @@ export class ExchangeWrapper extends ContractWrapper {
);
return isValidSignature;
}
private async _getOrderHashHexAsync(order: Order|SignedOrder): Promise<string> {
const exchangeInstance = await this._getExchangeContractAsync();
const orderHashHex = utils.getOrderHashHex(order, exchangeInstance.address);
return orderHashHex;
}
private async _getOrderHashHexUsingContractCallAsync(order: Order|SignedOrder): Promise<string> {
const exchangeInstance = await this._getExchangeContractAsync();
const [orderAddresses, orderValues] = ExchangeWrapper._getOrderAddressesAndValues(order);
const orderHashHex = await exchangeInstance.getOrderHash.call(orderAddresses, orderValues);
return orderHashHex;
}
private async _stopWatchingExchangeLogEventsAsync() {
const stopWatchingPromises = _.map(this._exchangeLogEventObjs, logEventObj => {
return promisify(logEventObj.stopWatching, logEventObj)();
});
await Promise.all(stopWatchingPromises);
this._exchangeLogEventObjs = [];
}
private async _validateFillOrderAndThrowIfInvalidAsync(signedOrder: SignedOrder,
fillTakerAmount: BigNumber.BigNumber,
senderAddress: string): Promise<void> {
if (fillTakerAmount.eq(0)) {
throw new Error(ExchangeContractErrs.ORDER_REMAINING_FILL_AMOUNT_ZERO);
}
if (signedOrder.taker !== constants.NULL_ADDRESS && signedOrder.taker !== senderAddress) {
throw new Error(ExchangeContractErrs.TRANSACTION_SENDER_IS_NOT_FILL_ORDER_TAKER);
}
const currentUnixTimestampSec = utils.getCurrentUnixTimestamp();
if (signedOrder.expirationUnixTimestampSec.lessThan(currentUnixTimestampSec)) {
throw new Error(ExchangeContractErrs.ORDER_FILL_EXPIRED);
}
const zrxTokenAddress = await this._getZRXTokenAddressAsync();
await this._validateFillOrderBalancesAndAllowancesAndThrowIfInvalidAsync(signedOrder, fillTakerAmount,
senderAddress, zrxTokenAddress);
const wouldRoundingErrorOccur = await this._isRoundingErrorAsync(
signedOrder.takerTokenAmount, fillTakerAmount, signedOrder.makerTokenAmount,
);
if (wouldRoundingErrorOccur) {
throw new Error(ExchangeContractErrs.ORDER_FILL_ROUNDING_ERROR);
}
}
private async _validateCancelOrderAndThrowIfInvalidAsync(
order: Order, takerTokenCancelAmount: BigNumber.BigNumber): Promise<void> {
if (takerTokenCancelAmount.eq(0)) {
throw new Error(ExchangeContractErrs.ORDER_CANCEL_AMOUNT_ZERO);
}
const orderHash = await this._getOrderHashHexAsync(order);
const unavailableAmount = await this.getUnavailableTakerAmountAsync(orderHash);
if (order.takerTokenAmount.minus(unavailableAmount).eq(0)) {
throw new Error(ExchangeContractErrs.ORDER_ALREADY_CANCELLED_OR_FILLED);
}
const currentUnixTimestampSec = utils.getCurrentUnixTimestamp();
if (order.expirationUnixTimestampSec.lessThan(currentUnixTimestampSec)) {
throw new Error(ExchangeContractErrs.ORDER_CANCEL_EXPIRED);
}
}
private async _validateFillOrKillOrderAndThrowIfInvalidAsync(signedOrder: SignedOrder,
exchangeAddress: string,
fillTakerAmount: BigNumber.BigNumber) {
// Check that fillValue available >= fillTakerAmount
const orderHashHex = utils.getOrderHashHex(signedOrder, exchangeAddress);
const unavailableTakerAmount = await this.getUnavailableTakerAmountAsync(orderHashHex);
const remainingTakerAmount = signedOrder.takerTokenAmount.minus(unavailableTakerAmount);
if (remainingTakerAmount < fillTakerAmount) {
throw new Error(ExchangeContractErrs.INSUFFICIENT_REMAINING_FILL_AMOUNT);
}
}
/**
* This method does not currently validate the edge-case where the makerToken or takerToken is also the token used
* to pay fees (ZRX). It is possible for them to have enough for fees and the transfer but not both.
* Handling the edge-cases that arise when this happens would require making sure that the user has sufficient
* funds to pay both the fees and the transfer amount. We decided to punt on this for now as the contracts
* will throw for these edge-cases.
* TODO: Throw errors before calling the smart contract for these edge-cases in order to minimize
* the callers gas costs.
*/
private async _validateFillOrderBalancesAndAllowancesAndThrowIfInvalidAsync(signedOrder: SignedOrder,
fillTakerAmount: BigNumber.BigNumber,
senderAddress: string,
zrxTokenAddress: string,
): Promise<void> {
const makerBalance = await this._tokenWrapper.getBalanceAsync(signedOrder.makerTokenAddress,
signedOrder.maker);
const takerBalance = await this._tokenWrapper.getBalanceAsync(signedOrder.takerTokenAddress, senderAddress);
const makerAllowance = await this._tokenWrapper.getProxyAllowanceAsync(signedOrder.makerTokenAddress,
signedOrder.maker);
const takerAllowance = await this._tokenWrapper.getProxyAllowanceAsync(signedOrder.takerTokenAddress,
senderAddress);
// exchangeRate is the price of one maker token denominated in taker tokens
const exchangeRate = signedOrder.takerTokenAmount.div(signedOrder.makerTokenAmount);
const fillMakerAmountInBaseUnits = fillTakerAmount.div(exchangeRate);
if (fillTakerAmount.greaterThan(takerBalance)) {
throw new Error(ExchangeContractErrs.INSUFFICIENT_TAKER_BALANCE);
}
if (fillTakerAmount.greaterThan(takerAllowance)) {
throw new Error(ExchangeContractErrs.INSUFFICIENT_TAKER_ALLOWANCE);
}
if (fillMakerAmountInBaseUnits.greaterThan(makerBalance)) {
throw new Error(ExchangeContractErrs.INSUFFICIENT_MAKER_BALANCE);
}
if (fillMakerAmountInBaseUnits.greaterThan(makerAllowance)) {
throw new Error(ExchangeContractErrs.INSUFFICIENT_MAKER_ALLOWANCE);
}
const makerFeeBalance = await this._tokenWrapper.getBalanceAsync(zrxTokenAddress,
signedOrder.maker);
const takerFeeBalance = await this._tokenWrapper.getBalanceAsync(zrxTokenAddress, senderAddress);
const makerFeeAllowance = await this._tokenWrapper.getProxyAllowanceAsync(zrxTokenAddress,
signedOrder.maker);
const takerFeeAllowance = await this._tokenWrapper.getProxyAllowanceAsync(zrxTokenAddress,
senderAddress);
if (signedOrder.takerFee.greaterThan(takerFeeBalance)) {
throw new Error(ExchangeContractErrs.INSUFFICIENT_TAKER_FEE_BALANCE);
}
if (signedOrder.takerFee.greaterThan(takerFeeAllowance)) {
throw new Error(ExchangeContractErrs.INSUFFICIENT_TAKER_FEE_ALLOWANCE);
}
if (signedOrder.makerFee.greaterThan(makerFeeBalance)) {
throw new Error(ExchangeContractErrs.INSUFFICIENT_MAKER_FEE_BALANCE);
}
if (signedOrder.makerFee.greaterThan(makerFeeAllowance)) {
throw new Error(ExchangeContractErrs.INSUFFICIENT_MAKER_FEE_ALLOWANCE);
}
}
private _throwErrorLogsAsErrors(logs: ContractEvent[]): void {
const errEvent = _.find(logs, {event: 'LogError'});
if (!_.isUndefined(errEvent)) {
const errCode = errEvent.args.errorId.toNumber();
const errCode = (errEvent.args as LogErrorContractEventArgs).errorId.toNumber();
const errMessage = this._exchangeContractErrCodesToMsg[errCode];
throw new Error(errMessage);
}
}
private async _isRoundingErrorAsync(takerTokenAmount: BigNumber.BigNumber,
fillTakerAmount: BigNumber.BigNumber,
makerTokenAmount: BigNumber.BigNumber): Promise<boolean> {
await assert.isUserAddressAvailableAsync(this._web3Wrapper);
const exchangeInstance = await this._getExchangeContractAsync();
const isRoundingError = await exchangeInstance.isRoundingError.call(
takerTokenAmount, fillTakerAmount, makerTokenAmount,
);
return isRoundingError;
}
private async _getExchangeContractAsync(): Promise<ExchangeContract> {
if (!_.isUndefined(this._exchangeContractIfExists)) {
return this._exchangeContractIfExists;
@@ -717,6 +736,7 @@ export class ExchangeWrapper extends ContractWrapper {
}
private async _getZRXTokenAddressAsync(): Promise<string> {
const exchangeInstance = await this._getExchangeContractAsync();
return exchangeInstance.ZRX.call();
const ZRXtokenAddress = await exchangeInstance.ZRX_TOKEN_CONTRACT.call();
return ZRXtokenAddress;
}
}

View File

@@ -1,41 +1,101 @@
import * as _ from 'lodash';
import {Web3Wrapper} from '../web3_wrapper';
import {Token, TokenRegistryContract, TokenMetadata} from '../types';
import {assert} from '../utils/assert';
import {Token, TokenRegistryContract, TokenMetadata} from '../types';
import {constants} from '../utils/constants';
import {ContractWrapper} from './contract_wrapper';
import * as TokenRegistryArtifacts from '../artifacts/TokenRegistry.json';
/**
* This class includes all the functionality related to interacting with the 0x Token Registry smart contract.
*/
export class TokenRegistryWrapper extends ContractWrapper {
private _tokenRegistryContractIfExists?: TokenRegistryContract;
constructor(web3Wrapper: Web3Wrapper) {
super(web3Wrapper);
}
public invalidateContractInstance(): void {
delete this._tokenRegistryContractIfExists;
constructor(web3Wrapper: Web3Wrapper, gasPrice?: BigNumber.BigNumber) {
super(web3Wrapper, gasPrice);
}
/**
* Retrieves all the tokens currently listed in the Token Registry smart contract
* @return An array of JS objects that conform to the Token interface.
* @return An array of objects that conform to the Token interface.
*/
public async getTokensAsync(): Promise<Token[]> {
const tokenRegistryContract = await this._getTokenRegistryContractAsync();
const addresses = await tokenRegistryContract.getTokenAddresses.call();
const tokenMetadataPromises: Array<Promise<TokenMetadata>> = _.map(
const addresses = await this.getTokenAddressesAsync();
const tokenPromises: Array<Promise<Token|undefined>> = _.map(
addresses,
(address: string) => (tokenRegistryContract.getTokenMetaData.call(address)),
(address: string) => (this.getTokenIfExistsAsync(address)),
);
const tokensMetadata = await Promise.all(tokenMetadataPromises);
const tokens = _.map(tokensMetadata, metadata => {
return {
address: metadata[0],
name: metadata[1],
symbol: metadata[2],
url: metadata[3],
decimals: metadata[4].toNumber(),
};
});
return tokens;
const tokens = await Promise.all(tokenPromises);
return tokens as Token[];
}
/**
* Retrieves all the addresses of the tokens currently listed in the Token Registry smart contract
* @return An array of token addresses.
*/
public async getTokenAddressesAsync(): Promise<string[]> {
const tokenRegistryContract = await this._getTokenRegistryContractAsync();
const addresses = await tokenRegistryContract.getTokenAddresses.call();
return addresses;
}
/**
* Retrieves a token by address currently listed in the Token Registry smart contract
* @return An object that conforms to the Token interface or undefined if token not found.
*/
public async getTokenIfExistsAsync(address: string): Promise<Token|undefined> {
assert.isETHAddressHex('address', address);
const tokenRegistryContract = await this._getTokenRegistryContractAsync();
const metadata = await tokenRegistryContract.getTokenMetaData.call(address);
const token = this._createTokenFromMetadata(metadata);
return token;
}
public async getTokenAddressBySymbolIfExistsAsync(symbol: string): Promise<string|undefined> {
assert.isString('symbol', symbol);
const tokenRegistryContract = await this._getTokenRegistryContractAsync();
const addressIfExists = await tokenRegistryContract.getTokenAddressBySymbol.call(symbol);
if (addressIfExists === constants.NULL_ADDRESS) {
return undefined;
}
return addressIfExists;
}
public async getTokenAddressByNameIfExistsAsync(name: string): Promise<string|undefined> {
assert.isString('name', name);
const tokenRegistryContract = await this._getTokenRegistryContractAsync();
const addressIfExists = await tokenRegistryContract.getTokenAddressByName.call(name);
if (addressIfExists === constants.NULL_ADDRESS) {
return undefined;
}
return addressIfExists;
}
public async getTokenBySymbolIfExistsAsync(symbol: string): Promise<Token|undefined> {
assert.isString('symbol', symbol);
const tokenRegistryContract = await this._getTokenRegistryContractAsync();
const metadata = await tokenRegistryContract.getTokenBySymbol.call(symbol);
const token = this._createTokenFromMetadata(metadata);
return token;
}
public async getTokenByNameIfExistsAsync(name: string): Promise<Token|undefined> {
assert.isString('name', name);
const tokenRegistryContract = await this._getTokenRegistryContractAsync();
const metadata = await tokenRegistryContract.getTokenByName.call(name);
const token = this._createTokenFromMetadata(metadata);
return token;
}
private _createTokenFromMetadata(metadata: TokenMetadata): Token|undefined {
if (metadata[0] === constants.NULL_ADDRESS) {
return undefined;
}
const token = {
address: metadata[0],
name: metadata[1],
symbol: metadata[2],
decimals: metadata[3].toNumber(),
};
return token;
}
private _invalidateContractInstance(): void {
delete this._tokenRegistryContractIfExists;
}
private async _getTokenRegistryContractAsync(): Promise<TokenRegistryContract> {
if (!_.isUndefined(this._tokenRegistryContractIfExists)) {

View File

@@ -0,0 +1,51 @@
import * as _ from 'lodash';
import {ContractWrapper} from './contract_wrapper';
import * as TokenTransferProxyArtifacts from '../artifacts/TokenTransferProxy.json';
import {TokenTransferProxyContract} from '../types';
/**
* This class includes the functionality related to interacting with the TokenTransferProxy contract.
*/
export class TokenTransferProxyWrapper extends ContractWrapper {
private _tokenTransferProxyContractIfExists?: TokenTransferProxyContract;
/**
* Check if the Exchange contract address is authorized by the TokenTransferProxy contract.
* @param exchangeContractAddress The hex encoded address of the Exchange contract to call.
* @return Whether the exchangeContractAddress is authorized.
*/
public async isAuthorizedAsync(exchangeContractAddress: string): Promise<boolean> {
const tokenTransferProxyContractInstance = await this._getTokenTransferProxyContractAsync();
const isAuthorized = await tokenTransferProxyContractInstance.authorized.call(exchangeContractAddress);
return isAuthorized;
}
/**
* Get the list of all Exchange contract addresses authorized by the TokenTransferProxy contract.
* @return The list of authorized addresses.
*/
public async getAuthorizedAddressesAsync(): Promise<string[]> {
const tokenTransferProxyContractInstance = await this._getTokenTransferProxyContractAsync();
const authorizedAddresses = await tokenTransferProxyContractInstance.getAuthorizedAddresses.call();
return authorizedAddresses;
}
/**
* Retrieves the Ethereum address of the TokenTransferProxy contract deployed on the network
* that the user-passed web3 provider is connected to.
* @returns The Ethereum address of the TokenTransferProxy contract being used.
*/
public async getContractAddressAsync(): Promise<string> {
const proxyInstance = await this._getTokenTransferProxyContractAsync();
const proxyAddress = proxyInstance.address;
return proxyAddress;
}
private _invalidateContractInstance(): void {
delete this._tokenTransferProxyContractIfExists;
}
private async _getTokenTransferProxyContractAsync(): Promise<TokenTransferProxyContract> {
if (!_.isUndefined(this._tokenTransferProxyContractIfExists)) {
return this._tokenTransferProxyContractIfExists;
}
const contractInstance = await this._instantiateContractIfExistsAsync((TokenTransferProxyArtifacts as any));
this._tokenTransferProxyContractIfExists = contractInstance as TokenTransferProxyContract;
return this._tokenTransferProxyContractIfExists;
}
}

View File

@@ -1,33 +1,50 @@
import * as _ from 'lodash';
import * as BigNumber from 'bignumber.js';
import {SchemaValidator, schemas} from '0x-json-schemas';
import {Web3Wrapper} from '../web3_wrapper';
import {assert} from '../utils/assert';
import {utils} from '../utils/utils';
import {eventUtils} from '../utils/event_utils';
import {constants} from '../utils/constants';
import {ContractWrapper} from './contract_wrapper';
import * as TokenArtifacts from '../artifacts/Token.json';
import * as ProxyArtifacts from '../artifacts/Proxy.json';
import {TokenContract, ZeroExError} from '../types';
import * as TokenTransferProxyArtifacts from '../artifacts/TokenTransferProxy.json';
import {
TokenContract,
ZeroExError,
TokenEvents,
IndexedFilterValues,
SubscriptionOpts,
CreateContractEvent,
ContractEventEmitter,
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.
* All ERC20 method calls are supported, along with some convenience methods for getting/setting allowances
* to the 0x Proxy smart contract.
*/
export class TokenWrapper extends ContractWrapper {
public UNLIMITED_ALLOWANCE_IN_BASE_UNITS = constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS;
private _tokenContractsByAddress: {[address: string]: TokenContract};
constructor(web3Wrapper: Web3Wrapper) {
super(web3Wrapper);
this._tokenContractsByAddress = {};
}
public invalidateContractInstances() {
private _tokenLogEventEmitters: ContractEventEmitter[];
constructor(web3Wrapper: Web3Wrapper, gasPrice?: BigNumber.BigNumber) {
super(web3Wrapper, gasPrice);
this._tokenContractsByAddress = {};
this._tokenLogEventEmitters = [];
}
/**
* Retrieves an owner's ERC20 token balance.
* @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
* @param ownerAddress The hex encoded user Ethereum address whose balance you would like to check.
* @return The owner's ERC20 token balance in base units.
*/
public async getBalanceAsync(tokenAddress: string, ownerAddress: string): Promise<BigNumber.BigNumber> {
assert.isETHAddressHex('ownerAddress', ownerAddress);
assert.isETHAddressHex('tokenAddress', tokenAddress);
await assert.isUserAddressAvailableAsync(this._web3Wrapper);
const tokenContract = await this._getTokenContractAsync(tokenAddress);
let balance = await tokenContract.balanceOf.call(ownerAddress);
@@ -62,6 +79,22 @@ 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`.
* 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.
* @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<void> {
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.
@@ -72,7 +105,6 @@ export class TokenWrapper extends ContractWrapper {
public async getAllowanceAsync(tokenAddress: string, ownerAddress: string, spenderAddress: string) {
assert.isETHAddressHex('ownerAddress', ownerAddress);
assert.isETHAddressHex('tokenAddress', tokenAddress);
await assert.isUserAddressAvailableAsync(this._web3Wrapper);
const tokenContract = await this._getTokenContractAsync(tokenAddress);
let allowanceInBaseUnits = await tokenContract.allowance.call(ownerAddress, spenderAddress);
@@ -110,6 +142,18 @@ 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.
* 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.
*/
public async setUnlimitedProxyAllowanceAsync(tokenAddress: string, ownerAddress: string): Promise<void> {
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.
@@ -128,7 +172,7 @@ export class TokenWrapper extends ContractWrapper {
const fromAddressBalance = await this.getBalanceAsync(tokenAddress, fromAddress);
if (fromAddressBalance.lessThan(amountInBaseUnits)) {
throw new Error(ZeroExError.INSUFFICIENT_BALANCE_FOR_TRANSFER);
throw new Error(ZeroExError.InsufficientBalanceForTransfer);
}
await tokenContract.transfer(toAddress, amountInBaseUnits, {
@@ -138,12 +182,13 @@ export class TokenWrapper extends ContractWrapper {
/**
* Transfers `amountInBaseUnits` ERC20 tokens from `fromAddress` to `toAddress`.
* Requires the fromAddress to have sufficient funds and to have approved an allowance of
* `amountInBaseUnits` for senderAddress.
* `amountInBaseUnits` to `senderAddress`.
* @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
* @param fromAddress The hex encoded user Ethereum address that will send the funds.
* @param fromAddress The hex encoded user Ethereum address whose funds are being sent.
* @param toAddress The hex encoded user Ethereum address that will receive the funds.
* @param senderAddress The hex encoded user Ethereum address whose funds are being sent. The senderAddress
* must have set an allowance to the fromAddress before this call.
* @param senderAddress The hex encoded user Ethereum address whose initiates the fund transfer. The
* `fromAddress` must have set an allowance to the `senderAddress`
* before this call.
* @param amountInBaseUnits The amount (specified in baseUnits) of the token to transfer.
*/
public async transferFromAsync(tokenAddress: string, fromAddress: string, toAddress: string,
@@ -159,18 +204,64 @@ export class TokenWrapper extends ContractWrapper {
const fromAddressAllowance = await this.getAllowanceAsync(tokenAddress, fromAddress, senderAddress);
if (fromAddressAllowance.lessThan(amountInBaseUnits)) {
throw new Error(ZeroExError.INSUFFICIENT_ALLOWANCE_FOR_TRANSFER);
throw new Error(ZeroExError.InsufficientAllowanceForTransfer);
}
const fromAddressBalance = await this.getBalanceAsync(tokenAddress, fromAddress);
if (fromAddressBalance.lessThan(amountInBaseUnits)) {
throw new Error(ZeroExError.INSUFFICIENT_BALANCE_FOR_TRANSFER);
throw new Error(ZeroExError.InsufficientBalanceForTransfer);
}
await tokenContract.transferFrom(fromAddress, toAddress, amountInBaseUnits, {
from: senderAddress,
});
}
/**
* Subscribe to an event type emitted by the Token contract.
* @param tokenAddress The hex encoded address where the ERC20 token is deployed.
* @param eventName The token contract event you would like to subscribe to.
* @param subscriptionOpts Subscriptions options that let you configure the subscription.
* @param indexFilterValues An object where the keys are indexed args returned by the event and
* the value is the value you are interested in. E.g `{maker: aUserAddressHex}`
* @return ContractEventEmitter object
*/
public async subscribeAsync(tokenAddress: string, eventName: TokenEvents, subscriptionOpts: SubscriptionOpts,
indexFilterValues: IndexedFilterValues): Promise<ContractEventEmitter> {
assert.isETHAddressHex('tokenAddress', tokenAddress);
assert.doesBelongToStringEnum('eventName', eventName, TokenEvents);
assert.doesConformToSchema('subscriptionOpts', subscriptionOpts, schemas.subscriptionOptsSchema);
assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
const tokenContract = await this._getTokenContractAsync(tokenAddress);
let createLogEvent: CreateContractEvent;
switch (eventName) {
case TokenEvents.Approval:
createLogEvent = tokenContract.Approval;
break;
case TokenEvents.Transfer:
createLogEvent = tokenContract.Transfer;
break;
default:
throw utils.spawnSwitchErr('TokenEvents', eventName);
}
const logEventObj: ContractEventObj = createLogEvent(indexFilterValues, subscriptionOpts);
const eventEmitter = eventUtils.wrapEventEmitter(logEventObj);
this._tokenLogEventEmitters.push(eventEmitter);
return eventEmitter;
}
/**
* Stops watching for all token events
*/
public async stopWatchingAllEventsAsync(): Promise<void> {
const stopWatchingPromises = _.map(this._tokenLogEventEmitters,
logEventObj => logEventObj.stopWatchingAsync());
await Promise.all(stopWatchingPromises);
this._tokenLogEventEmitters = [];
}
private async _invalidateContractInstancesAsync(): Promise<void> {
await this.stopWatchingAllEventsAsync();
this._tokenContractsByAddress = {};
}
private async _getTokenContractAsync(tokenAddress: string): Promise<TokenContract> {
let tokenContract = this._tokenContractsByAddress[tokenAddress];
if (!_.isUndefined(tokenContract)) {
@@ -185,11 +276,11 @@ export class TokenWrapper extends ContractWrapper {
const networkIdIfExists = await this._web3Wrapper.getNetworkIdIfExistsAsync();
const proxyNetworkConfigsIfExists = _.isUndefined(networkIdIfExists) ?
undefined :
(ProxyArtifacts as any).networks[networkIdIfExists];
(TokenTransferProxyArtifacts as any).networks[networkIdIfExists];
if (_.isUndefined(proxyNetworkConfigsIfExists)) {
throw new Error(ZeroExError.CONTRACT_NOT_DEPLOYED_ON_NETWORK);
throw new Error(ZeroExError.ContractNotDeployedOnNetwork);
}
const proxyAddress = proxyNetworkConfigsIfExists.address;
const proxyAddress = proxyNetworkConfigsIfExists.address.toLowerCase();
return proxyAddress;
}
}

27
src/globals.d.ts vendored
View File

@@ -1,16 +1,12 @@
/// <reference types='chai-typescript-typings' />
/// <reference types='chai-as-promised-typescript-typings' />
declare module 'web3_beta';
declare module 'chai-bignumber';
declare module 'dirty-chai';
declare module 'bn.js';
declare module 'request-promise-native';
declare module 'web3-provider-engine';
declare module 'web3-provider-engine/subproviders/rpc';
declare interface Schema {
id: string;
}
// HACK: In order to merge the bignumber declaration added by chai-bignumber to the chai Assertion
// interface we must use `namespace` as the Chai definitelyTyped definition does. Since we otherwise
// disallow `namespace`, we disable tslint for the following.
@@ -37,17 +33,6 @@ declare module '*.json' {
/* tslint:enable */
}
declare module 'ethereumjs-util' {
const toBuffer: (dataHex: string) => Buffer;
const hashPersonalMessage: (msg: Buffer) => Buffer;
const bufferToHex: (buff: Buffer) => string;
const ecrecover: (msgHashBuff: Buffer, v: number, r: Buffer, s: Buffer) => string;
const pubToAddress: (pubKey: string) => Buffer;
const isValidAddress: (address: string) => boolean;
const bufferToInt: (buffer: Buffer) => number;
const fromRpcSig: (signature: string) => {v: number, r: Buffer, s: Buffer};
}
// truffle-contract declarations
declare interface ContractInstance {
address: string;
@@ -55,6 +40,8 @@ declare interface ContractInstance {
declare interface ContractFactory {
setProvider: (providerObj: any) => void;
deployed: () => ContractInstance;
// Both any's are Web3.CallData, but I was unable to import it in this file
defaults: (config: any) => any;
at: (address: string) => ContractInstance;
}
declare interface Artifact {
@@ -86,3 +73,11 @@ declare module 'es6-promisify' {
declare module 'ethereumjs-abi' {
const soliditySHA3: (argTypes: string[], args: any[]) => Buffer;
}
// truffle-hdwallet-provider declarations
declare class HDWalletProvider {
constructor(mnemonic: string, rpcUrl: string);
}
declare module 'truffle-hdwallet-provider' {
export = HDWalletProvider;
}

33
src/index.ts Normal file
View File

@@ -0,0 +1,33 @@
export {ZeroEx} from './0x';
export {
Order,
SignedOrder,
ECSignature,
ZeroExError,
EventCallback,
EventCallbackAsync,
EventCallbackSync,
ExchangeContractErrs,
ContractEvent,
Token,
ExchangeEvents,
TokenEvents,
IndexedFilterValues,
SubscriptionOpts,
BlockParam,
OrderFillOrKillRequest,
OrderCancellationRequest,
OrderFillRequest,
ContractEventEmitter,
LogErrorContractEventArgs,
LogCancelContractEventArgs,
LogFillContractEventArgs,
ExchangeContractEventArgs,
TransferContractEventArgs,
ApprovalContractEventArgs,
TokenContractEventArgs,
ContractEventArgs,
Web3Provider,
ZeroExConfig,
} from './types';

View File

@@ -1,11 +0,0 @@
export const addressSchema = {
id: '/addressSchema',
type: 'string',
pattern: '^0[xX][0-9A-Fa-f]{40}$',
};
export const numberSchema = {
id: '/numberSchema',
type: 'string',
pattern: '^\\d+(\\.\\d+)?$',
};

View File

@@ -1,20 +0,0 @@
export const ecSignatureParameterSchema = {
id: '/ecSignatureParameter',
type: 'string',
pattern: '^0[xX][0-9A-Fa-f]{64}$',
};
export const ecSignatureSchema = {
id: '/ECSignature',
properties: {
v: {
type: 'number',
minimum: 27,
maximum: 28,
},
r: {$ref: '/ecSignatureParameter'},
s: {$ref: '/ecSignatureParameter'},
},
required: ['v', 'r', 's'],
type: 'object',
};

View File

@@ -1,12 +0,0 @@
export const orderCancellationRequestsSchema = {
id: '/OrderCancellationRequests',
type: 'array',
items: {
properties: {
order: {$ref: '/orderSchema'},
takerTokenCancelAmount: {$ref: '/numberSchema'},
},
required: ['order', 'takerTokenCancelAmount'],
type: 'object',
},
};

View File

@@ -1,12 +0,0 @@
export const orderFillOrKillRequestsSchema = {
id: '/OrderFillOrKillRequests',
type: 'array',
items: {
properties: {
signedOrder: {$ref: '/signedOrderSchema'},
fillTakerAmount: {$ref: '/numberSchema'},
},
required: ['signedOrder', 'fillTakerAmount'],
type: 'object',
},
};

View File

@@ -1,12 +0,0 @@
export const orderFillRequestsSchema = {
id: '/OrderFillRequests',
type: 'array',
items: {
properties: {
signedOrder: {$ref: '/signedOrderSchema'},
takerTokenFillAmount: {$ref: '/numberSchema'},
},
required: ['signedOrder', 'takerTokenFillAmount'],
type: 'object',
},
};

View File

@@ -1,38 +0,0 @@
export const orderSchema = {
id: '/orderSchema',
properties: {
maker: {$ref: '/addressSchema'},
taker: {$ref: '/addressSchema'},
makerFee: {$ref: '/numberSchema'},
takerFee: {$ref: '/numberSchema'},
makerTokenAmount: {$ref: '/numberSchema'},
takerTokenAmount: {$ref: '/numberSchema'},
makerTokenAddress: {$ref: '/addressSchema'},
takerTokenAddress: {$ref: '/addressSchema'},
salt: {$ref: '/numberSchema'},
feeRecipient: {$ref: '/addressSchema'},
expirationUnixTimestampSec: {$ref: '/numberSchema'},
},
required: [
'maker', 'taker', 'makerFee', 'takerFee', 'makerTokenAmount', 'takerTokenAmount',
'salt', 'feeRecipient', 'expirationUnixTimestampSec',
],
type: 'object',
};
export const signedOrderSchema = {
id: '/signedOrderSchema',
allOf: [
{ $ref: '/orderSchema' },
{
properties: {
ecSignature: {$ref: '/ECSignature'},
},
required: ['ecSignature'],
},
],
};

View File

@@ -1,5 +0,0 @@
export const signedOrdersSchema = {
id: '/signedOrdersSchema',
type: 'array',
items: {$ref: '/signedOrderSchema'},
};

View File

@@ -1,22 +0,0 @@
export const tokenSchema = {
id: '/token',
properties: {
name: {type: 'string'},
symbol: {type: 'string'},
decimals: {type: 'number'},
address: {$ref: '/addressSchema'},
url: {
oneOf: [
{
type: 'string',
format: 'uri',
},
{
enum: [''],
},
],
},
},
required: ['name', 'symbol', 'decimals', 'address', 'url'],
type: 'object',
};

View File

@@ -0,0 +1,25 @@
import * as Web3 from 'web3';
import {JSONRPCPayload} from '../types';
/*
* This class implements the web3-provider-engine subprovider interface and returns
* that the provider has no addresses when queried.
* Source: https://github.com/MetaMask/provider-engine/blob/master/subproviders/subprovider.js
*/
export class EmptyWalletSubProvider {
public handleRequest(payload: JSONRPCPayload, next: () => void, end: (err: Error|null, result: any) => void) {
switch (payload.method) {
case 'eth_accounts':
end(null, []);
return;
default:
next();
return;
}
}
// Required to implement this method despite not needing it for this subprovider
public setEngine(engine: any) {
// noop
}
}

View File

@@ -1,28 +1,21 @@
import * as _ from 'lodash';
import * as Web3 from 'web3';
// Utility function to create a K:V from a list of strings
// Adapted from: https://basarat.gitbooks.io/typescript/content/docs/types/literal-types.html
function strEnum(values: string[]): {[key: string]: string} {
return _.reduce(values, (result, key) => {
result[key] = key;
return result;
}, Object.create(null));
export enum ZeroExError {
ContractDoesNotExist = 'CONTRACT_DOES_NOT_EXIST',
ExchangeContractDoesNotExist = 'EXCHANGE_CONTRACT_DOES_NOT_EXIST',
UnhandledError = 'UNHANDLED_ERROR',
UserHasNoAssociatedAddress = 'USER_HAS_NO_ASSOCIATED_ADDRESSES',
InvalidSignature = 'INVALID_SIGNATURE',
ContractNotDeployedOnNetwork = 'CONTRACT_NOT_DEPLOYED_ON_NETWORK',
ZrxNotInTokenRegistry = 'ZRX_NOT_IN_TOKEN_REGISTRY',
InsufficientAllowanceForTransfer = 'INSUFFICIENT_ALLOWANCE_FOR_TRANSFER',
InsufficientBalanceForTransfer = 'INSUFFICIENT_BALANCE_FOR_TRANSFER',
InsufficientEthBalanceForDeposit = 'INSUFFICIENT_ETH_BALANCE_FOR_DEPOSIT',
InsufficientWEthBalanceForWithdrawal = 'INSUFFICIENT_WETH_BALANCE_FOR_WITHDRAWAL',
InvalidJump = 'INVALID_JUMP',
OutOfGas = 'OUT_OF_GAS',
}
export const ZeroExError = strEnum([
'CONTRACT_DOES_NOT_EXIST',
'UNHANDLED_ERROR',
'USER_HAS_NO_ASSOCIATED_ADDRESSES',
'INVALID_SIGNATURE',
'CONTRACT_NOT_DEPLOYED_ON_NETWORK',
'ZRX_NOT_IN_TOKEN_REGISTRY',
'INSUFFICIENT_ALLOWANCE_FOR_TRANSFER',
'INSUFFICIENT_BALANCE_FOR_TRANSFER',
'INVALID_JUMP',
'OUT_OF_GAS',
]);
export type ZeroExError = keyof typeof ZeroExError;
/**
* Elliptic Curve signature
*/
@@ -44,7 +37,7 @@ export interface ContractEventObj {
watch: (eventWatch: EventCallback) => void;
stopWatching: () => void;
}
export type CreateContractEvent = (indexFilterValues: IndexFilterValues,
export type CreateContractEvent = (indexFilterValues: IndexedFilterValues,
subscriptionOpts: SubscriptionOpts) => ContractEventObj;
export interface ExchangeContract extends ContractInstance {
isValidSignature: {
@@ -54,70 +47,85 @@ export interface ExchangeContract extends ContractInstance {
LogFill: CreateContractEvent;
LogCancel: CreateContractEvent;
LogError: CreateContractEvent;
ZRX: {
ZRX_TOKEN_CONTRACT: {
call: () => Promise<string>;
};
getUnavailableValueT: {
call: (orderHash: string) => BigNumber.BigNumber;
getUnavailableTakerTokenAmount: {
call: (orderHash: string) => Promise<BigNumber.BigNumber>;
};
isRoundingError: {
call: (takerTokenAmount: BigNumber.BigNumber, fillTakerAmount: BigNumber.BigNumber,
call: (fillTakerAmount: BigNumber.BigNumber, takerTokenAmount: BigNumber.BigNumber,
makerTokenAmount: BigNumber.BigNumber, txOpts?: TxOpts) => Promise<boolean>;
};
fill: {
(orderAddresses: OrderAddresses, orderValues: OrderValues, fillAmount: BigNumber.BigNumber,
shouldCheckTransfer: boolean, v: number, r: string, s: string, txOpts?: TxOpts): ContractResponse;
estimateGas: (orderAddresses: OrderAddresses, orderValues: OrderValues, fillAmount: BigNumber.BigNumber,
shouldCheckTransfer: boolean, v: number, r: string, s: string, txOpts?: TxOpts) => number;
fillOrder: {
(orderAddresses: OrderAddresses, orderValues: OrderValues, fillTakerTokenAmount: BigNumber.BigNumber,
shouldThrowOnInsufficientBalanceOrAllowance: boolean,
v: number, r: string, s: string, txOpts?: TxOpts): Promise<ContractResponse>;
estimateGas: (orderAddresses: OrderAddresses, orderValues: OrderValues,
fillTakerTokenAmount: BigNumber.BigNumber,
shouldThrowOnInsufficientBalanceOrAllowance: boolean,
v: number, r: string, s: string, txOpts?: TxOpts) => Promise<number>;
};
batchFill: {
(orderAddresses: OrderAddresses[], orderValues: OrderValues[], fillAmounts: BigNumber.BigNumber[],
shouldCheckTransfer: boolean, v: number[], r: string[], s: string[], txOpts?: TxOpts): ContractResponse;
estimateGas: (orderAddresses: OrderAddresses[], orderValues: OrderValues[], fillAmounts: BigNumber.BigNumber[],
shouldCheckTransfer: boolean, v: number[], r: string[], s: string[], txOpts?: TxOpts) => number;
batchFillOrders: {
(orderAddresses: OrderAddresses[], orderValues: OrderValues[], fillTakerTokenAmounts: BigNumber.BigNumber[],
shouldThrowOnInsufficientBalanceOrAllowance: boolean,
v: number[], r: string[], s: string[], txOpts?: TxOpts): Promise<ContractResponse>;
estimateGas: (orderAddresses: OrderAddresses[], orderValues: OrderValues[],
fillTakerTokenAmounts: BigNumber.BigNumber[],
shouldThrowOnInsufficientBalanceOrAllowance: boolean,
v: number[], r: string[], s: string[], txOpts?: TxOpts) => Promise<number>;
};
fillUpTo: {
(orderAddresses: OrderAddresses[], orderValues: OrderValues[], fillAmount: BigNumber.BigNumber,
shouldCheckTransfer: boolean, v: number[], r: string[], s: string[], txOpts?: TxOpts): ContractResponse;
estimateGas: (orderAddresses: OrderAddresses[], orderValues: OrderValues[], fillAmount: BigNumber.BigNumber,
shouldCheckTransfer: boolean, v: number[], r: string[], s: string[], txOpts?: TxOpts) => number;
fillOrdersUpTo: {
(orderAddresses: OrderAddresses[], orderValues: OrderValues[], fillTakerTokenAmount: BigNumber.BigNumber,
shouldThrowOnInsufficientBalanceOrAllowance: boolean,
v: number[], r: string[], s: string[], txOpts?: TxOpts): Promise<ContractResponse>;
estimateGas: (orderAddresses: OrderAddresses[], orderValues: OrderValues[],
fillTakerTokenAmount: BigNumber.BigNumber,
shouldThrowOnInsufficientBalanceOrAllowance: boolean,
v: number[], r: string[], s: string[], txOpts?: TxOpts) => Promise<number>;
};
cancel: {
(orderAddresses: OrderAddresses, orderValues: OrderValues, cancelAmount: BigNumber.BigNumber,
txOpts?: TxOpts): ContractResponse;
estimateGas: (orderAddresses: OrderAddresses, orderValues: OrderValues, cancelAmount: BigNumber.BigNumber,
txOpts?: TxOpts) => number;
cancelOrder: {
(orderAddresses: OrderAddresses, orderValues: OrderValues, cancelTakerTokenAmount: BigNumber.BigNumber,
txOpts?: TxOpts): Promise<ContractResponse>;
estimateGas: (orderAddresses: OrderAddresses, orderValues: OrderValues,
cancelTakerTokenAmount: BigNumber.BigNumber,
txOpts?: TxOpts) => Promise<number>;
};
batchCancel: {
(orderAddresses: OrderAddresses[], orderValues: OrderValues[], cancelAmount: BigNumber.BigNumber[],
txOpts?: TxOpts): ContractResponse;
estimateGas: (orderAddresses: OrderAddresses[], orderValues: OrderValues[], cancelAmount: BigNumber.BigNumber[],
txOpts?: TxOpts) => number;
batchCancelOrders: {
(orderAddresses: OrderAddresses[], orderValues: OrderValues[], cancelTakerTokenAmounts: BigNumber.BigNumber[],
txOpts?: TxOpts): Promise<ContractResponse>;
estimateGas: (orderAddresses: OrderAddresses[], orderValues: OrderValues[],
cancelTakerTokenAmounts: BigNumber.BigNumber[],
txOpts?: TxOpts) => Promise<number>;
};
fillOrKill: {
(orderAddresses: OrderAddresses, orderValues: OrderValues, fillAmount: BigNumber.BigNumber,
v: number, r: string, s: string, txOpts?: TxOpts): ContractResponse;
estimateGas: (orderAddresses: OrderAddresses, orderValues: OrderValues, fillAmount: BigNumber.BigNumber,
v: number, r: string, s: string, txOpts?: TxOpts) => number;
fillOrKillOrder: {
(orderAddresses: OrderAddresses, orderValues: OrderValues, fillTakerTokenAmount: BigNumber.BigNumber,
v: number, r: string, s: string, txOpts?: TxOpts): Promise<ContractResponse>;
estimateGas: (orderAddresses: OrderAddresses, orderValues: OrderValues,
fillTakerTokenAmount: BigNumber.BigNumber,
v: number, r: string, s: string, txOpts?: TxOpts) => Promise<number>;
};
batchFillOrKill: {
(orderAddresses: OrderAddresses[], orderValues: OrderValues[], fillValuesT: BigNumber.BigNumber[],
v: number[], r: string[], s: string[], txOpts: TxOpts): ContractResponse;
estimateGas: (orderAddresses: OrderAddresses[], orderValues: OrderValues[], fillValuesT: BigNumber.BigNumber[],
v: number[], r: string[], s: string[], txOpts?: TxOpts) => number;
batchFillOrKillOrders: {
(orderAddresses: OrderAddresses[], orderValues: OrderValues[], fillTakerTokenAmounts: BigNumber.BigNumber[],
v: number[], r: string[], s: string[], txOpts: TxOpts): Promise<ContractResponse>;
estimateGas: (orderAddresses: OrderAddresses[], orderValues: OrderValues[],
fillTakerTokenAmounts: BigNumber.BigNumber[],
v: number[], r: string[], s: string[], txOpts?: TxOpts) => Promise<number>;
};
filled: {
call: (orderHash: string) => BigNumber.BigNumber;
call: (orderHash: string) => Promise<BigNumber.BigNumber>;
};
cancelled: {
call: (orderHash: string) => BigNumber.BigNumber;
call: (orderHash: string) => Promise<BigNumber.BigNumber>;
};
getOrderHash: {
call: (orderAddresses: OrderAddresses, orderValues: OrderValues) => string;
call: (orderAddresses: OrderAddresses, orderValues: OrderValues) => Promise<string>;
};
}
export interface TokenContract extends ContractInstance {
Transfer: CreateContractEvent;
Approval: CreateContractEvent;
balanceOf: {
call: (address: string) => Promise<BigNumber.BigNumber>;
};
@@ -127,7 +135,7 @@ export interface TokenContract extends ContractInstance {
transfer: (toAddress: string, amountInBaseUnits: BigNumber.BigNumber, txOpts?: TxOpts) => Promise<boolean>;
transferFrom: (fromAddress: string, toAddress: string, amountInBaseUnits: BigNumber.BigNumber,
txOpts?: TxOpts) => Promise<boolean>;
approve: (proxyAddress: string, amountInBaseUnits: BigNumber.BigNumber, txOpts?: TxOpts) => void;
approve: (proxyAddress: string, amountInBaseUnits: BigNumber.BigNumber, txOpts?: TxOpts) => Promise<void>;
}
export interface TokenRegistryContract extends ContractInstance {
@@ -137,13 +145,38 @@ export interface TokenRegistryContract extends ContractInstance {
getTokenAddresses: {
call: () => Promise<string[]>;
};
getTokenAddressBySymbol: {
call: (symbol: string) => Promise<string>;
};
getTokenAddressByName: {
call: (name: string) => Promise<string>;
};
getTokenBySymbol: {
call: (symbol: string) => Promise<TokenMetadata>;
};
getTokenByName: {
call: (name: string) => Promise<TokenMetadata>;
};
}
export const SolidityTypes = strEnum([
'address',
'uint256',
]);
export type SolidityTypes = keyof typeof SolidityTypes;
export interface EtherTokenContract extends ContractInstance {
deposit: (txOpts: TxOpts) => Promise<void>;
withdraw: (amount: BigNumber.BigNumber, txOpts: TxOpts) => Promise<void>;
}
export interface TokenTransferProxyContract extends ContractInstance {
getAuthorizedAddresses: {
call: () => Promise<string[]>;
};
authorized: {
call: (address: string) => Promise<boolean>;
};
}
export enum SolidityTypes {
Address = 'address',
Uint256 = 'uint256',
}
export enum ExchangeContractErrCodes {
ERROR_FILL_EXPIRED, // Order has already expired
@@ -154,28 +187,29 @@ export enum ExchangeContractErrCodes {
ERROR_CANCEL_NO_VALUE, // Order has already been fully filled or cancelled
}
export const ExchangeContractErrs = strEnum([
'ORDER_FILL_EXPIRED',
'ORDER_CANCEL_EXPIRED',
'ORDER_CANCEL_AMOUNT_ZERO',
'ORDER_ALREADY_CANCELLED_OR_FILLED',
'ORDER_REMAINING_FILL_AMOUNT_ZERO',
'ORDER_FILL_ROUNDING_ERROR',
'FILL_BALANCE_ALLOWANCE_ERROR',
'INSUFFICIENT_TAKER_BALANCE',
'INSUFFICIENT_TAKER_ALLOWANCE',
'INSUFFICIENT_MAKER_BALANCE',
'INSUFFICIENT_MAKER_ALLOWANCE',
'INSUFFICIENT_TAKER_FEE_BALANCE',
'INSUFFICIENT_TAKER_FEE_ALLOWANCE',
'INSUFFICIENT_MAKER_FEE_BALANCE',
'INSUFFICIENT_MAKER_FEE_ALLOWANCE',
'TRANSACTION_SENDER_IS_NOT_FILL_ORDER_TAKER',
'MULTIPLE_MAKERS_IN_SINGLE_CANCEL_BATCH_DISALLOWED',
'INSUFFICIENT_REMAINING_FILL_AMOUNT',
'MULTIPLE_TAKER_TOKENS_IN_FILL_UP_TO_DISALLOWED',
]);
export type ExchangeContractErrs = keyof typeof ExchangeContractErrs;
export enum ExchangeContractErrs {
OrderFillExpired = 'ORDER_FILL_EXPIRED',
OrderCancelExpired = 'ORDER_CANCEL_EXPIRED',
OrderCancelAmountZero = 'ORDER_CANCEL_AMOUNT_ZERO',
OrderAlreadyCancelledOrFilled = 'ORDER_ALREADY_CANCELLED_OR_FILLED',
OrderFillAmountZero = 'ORDER_FILL_AMOUNT_ZERO',
OrderRemainingFillAmountZero = 'ORDER_REMAINING_FILL_AMOUNT_ZERO',
OrderFillRoundingError = 'ORDER_FILL_ROUNDING_ERROR',
FillBalanceAllowanceError = 'FILL_BALANCE_ALLOWANCE_ERROR',
InsufficientTakerBalance = 'INSUFFICIENT_TAKER_BALANCE',
InsufficientTakerAllowance = 'INSUFFICIENT_TAKER_ALLOWANCE',
InsufficientMakerBalance = 'INSUFFICIENT_MAKER_BALANCE',
InsufficientMakerAllowance = 'INSUFFICIENT_MAKER_ALLOWANCE',
InsufficientTakerFeeBalance = 'INSUFFICIENT_TAKER_FEE_BALANCE',
InsufficientTakerFeeAllowance = 'INSUFFICIENT_TAKER_FEE_ALLOWANCE',
InsufficientMakerFeeBalance = 'INSUFFICIENT_MAKER_FEE_BALANCE',
InsufficientMakerFeeAllowance = 'INSUFFICIENT_MAKER_FEE_ALLOWANCE',
TransactionSenderIsNotFillOrderTaker = 'TRANSACTION_SENDER_IS_NOT_FILL_ORDER_TAKER',
MultipleMakersInSingleCancelBatchDisallowed = 'MULTIPLE_MAKERS_IN_SINGLE_CANCEL_BATCH_DISALLOWED',
InsufficientRemainingFillAmount = 'INSUFFICIENT_REMAINING_FILL_AMOUNT',
MultipleTakerTokensInFillUpToDisallowed = 'MULTIPLE_TAKER_TOKENS_IN_FILL_UP_TO_DISALLOWED',
BatchOrdersMustHaveSameExchangeAddress = 'BATCH_ORDERS_MUST_HAVE_SAME_EXCHANGE_ADDRESS',
}
export interface ContractResponse {
logs: ContractEvent[];
@@ -190,9 +224,51 @@ export interface ContractEvent {
address: string;
type: string;
event: string;
args: any;
args: ContractEventArgs;
}
export interface LogFillContractEventArgs {
maker: string;
taker: string;
feeRecipient: string;
makerToken: string;
takerToken: string;
filledMakerTokenAmount: BigNumber.BigNumber;
filledTakerTokenAmount: BigNumber.BigNumber;
paidMakerFee: BigNumber.BigNumber;
paidTakerFee: BigNumber.BigNumber;
tokens: string;
orderHash: string;
}
export interface LogCancelContractEventArgs {
maker: string;
feeRecipient: string;
makerToken: string;
takerToken: string;
cancelledMakerTokenAmount: BigNumber.BigNumber;
cancelledTakerTokenAmount: BigNumber.BigNumber;
tokens: string;
orderHash: string;
}
export interface LogErrorContractEventArgs {
errorId: BigNumber.BigNumber;
orderHash: string;
}
export type ExchangeContractEventArgs = LogFillContractEventArgs|LogCancelContractEventArgs|LogErrorContractEventArgs;
export interface TransferContractEventArgs {
_from: string;
_to: string;
_value: BigNumber.BigNumber;
}
export interface ApprovalContractEventArgs {
_owner: string;
_spender: string;
_value: BigNumber.BigNumber;
}
export type TokenContractEventArgs = TransferContractEventArgs|ApprovalContractEventArgs;
export type ContractEventArgs = ExchangeContractEventArgs|TokenContractEventArgs;
export type ContractEventArg = string|BigNumber.BigNumber;
export interface Order {
maker: string;
taker: string;
@@ -203,6 +279,7 @@ export interface Order {
makerTokenAddress: string;
takerTokenAddress: string;
salt: BigNumber.BigNumber;
exchangeContractAddress: string;
feeRecipient: string;
expirationUnixTimestampSec: BigNumber.BigNumber;
}
@@ -211,35 +288,39 @@ export interface SignedOrder extends Order {
ecSignature: ECSignature;
}
// [address, name, symbol, projectUrl, decimals, ipfsHash, swarmHash]
export type TokenMetadata = [string, string, string, string, BigNumber.BigNumber, string, string];
// [address, name, symbol, decimals, ipfsHash, swarmHash]
export type TokenMetadata = [string, string, string, BigNumber.BigNumber, string, string];
export interface Token {
name: string;
address: string;
symbol: string;
decimals: number;
url: string;
}
export interface TxOpts {
from: string;
gas?: number;
value?: BigNumber.BigNumber;
}
export interface TokenAddressBySymbol {
[symbol: string]: string;
}
export const ExchangeEvents = strEnum([
'LogFill',
'LogCancel',
'LogError',
]);
export type ExchangeEvents = keyof typeof ExchangeEvents;
export enum ExchangeEvents {
LogFill = 'LogFill',
LogCancel = 'LogCancel',
LogError = 'LogError',
}
export interface IndexFilterValues {
[index: string]: any;
export enum TokenEvents {
Transfer = 'Transfer',
Approval = 'Approval',
}
export interface IndexedFilterValues {
[index: string]: ContractEventArg;
}
export type BlockParam = 'latest'|'earliest'|'pending'|number;
@@ -275,3 +356,36 @@ export interface ContractInstance {
export interface Artifact {
networks: {[networkId: number]: any};
}
export interface ContractEventEmitter {
watch: (eventCallback: EventCallback) => void;
stopWatchingAsync: () => Promise<void>;
}
/**
* We re-export the `Web3.Provider` type specified in the Web3 Typescript typings
* since it is the type of the `provider` argument to the `ZeroEx` constructor.
* It is however a `Web3` library type, not a native `0x.js` type.
*/
export type Web3Provider = Web3.Provider;
export interface ExchangeContractByAddress {
[address: string]: ExchangeContract;
}
export interface ContractArtifact {
networks: {
[networkId: number]: {
address: string;
};
};
}
export interface JSONRPCPayload {
params: any[];
method: string;
}
export interface ZeroExConfig {
gasPrice?: BigNumber.BigNumber; // Gas price to use with every transaction
}

View File

@@ -2,8 +2,7 @@ import * as _ from 'lodash';
import * as BigNumber from 'bignumber.js';
import * as Web3 from 'web3';
import {Web3Wrapper} from '../web3_wrapper';
import {SchemaValidator} from './schema_validator';
import {utils} from './utils';
import {SchemaValidator, Schema} from '0x-json-schemas';
const HEX_REGEX = /^0x[0-9A-F]*$/i;
@@ -25,18 +24,33 @@ export const assert = {
isETHAddressHex(variableName: string, value: string): void {
const web3 = new Web3();
this.assert(web3.isAddress(value), this.typeAssertionMessage(variableName, 'ETHAddressHex', value));
this.assert(
web3.isAddress(value) && !web3.isChecksumAddress(value),
`Checksummed addresses are not supported. Convert ${variableName} to lower case before passing`,
);
},
doesBelongToStringEnum(variableName: string, value: string,
stringEnum: any /* There is no base type for every string enum */): void {
const doesBelongToStringEnum = !_.isUndefined(stringEnum[value]);
const enumValues = _.keys(stringEnum);
const enumValuesAsStrings = _.map(enumValues, enumValue => `'${enumValue}'`);
const enumValuesAsString = enumValuesAsStrings.join(', ');
assert.assert(
doesBelongToStringEnum,
`Expected ${variableName} to be one of: ${enumValuesAsString}, encountered: ${value}`,
);
},
async isSenderAddressAsync(variableName: string, senderAddressHex: string,
web3Wrapper: Web3Wrapper): Promise<void> {
assert.isETHAddressHex(variableName, senderAddressHex);
const isSenderAddressAvailable = await web3Wrapper.isSenderAddressAvailableAsync(senderAddressHex);
assert.assert(isSenderAddressAvailable,
`Specified ${variableName} ${senderAddressHex} isn't available through the supplied web3 instance`,
`Specified ${variableName} ${senderAddressHex} isn't available through the supplied web3 provider`,
);
},
async isUserAddressAvailableAsync(web3Wrapper: Web3Wrapper): Promise<void> {
const availableAddresses = await web3Wrapper.getAvailableAddressesAsync();
this.assert(!_.isEmpty(availableAddresses), 'No addresses were available on the provided web3 instance');
this.assert(!_.isEmpty(availableAddresses), 'No addresses were available on the provided web3 provider');
},
hasAtMostOneUniqueValue(value: any[], errMsg: string): void {
this.assert(_.uniq(value).length <= 1, errMsg);
@@ -44,13 +58,14 @@ export const assert = {
isNumber(variableName: string, value: number): void {
this.assert(_.isFinite(value), this.typeAssertionMessage(variableName, 'number', value));
},
isValidOrderHash(variableName: string, value: string): void {
this.assert(utils.isValidOrderHash(value), this.typeAssertionMessage(variableName, 'orderHash', value));
},
isBoolean(variableName: string, value: boolean): void {
this.assert(_.isBoolean(value), this.typeAssertionMessage(variableName, 'boolean', value));
},
doesConformToSchema(variableName: string, value: object, schema: Schema): void {
isWeb3Provider(variableName: string, value: Web3.Provider): void {
const isWeb3Provider = _.isFunction((value as any).send) || _.isFunction((value as any).sendAsync);
this.assert(isWeb3Provider, this.typeAssertionMessage(variableName, 'Web3.Provider', value));
},
doesConformToSchema(variableName: string, value: any, schema: Schema): void {
const schemaValidator = new SchemaValidator();
const validationResult = schemaValidator.validate(value, schema);
const hasValidationErrors = validationResult.errors.length > 0;

View File

@@ -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),
};

View File

@@ -21,10 +21,10 @@ export const decorators = {
return result;
} catch (error) {
if (_.includes(error.message, constants.INVALID_JUMP_PATTERN)) {
throw new Error(ZeroExError.INVALID_JUMP);
throw new Error(ZeroExError.InvalidJump);
}
if (_.includes(error.message, constants.OUT_OF_GAS_PATTERN)) {
throw new Error(ZeroExError.OUT_OF_GAS);
throw new Error(ZeroExError.OutOfGas);
}
throw error;
}

41
src/utils/event_utils.ts Normal file
View File

@@ -0,0 +1,41 @@
import * as _ from 'lodash';
import {EventCallback, ContractEventArg, ContractEvent, ContractEventObj, ContractEventEmitter} from '../types';
import * as BigNumber from 'bignumber.js';
import promisify = require('es6-promisify');
export const eventUtils = {
wrapEventEmitter(event: ContractEventObj): ContractEventEmitter {
const watch = (eventCallback: EventCallback) => {
const bignumberWrappingEventCallback = eventUtils._getBigNumberWrappingEventCallback(eventCallback);
event.watch(bignumberWrappingEventCallback);
};
const zeroExEvent = {
watch,
stopWatchingAsync: async () => {
await promisify(event.stopWatching, event)();
},
};
return zeroExEvent;
},
/**
* Wraps eventCallback function so that all the BigNumber arguments are wrapped in a newer version of BigNumber.
* @param eventCallback Event callback function to be wrapped
* @return Wrapped event callback function
*/
_getBigNumberWrappingEventCallback(eventCallback: EventCallback): EventCallback {
const bignumberWrappingEventCallback = (err: Error, event: ContractEvent) => {
if (_.isNull(err)) {
const wrapIfBigNumber = (value: ContractEventArg): ContractEventArg => {
// HACK: The old version of BigNumber used by Web3@0.19.0 does not support the `isBigNumber`
// and checking for a BigNumber instance using `instanceof` does not work either. We therefore
// check if the value constructor is a bignumber constructor.
const isWeb3BigNumber = _.startsWith(value.constructor.toString(), 'function BigNumber(');
return isWeb3BigNumber ? new BigNumber(value) : value;
};
event.args = _.mapValues(event.args, wrapIfBigNumber);
}
eventCallback(err, event);
};
return bignumberWrappingEventCallback;
},
};

View File

@@ -0,0 +1,146 @@
import {ExchangeContractErrs, SignedOrder, Order} from '../types';
import {TokenWrapper} from '../contract_wrappers/token_wrapper';
import {ExchangeWrapper} from '../contract_wrappers/exchange_wrapper';
import {utils} from '../utils/utils';
import {constants} from '../utils/constants';
export class OrderValidationUtils {
private tokenWrapper: TokenWrapper;
private exchangeWrapper: ExchangeWrapper;
constructor(tokenWrapper: TokenWrapper, exchangeWrapper: ExchangeWrapper) {
this.tokenWrapper = tokenWrapper;
this.exchangeWrapper = exchangeWrapper;
}
public async validateFillOrderThrowIfInvalidAsync(signedOrder: SignedOrder,
fillTakerTokenAmount: BigNumber.BigNumber,
takerAddress: string,
zrxTokenAddress: string): Promise<void> {
if (fillTakerTokenAmount.eq(0)) {
throw new Error(ExchangeContractErrs.OrderFillAmountZero);
}
const orderHash = utils.getOrderHashHex(signedOrder);
const unavailableTakerTokenAmount = await this.exchangeWrapper.getUnavailableTakerAmountAsync(orderHash);
if (signedOrder.makerTokenAmount.eq(unavailableTakerTokenAmount)) {
throw new Error(ExchangeContractErrs.OrderRemainingFillAmountZero);
}
if (signedOrder.taker !== constants.NULL_ADDRESS && signedOrder.taker !== takerAddress) {
throw new Error(ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker);
}
const currentUnixTimestampSec = utils.getCurrentUnixTimestamp();
if (signedOrder.expirationUnixTimestampSec.lessThan(currentUnixTimestampSec)) {
throw new Error(ExchangeContractErrs.OrderFillExpired);
}
await this.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
signedOrder, fillTakerTokenAmount, takerAddress, zrxTokenAddress,
);
const wouldRoundingErrorOccur = await this.exchangeWrapper.isRoundingErrorAsync(
fillTakerTokenAmount, signedOrder.takerTokenAmount, signedOrder.makerTokenAmount,
);
if (wouldRoundingErrorOccur) {
throw new Error(ExchangeContractErrs.OrderFillRoundingError);
}
}
public async validateFillOrKillOrderThrowIfInvalidAsync(signedOrder: SignedOrder,
fillTakerTokenAmount: BigNumber.BigNumber,
takerAddress: string,
zrxTokenAddress: string): Promise<void> {
await this.validateFillOrderThrowIfInvalidAsync(
signedOrder, fillTakerTokenAmount, takerAddress, zrxTokenAddress,
);
// Check that fillValue available >= fillTakerAmount
const orderHashHex = utils.getOrderHashHex(signedOrder);
const unavailableTakerAmount = await this.exchangeWrapper.getUnavailableTakerAmountAsync(orderHashHex);
const remainingTakerAmount = signedOrder.takerTokenAmount.minus(unavailableTakerAmount);
if (remainingTakerAmount < fillTakerTokenAmount) {
throw new Error(ExchangeContractErrs.InsufficientRemainingFillAmount);
}
}
public async validateCancelOrderThrowIfInvalidAsync(order: Order,
cancelTakerTokenAmount: BigNumber.BigNumber,
unavailableTakerTokenAmount: BigNumber.BigNumber,
): Promise<void> {
if (cancelTakerTokenAmount.eq(0)) {
throw new Error(ExchangeContractErrs.OrderCancelAmountZero);
}
if (order.takerTokenAmount.eq(unavailableTakerTokenAmount)) {
throw new Error(ExchangeContractErrs.OrderAlreadyCancelledOrFilled);
}
const currentUnixTimestampSec = utils.getCurrentUnixTimestamp();
if (order.expirationUnixTimestampSec.lessThan(currentUnixTimestampSec)) {
throw new Error(ExchangeContractErrs.OrderCancelExpired);
}
}
public async validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
signedOrder: SignedOrder, fillTakerAmount: BigNumber.BigNumber, senderAddress: string, zrxTokenAddress: string,
): Promise<void> {
await this.validateFillOrderMakerBalancesAllowancesThrowIfInvalidAsync(
signedOrder, fillTakerAmount, zrxTokenAddress,
);
await this.validateFillOrderTakerBalancesAllowancesThrowIfInvalidAsync(
signedOrder, fillTakerAmount, senderAddress, zrxTokenAddress,
);
}
private async validateFillOrderMakerBalancesAllowancesThrowIfInvalidAsync(
signedOrder: SignedOrder, fillTakerAmount: BigNumber.BigNumber, zrxTokenAddress: string,
): Promise<void> {
const makerBalance = await this.tokenWrapper.getBalanceAsync(signedOrder.makerTokenAddress, signedOrder.maker);
const makerAllowance = await this.tokenWrapper.getProxyAllowanceAsync(
signedOrder.makerTokenAddress, signedOrder.maker);
const isMakerTokenZRX = signedOrder.makerTokenAddress === zrxTokenAddress;
// exchangeRate is the price of one maker token denominated in taker tokens
const exchangeRate = signedOrder.takerTokenAmount.div(signedOrder.makerTokenAmount);
const fillMakerAmount = fillTakerAmount.div(exchangeRate);
const requiredMakerAmount = isMakerTokenZRX ? fillMakerAmount.plus(signedOrder.makerFee) : fillMakerAmount;
if (requiredMakerAmount.greaterThan(makerBalance)) {
throw new Error(ExchangeContractErrs.InsufficientMakerBalance);
}
if (requiredMakerAmount.greaterThan(makerAllowance)) {
throw new Error(ExchangeContractErrs.InsufficientMakerAllowance);
}
if (!isMakerTokenZRX) {
const makerZRXBalance = await this.tokenWrapper.getBalanceAsync(zrxTokenAddress, signedOrder.maker);
const makerZRXAllowance = await this.tokenWrapper.getProxyAllowanceAsync(
zrxTokenAddress, signedOrder.maker);
if (signedOrder.makerFee.greaterThan(makerZRXBalance)) {
throw new Error(ExchangeContractErrs.InsufficientMakerFeeBalance);
}
if (signedOrder.makerFee.greaterThan(makerZRXAllowance)) {
throw new Error(ExchangeContractErrs.InsufficientMakerFeeAllowance);
}
}
}
private async validateFillOrderTakerBalancesAllowancesThrowIfInvalidAsync(
signedOrder: SignedOrder, fillTakerAmount: BigNumber.BigNumber, senderAddress: string, zrxTokenAddress: string,
): Promise<void> {
const takerBalance = await this.tokenWrapper.getBalanceAsync(signedOrder.takerTokenAddress, senderAddress);
const takerAllowance = await this.tokenWrapper.getProxyAllowanceAsync(
signedOrder.takerTokenAddress, senderAddress);
const isTakerTokenZRX = signedOrder.takerTokenAddress === zrxTokenAddress;
const requiredTakerAmount = isTakerTokenZRX ? fillTakerAmount.plus(signedOrder.takerFee) : fillTakerAmount;
if (requiredTakerAmount.greaterThan(takerBalance)) {
throw new Error(ExchangeContractErrs.InsufficientTakerBalance);
}
if (requiredTakerAmount.greaterThan(takerAllowance)) {
throw new Error(ExchangeContractErrs.InsufficientTakerAllowance);
}
if (!isTakerTokenZRX) {
const takerZRXBalance = await this.tokenWrapper.getBalanceAsync(zrxTokenAddress, senderAddress);
const takerZRXAllowance = await this.tokenWrapper.getProxyAllowanceAsync(zrxTokenAddress, senderAddress);
if (signedOrder.takerFee.greaterThan(takerZRXBalance)) {
throw new Error(ExchangeContractErrs.InsufficientTakerFeeBalance);
}
if (signedOrder.takerFee.greaterThan(takerZRXAllowance)) {
throw new Error(ExchangeContractErrs.InsufficientTakerFeeAllowance);
}
}
}
}

View File

@@ -1,29 +0,0 @@
import {Validator, ValidatorResult} from 'jsonschema';
import {ecSignatureSchema, ecSignatureParameterSchema} from '../schemas/ec_signature_schema';
import {orderSchema, signedOrderSchema} from '../schemas/order_schemas';
import {addressSchema, numberSchema} from '../schemas/basic_type_schemas';
import {tokenSchema} from '../schemas/token_schema';
import {orderFillOrKillRequestsSchema} from '../schemas/order_fill_or_kill_requests_schema';
export class SchemaValidator {
private validator: Validator;
constructor() {
this.validator = new Validator();
this.validator.addSchema(tokenSchema, tokenSchema.id);
this.validator.addSchema(orderSchema, orderSchema.id);
this.validator.addSchema(numberSchema, numberSchema.id);
this.validator.addSchema(addressSchema, addressSchema.id);
this.validator.addSchema(ecSignatureSchema, ecSignatureSchema.id);
this.validator.addSchema(signedOrderSchema, signedOrderSchema.id);
this.validator.addSchema(ecSignatureParameterSchema, ecSignatureParameterSchema.id);
this.validator.addSchema(orderFillOrKillRequestsSchema, orderFillOrKillRequestsSchema.id);
}
// In order to validate a complex JS object using jsonschema, we must replace any complex
// sub-types (e.g BigNumber) with a simpler string representation. Since BigNumber and other
// complex types implement the `toString` method, we can stringify the object and
// then parse it. The resultant object can then be checked using jsonschema.
public validate(instance: any, schema: Schema): ValidatorResult {
const jsonSchemaCompatibleObject = JSON.parse(JSON.stringify(instance));
return this.validator.validate(jsonSchemaCompatibleObject, schema);
}
}

View File

@@ -0,0 +1,29 @@
import * as ethUtil from 'ethereumjs-util';
import {ECSignature} from '../types';
export const signatureUtils = {
parseSignatureHexAsVRS(signatureHex: string): ECSignature {
const signatureBuffer = ethUtil.toBuffer(signatureHex);
let v = signatureBuffer[0];
if (v < 27) {
v += 27;
}
const r = signatureBuffer.slice(1, 33);
const s = signatureBuffer.slice(33, 65);
const ecSignature: ECSignature = {
v,
r: ethUtil.bufferToHex(r),
s: ethUtil.bufferToHex(s),
};
return ecSignature;
},
parseSignatureHexAsRSV(signatureHex: string): ECSignature {
const {v, r, s} = ethUtil.fromRpcSig(signatureHex);
const ecSignature: ECSignature = {
v,
r: ethUtil.bufferToHex(r),
s: ethUtil.bufferToHex(s),
};
return ecSignature;
},
};

View File

@@ -1,9 +1,9 @@
import * as _ from 'lodash';
import * as BN from 'bn.js';
import * as ethABI from 'ethereumjs-abi';
import * as ethUtil from 'ethereumjs-util';
import {Order, SignedOrder, SolidityTypes} from '../types';
import * as BigNumber from 'bignumber.js';
import BN = require('bn.js');
export const utils = {
/**
@@ -22,27 +22,26 @@ export const utils = {
isParityNode(nodeVersion: string): boolean {
return _.includes(nodeVersion, 'Parity');
},
isValidOrderHash(orderHashHex: string) {
const isValid = /^0x[0-9A-F]{64}$/i.test(orderHashHex);
return isValid;
isTestRpc(nodeVersion: string): boolean {
return _.includes(nodeVersion, 'TestRPC');
},
spawnSwitchErr(name: string, value: any) {
spawnSwitchErr(name: string, value: any): Error {
return new Error(`Unexpected switch value: ${value} encountered for ${name}`);
},
getOrderHashHex(order: Order|SignedOrder, exchangeContractAddr: string): string {
getOrderHashHex(order: Order|SignedOrder): string {
const orderParts = [
{value: exchangeContractAddr, type: SolidityTypes.address},
{value: order.maker, type: SolidityTypes.address},
{value: order.taker, type: SolidityTypes.address},
{value: order.makerTokenAddress, type: SolidityTypes.address},
{value: order.takerTokenAddress, type: SolidityTypes.address},
{value: order.feeRecipient, type: SolidityTypes.address},
{value: utils.bigNumberToBN(order.makerTokenAmount), type: SolidityTypes.uint256},
{value: utils.bigNumberToBN(order.takerTokenAmount), type: SolidityTypes.uint256},
{value: utils.bigNumberToBN(order.makerFee), type: SolidityTypes.uint256},
{value: utils.bigNumberToBN(order.takerFee), type: SolidityTypes.uint256},
{value: utils.bigNumberToBN(order.expirationUnixTimestampSec), type: SolidityTypes.uint256},
{value: utils.bigNumberToBN(order.salt), type: SolidityTypes.uint256},
{value: order.exchangeContractAddress, type: SolidityTypes.Address},
{value: order.maker, type: SolidityTypes.Address},
{value: order.taker, type: SolidityTypes.Address},
{value: order.makerTokenAddress, type: SolidityTypes.Address},
{value: order.takerTokenAddress, type: SolidityTypes.Address},
{value: order.feeRecipient, type: SolidityTypes.Address},
{value: utils.bigNumberToBN(order.makerTokenAmount), type: SolidityTypes.Uint256},
{value: utils.bigNumberToBN(order.takerTokenAmount), type: SolidityTypes.Uint256},
{value: utils.bigNumberToBN(order.makerFee), type: SolidityTypes.Uint256},
{value: utils.bigNumberToBN(order.takerFee), type: SolidityTypes.Uint256},
{value: utils.bigNumberToBN(order.expirationUnixTimestampSec), type: SolidityTypes.Uint256},
{value: utils.bigNumberToBN(order.salt), type: SolidityTypes.Uint256},
];
const types = _.map(orderParts, o => o.type);
const values = _.map(orderParts, o => o.value);

View File

@@ -2,16 +2,16 @@ import * as _ from 'lodash';
import * as Web3 from 'web3';
import * as BigNumber from 'bignumber.js';
import promisify = require('es6-promisify');
import {ZeroExError} from './types';
import {assert} from './utils/assert';
export class Web3Wrapper {
private web3: Web3;
constructor(web3: Web3) {
private networkIdIfExists?: number;
constructor(provider: Web3.Provider) {
this.web3 = new Web3();
this.web3.setProvider(web3.currentProvider);
this.web3.setProvider(provider);
}
public setProvider(provider: Web3.Provider) {
delete this.networkIdIfExists;
this.web3.setProvider(provider);
}
public isAddress(address: string): boolean {
@@ -29,17 +29,26 @@ export class Web3Wrapper {
return this.web3.currentProvider;
}
public async getNetworkIdIfExistsAsync(): Promise<number|undefined> {
if (!_.isUndefined(this.networkIdIfExists)) {
return this.networkIdIfExists;
}
try {
const networkId = await this.getNetworkAsync();
return Number(networkId);
this.networkIdIfExists = Number(networkId);
return this.networkIdIfExists;
} catch (err) {
return undefined;
}
}
public async getBalanceInEthAsync(owner: string): Promise<BigNumber.BigNumber> {
const balanceInWei = await promisify(this.web3.eth.getBalance)(owner);
const balanceEth = this.web3.fromWei(balanceInWei, 'ether');
return balanceEth;
public toWei(ethAmount: BigNumber.BigNumber): BigNumber.BigNumber {
const balanceWei = this.web3.toWei(ethAmount, 'ether');
return balanceWei;
}
public async getBalanceInWeiAsync(owner: string): Promise<BigNumber.BigNumber> {
let balanceInWei = await promisify(this.web3.eth.getBalance)(owner);
balanceInWei = new BigNumber(balanceInWei);
return balanceInWei;
}
public async doesContractExistAtAddressAsync(address: string): Promise<boolean> {
const code = await promisify(this.web3.eth.getCode)(address);

View File

@@ -4,20 +4,18 @@ import {chaiSetup} from './utils/chai_setup';
import 'mocha';
import * as BigNumber from 'bignumber.js';
import * as Sinon from 'sinon';
import {ZeroEx} from '../src/0x';
import {ZeroEx, Order} from '../src';
import {constants} from './utils/constants';
import {Order} from '../src/types';
import {ECSignature} from '../src/types';
import {web3Factory} from './utils/web3_factory';
chaiSetup.configure();
const expect = chai.expect;
describe('ZeroEx library', () => {
const web3 = web3Factory.create();
const zeroEx = new ZeroEx(web3.currentProvider);
describe('#setProvider', () => {
it('overrides provider in nested web3s and invalidates contractInstances', async () => {
const web3 = web3Factory.create();
const zeroEx = new ZeroEx(web3);
// Instantiate the contract instances with the current provider
await (zeroEx.exchange as any)._getExchangeContractAsync();
await (zeroEx.tokenRegistry as any)._getTokenRegistryContractAsync();
@@ -33,7 +31,7 @@ describe('ZeroEx library', () => {
expect((zeroEx.exchange as any)._exchangeContractIfExists).to.be.undefined();
expect((zeroEx.tokenRegistry as any)._tokenRegistryContractIfExists).to.be.undefined();
// Check that all nested web3 instances return the updated provider
// Check that all nested web3 wrapper instances return the updated provider
const nestedWeb3WrapperProvider = (zeroEx as any)._web3Wrapper.getCurrentProvider();
expect((nestedWeb3WrapperProvider as any).zeroExTestId).to.be.a('number');
const exchangeWeb3WrapperProvider = (zeroEx.exchange as any)._web3Wrapper.getCurrentProvider();
@@ -52,8 +50,6 @@ describe('ZeroEx library', () => {
s: '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
};
const address = '0x5409ed021d9299bf6814279a6a1411a7e866a631';
const web3 = web3Factory.create();
const zeroEx = new ZeroEx(web3);
it('should return false if the data doesn\'t pertain to the signature & address', async () => {
expect(ZeroEx.isValidSignature('0x0', signature, address)).to.be.false();
return expect(
@@ -61,7 +57,7 @@ describe('ZeroEx library', () => {
).to.become(false);
});
it('should return false if the address doesn\'t pertain to the signature & data', async () => {
const validUnrelatedAddress = '0x8b0292B11a196601eD2ce54B665CaFEca0347D42';
const validUnrelatedAddress = '0x8b0292b11a196601ed2ce54b665cafeca0347d42';
expect(ZeroEx.isValidSignature(dataHex, signature, validUnrelatedAddress)).to.be.false();
return expect(
(zeroEx.exchange as any)._isValidSignatureUsingContractCallAsync(dataHex, signature,
@@ -127,15 +123,16 @@ describe('ZeroEx library', () => {
expect(baseUnitAmount).to.be.bignumber.equal(expectedUnitAmount);
});
});
describe('#getOrderHashHexAsync', () => {
const exchangeContractAddress = constants.NULL_ADDRESS;
const expectedOrderHash = '0x103a5e97dab5dbeb8f385636f86a7d1e458a7ccbe1bd194727f0b2f85ab116c7';
describe('#getOrderHashHex', () => {
const expectedOrderHash = '0x39da987067a3c9e5f1617694f1301326ba8c8b0498ebef5df4863bed394e3c83';
const fakeExchangeContractAddress = '0xb69e673309512a9d726f87304c6984054f87a93b';
const order: Order = {
maker: constants.NULL_ADDRESS,
taker: constants.NULL_ADDRESS,
feeRecipient: constants.NULL_ADDRESS,
makerTokenAddress: constants.NULL_ADDRESS,
takerTokenAddress: constants.NULL_ADDRESS,
exchangeContractAddress: fakeExchangeContractAddress,
salt: new BigNumber(0),
makerFee: new BigNumber(0),
takerFee: new BigNumber(0),
@@ -143,30 +140,14 @@ describe('ZeroEx library', () => {
takerTokenAmount: new BigNumber(0),
expirationUnixTimestampSec: new BigNumber(0),
};
let stubs: Sinon.SinonStub[] = [];
afterEach(() => {
// clean up any stubs after the test has completed
_.each(stubs, s => s.restore());
stubs = [];
});
it('calculates the order hash', async () => {
const web3 = web3Factory.create();
const zeroEx = new ZeroEx(web3);
stubs = [
Sinon.stub((zeroEx as any), '_getExchangeAddressAsync')
.returns(Promise.resolve(exchangeContractAddress)),
];
const orderHash = await zeroEx.getOrderHashHexAsync(order);
const orderHash = ZeroEx.getOrderHashHex(order);
expect(orderHash).to.be.equal(expectedOrderHash);
});
});
describe('#signOrderHashAsync', () => {
let stubs: Sinon.SinonStub[] = [];
let makerAddress: string;
const web3 = web3Factory.create();
const zeroEx = new ZeroEx(web3);
before(async () => {
const availableAddreses = await zeroEx.getAvailableAddressesAsync();
makerAddress = availableAddreses[0];
@@ -176,7 +157,7 @@ describe('ZeroEx library', () => {
_.each(stubs, s => s.restore());
stubs = [];
});
it ('Should return the correct ECSignature on TestPRC nodeVersion', async () => {
it('Should return the correct ECSignature', async () => {
const orderHash = '0x6927e990021d23b1eb7b8789f6a6feaf98fe104bb0cf8259421b79f9a34222b0';
const expectedECSignature = {
v: 27,
@@ -186,8 +167,7 @@ describe('ZeroEx library', () => {
const ecSignature = await zeroEx.signOrderHashAsync(orderHash, makerAddress);
expect(ecSignature).to.deep.equal(expectedECSignature);
});
it ('should return the correct ECSignature on Parity > V1.6.6', async () => {
const newParityNodeVersion = 'Parity//v1.6.7-beta-e128418-20170518/x86_64-macos/rustc1.17.0';
it('should return the correct ECSignature for signatureHex concatenated as R + S + V', async () => {
const orderHash = '0x34decbedc118904df65f379a175bb39ca18209d6ce41d5ed549d54e6e0a95004';
// tslint:disable-next-line: max-line-length
const signature = '0x22109d11d79cb8bf96ed88625e1cd9558800c4073332a9a02857499883ee5ce3050aa3cc1f2c435e67e114cdce54b9527b4f50548342401bc5d2b77adbdacb021b';
@@ -197,8 +177,6 @@ describe('ZeroEx library', () => {
s: '0x050aa3cc1f2c435e67e114cdce54b9527b4f50548342401bc5d2b77adbdacb02',
};
stubs = [
Sinon.stub((zeroEx as any)._web3Wrapper, 'getNodeVersionAsync')
.returns(Promise.resolve(newParityNodeVersion)),
Sinon.stub((zeroEx as any)._web3Wrapper, 'signTransactionAsync')
.returns(Promise.resolve(signature)),
Sinon.stub(ZeroEx, 'isValidSignature').returns(true),
@@ -207,8 +185,7 @@ describe('ZeroEx library', () => {
const ecSignature = await zeroEx.signOrderHashAsync(orderHash, makerAddress);
expect(ecSignature).to.deep.equal(expectedECSignature);
});
it ('should return the correct ECSignature on Parity < V1.6.6', async () => {
const newParityNodeVersion = 'Parity//v1.6.6-beta-8c6e3f3-20170411/x86_64-macos/rustc1.16.0';
it('should return the correct ECSignature for signatureHex concatenated as V + R + S', async () => {
const orderHash = '0xc793e33ffded933b76f2f48d9aa3339fc090399d5e7f5dec8d3660f5480793f7';
// tslint:disable-next-line: max-line-length
const signature = '0x1bc80bedc6756722672753413efdd749b5adbd4fd552595f59c13427407ee9aee02dea66f25a608bbae457e020fb6decb763deb8b7192abab624997242da248960';
@@ -218,8 +195,6 @@ describe('ZeroEx library', () => {
s: '0x2dea66f25a608bbae457e020fb6decb763deb8b7192abab624997242da248960',
};
stubs = [
Sinon.stub((zeroEx as any)._web3Wrapper, 'getNodeVersionAsync')
.returns(Promise.resolve(newParityNodeVersion)),
Sinon.stub((zeroEx as any)._web3Wrapper, 'signTransactionAsync')
.returns(Promise.resolve(signature)),
Sinon.stub(ZeroEx, 'isValidSignature').returns(true),

32
test/artifacts_test.ts Normal file
View File

@@ -0,0 +1,32 @@
import * as fs from 'fs';
import * as chai from 'chai';
import {chaiSetup} from './utils/chai_setup';
import HDWalletProvider = require('truffle-hdwallet-provider');
import {ZeroEx} from '../src';
import {constants} from './utils/constants';
chaiSetup.configure();
const expect = chai.expect;
// Those tests are slower cause they're talking to a remote node
const TIMEOUT = 10000;
describe('Artifacts', () => {
describe('contracts are deployed on kovan', () => {
const kovanRpcUrl = constants.KOVAN_RPC_URL;
const packageJSONContent = fs.readFileSync('package.json', 'utf-8');
const packageJSON = JSON.parse(packageJSONContent);
const mnemonic = packageJSON.config.mnemonic;
const web3Provider = new HDWalletProvider(mnemonic, kovanRpcUrl);
const zeroEx = new ZeroEx(web3Provider);
it('token registry contract is deployed', async () => {
await (zeroEx.tokenRegistry as any)._getTokenRegistryContractAsync();
}).timeout(TIMEOUT);
it('proxy contract is deployed', async () => {
await (zeroEx.token as any)._getProxyAddressAsync();
}).timeout(TIMEOUT);
it('exchange contract is deployed', async () => {
await zeroEx.exchange.getContractAddressAsync();
}).timeout(TIMEOUT);
});
});

View File

@@ -1,6 +1,6 @@
import * as chai from 'chai';
import 'mocha';
import {ZeroEx} from '../src/0x';
import {ZeroEx} from '../src';
import {assert} from '../src/utils/assert';
import {web3Factory} from './utils/web3_factory';
@@ -8,7 +8,7 @@ const expect = chai.expect;
describe('Assertion library', () => {
const web3 = web3Factory.create();
const zeroEx = new ZeroEx(web3);
const zeroEx = new ZeroEx(web3.currentProvider);
describe('#isSenderAddressHexAsync', () => {
it('throws when address is invalid', async () => {
const address = '0xdeadbeef';
@@ -21,7 +21,7 @@ describe('Assertion library', () => {
const varName = 'address';
return expect(assert.isSenderAddressAsync(varName, validUnrelatedAddress, (zeroEx as any)._web3Wrapper))
.to.be.rejectedWith(
`Specified ${varName} ${validUnrelatedAddress} isn't available through the supplied web3 instance`,
`Specified ${varName} ${validUnrelatedAddress} isn't available through the supplied web3 provider`,
);
});
it('doesn\'t throw if address is available', async () => {

View File

@@ -0,0 +1,110 @@
import 'mocha';
import * as chai from 'chai';
import {chaiSetup} from './utils/chai_setup';
import * as Web3 from 'web3';
import * as BigNumber from 'bignumber.js';
import promisify = require('es6-promisify');
import {web3Factory} from './utils/web3_factory';
import {ZeroEx, ZeroExError} from '../src';
import {BlockchainLifecycle} from './utils/blockchain_lifecycle';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle();
// Since the address depositing/withdrawing ETH/WETH also needs to pay gas costs for the transaction,
// a small amount of ETH will be used to pay this gas cost. We therefore check that the difference between
// the expected balance and actual balance (given the amount of ETH deposited), only deviates by the amount
// required to pay gas costs.
const MAX_REASONABLE_GAS_COST_IN_WEI = 62237;
describe('EtherTokenWrapper', () => {
let web3: Web3;
let zeroEx: ZeroEx;
let userAddresses: string[];
let addressWithETH: string;
let wethContractAddress: string;
let depositWeiAmount: BigNumber.BigNumber;
let decimalPlaces: number;
const gasPrice = new BigNumber(1);
const zeroExConfig = {
gasPrice,
};
before(async () => {
web3 = web3Factory.create();
zeroEx = new ZeroEx(web3.currentProvider, zeroExConfig);
userAddresses = await zeroEx.getAvailableAddressesAsync();
addressWithETH = userAddresses[0];
wethContractAddress = await zeroEx.etherToken.getContractAddressAsync();
depositWeiAmount = (zeroEx as any)._web3Wrapper.toWei(new BigNumber(5));
decimalPlaces = 7;
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('#depositAsync', () => {
it('should successfully deposit ETH and issue Wrapped ETH tokens', async () => {
const preETHBalance = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH);
const preWETHBalance = await zeroEx.token.getBalanceAsync(wethContractAddress, addressWithETH);
expect(preETHBalance).to.be.bignumber.gt(0);
expect(preWETHBalance).to.be.bignumber.equal(0);
await zeroEx.etherToken.depositAsync(depositWeiAmount, addressWithETH);
const postETHBalanceInWei = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH);
const postWETHBalanceInBaseUnits = await zeroEx.token.getBalanceAsync(wethContractAddress, addressWithETH);
expect(postWETHBalanceInBaseUnits).to.be.bignumber.equal(depositWeiAmount);
const remainingETHInWei = preETHBalance.minus(depositWeiAmount);
const gasCost = remainingETHInWei.minus(postETHBalanceInWei);
expect(gasCost).to.be.bignumber.lte(MAX_REASONABLE_GAS_COST_IN_WEI);
});
it('should throw if user has insufficient ETH balance for deposit', async () => {
const preETHBalance = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH);
const extraETHBalance = (zeroEx as any)._web3Wrapper.toWei(5, 'ether');
const overETHBalanceinWei = preETHBalance.add(extraETHBalance);
return expect(
zeroEx.etherToken.depositAsync(overETHBalanceinWei, addressWithETH),
).to.be.rejectedWith(ZeroExError.InsufficientEthBalanceForDeposit);
});
});
describe('#withdrawAsync', () => {
it('should successfully withdraw ETH in return for Wrapped ETH tokens', async () => {
const ETHBalanceInWei = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH);
await zeroEx.etherToken.depositAsync(depositWeiAmount, addressWithETH);
const expectedPreETHBalance = ETHBalanceInWei.minus(depositWeiAmount);
const preETHBalance = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH);
const preWETHBalance = await zeroEx.token.getBalanceAsync(wethContractAddress, addressWithETH);
let gasCost = expectedPreETHBalance.minus(preETHBalance);
expect(gasCost).to.be.bignumber.lte(MAX_REASONABLE_GAS_COST_IN_WEI);
expect(preWETHBalance).to.be.bignumber.equal(depositWeiAmount);
await zeroEx.etherToken.withdrawAsync(depositWeiAmount, addressWithETH);
const postETHBalance = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH);
const postWETHBalanceInBaseUnits = await zeroEx.token.getBalanceAsync(wethContractAddress, addressWithETH);
expect(postWETHBalanceInBaseUnits).to.be.bignumber.equal(0);
const expectedETHBalance = preETHBalance.add(depositWeiAmount).round(decimalPlaces);
gasCost = expectedETHBalance.minus(postETHBalance);
expect(gasCost).to.be.bignumber.lte(MAX_REASONABLE_GAS_COST_IN_WEI);
});
it('should throw if user has insufficient WETH balance for withdrawl', async () => {
const preWETHBalance = await zeroEx.token.getBalanceAsync(wethContractAddress, addressWithETH);
expect(preWETHBalance).to.be.bignumber.equal(0);
const overWETHBalance = preWETHBalance.add(999999999);
return expect(
zeroEx.etherToken.withdrawAsync(overWETHBalance, addressWithETH),
).to.be.rejectedWith(ZeroExError.InsufficientWEthBalanceForWithdrawal);
});
});
});

View File

@@ -6,22 +6,24 @@ import {chaiSetup} from './utils/chai_setup';
import ChaiBigNumber = require('chai-bignumber');
import promisify = require('es6-promisify');
import {web3Factory} from './utils/web3_factory';
import {ZeroEx} from '../src/0x';
import {BlockchainLifecycle} from './utils/blockchain_lifecycle';
import {
ZeroEx,
Token,
Order,
SignedOrder,
SubscriptionOpts,
ExchangeEvents,
ContractEvent,
DoneCallback,
ExchangeContractErrs,
OrderCancellationRequest,
OrderFillRequest,
} from '../src/types';
LogFillContractEventArgs,
} from '../src';
import {DoneCallback} from '../src/types';
import {FillScenarios} from './utils/fill_scenarios';
import {TokenUtils} from './utils/token_utils';
import {assert} from '../src/utils/assert';
import {TokenTransferProxyWrapper} from '../src/contract_wrappers/token_transfer_proxy_wrapper';
chaiSetup.configure();
const expect = chai.expect;
@@ -37,14 +39,16 @@ describe('ExchangeWrapper', () => {
let userAddresses: string[];
let zrxTokenAddress: string;
let fillScenarios: FillScenarios;
let exchangeContractAddress: string;
before(async () => {
web3 = web3Factory.create();
zeroEx = new ZeroEx(web3);
userAddresses = await promisify(web3.eth.getAccounts)();
zeroEx = new ZeroEx(web3.currentProvider);
exchangeContractAddress = await zeroEx.exchange.getContractAddressAsync();
userAddresses = await zeroEx.getAvailableAddressesAsync();
tokens = await zeroEx.tokenRegistry.getTokensAsync();
tokenUtils = new TokenUtils(tokens);
zrxTokenAddress = tokenUtils.getProtocolTokenOrThrow().address;
fillScenarios = new FillScenarios(zeroEx, userAddresses, tokens, zrxTokenAddress);
fillScenarios = new FillScenarios(zeroEx, userAddresses, tokens, zrxTokenAddress, exchangeContractAddress);
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
@@ -91,24 +95,6 @@ describe('ExchangeWrapper', () => {
});
});
describe('#fillOrKillOrderAsync', () => {
describe('failed fillOrKill', () => {
it('should throw if remaining fillAmount is less then the desired fillAmount', async () => {
const fillableAmount = new BigNumber(5);
const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount,
);
const tooLargeFillAmount = new BigNumber(7);
const fillAmountDifference = tooLargeFillAmount.minus(fillableAmount);
await zeroEx.token.transferAsync(takerTokenAddress, coinbase, takerAddress, fillAmountDifference);
await zeroEx.token.setProxyAllowanceAsync(takerTokenAddress, takerAddress, tooLargeFillAmount);
await zeroEx.token.transferAsync(makerTokenAddress, coinbase, makerAddress, fillAmountDifference);
await zeroEx.token.setProxyAllowanceAsync(makerTokenAddress, makerAddress, tooLargeFillAmount);
return expect(zeroEx.exchange.fillOrKillOrderAsync(
signedOrder, tooLargeFillAmount, takerAddress,
)).to.be.rejectedWith(ExchangeContractErrs.INSUFFICIENT_REMAINING_FILL_AMOUNT);
});
});
describe('successful fills', () => {
it('should fill a valid order', async () => {
const fillableAmount = new BigNumber(5);
@@ -161,7 +147,7 @@ describe('ExchangeWrapper', () => {
let feeRecipient: string;
const fillableAmount = new BigNumber(5);
const fillTakerAmount = new BigNumber(5);
const shouldCheckTransfer = false;
const shouldThrowOnInsufficientBalanceOrAllowance = true;
before(async () => {
[coinbase, makerAddress, takerAddress, feeRecipient] = userAddresses;
tokens = await zeroEx.tokenRegistry.getTokensAsync();
@@ -170,135 +156,6 @@ describe('ExchangeWrapper', () => {
takerTokenAddress = takerToken.address;
});
describe('#fillOrderAsync', () => {
describe('failed fills', () => {
it('should throw when the fill amount is zero', async () => {
const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount,
);
const zeroFillAmount = new BigNumber(0);
return expect(zeroEx.exchange.fillOrderAsync(
signedOrder, zeroFillAmount, shouldCheckTransfer, takerAddress,
)).to.be.rejectedWith(ExchangeContractErrs.ORDER_REMAINING_FILL_AMOUNT_ZERO);
});
it('should throw when sender is not a taker', async () => {
const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount,
);
const nonTakerAddress = userAddresses[6];
return expect(zeroEx.exchange.fillOrderAsync(
signedOrder, fillTakerAmount, shouldCheckTransfer, nonTakerAddress,
)).to.be.rejectedWith(ExchangeContractErrs.TRANSACTION_SENDER_IS_NOT_FILL_ORDER_TAKER);
});
it('should throw when order is expired', async () => {
const expirationInPast = new BigNumber(1496826058); // 7th Jun 2017
const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress,
fillableAmount, expirationInPast,
);
return expect(zeroEx.exchange.fillOrderAsync(
signedOrder, fillTakerAmount, shouldCheckTransfer, takerAddress,
)).to.be.rejectedWith(ExchangeContractErrs.ORDER_FILL_EXPIRED);
});
describe('should throw when not enough balance or allowance to fulfill the order', () => {
const balanceToSubtractFromMaker = new BigNumber(3);
const lackingAllowance = new BigNumber(3);
let signedOrder: SignedOrder;
beforeEach('create fillable signed order', async () => {
signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount,
);
});
it('should throw when taker balance is less than fill amount', async () => {
await zeroEx.token.transferAsync(
takerTokenAddress, takerAddress, coinbase, balanceToSubtractFromMaker,
);
return expect(zeroEx.exchange.fillOrderAsync(
signedOrder, fillTakerAmount, shouldCheckTransfer, takerAddress,
)).to.be.rejectedWith(ExchangeContractErrs.INSUFFICIENT_TAKER_BALANCE);
});
it('should throw when taker allowance is less than fill amount', async () => {
const newAllowanceWhichIsLessThanFillAmount = fillTakerAmount.minus(lackingAllowance);
await zeroEx.token.setProxyAllowanceAsync(takerTokenAddress, takerAddress,
newAllowanceWhichIsLessThanFillAmount);
return expect(zeroEx.exchange.fillOrderAsync(
signedOrder, fillTakerAmount, shouldCheckTransfer, takerAddress,
)).to.be.rejectedWith(ExchangeContractErrs.INSUFFICIENT_TAKER_ALLOWANCE);
});
it('should throw when maker balance is less than maker fill amount', async () => {
await zeroEx.token.transferAsync(
makerTokenAddress, makerAddress, coinbase, balanceToSubtractFromMaker,
);
return expect(zeroEx.exchange.fillOrderAsync(
signedOrder, fillTakerAmount, shouldCheckTransfer, takerAddress,
)).to.be.rejectedWith(ExchangeContractErrs.INSUFFICIENT_MAKER_BALANCE);
});
it('should throw when maker allowance is less than maker fill amount', async () => {
const newAllowanceWhichIsLessThanFillAmount = fillTakerAmount.minus(lackingAllowance);
await zeroEx.token.setProxyAllowanceAsync(makerTokenAddress, makerAddress,
newAllowanceWhichIsLessThanFillAmount);
return expect(zeroEx.exchange.fillOrderAsync(
signedOrder, fillTakerAmount, shouldCheckTransfer, takerAddress,
)).to.be.rejectedWith(ExchangeContractErrs.INSUFFICIENT_MAKER_ALLOWANCE);
});
});
it('should throw when there a rounding error would have occurred', async () => {
const makerAmount = new BigNumber(3);
const takerAmount = new BigNumber(5);
const signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress,
makerAmount, takerAmount,
);
const fillTakerAmountThatCausesRoundingError = new BigNumber(3);
return expect(zeroEx.exchange.fillOrderAsync(
signedOrder, fillTakerAmountThatCausesRoundingError, shouldCheckTransfer, takerAddress,
)).to.be.rejectedWith(ExchangeContractErrs.ORDER_FILL_ROUNDING_ERROR);
});
describe('should throw when not enough balance or allowance to pay fees', () => {
const makerFee = new BigNumber(2);
const takerFee = new BigNumber(2);
let signedOrder: SignedOrder;
beforeEach('setup', async () => {
signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
makerTokenAddress, takerTokenAddress, makerFee, takerFee,
makerAddress, takerAddress, fillableAmount, feeRecipient,
);
});
it('should throw when maker doesn\'t have enough balance to pay fees', async () => {
const balanceToSubtractFromMaker = new BigNumber(1);
await zeroEx.token.transferAsync(
zrxTokenAddress, makerAddress, coinbase, balanceToSubtractFromMaker,
);
return expect(zeroEx.exchange.fillOrderAsync(
signedOrder, fillTakerAmount, shouldCheckTransfer, takerAddress,
)).to.be.rejectedWith(ExchangeContractErrs.INSUFFICIENT_MAKER_FEE_BALANCE);
});
it('should throw when maker doesn\'t have enough allowance to pay fees', async () => {
const newAllowanceWhichIsLessThanFees = makerFee.minus(1);
await zeroEx.token.setProxyAllowanceAsync(zrxTokenAddress, makerAddress,
newAllowanceWhichIsLessThanFees);
return expect(zeroEx.exchange.fillOrderAsync(
signedOrder, fillTakerAmount, shouldCheckTransfer, takerAddress,
)).to.be.rejectedWith(ExchangeContractErrs.INSUFFICIENT_MAKER_FEE_ALLOWANCE);
});
it('should throw when taker doesn\'t have enough balance to pay fees', async () => {
const balanceToSubtractFromTaker = new BigNumber(1);
await zeroEx.token.transferAsync(
zrxTokenAddress, takerAddress, coinbase, balanceToSubtractFromTaker,
);
return expect(zeroEx.exchange.fillOrderAsync(
signedOrder, fillTakerAmount, shouldCheckTransfer, takerAddress,
)).to.be.rejectedWith(ExchangeContractErrs.INSUFFICIENT_TAKER_FEE_BALANCE);
});
it('should throw when taker doesn\'t have enough allowance to pay fees', async () => {
const newAllowanceWhichIsLessThanFees = makerFee.minus(1);
await zeroEx.token.setProxyAllowanceAsync(zrxTokenAddress, takerAddress,
newAllowanceWhichIsLessThanFees);
return expect(zeroEx.exchange.fillOrderAsync(
signedOrder, fillTakerAmount, shouldCheckTransfer, takerAddress,
)).to.be.rejectedWith(ExchangeContractErrs.INSUFFICIENT_TAKER_FEE_ALLOWANCE);
});
});
});
describe('successful fills', () => {
it('should fill a valid order', async () => {
const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
@@ -313,7 +170,7 @@ describe('ExchangeWrapper', () => {
expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress))
.to.be.bignumber.equal(fillableAmount);
await zeroEx.exchange.fillOrderAsync(
signedOrder, fillTakerAmount, shouldCheckTransfer, takerAddress);
signedOrder, fillTakerAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress);
expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress))
.to.be.bignumber.equal(fillableAmount.minus(fillTakerAmount));
expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress))
@@ -329,7 +186,7 @@ describe('ExchangeWrapper', () => {
);
const partialFillAmount = new BigNumber(3);
await zeroEx.exchange.fillOrderAsync(
signedOrder, partialFillAmount, shouldCheckTransfer, takerAddress);
signedOrder, partialFillAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress);
expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress))
.to.be.bignumber.equal(fillableAmount.minus(partialFillAmount));
expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress))
@@ -339,6 +196,34 @@ describe('ExchangeWrapper', () => {
expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress))
.to.be.bignumber.equal(fillableAmount.minus(partialFillAmount));
});
it('should return filled amount', async () => {
const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount,
);
const partialFillAmount = new BigNumber(3);
const filledAmount = await zeroEx.exchange.fillOrderAsync(
signedOrder, partialFillAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress);
expect(filledAmount).to.be.bignumber.equal(partialFillAmount);
});
it('should return the partially filled amount \
if the fill amount specified is greater then the amount available', async () => {
const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount,
);
const partialFillAmount = new BigNumber(3);
await zeroEx.exchange.fillOrderAsync(
signedOrder, partialFillAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress);
const missingBalance = new BigNumber(1);
const totalBalance = partialFillAmount.plus(missingBalance);
await zeroEx.token.transferAsync(takerTokenAddress, coinbase, takerAddress, missingBalance);
await zeroEx.token.setProxyAllowanceAsync(takerTokenAddress, takerAddress, totalBalance);
await zeroEx.token.transferAsync(makerTokenAddress, coinbase, makerAddress, missingBalance);
await zeroEx.token.setProxyAllowanceAsync(makerTokenAddress, makerAddress, totalBalance);
const remainingFillAmount = fillableAmount.minus(partialFillAmount);
const filledAmount = await zeroEx.exchange.fillOrderAsync(
signedOrder, partialFillAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress);
expect(filledAmount).to.be.bignumber.equal(remainingFillAmount);
});
it('should fill the valid orders with fees', async () => {
const makerFee = new BigNumber(1);
const takerFee = new BigNumber(2);
@@ -347,13 +232,13 @@ describe('ExchangeWrapper', () => {
makerAddress, takerAddress, fillableAmount, feeRecipient,
);
await zeroEx.exchange.fillOrderAsync(
signedOrder, fillTakerAmount, shouldCheckTransfer, takerAddress);
signedOrder, fillTakerAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress);
expect(await zeroEx.token.getBalanceAsync(zrxTokenAddress, feeRecipient))
.to.be.bignumber.equal(makerFee.plus(takerFee));
});
});
});
describe('#batchFillOrderAsync', () => {
describe('#batchFillOrdersAsync', () => {
let signedOrder: SignedOrder;
let signedOrderHashHex: string;
let anotherSignedOrder: SignedOrder;
@@ -363,11 +248,11 @@ describe('ExchangeWrapper', () => {
signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount,
);
signedOrderHashHex = await zeroEx.getOrderHashHexAsync(signedOrder);
signedOrderHashHex = ZeroEx.getOrderHashHex(signedOrder);
anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount,
);
anotherOrderHashHex = await zeroEx.getOrderHashHexAsync(anotherSignedOrder);
anotherOrderHashHex = ZeroEx.getOrderHashHex(anotherSignedOrder);
orderFillBatch = [
{
signedOrder,
@@ -381,10 +266,12 @@ describe('ExchangeWrapper', () => {
});
describe('successful batch fills', () => {
it('should no-op for an empty batch', async () => {
await zeroEx.exchange.batchFillOrderAsync([], shouldCheckTransfer, takerAddress);
await zeroEx.exchange.batchFillOrdersAsync(
[], shouldThrowOnInsufficientBalanceOrAllowance, takerAddress);
});
it('should successfully fill multiple orders', async () => {
await zeroEx.exchange.batchFillOrderAsync(orderFillBatch, shouldCheckTransfer, takerAddress);
await zeroEx.exchange.batchFillOrdersAsync(
orderFillBatch, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress);
const filledAmount = await zeroEx.exchange.getFilledTakerAmountAsync(signedOrderHashHex);
const anotherFilledAmount = await zeroEx.exchange.getFilledTakerAmountAsync(anotherOrderHashHex);
expect(filledAmount).to.be.bignumber.equal(fillTakerAmount);
@@ -403,20 +290,21 @@ describe('ExchangeWrapper', () => {
signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount,
);
signedOrderHashHex = await zeroEx.getOrderHashHexAsync(signedOrder);
signedOrderHashHex = ZeroEx.getOrderHashHex(signedOrder);
anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount,
);
anotherOrderHashHex = await zeroEx.getOrderHashHexAsync(anotherSignedOrder);
anotherOrderHashHex = ZeroEx.getOrderHashHex(anotherSignedOrder);
signedOrders = [signedOrder, anotherSignedOrder];
});
describe('successful batch fills', () => {
it('should no-op for an empty batch', async () => {
await zeroEx.exchange.fillOrdersUpToAsync([], fillUpToAmount, shouldCheckTransfer, takerAddress);
await zeroEx.exchange.fillOrdersUpToAsync(
[], fillUpToAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress);
});
it('should successfully fill up to specified amount', async () => {
await zeroEx.exchange.fillOrdersUpToAsync(
signedOrders, fillUpToAmount, shouldCheckTransfer, takerAddress,
signedOrders, fillUpToAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress,
);
const filledAmount = await zeroEx.exchange.getFilledTakerAmountAsync(signedOrderHashHex);
const anotherFilledAmount = await zeroEx.exchange.getFilledTakerAmountAsync(anotherOrderHashHex);
@@ -424,6 +312,12 @@ describe('ExchangeWrapper', () => {
const remainingFillAmount = fillableAmount.minus(1);
expect(anotherFilledAmount).to.be.bignumber.equal(remainingFillAmount);
});
it('should return filled amount', async () => {
const filledTakerTokenAmount = await zeroEx.exchange.fillOrdersUpToAsync(
signedOrders, fillUpToAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress,
);
expect(filledTakerTokenAmount).to.be.bignumber.equal(fillUpToAmount);
});
});
});
});
@@ -445,40 +339,22 @@ describe('ExchangeWrapper', () => {
signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount,
);
orderHashHex = await zeroEx.getOrderHashHexAsync(signedOrder);
orderHashHex = ZeroEx.getOrderHashHex(signedOrder);
});
describe('#cancelOrderAsync', () => {
describe('failed cancels', () => {
it('should throw when cancel amount is zero', async () => {
const zeroCancelAmount = new BigNumber(0);
return expect(zeroEx.exchange.cancelOrderAsync(signedOrder, zeroCancelAmount))
.to.be.rejectedWith(ExchangeContractErrs.ORDER_CANCEL_AMOUNT_ZERO);
});
it('should throw when order is expired', async () => {
const expirationInPast = new BigNumber(1496826058); // 7th Jun 2017
const expiredSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress,
fillableAmount, expirationInPast,
);
orderHashHex = await zeroEx.getOrderHashHexAsync(expiredSignedOrder);
return expect(zeroEx.exchange.cancelOrderAsync(expiredSignedOrder, cancelAmount))
.to.be.rejectedWith(ExchangeContractErrs.ORDER_CANCEL_EXPIRED);
});
it('should throw when order is already cancelled or filled', async () => {
await zeroEx.exchange.cancelOrderAsync(signedOrder, fillableAmount);
return expect(zeroEx.exchange.cancelOrderAsync(signedOrder, fillableAmount))
.to.be.rejectedWith(ExchangeContractErrs.ORDER_ALREADY_CANCELLED_OR_FILLED);
});
});
describe('successful cancels', () => {
it('should cancel an order', async () => {
await zeroEx.exchange.cancelOrderAsync(signedOrder, cancelAmount);
const cancelledAmount = await zeroEx.exchange.getCanceledTakerAmountAsync(orderHashHex);
expect(cancelledAmount).to.be.bignumber.equal(cancelAmount);
});
it('should return cancelled amount', async () => {
const cancelledAmount = await zeroEx.exchange.cancelOrderAsync(signedOrder, cancelAmount);
expect(cancelledAmount).to.be.bignumber.equal(cancelAmount);
});
});
});
describe('#batchCancelOrderAsync', () => {
describe('#batchCancelOrdersAsync', () => {
let anotherSignedOrder: SignedOrder;
let anotherOrderHashHex: string;
let cancelBatch: OrderCancellationRequest[];
@@ -486,7 +362,7 @@ describe('ExchangeWrapper', () => {
anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount,
);
anotherOrderHashHex = await zeroEx.getOrderHashHexAsync(anotherSignedOrder);
anotherOrderHashHex = ZeroEx.getOrderHashHex(anotherSignedOrder);
cancelBatch = [
{
order: signedOrder,
@@ -503,21 +379,22 @@ describe('ExchangeWrapper', () => {
const signedOrderWithDifferentMaker = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, takerAddress, takerAddress, fillableAmount,
);
return expect(zeroEx.exchange.batchCancelOrderAsync([
return expect(zeroEx.exchange.batchCancelOrdersAsync([
cancelBatch[0],
{
order: signedOrderWithDifferentMaker,
takerTokenCancelAmount: cancelAmount,
},
])).to.be.rejectedWith(ExchangeContractErrs.MULTIPLE_MAKERS_IN_SINGLE_CANCEL_BATCH);
])).to.be.rejectedWith(ExchangeContractErrs.MultipleMakersInSingleCancelBatchDisallowed);
});
});
describe('successful batch cancels', () => {
it('should cancel a batch of orders', async () => {
await zeroEx.exchange.batchCancelOrderAsync(cancelBatch);
await zeroEx.exchange.batchCancelOrdersAsync(cancelBatch);
const cancelledAmount = await zeroEx.exchange.getCanceledTakerAmountAsync(orderHashHex);
const anotherCancelledAmount = await zeroEx.exchange.getCanceledTakerAmountAsync(
anotherOrderHashHex);
anotherOrderHashHex,
);
expect(cancelledAmount).to.be.bignumber.equal(cancelAmount);
expect(anotherCancelledAmount).to.be.bignumber.equal(cancelAmount);
});
@@ -544,50 +421,51 @@ describe('ExchangeWrapper', () => {
signedOrder = await fillScenarios.createPartiallyFilledSignedOrderAsync(
makerTokenAddress, takerTokenAddress, takerAddress, fillableAmount, partialFillAmount,
);
orderHash = await zeroEx.getOrderHashHexAsync(signedOrder);
orderHash = ZeroEx.getOrderHashHex(signedOrder);
});
describe('#getUnavailableTakerAmountAsync', () => {
it ('should throw if passed an invalid orderHash', async () => {
it('should throw if passed an invalid orderHash', async () => {
const invalidOrderHashHex = '0x123';
return expect(zeroEx.exchange.getUnavailableTakerAmountAsync(invalidOrderHashHex)).to.be.rejected();
});
it ('should return zero if passed a valid but non-existent orderHash', async () => {
it('should return zero if passed a valid but non-existent orderHash', async () => {
const unavailableValueT = await zeroEx.exchange.getUnavailableTakerAmountAsync(NON_EXISTENT_ORDER_HASH);
expect(unavailableValueT).to.be.bignumber.equal(0);
});
it ('should return the unavailableValueT for a valid and partially filled orderHash', async () => {
it('should return the unavailableValueT for a valid and partially filled orderHash', async () => {
const unavailableValueT = await zeroEx.exchange.getUnavailableTakerAmountAsync(orderHash);
expect(unavailableValueT).to.be.bignumber.equal(partialFillAmount);
});
});
describe('#getFilledTakerAmountAsync', () => {
it ('should throw if passed an invalid orderHash', async () => {
it('should throw if passed an invalid orderHash', async () => {
const invalidOrderHashHex = '0x123';
return expect(zeroEx.exchange.getFilledTakerAmountAsync(invalidOrderHashHex)).to.be.rejected();
});
it ('should return zero if passed a valid but non-existent orderHash', async () => {
const filledValueT = await zeroEx.exchange.getFilledTakerAmountAsync(NON_EXISTENT_ORDER_HASH);
it('should return zero if passed a valid but non-existent orderHash', async () => {
const filledValueT = await zeroEx.exchange.getFilledTakerAmountAsync(NON_EXISTENT_ORDER_HASH,
);
expect(filledValueT).to.be.bignumber.equal(0);
});
it ('should return the filledValueT for a valid and partially filled orderHash', async () => {
it('should return the filledValueT for a valid and partially filled orderHash', async () => {
const filledValueT = await zeroEx.exchange.getFilledTakerAmountAsync(orderHash);
expect(filledValueT).to.be.bignumber.equal(partialFillAmount);
});
});
describe('#getCanceledTakerAmountAsync', () => {
it ('should throw if passed an invalid orderHash', async () => {
it('should throw if passed an invalid orderHash', async () => {
const invalidOrderHashHex = '0x123';
return expect(zeroEx.exchange.getCanceledTakerAmountAsync(invalidOrderHashHex)).to.be.rejected();
});
it ('should return zero if passed a valid but non-existent orderHash', async () => {
it('should return zero if passed a valid but non-existent orderHash', async () => {
const cancelledValueT = await zeroEx.exchange.getCanceledTakerAmountAsync(NON_EXISTENT_ORDER_HASH);
expect(cancelledValueT).to.be.bignumber.equal(0);
});
it ('should return the cancelledValueT for a valid and partially filled orderHash', async () => {
it('should return the cancelledValueT for a valid and partially filled orderHash', async () => {
const cancelledValueT = await zeroEx.exchange.getCanceledTakerAmountAsync(orderHash);
expect(cancelledValueT).to.be.bignumber.equal(0);
});
it ('should return the cancelledValueT for a valid and cancelled orderHash', async () => {
it('should return the cancelledValueT for a valid and cancelled orderHash', async () => {
const cancelAmount = fillableAmount.minus(partialFillAmount);
await zeroEx.exchange.cancelOrderAsync(signedOrder, cancelAmount);
const cancelledValueT = await zeroEx.exchange.getCanceledTakerAmountAsync(orderHash);
@@ -597,7 +475,7 @@ describe('ExchangeWrapper', () => {
});
describe('#subscribeAsync', () => {
const indexFilterValues = {};
const shouldCheckTransfer = false;
const shouldThrowOnInsufficientBalanceOrAllowance = true;
let makerTokenAddress: string;
let takerTokenAddress: string;
let coinbase: string;
@@ -605,6 +483,12 @@ describe('ExchangeWrapper', () => {
let makerAddress: string;
let fillableAmount: BigNumber.BigNumber;
let signedOrder: SignedOrder;
const subscriptionOpts: SubscriptionOpts = {
fromBlock: 0,
toBlock: 'latest',
};
const fillTakerAmountInBaseUnits = new BigNumber(1);
const cancelTakerAmountInBaseUnits = new BigNumber(1);
before(() => {
[coinbase, makerAddress, takerAddress] = userAddresses;
const [makerToken, takerToken] = tokens;
@@ -618,7 +502,7 @@ describe('ExchangeWrapper', () => {
);
});
afterEach(async () => {
await (zeroEx.exchange as any)._stopWatchingExchangeLogEventsAsync();
await zeroEx.exchange.stopWatchingAllEventsAsync();
});
// Hack: Mocha does not allow a test to be both async and have a `done` callback
// Since we need to await the receipt of the event in the `subscribeAsync` callback,
@@ -627,64 +511,92 @@ describe('ExchangeWrapper', () => {
// Source: https://github.com/mochajs/mocha/issues/2407
it('Should receive the LogFill event when an order is filled', (done: DoneCallback) => {
(async () => {
const subscriptionOpts: SubscriptionOpts = {
fromBlock: 0,
toBlock: 'latest',
};
await zeroEx.exchange.subscribeAsync(ExchangeEvents.LogFill, subscriptionOpts,
indexFilterValues, (err: Error, event: ContractEvent) => {
const zeroExEvent = await zeroEx.exchange.subscribeAsync(
ExchangeEvents.LogFill, subscriptionOpts, indexFilterValues, exchangeContractAddress,
);
zeroExEvent.watch((err: Error, event: ContractEvent) => {
expect(err).to.be.null();
expect(event).to.not.be.undefined();
expect(event.event).to.be.equal('LogFill');
done();
});
const fillTakerAmountInBaseUnits = new BigNumber(1);
await zeroEx.exchange.fillOrderAsync(
signedOrder, fillTakerAmountInBaseUnits, shouldCheckTransfer, takerAddress,
signedOrder, fillTakerAmountInBaseUnits, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress,
);
})();
})().catch(done);
});
it('Should receive the LogCancel event when an order is cancelled', (done: DoneCallback) => {
(async () => {
const subscriptionOpts: SubscriptionOpts = {
fromBlock: 0,
toBlock: 'latest',
};
await zeroEx.exchange.subscribeAsync(ExchangeEvents.LogCancel, subscriptionOpts,
indexFilterValues, (err: Error, event: ContractEvent) => {
const zeroExEvent = await zeroEx.exchange.subscribeAsync(
ExchangeEvents.LogCancel, subscriptionOpts, indexFilterValues, exchangeContractAddress,
);
zeroExEvent.watch((err: Error, event: ContractEvent) => {
expect(err).to.be.null();
expect(event).to.not.be.undefined();
expect(event.event).to.be.equal('LogCancel');
done();
});
const cancelTakerAmountInBaseUnits = new BigNumber(1);
});
await zeroEx.exchange.cancelOrderAsync(signedOrder, cancelTakerAmountInBaseUnits);
})();
})().catch(done);
});
it('Outstanding subscriptions are cancelled when zeroEx.setProviderAsync called', (done: DoneCallback) => {
(async () => {
const subscriptionOpts: SubscriptionOpts = {
fromBlock: 0,
toBlock: 'latest',
};
await zeroEx.exchange.subscribeAsync(ExchangeEvents.LogFill, subscriptionOpts,
indexFilterValues, (err: Error, event: ContractEvent) => {
const eventSubscriptionToBeCancelled = await zeroEx.exchange.subscribeAsync(
ExchangeEvents.LogFill, subscriptionOpts, indexFilterValues, exchangeContractAddress,
);
eventSubscriptionToBeCancelled.watch((err: Error, event: ContractEvent) => {
done(new Error('Expected this subscription to have been cancelled'));
});
const newProvider = web3Factory.getRpcProvider();
await zeroEx.setProviderAsync(newProvider);
await zeroEx.exchange.subscribeAsync(ExchangeEvents.LogFill, subscriptionOpts,
indexFilterValues, (err: Error, event: ContractEvent) => {
const eventSubscriptionToStay = await zeroEx.exchange.subscribeAsync(
ExchangeEvents.LogFill, subscriptionOpts, indexFilterValues, exchangeContractAddress,
);
eventSubscriptionToStay.watch((err: Error, event: ContractEvent) => {
expect(err).to.be.null();
expect(event).to.not.be.undefined();
expect(event.event).to.be.equal('LogFill');
done();
});
const fillTakerAmountInBaseUnits = new BigNumber(1);
await zeroEx.exchange.fillOrderAsync(
signedOrder, fillTakerAmountInBaseUnits, shouldCheckTransfer, takerAddress,
signedOrder, fillTakerAmountInBaseUnits, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress,
);
})();
})().catch(done);
});
it('Should stop watch for events when stopWatchingAsync called on the eventEmitter', (done: DoneCallback) => {
(async () => {
const eventSubscriptionToBeStopped = await zeroEx.exchange.subscribeAsync(
ExchangeEvents.LogFill, subscriptionOpts, indexFilterValues, exchangeContractAddress,
);
eventSubscriptionToBeStopped.watch((err: Error, event: ContractEvent) => {
done(new Error('Expected this subscription to have been stopped'));
});
await eventSubscriptionToBeStopped.stopWatchingAsync();
await zeroEx.exchange.fillOrderAsync(
signedOrder, fillTakerAmountInBaseUnits, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress,
);
done();
})().catch(done);
});
it('Should wrap all event args BigNumber instances in a newer version of BigNumber', (done: DoneCallback) => {
(async () => {
const zeroExEvent = await zeroEx.exchange.subscribeAsync(
ExchangeEvents.LogFill, subscriptionOpts, indexFilterValues, exchangeContractAddress,
);
zeroExEvent.watch((err: Error, event: ContractEvent) => {
const args = event.args as LogFillContractEventArgs;
expect(args.filledMakerTokenAmount.isBigNumber).to.be.true();
expect(args.filledTakerTokenAmount.isBigNumber).to.be.true();
expect(args.paidMakerFee.isBigNumber).to.be.true();
expect(args.paidTakerFee.isBigNumber).to.be.true();
done();
});
await zeroEx.exchange.fillOrderAsync(
signedOrder, fillTakerAmountInBaseUnits, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress,
);
})().catch(done);
});
});
describe('#getOrderHashHexUsingContractCallAsync', () => {
@@ -703,7 +615,7 @@ describe('ExchangeWrapper', () => {
const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount,
);
const orderHash = await zeroEx.getOrderHashHexAsync(signedOrder);
const orderHash = ZeroEx.getOrderHashHex(signedOrder);
const orderHashFromContract = await (zeroEx.exchange as any)
._getOrderHashHexUsingContractCallAsync(signedOrder);
expect(orderHash).to.equal(orderHashFromContract);

View File

@@ -0,0 +1,310 @@
import * as chai from 'chai';
import * as Web3 from 'web3';
import * as BigNumber from 'bignumber.js';
import promisify = require('es6-promisify');
import {chaiSetup} from './utils/chai_setup';
import {web3Factory} from './utils/web3_factory';
import {ZeroEx, SignedOrder, Token, ExchangeContractErrs} from '../src';
import {TokenUtils} from './utils/token_utils';
import {BlockchainLifecycle} from './utils/blockchain_lifecycle';
import {FillScenarios} from './utils/fill_scenarios';
import {OrderValidationUtils} from '../src/utils/order_validation_utils';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle();
describe('OrderValidation', () => {
let web3: Web3;
let zeroEx: ZeroEx;
let userAddresses: string[];
let tokens: Token[];
let tokenUtils: TokenUtils;
let exchangeContractAddress: string;
let zrxTokenAddress: string;
let fillScenarios: FillScenarios;
let makerTokenAddress: string;
let takerTokenAddress: string;
let coinbase: string;
let makerAddress: string;
let takerAddress: string;
let feeRecipient: string;
let orderValidationUtils: OrderValidationUtils;
const fillableAmount = new BigNumber(5);
const fillTakerAmount = new BigNumber(5);
const shouldThrowOnInsufficientBalanceOrAllowance = false;
before(async () => {
web3 = web3Factory.create();
zeroEx = new ZeroEx(web3.currentProvider);
exchangeContractAddress = await zeroEx.exchange.getContractAddressAsync();
userAddresses = await zeroEx.getAvailableAddressesAsync();
[coinbase, makerAddress, takerAddress, feeRecipient] = userAddresses;
tokens = await zeroEx.tokenRegistry.getTokensAsync();
tokenUtils = new TokenUtils(tokens);
zrxTokenAddress = tokenUtils.getProtocolTokenOrThrow().address;
fillScenarios = new FillScenarios(zeroEx, userAddresses, tokens, zrxTokenAddress, exchangeContractAddress);
const [makerToken, takerToken] = tokenUtils.getNonProtocolTokens();
makerTokenAddress = makerToken.address;
takerTokenAddress = takerToken.address;
orderValidationUtils = new OrderValidationUtils(zeroEx.token, zeroEx.exchange);
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('validateFillOrderAndThrowIfInvalidAsync', () => {
it('should throw when the fill amount is zero', async () => {
const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount,
);
const zeroFillAmount = new BigNumber(0);
return expect(zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(
signedOrder, zeroFillAmount, takerAddress,
)).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
});
it('should throw when the order is fully filled or cancelled', async () => {
const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount,
);
await zeroEx.exchange.cancelOrderAsync(signedOrder, fillableAmount);
return expect(zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(
signedOrder, fillableAmount, takerAddress,
)).to.be.rejectedWith(ExchangeContractErrs.OrderRemainingFillAmountZero);
});
it('should throw when sender is not a taker', async () => {
const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount,
);
const nonTakerAddress = userAddresses[6];
return expect(zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(
signedOrder, fillTakerAmount, nonTakerAddress,
)).to.be.rejectedWith(ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker);
});
it('should throw when order is expired', async () => {
const expirationInPast = new BigNumber(1496826058); // 7th Jun 2017
const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress,
fillableAmount, expirationInPast,
);
return expect(zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(
signedOrder, fillTakerAmount, takerAddress,
)).to.be.rejectedWith(ExchangeContractErrs.OrderFillExpired);
});
it('should throw when there a rounding error would have occurred', async () => {
const makerAmount = new BigNumber(3);
const takerAmount = new BigNumber(5);
const signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress,
makerAmount, takerAmount,
);
const fillTakerAmountThatCausesRoundingError = new BigNumber(3);
return expect(zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(
signedOrder, fillTakerAmountThatCausesRoundingError, takerAddress,
)).to.be.rejectedWith(ExchangeContractErrs.OrderFillRoundingError);
});
});
describe('#validateFillOrKillOrderAndThrowIfInvalidAsync', () => {
it('should throw if remaining fillAmount is less then the desired fillAmount', async () => {
const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount,
);
const tooLargeFillAmount = new BigNumber(7);
const fillAmountDifference = tooLargeFillAmount.minus(fillableAmount);
await zeroEx.token.transferAsync(takerTokenAddress, coinbase, takerAddress, fillAmountDifference);
await zeroEx.token.setProxyAllowanceAsync(takerTokenAddress, takerAddress, tooLargeFillAmount);
await zeroEx.token.transferAsync(makerTokenAddress, coinbase, makerAddress, fillAmountDifference);
await zeroEx.token.setProxyAllowanceAsync(makerTokenAddress, makerAddress, tooLargeFillAmount);
return expect(zeroEx.exchange.validateFillOrKillOrderThrowIfInvalidAsync(
signedOrder, tooLargeFillAmount, takerAddress,
)).to.be.rejectedWith(ExchangeContractErrs.InsufficientRemainingFillAmount);
});
});
describe('validateCancelOrderAndThrowIfInvalidAsync', () => {
let signedOrder: SignedOrder;
let orderHashHex: string;
const cancelAmount = new BigNumber(3);
beforeEach(async () => {
[coinbase, makerAddress, takerAddress] = userAddresses;
const [makerToken, takerToken] = tokenUtils.getNonProtocolTokens();
makerTokenAddress = makerToken.address;
takerTokenAddress = takerToken.address;
signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount,
);
orderHashHex = ZeroEx.getOrderHashHex(signedOrder);
});
it('should throw when cancel amount is zero', async () => {
const zeroCancelAmount = new BigNumber(0);
return expect(zeroEx.exchange.validateCancelOrderThrowIfInvalidAsync(signedOrder, zeroCancelAmount))
.to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero);
});
it('should throw when order is expired', async () => {
const expirationInPast = new BigNumber(1496826058); // 7th Jun 2017
const expiredSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress,
fillableAmount, expirationInPast,
);
orderHashHex = ZeroEx.getOrderHashHex(expiredSignedOrder);
return expect(zeroEx.exchange.validateCancelOrderThrowIfInvalidAsync(expiredSignedOrder, cancelAmount))
.to.be.rejectedWith(ExchangeContractErrs.OrderCancelExpired);
});
it('should throw when order is already cancelled or filled', async () => {
await zeroEx.exchange.cancelOrderAsync(signedOrder, fillableAmount);
return expect(zeroEx.exchange.validateCancelOrderThrowIfInvalidAsync(signedOrder, fillableAmount))
.to.be.rejectedWith(ExchangeContractErrs.OrderAlreadyCancelledOrFilled);
});
});
describe('#validateFillOrderTakerBalancesAllowancesThrowIfInvalidAsync', () => {
describe('should throw when not enough balance or allowance to fulfill the order', () => {
const balanceToSubtractFromMaker = new BigNumber(3);
const balanceToSubtractFromTaker = new BigNumber(3);
const lackingAllowance = new BigNumber(3);
let signedOrder: SignedOrder;
beforeEach('create fillable signed order', async () => {
signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount,
);
});
it('should throw when taker balance is less than fill amount', async () => {
await zeroEx.token.transferAsync(
takerTokenAddress, takerAddress, coinbase, balanceToSubtractFromTaker,
);
return expect((orderValidationUtils as any).validateFillOrderTakerBalancesAllowancesThrowIfInvalidAsync(
signedOrder, fillTakerAmount, takerAddress, zrxTokenAddress,
)).to.be.rejectedWith(ExchangeContractErrs.InsufficientTakerBalance);
});
it('should throw when taker allowance is less than fill amount', async () => {
const newAllowanceWhichIsLessThanFillAmount = fillTakerAmount.minus(lackingAllowance);
await zeroEx.token.setProxyAllowanceAsync(takerTokenAddress, takerAddress,
newAllowanceWhichIsLessThanFillAmount);
return expect((orderValidationUtils as any).validateFillOrderTakerBalancesAllowancesThrowIfInvalidAsync(
signedOrder, fillTakerAmount, takerAddress, zrxTokenAddress,
)).to.be.rejectedWith(ExchangeContractErrs.InsufficientTakerAllowance);
});
it('should throw when maker balance is less than maker fill amount', async () => {
await zeroEx.token.transferAsync(
makerTokenAddress, makerAddress, coinbase, balanceToSubtractFromMaker,
);
return expect((orderValidationUtils as any).validateFillOrderMakerBalancesAllowancesThrowIfInvalidAsync(
signedOrder, fillTakerAmount, takerAddress, zrxTokenAddress,
)).to.be.rejectedWith(ExchangeContractErrs.InsufficientMakerBalance);
});
it('should throw when maker allowance is less than maker fill amount', async () => {
const newAllowanceWhichIsLessThanFillAmount = fillTakerAmount.minus(lackingAllowance);
await zeroEx.token.setProxyAllowanceAsync(makerTokenAddress, makerAddress,
newAllowanceWhichIsLessThanFillAmount);
return expect((orderValidationUtils as any).validateFillOrderMakerBalancesAllowancesThrowIfInvalidAsync(
signedOrder, fillTakerAmount, takerAddress, zrxTokenAddress,
)).to.be.rejectedWith(ExchangeContractErrs.InsufficientMakerAllowance);
});
});
describe('should throw when not enough balance or allowance to pay fees', () => {
const makerFee = new BigNumber(2);
const takerFee = new BigNumber(2);
let signedOrder: SignedOrder;
beforeEach('setup', async () => {
signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
makerTokenAddress, takerTokenAddress, makerFee, takerFee,
makerAddress, takerAddress, fillableAmount, feeRecipient,
);
});
it('should throw when maker doesn\'t have enough balance to pay fees', async () => {
const balanceToSubtractFromMaker = new BigNumber(1);
await zeroEx.token.transferAsync(
zrxTokenAddress, makerAddress, coinbase, balanceToSubtractFromMaker,
);
return expect((orderValidationUtils as any).validateFillOrderMakerBalancesAllowancesThrowIfInvalidAsync(
signedOrder, fillTakerAmount, zrxTokenAddress,
)).to.be.rejectedWith(ExchangeContractErrs.InsufficientMakerFeeBalance);
});
it('should throw when maker doesn\'t have enough allowance to pay fees', async () => {
const newAllowanceWhichIsLessThanFees = makerFee.minus(1);
await zeroEx.token.setProxyAllowanceAsync(zrxTokenAddress, makerAddress,
newAllowanceWhichIsLessThanFees);
return expect((orderValidationUtils as any).validateFillOrderMakerBalancesAllowancesThrowIfInvalidAsync(
signedOrder, fillTakerAmount, zrxTokenAddress,
)).to.be.rejectedWith(ExchangeContractErrs.InsufficientMakerFeeAllowance);
});
it('should throw when taker doesn\'t have enough balance to pay fees', async () => {
const balanceToSubtractFromTaker = new BigNumber(1);
await zeroEx.token.transferAsync(
zrxTokenAddress, takerAddress, coinbase, balanceToSubtractFromTaker,
);
return expect((orderValidationUtils as any).validateFillOrderTakerBalancesAllowancesThrowIfInvalidAsync(
signedOrder, fillTakerAmount, takerAddress, zrxTokenAddress,
)).to.be.rejectedWith(ExchangeContractErrs.InsufficientTakerFeeBalance);
});
it('should throw when taker doesn\'t have enough allowance to pay fees', async () => {
const newAllowanceWhichIsLessThanFees = makerFee.minus(1);
await zeroEx.token.setProxyAllowanceAsync(zrxTokenAddress, takerAddress,
newAllowanceWhichIsLessThanFees);
return expect((orderValidationUtils as any).validateFillOrderTakerBalancesAllowancesThrowIfInvalidAsync(
signedOrder, fillTakerAmount, takerAddress, zrxTokenAddress,
)).to.be.rejectedWith(ExchangeContractErrs.InsufficientTakerFeeAllowance);
});
});
describe('should throw on insufficient balance or allowance when makerToken is ZRX',
() => {
const makerFee = new BigNumber(2);
const takerFee = new BigNumber(2);
let signedOrder: SignedOrder;
beforeEach(async () => {
signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
zrxTokenAddress, takerTokenAddress, makerFee, takerFee,
makerAddress, takerAddress, fillableAmount, feeRecipient,
);
});
it('should throw on insufficient balance when makerToken is ZRX', async () => {
const balanceToSubtractFromMaker = new BigNumber(1);
await zeroEx.token.transferAsync(
zrxTokenAddress, makerAddress, coinbase, balanceToSubtractFromMaker,
);
return expect((orderValidationUtils as any).validateFillOrderMakerBalancesAllowancesThrowIfInvalidAsync(
signedOrder, fillTakerAmount, zrxTokenAddress,
)).to.be.rejectedWith(ExchangeContractErrs.InsufficientMakerBalance);
});
it('should throw on insufficient allowance when makerToken is ZRX', async () => {
const oldAllowance = await zeroEx.token.getProxyAllowanceAsync(zrxTokenAddress, makerAddress);
const newAllowanceWhichIsInsufficient = oldAllowance.minus(1);
await zeroEx.token.setProxyAllowanceAsync(
zrxTokenAddress, makerAddress, newAllowanceWhichIsInsufficient);
return expect((orderValidationUtils as any).validateFillOrderMakerBalancesAllowancesThrowIfInvalidAsync(
signedOrder, fillTakerAmount, zrxTokenAddress,
)).to.be.rejectedWith(ExchangeContractErrs.InsufficientMakerAllowance);
});
});
describe('should throw on insufficient balance or allowance when takerToken is ZRX',
() => {
const makerFee = new BigNumber(2);
const takerFee = new BigNumber(2);
let signedOrder: SignedOrder;
beforeEach(async () => {
signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
makerTokenAddress, zrxTokenAddress, makerFee, takerFee,
makerAddress, takerAddress, fillableAmount, feeRecipient,
);
});
it('should throw on insufficient balance when takerToken is ZRX', async () => {
const balanceToSubtractFromTaker = new BigNumber(1);
await zeroEx.token.transferAsync(
zrxTokenAddress, takerAddress, coinbase, balanceToSubtractFromTaker,
);
return expect((orderValidationUtils as any).validateFillOrderTakerBalancesAllowancesThrowIfInvalidAsync(
signedOrder, fillTakerAmount, takerAddress, zrxTokenAddress,
)).to.be.rejectedWith(ExchangeContractErrs.InsufficientTakerBalance);
});
it('should throw on insufficient allowance when takerToken is ZRX', async () => {
const oldAllowance = await zeroEx.token.getProxyAllowanceAsync(zrxTokenAddress, takerAddress);
const newAllowanceWhichIsInsufficient = oldAllowance.minus(1);
await zeroEx.token.setProxyAllowanceAsync(
zrxTokenAddress, takerAddress, newAllowanceWhichIsInsufficient);
return expect((orderValidationUtils as any).validateFillOrderTakerBalancesAllowancesThrowIfInvalidAsync(
signedOrder, fillTakerAmount, takerAddress, zrxTokenAddress,
)).to.be.rejectedWith(ExchangeContractErrs.InsufficientTakerAllowance);
});
});
});
});

View File

@@ -1,293 +0,0 @@
import 'mocha';
import * as _ from 'lodash';
import * as chai from 'chai';
import * as BigNumber from 'bignumber.js';
import promisify = require('es6-promisify');
import {constants} from './utils/constants';
import {SchemaValidator} from '../src/utils/schema_validator';
import {tokenSchema} from '../src/schemas/token_schema';
import {orderSchema, signedOrderSchema} from '../src/schemas/order_schemas';
import {addressSchema, numberSchema} from '../src/schemas/basic_type_schemas';
import {orderFillOrKillRequestsSchema} from '../src/schemas/order_fill_or_kill_requests_schema';
import {ecSignatureParameterSchema, ecSignatureSchema} from '../src/schemas/ec_signature_schema';
import {orderCancellationRequestsSchema} from '../src/schemas/order_cancel_schema';
import {orderFillRequestsSchema} from '../src/schemas/order_fill_requests_schema';
chai.config.includeStack = true;
const expect = chai.expect;
describe('Schema', () => {
const validator = new SchemaValidator();
const validateAgainstSchema = (testCases: any[], schema: any, shouldFail = false) => {
_.forEach(testCases, (testCase: any) => {
if (shouldFail) {
expect(validator.validate(testCase, schema).errors).to.be.lengthOf.at.least(1);
} else {
expect(validator.validate(testCase, schema).errors).to.be.lengthOf(0);
}
});
};
describe('#numberSchema', () => {
it('should validate valid numbers', () => {
const testCases = ['42', '0', '1.3', '0.2', '00.00'];
validateAgainstSchema(testCases, numberSchema);
});
it('should fail for invalid numbers', () => {
const testCases = ['.3', '1.', 'abacaba', 'и', '1..0'];
const shouldFail = true;
validateAgainstSchema(testCases, numberSchema, shouldFail);
});
});
describe('#addressSchema', () => {
it('should validate valid addresses', () => {
const testCases = ['0x8b0292B11a196601eD2ce54B665CaFEca0347D42', constants.NULL_ADDRESS];
validateAgainstSchema(testCases, addressSchema);
});
it('should fail for invalid addresses', () => {
const testCases = ['0x', '0', '0x00', '0xzzzzzzB11a196601eD2ce54B665CaFEca0347D42'];
const shouldFail = true;
validateAgainstSchema(testCases, addressSchema, shouldFail);
});
});
describe('#ecSignatureParameterSchema', () => {
it('should validate valid parameters', () => {
const testCases = [
'0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33',
'0X40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
];
validateAgainstSchema(testCases, ecSignatureParameterSchema);
});
it('should fail for invalid parameters', () => {
const testCases = [
'0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc3', // shorter
'0xzzzz9190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254', // invalid characters
'40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254', // no 0x
];
const shouldFail = true;
validateAgainstSchema(testCases, ecSignatureParameterSchema, shouldFail);
});
});
describe('#ecSignatureSchema', () => {
it('should validate valid signature', () => {
const signature = {
v: 27,
r: '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33',
s: '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
};
const testCases = [
signature,
{
...signature,
v: 28,
},
];
validateAgainstSchema(testCases, ecSignatureSchema);
});
it('should fail for invalid signature', () => {
const v = 27;
const r = '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33';
const s = '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254';
const testCases = [
{},
{v},
{r, s, v: 31},
];
const shouldFail = true;
validateAgainstSchema(testCases, ecSignatureSchema, shouldFail);
});
});
describe('#tokenSchema', () => {
const token = {
name: 'Zero Ex',
symbol: 'ZRX',
decimals: 100500,
address: '0x8b0292B11a196601eD2ce54B665CaFEca0347D42',
url: 'https://0xproject.com',
};
it('should validate valid token', () => {
const testCases = [
token,
];
validateAgainstSchema(testCases, tokenSchema);
});
it('should fail for invalid token', () => {
const testCases = [
{
...token,
address: null,
},
{
...token,
decimals: undefined,
},
[],
4,
{
...token,
url: 'not an url',
},
];
const shouldFail = true;
validateAgainstSchema(testCases, tokenSchema, shouldFail);
});
});
describe('order including schemas', () => {
const order = {
maker: constants.NULL_ADDRESS,
taker: constants.NULL_ADDRESS,
makerFee: '1',
takerFee: '2',
makerTokenAmount: '1',
takerTokenAmount: '2',
makerTokenAddress: constants.NULL_ADDRESS,
takerTokenAddress: constants.NULL_ADDRESS,
salt: '256',
feeRecipient: constants.NULL_ADDRESS,
expirationUnixTimestampSec: '42',
};
describe('#orderSchema', () => {
it('should validate valid order', () => {
const testCases = [
order,
];
validateAgainstSchema(testCases, orderSchema);
});
it('should fail for invalid order', () => {
const testCases = [
{
...order,
salt: undefined,
},
{
...order,
salt: 'salt',
},
'order',
];
const shouldFail = true;
validateAgainstSchema(testCases, orderSchema, shouldFail);
});
});
describe('signed order including schemas', () => {
const signedOrder = {
...order,
ecSignature: {
v: 27,
r: '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33',
s: '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
},
};
describe('#signedOrderSchema', () => {
it('should validate valid signed order', () => {
const testCases = [
signedOrder,
];
validateAgainstSchema(testCases, signedOrderSchema);
});
it('should fail for invalid signed order', () => {
const testCases = [
{
...signedOrder,
ecSignature: undefined,
},
];
const shouldFail = true;
validateAgainstSchema(testCases, signedOrderSchema, shouldFail);
});
});
describe('#orderFillOrKillRequestsSchema', () => {
const orderFillOrKillRequests = [
{
signedOrder,
fillTakerAmount: '5',
},
];
it('should validate valid order fill or kill requests', () => {
const testCases = [
orderFillOrKillRequests,
];
validateAgainstSchema(testCases, orderFillOrKillRequestsSchema);
});
it('should fail for invalid order fill or kill requests', () => {
const testCases = [
[
{
...orderFillOrKillRequests[0],
fillTakerAmount: undefined,
},
],
];
const shouldFail = true;
validateAgainstSchema(testCases, orderFillOrKillRequestsSchema, shouldFail);
});
});
describe('#orderCancellationRequestsSchema', () => {
const orderCancellationRequests = [
{
order,
takerTokenCancelAmount: '5',
},
];
it('should validate valid order cancellation requests', () => {
const testCases = [
orderCancellationRequests,
];
validateAgainstSchema(testCases, orderCancellationRequestsSchema);
});
it('should fail for invalid order cancellation requests', () => {
const testCases = [
[
{
...orderCancellationRequests[0],
takerTokenCancelAmount: undefined,
},
],
];
const shouldFail = true;
validateAgainstSchema(testCases, orderCancellationRequestsSchema, shouldFail);
});
});
describe('#orderFillRequestsSchema', () => {
const orderFillRequests = [
{
signedOrder,
takerTokenFillAmount: '5',
},
];
it('should validate valid order fill requests', () => {
const testCases = [
orderFillRequests,
];
validateAgainstSchema(testCases, orderFillRequestsSchema);
});
it('should fail for invalid order fill requests', () => {
const testCases = [
[
{
...orderFillRequests[0],
takerTokenFillAmount: undefined,
},
],
];
const shouldFail = true;
validateAgainstSchema(testCases, orderFillRequestsSchema, shouldFail);
});
});
});
});
describe('BigNumber serialization', () => {
it('should correctly serialize BigNumbers', () => {
const testCases = {
'42': '42',
'0': '0',
'1.3': '1.3',
'0.2': '0.2',
'00.00': '0',
'.3': '0.3',
};
_.forEach(testCases, (serialized: string, input: string) => {
expect(JSON.parse(JSON.stringify(new BigNumber(input)))).to.be.equal(serialized);
});
});
});
});

View File

@@ -1,12 +1,11 @@
import * as _ from 'lodash';
import 'mocha';
import * as chai from 'chai';
import {SchemaValidator, schemas} from '0x-json-schemas';
import {chaiSetup} from './utils/chai_setup';
import {web3Factory} from './utils/web3_factory';
import {ZeroEx} from '../src/0x';
import {ZeroEx, Token} from '../src';
import {BlockchainLifecycle} from './utils/blockchain_lifecycle';
import {SchemaValidator} from '../src/utils/schema_validator';
import {tokenSchema} from '../src/schemas/token_schema';
chaiSetup.configure();
const expect = chai.expect;
@@ -16,9 +15,25 @@ const TOKEN_REGISTRY_SIZE_AFTER_MIGRATION = 7;
describe('TokenRegistryWrapper', () => {
let zeroEx: ZeroEx;
let tokens: Token[];
const tokenAddressBySymbol: {[symbol: string]: string} = {};
const tokenAddressByName: {[symbol: string]: string} = {};
const tokenBySymbol: {[symbol: string]: Token} = {};
const tokenByName: {[symbol: string]: Token} = {};
const registeredSymbol = 'ZRX';
const registeredName = '0x Protocol Token';
const unregisteredSymbol = 'MAL';
const unregisteredName = 'Malicious Token';
before(async () => {
const web3 = web3Factory.create();
zeroEx = new ZeroEx(web3);
zeroEx = new ZeroEx(web3.currentProvider);
tokens = await zeroEx.tokenRegistry.getTokensAsync();
_.map(tokens, token => {
tokenAddressBySymbol[token.symbol] = token.address;
tokenAddressByName[token.name] = token.address;
tokenBySymbol[token.symbol] = token;
tokenByName[token.name] = token;
});
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
@@ -28,14 +43,81 @@ describe('TokenRegistryWrapper', () => {
});
describe('#getTokensAsync', () => {
it('should return all the tokens added to the tokenRegistry during the migration', async () => {
const tokens = await zeroEx.tokenRegistry.getTokensAsync();
expect(tokens).to.have.lengthOf(TOKEN_REGISTRY_SIZE_AFTER_MIGRATION);
const schemaValidator = new SchemaValidator();
_.each(tokens, token => {
const validationResult = schemaValidator.validate(token, tokenSchema);
const validationResult = schemaValidator.validate(token, schemas.tokenSchema);
expect(validationResult.errors).to.have.lengthOf(0);
});
});
});
describe('#getTokenAddressesAsync', () => {
it('should return all the token addresses added to the tokenRegistry during the migration', async () => {
const tokenAddresses = await zeroEx.tokenRegistry.getTokenAddressesAsync();
expect(tokenAddresses).to.have.lengthOf(TOKEN_REGISTRY_SIZE_AFTER_MIGRATION);
const schemaValidator = new SchemaValidator();
_.each(tokenAddresses, tokenAddress => {
const validationResult = schemaValidator.validate(tokenAddress, schemas.addressSchema);
expect(validationResult.errors).to.have.lengthOf(0);
expect(tokenAddress).to.not.be.equal(ZeroEx.NULL_ADDRESS);
});
});
});
describe('#getTokenAddressBySymbol', () => {
it('should return correct address for a token in the registry', async () => {
const tokenAddress = await zeroEx.tokenRegistry.getTokenAddressBySymbolIfExistsAsync(registeredSymbol);
expect(tokenAddress).to.be.equal(tokenAddressBySymbol[registeredSymbol]);
});
it('should return undefined for a token out of registry', async () => {
const tokenAddress = await zeroEx.tokenRegistry.getTokenAddressBySymbolIfExistsAsync(unregisteredSymbol);
expect(tokenAddress).to.be.undefined();
});
});
describe('#getTokenAddressByName', () => {
it('should return correct address for a token in the registry', async () => {
const tokenAddress = await zeroEx.tokenRegistry.getTokenAddressByNameIfExistsAsync(registeredName);
expect(tokenAddress).to.be.equal(tokenAddressByName[registeredName]);
});
it('should return undefined for a token out of registry', async () => {
const tokenAddress = await zeroEx.tokenRegistry.getTokenAddressByNameIfExistsAsync(unregisteredName);
expect(tokenAddress).to.be.undefined();
});
});
describe('#getTokenBySymbol', () => {
it('should return correct token for a token in the registry', async () => {
const token = await zeroEx.tokenRegistry.getTokenBySymbolIfExistsAsync(registeredSymbol);
expect(token).to.be.deep.equal(tokenBySymbol[registeredSymbol]);
});
it('should return undefined for a token out of registry', async () => {
const token = await zeroEx.tokenRegistry.getTokenBySymbolIfExistsAsync(unregisteredSymbol);
expect(token).to.be.undefined();
});
});
describe('#getTokenByName', () => {
it('should return correct token for a token in the registry', async () => {
const token = await zeroEx.tokenRegistry.getTokenByNameIfExistsAsync(registeredName);
expect(token).to.be.deep.equal(tokenByName[registeredName]);
});
it('should return undefined for a token out of registry', async () => {
const token = await zeroEx.tokenRegistry.getTokenByNameIfExistsAsync(unregisteredName);
expect(token).to.be.undefined();
});
});
describe('#getTokenIfExistsAsync', () => {
it('should return the token added to the tokenRegistry during the migration', async () => {
const aToken = tokens[0];
const token = await zeroEx.tokenRegistry.getTokenIfExistsAsync(aToken.address);
const schemaValidator = new SchemaValidator();
const validationResult = schemaValidator.validate(token, schemas.tokenSchema);
expect(validationResult.errors).to.have.lengthOf(0);
});
it('should return return undefined when passed a token address not in the tokenRegistry', async () => {
const unregisteredTokenAddress = '0x5409ed021d9299bf6814279a6a1411a7e866a631';
const tokenIfExists = await zeroEx.tokenRegistry.getTokenIfExistsAsync(unregisteredTokenAddress);
expect(tokenIfExists).to.be.undefined();
});
});
});

View File

@@ -0,0 +1,31 @@
import * as chai from 'chai';
import {chaiSetup} from './utils/chai_setup';
import {web3Factory} from './utils/web3_factory';
import {ZeroEx} from '../src';
import {TokenTransferProxyWrapper} from '../src/contract_wrappers/token_transfer_proxy_wrapper';
chaiSetup.configure();
const expect = chai.expect;
describe('TokenTransferProxyWrapper', () => {
let zeroEx: ZeroEx;
before(async () => {
const web3 = web3Factory.create();
zeroEx = new ZeroEx(web3.currentProvider);
});
describe('#isAuthorizedAsync', () => {
it('should return false if the address is not authorized', async () => {
const isAuthorized = await zeroEx.proxy.isAuthorizedAsync(ZeroEx.NULL_ADDRESS);
expect(isAuthorized).to.be.false();
});
});
describe('#getAuthorizedAddressesAsync', () => {
it('should return the list of authorized addresses', async () => {
const authorizedAddresses = await zeroEx.proxy.getAuthorizedAddressesAsync();
for (const authorizedAddress of authorizedAddresses) {
const isAuthorized = await zeroEx.proxy.isAuthorizedAsync(authorizedAddress);
expect(isAuthorized).to.be.true();
}
});
});
});

View File

@@ -5,9 +5,19 @@ import * as Web3 from 'web3';
import * as BigNumber from 'bignumber.js';
import promisify = require('es6-promisify');
import {web3Factory} from './utils/web3_factory';
import {ZeroEx} from '../src/0x';
import {ZeroExError, Token} from '../src/types';
import {
ZeroEx,
ZeroExError,
Token,
SubscriptionOpts,
TokenEvents,
ContractEvent,
TransferContractEventArgs,
ApprovalContractEventArgs,
} from '../src';
import {BlockchainLifecycle} from './utils/blockchain_lifecycle';
import {TokenUtils} from './utils/token_utils';
import {DoneCallback} from '../src/types';
chaiSetup.configure();
const expect = chai.expect;
@@ -18,13 +28,15 @@ describe('TokenWrapper', () => {
let zeroEx: ZeroEx;
let userAddresses: string[];
let tokens: Token[];
let tokenUtils: TokenUtils;
let coinbase: string;
let addressWithoutFunds: string;
before(async () => {
web3 = web3Factory.create();
zeroEx = new ZeroEx(web3);
userAddresses = await promisify(web3.eth.getAccounts)();
zeroEx = new ZeroEx(web3.currentProvider);
userAddresses = await zeroEx.getAvailableAddressesAsync();
tokens = await zeroEx.tokenRegistry.getTokensAsync();
tokenUtils = new TokenUtils(tokens);
coinbase = userAddresses[0];
addressWithoutFunds = userAddresses[1];
});
@@ -55,7 +67,7 @@ describe('TokenWrapper', () => {
const toAddress = coinbase;
return expect(zeroEx.token.transferAsync(
token.address, fromAddress, toAddress, transferAmount,
)).to.be.rejectedWith(ZeroExError.INSUFFICIENT_BALANCE_FOR_TRANSFER);
)).to.be.rejectedWith(ZeroExError.InsufficientBalanceForTransfer);
});
it('should throw a CONTRACT_DOES_NOT_EXIST error for a non-existent token contract', async () => {
const nonExistentTokenAddress = '0x9dd402f14d67e001d8efbe6583e51bf9706aa065';
@@ -63,7 +75,7 @@ describe('TokenWrapper', () => {
const toAddress = coinbase;
return expect(zeroEx.token.transferAsync(
nonExistentTokenAddress, fromAddress, toAddress, transferAmount,
)).to.be.rejectedWith(ZeroExError.CONTRACT_DOES_NOT_EXIST);
)).to.be.rejectedWith(ZeroExError.ContractDoesNotExist);
});
});
describe('#transferFromAsync', () => {
@@ -88,7 +100,7 @@ describe('TokenWrapper', () => {
return expect(zeroEx.token.transferFromAsync(
token.address, fromAddress, toAddress, senderAddress, transferAmount,
)).to.be.rejectedWith(ZeroExError.INSUFFICIENT_ALLOWANCE_FOR_TRANSFER);
)).to.be.rejectedWith(ZeroExError.InsufficientAllowanceForTransfer);
});
it('[regression] should fail to transfer tokens if set allowance for toAddress instead of senderAddress',
async () => {
@@ -99,7 +111,7 @@ describe('TokenWrapper', () => {
return expect(zeroEx.token.transferFromAsync(
token.address, fromAddress, toAddress, senderAddress, transferAmount,
)).to.be.rejectedWith(ZeroExError.INSUFFICIENT_ALLOWANCE_FOR_TRANSFER);
)).to.be.rejectedWith(ZeroExError.InsufficientAllowanceForTransfer);
});
it('should fail to transfer tokens if fromAddress has insufficient balance', async () => {
const fromAddress = addressWithoutFunds;
@@ -115,7 +127,7 @@ describe('TokenWrapper', () => {
return expect(zeroEx.token.transferFromAsync(
token.address, fromAddress, toAddress, senderAddress, transferAmount,
)).to.be.rejectedWith(ZeroExError.INSUFFICIENT_BALANCE_FOR_TRANSFER);
)).to.be.rejectedWith(ZeroExError.InsufficientBalanceForTransfer);
});
it('should successfully transfer tokens', async () => {
const fromAddress = coinbase;
@@ -136,29 +148,46 @@ describe('TokenWrapper', () => {
const nonExistentTokenAddress = '0x9dd402f14d67e001d8efbe6583e51bf9706aa065';
return expect(zeroEx.token.transferFromAsync(
nonExistentTokenAddress, fromAddress, toAddress, senderAddress, new BigNumber(42),
)).to.be.rejectedWith(ZeroExError.CONTRACT_DOES_NOT_EXIST);
)).to.be.rejectedWith(ZeroExError.ContractDoesNotExist);
});
});
describe('#getBalanceAsync', () => {
it('should return the balance for an existing ERC20 token', async () => {
const token = tokens[0];
const ownerAddress = coinbase;
const balance = await zeroEx.token.getBalanceAsync(token.address, ownerAddress);
const expectedBalance = new BigNumber('100000000000000000000000000');
return expect(balance).to.be.bignumber.equal(expectedBalance);
describe('With web3 provider with accounts', () => {
it('should return the balance for an existing ERC20 token', async () => {
const token = tokens[0];
const ownerAddress = coinbase;
const balance = await zeroEx.token.getBalanceAsync(token.address, ownerAddress);
const expectedBalance = new BigNumber('100000000000000000000000000');
return expect(balance).to.be.bignumber.equal(expectedBalance);
});
it('should throw a CONTRACT_DOES_NOT_EXIST error for a non-existent token contract', async () => {
const nonExistentTokenAddress = '0x9dd402f14d67e001d8efbe6583e51bf9706aa065';
const ownerAddress = coinbase;
return expect(zeroEx.token.getBalanceAsync(nonExistentTokenAddress, ownerAddress))
.to.be.rejectedWith(ZeroExError.ContractDoesNotExist);
});
it('should return a balance of 0 for a non-existent owner address', async () => {
const token = tokens[0];
const nonExistentOwner = '0x198c6ad858f213fb31b6fe809e25040e6b964593';
const balance = await zeroEx.token.getBalanceAsync(token.address, nonExistentOwner);
const expectedBalance = new BigNumber(0);
return expect(balance).to.be.bignumber.equal(expectedBalance);
});
});
it('should throw a CONTRACT_DOES_NOT_EXIST error for a non-existent token contract', async () => {
const nonExistentTokenAddress = '0x9dd402f14d67e001d8efbe6583e51bf9706aa065';
const ownerAddress = coinbase;
return expect(zeroEx.token.getBalanceAsync(nonExistentTokenAddress, ownerAddress))
.to.be.rejectedWith(ZeroExError.CONTRACT_DOES_NOT_EXIST);
});
it('should return a balance of 0 for a non-existent owner address', async () => {
const token = tokens[0];
const nonExistentOwner = '0x198C6Ad858F213Fb31b6FE809E25040E6B964593';
const balance = await zeroEx.token.getBalanceAsync(token.address, nonExistentOwner);
const expectedBalance = new BigNumber(0);
return expect(balance).to.be.bignumber.equal(expectedBalance);
describe('With web3 provider without accounts', () => {
let zeroExWithoutAccounts: ZeroEx;
before(async () => {
const hasAddresses = false;
const web3WithoutAccounts = web3Factory.create(hasAddresses);
zeroExWithoutAccounts = new ZeroEx(web3WithoutAccounts.currentProvider);
});
it('should return balance even when called with Web3 provider instance without addresses', async () => {
const token = tokens[0];
const ownerAddress = coinbase;
const balance = await zeroExWithoutAccounts.token.getBalanceAsync(token.address, ownerAddress);
const expectedBalance = new BigNumber('100000000000000000000000000');
return expect(balance).to.be.bignumber.equal(expectedBalance);
});
});
});
describe('#setAllowanceAsync', () => {
@@ -180,26 +209,89 @@ describe('TokenWrapper', () => {
return expect(allowanceAfterSet).to.be.bignumber.equal(expectedAllowanceAfterAllowanceSet);
});
});
describe('#getAllowanceAsync', () => {
it('should get the proxy allowance', async () => {
describe('#setUnlimitedAllowanceAsync', () => {
it('should set the unlimited spender\'s allowance', async () => {
const token = tokens[0];
const ownerAddress = coinbase;
const spenderAddress = addressWithoutFunds;
const amountInBaseUnits = new BigNumber(50);
await zeroEx.token.setAllowanceAsync(token.address, ownerAddress, spenderAddress, amountInBaseUnits);
await zeroEx.token.setUnlimitedAllowanceAsync(token.address, ownerAddress, spenderAddress);
const allowance = await zeroEx.token.getAllowanceAsync(token.address, ownerAddress, spenderAddress);
const expectedAllowance = amountInBaseUnits;
return expect(allowance).to.be.bignumber.equal(expectedAllowance);
return expect(allowance).to.be.bignumber.equal(zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
});
it('should return 0 if no allowance set yet', async () => {
const token = tokens[0];
const ownerAddress = coinbase;
const spenderAddress = addressWithoutFunds;
const allowance = await zeroEx.token.getAllowanceAsync(token.address, ownerAddress, spenderAddress);
const expectedAllowance = new BigNumber(0);
return expect(allowance).to.be.bignumber.equal(expectedAllowance);
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);
// 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', () => {
describe('With web3 provider with accounts', () => {
it('should get the proxy allowance', async () => {
const token = tokens[0];
const ownerAddress = coinbase;
const spenderAddress = addressWithoutFunds;
const amountInBaseUnits = new BigNumber(50);
await zeroEx.token.setAllowanceAsync(token.address, ownerAddress, spenderAddress, amountInBaseUnits);
const allowance = await zeroEx.token.getAllowanceAsync(token.address, ownerAddress, spenderAddress);
const expectedAllowance = amountInBaseUnits;
return expect(allowance).to.be.bignumber.equal(expectedAllowance);
});
it('should return 0 if no allowance set yet', async () => {
const token = tokens[0];
const ownerAddress = coinbase;
const spenderAddress = addressWithoutFunds;
const allowance = await zeroEx.token.getAllowanceAsync(token.address, ownerAddress, spenderAddress);
const expectedAllowance = new BigNumber(0);
return expect(allowance).to.be.bignumber.equal(expectedAllowance);
});
});
describe('With web3 provider without accounts', () => {
let zeroExWithoutAccounts: ZeroEx;
before(async () => {
const hasAddresses = false;
const web3WithoutAccounts = web3Factory.create(hasAddresses);
zeroExWithoutAccounts = new ZeroEx(web3WithoutAccounts.currentProvider);
});
it('should get the proxy allowance', async () => {
const token = tokens[0];
const ownerAddress = coinbase;
const spenderAddress = addressWithoutFunds;
const amountInBaseUnits = new BigNumber(50);
await zeroEx.token.setAllowanceAsync(token.address, ownerAddress, spenderAddress, amountInBaseUnits);
const allowance = await zeroExWithoutAccounts.token.getAllowanceAsync(
token.address, ownerAddress, spenderAddress,
);
const expectedAllowance = amountInBaseUnits;
return expect(allowance).to.be.bignumber.equal(expectedAllowance);
});
});
});
describe('#getProxyAllowanceAsync', () => {
@@ -232,4 +324,114 @@ 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;
let tokenAddress: string;
const subscriptionOpts: SubscriptionOpts = {
fromBlock: 0,
toBlock: 'latest',
};
const transferAmount = new BigNumber(42);
const allowanceAmount = new BigNumber(42);
before(() => {
const token = tokens[0];
tokenAddress = token.address;
});
afterEach(async () => {
await zeroEx.token.stopWatchingAllEventsAsync();
});
// Hack: Mocha does not allow a test to be both async and have a `done` callback
// Since we need to await the receipt of the event in the `subscribeAsync` callback,
// we do need both. A hack is to make the top-level a sync fn w/ a done callback and then
// wrap the rest of the test in an async block
// Source: https://github.com/mochajs/mocha/issues/2407
it('Should receive the Transfer event when an order is filled', (done: DoneCallback) => {
(async () => {
const zeroExEvent = await zeroEx.token.subscribeAsync(
tokenAddress, TokenEvents.Transfer, subscriptionOpts, indexFilterValues);
zeroExEvent.watch((err: Error, event: ContractEvent) => {
expect(err).to.be.null();
expect(event).to.not.be.undefined();
const args = event.args as TransferContractEventArgs;
expect(args._from).to.be.equal(coinbase);
expect(args._to).to.be.equal(addressWithoutFunds);
expect(args._value).to.be.bignumber.equal(transferAmount);
done();
});
await zeroEx.token.transferAsync(tokenAddress, coinbase, addressWithoutFunds, transferAmount);
})().catch(done);
});
it('Should receive the Approval event when an order is cancelled', (done: DoneCallback) => {
(async () => {
const zeroExEvent = await zeroEx.token.subscribeAsync(
tokenAddress, TokenEvents.Approval, subscriptionOpts, indexFilterValues);
zeroExEvent.watch((err: Error, event: ContractEvent) => {
expect(err).to.be.null();
expect(event).to.not.be.undefined();
const args = event.args as ApprovalContractEventArgs;
expect(args._owner).to.be.equal(coinbase);
expect(args._spender).to.be.equal(addressWithoutFunds);
expect(args._value).to.be.bignumber.equal(allowanceAmount);
done();
});
await zeroEx.token.setAllowanceAsync(tokenAddress, coinbase, addressWithoutFunds, allowanceAmount);
})().catch(done);
});
it('Outstanding subscriptions are cancelled when zeroEx.setProviderAsync called', (done: DoneCallback) => {
(async () => {
const eventSubscriptionToBeCancelled = await zeroEx.token.subscribeAsync(
tokenAddress, TokenEvents.Transfer, subscriptionOpts, indexFilterValues);
eventSubscriptionToBeCancelled.watch((err: Error, event: ContractEvent) => {
done(new Error('Expected this subscription to have been cancelled'));
});
const newProvider = web3Factory.getRpcProvider();
await zeroEx.setProviderAsync(newProvider);
const eventSubscriptionToStay = await zeroEx.token.subscribeAsync(
tokenAddress, TokenEvents.Transfer, subscriptionOpts, indexFilterValues);
eventSubscriptionToStay.watch((err: Error, event: ContractEvent) => {
expect(err).to.be.null();
expect(event).to.not.be.undefined();
done();
});
await zeroEx.token.transferAsync(tokenAddress, coinbase, addressWithoutFunds, transferAmount);
})().catch(done);
});
it('Should stop watch for events when stopWatchingAsync called on the eventEmitter', (done: DoneCallback) => {
(async () => {
const eventSubscriptionToBeStopped = await zeroEx.token.subscribeAsync(
tokenAddress, TokenEvents.Transfer, subscriptionOpts, indexFilterValues);
eventSubscriptionToBeStopped.watch((err: Error, event: ContractEvent) => {
done(new Error('Expected this subscription to have been stopped'));
});
await eventSubscriptionToBeStopped.stopWatchingAsync();
await zeroEx.token.transferAsync(tokenAddress, coinbase, addressWithoutFunds, transferAmount);
done();
})().catch(done);
});
it('Should wrap all event args BigNumber instances in a newer version of BigNumber', (done: DoneCallback) => {
(async () => {
const zeroExEvent = await zeroEx.token.subscribeAsync(
tokenAddress, TokenEvents.Transfer, subscriptionOpts, indexFilterValues);
zeroExEvent.watch((err: Error, event: ContractEvent) => {
const args = event.args as TransferContractEventArgs;
expect(args._value.isBigNumber).to.be.true();
done();
});
await zeroEx.token.transferAsync(tokenAddress, coinbase, addressWithoutFunds, transferAmount);
})().catch(done);
});
});
});

View File

@@ -2,4 +2,6 @@ export const constants = {
NULL_ADDRESS: '0x0000000000000000000000000000000000000000',
RPC_HOST: 'localhost',
RPC_PORT: 8545,
TESTRPC_NETWORK_ID: 50,
KOVAN_RPC_URL: 'https://kovan.0xproject.com',
};

View File

@@ -1,6 +1,5 @@
import * as BigNumber from 'bignumber.js';
import {ZeroEx} from '../../src/0x';
import {Token, SignedOrder} from '../../src/types';
import {ZeroEx, Token, SignedOrder} from '../../src';
import {orderFactory} from '../utils/order_factory';
import {constants} from './constants';
@@ -10,12 +9,15 @@ export class FillScenarios {
private tokens: Token[];
private coinbase: string;
private zrxTokenAddress: string;
constructor(zeroEx: ZeroEx, userAddresses: string[], tokens: Token[], zrxTokenAddress: string) {
private exchangeContractAddress: string;
constructor(zeroEx: ZeroEx, userAddresses: string[],
tokens: Token[], zrxTokenAddress: string, exchangeContractAddress: string) {
this.zeroEx = zeroEx;
this.userAddresses = userAddresses;
this.tokens = tokens;
this.coinbase = userAddresses[0];
this.zrxTokenAddress = zrxTokenAddress;
this.exchangeContractAddress = exchangeContractAddress;
}
public async createFillableSignedOrderAsync(makerTokenAddress: string, takerTokenAddress: string,
makerAddress: string, takerAddress: string,
@@ -59,8 +61,8 @@ export class FillScenarios {
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress,
fillableAmount, fillableAmount,
);
const shouldCheckTransfer = false;
await this.zeroEx.exchange.fillOrderAsync(signedOrder, partialFillAmount, shouldCheckTransfer, takerAddress);
const shouldThrowOnInsufficientBalanceOrAllowance = false;
await this.zeroEx.exchange.fillOrderAsync(signedOrder, partialFillAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress);
return signedOrder;
}
private async createAsymmetricFillableSignedOrderWithFeesAsync(
@@ -69,42 +71,42 @@ export class FillScenarios {
makerAddress: string, takerAddress: string,
makerFillableAmount: BigNumber.BigNumber, takerFillableAmount: BigNumber.BigNumber,
feeRecepient: string, expirationUnixTimestampSec?: BigNumber.BigNumber): Promise<SignedOrder> {
await this.zeroEx.token.transferAsync(makerTokenAddress, this.coinbase, makerAddress, makerFillableAmount);
const oldMakerAllowance = await this.zeroEx.token.getProxyAllowanceAsync(makerTokenAddress, makerAddress);
const newMakerAllowance = oldMakerAllowance.plus(makerFillableAmount);
await this.zeroEx.token.setProxyAllowanceAsync(
makerTokenAddress, makerAddress, newMakerAllowance,
);
await this.zeroEx.token.transferAsync(takerTokenAddress, this.coinbase, takerAddress, takerFillableAmount);
const oldTakerAllowance = await this.zeroEx.token.getProxyAllowanceAsync(takerTokenAddress, takerAddress);
const newTakerAllowance = oldTakerAllowance.plus(takerFillableAmount);
await this.zeroEx.token.setProxyAllowanceAsync(
takerTokenAddress, takerAddress, newTakerAllowance,
);
if (!makerFee.isZero()) {
await this.zeroEx.token.transferAsync(this.zrxTokenAddress, this.coinbase, makerAddress, makerFee);
const oldMakerFeeAllowance =
await this.zeroEx.token.getProxyAllowanceAsync(this.zrxTokenAddress, makerAddress);
const newMakerFeeAllowance = oldMakerFeeAllowance.plus(makerFee);
await this.zeroEx.token.setProxyAllowanceAsync(
this.zrxTokenAddress, makerAddress, newMakerFeeAllowance,
);
}
if (!takerFee.isZero()) {
await this.zeroEx.token.transferAsync(this.zrxTokenAddress, this.coinbase, takerAddress, takerFee);
const oldTakerFeeAllowance =
await this.zeroEx.token.getProxyAllowanceAsync(this.zrxTokenAddress, takerAddress);
const newTakerFeeAllowance = oldTakerFeeAllowance.plus(takerFee);
await this.zeroEx.token.setProxyAllowanceAsync(
this.zrxTokenAddress, takerAddress, newTakerFeeAllowance,
);
}
await Promise.all([
this.increaseBalanceAndAllowanceAsync(makerTokenAddress, makerAddress, makerFillableAmount),
this.increaseBalanceAndAllowanceAsync(takerTokenAddress, takerAddress, takerFillableAmount),
]);
await Promise.all([
this.increaseBalanceAndAllowanceAsync(this.zrxTokenAddress, makerAddress, makerFee),
this.increaseBalanceAndAllowanceAsync(this.zrxTokenAddress, takerAddress, takerFee),
]);
const signedOrder = await orderFactory.createSignedOrderAsync(this.zeroEx,
makerAddress, takerAddress, makerFee, takerFee,
makerFillableAmount, makerTokenAddress, takerFillableAmount, takerTokenAddress,
feeRecepient, expirationUnixTimestampSec);
this.exchangeContractAddress, feeRecepient, expirationUnixTimestampSec);
return signedOrder;
}
private async increaseBalanceAndAllowanceAsync(
tokenAddress: string, address: string, amount: BigNumber.BigNumber): Promise<void> {
if (amount.isZero()) {
return; // noop
}
await Promise.all([
this.increaseBalanceAsync(tokenAddress, address, amount),
this.increaseAllowanceAsync(tokenAddress, address, amount),
]);
}
private async increaseBalanceAsync(
tokenAddress: string, address: string, amount: BigNumber.BigNumber): Promise<void> {
await this.zeroEx.token.transferAsync(tokenAddress, this.coinbase, address, amount);
}
private async increaseAllowanceAsync(
tokenAddress: string, address: string, amount: BigNumber.BigNumber): Promise<void> {
const oldMakerAllowance = await this.zeroEx.token.getProxyAllowanceAsync(tokenAddress, address);
const newMakerAllowance = oldMakerAllowance.plus(amount);
await this.zeroEx.token.setProxyAllowanceAsync(
tokenAddress, address, newMakerAllowance,
);
}
}

View File

@@ -1,7 +1,6 @@
import * as _ from 'lodash';
import * as BigNumber from 'bignumber.js';
import {SignedOrder} from '../../src/types';
import {ZeroEx} from '../../src/0x';
import {ZeroEx, SignedOrder} from '../../src';
export const orderFactory = {
async createSignedOrderAsync(
@@ -14,6 +13,7 @@ export const orderFactory = {
makerTokenAddress: string,
takerTokenAmount: BigNumber.BigNumber,
takerTokenAddress: string,
exchangeContractAddress: string,
feeRecipient: string,
expirationUnixTimestampSec?: BigNumber.BigNumber): Promise<SignedOrder> {
const defaultExpirationUnixTimestampSec = new BigNumber(2524604400); // Close to infinite
@@ -30,10 +30,11 @@ export const orderFactory = {
makerTokenAddress,
takerTokenAddress,
salt: ZeroEx.generatePseudoRandomSalt(),
exchangeContractAddress,
feeRecipient,
expirationUnixTimestampSec,
};
const orderHash = await zeroEx.getOrderHashHexAsync(order);
const orderHash = ZeroEx.getOrderHashHex(order);
const ecSignature = await zeroEx.signOrderHashAsync(orderHash, maker);
const signedOrder: SignedOrder = _.assign(order, {ecSignature});
return signedOrder;

View File

@@ -40,6 +40,9 @@ export class RPC {
method: 'POST',
uri: `http://${this.host}:${this.port}`,
body: payload,
headers: {
'content-type': 'application/json'
},
};
const bodyString = await request(opts);
const body = JSON.parse(bodyString);

View File

@@ -1,5 +1,5 @@
import * as _ from 'lodash';
import {Token, ZeroExError} from '../../src/types';
import {Token, ZeroExError} from '../../src';
const PROTOCOL_TOKEN_SYMBOL = 'ZRX';
@@ -11,7 +11,7 @@ export class TokenUtils {
public getProtocolTokenOrThrow(): Token {
const zrxToken = _.find(this.tokens, {symbol: PROTOCOL_TOKEN_SYMBOL});
if (_.isUndefined(zrxToken)) {
throw new Error(ZeroExError.ZRX_NOT_IN_TOKEN_REGISTRY);
throw new Error(ZeroExError.ZrxNotInTokenRegistry);
}
return zrxToken;
}

View File

@@ -6,22 +6,32 @@
import ProviderEngine = require('web3-provider-engine');
import RpcSubprovider = require('web3-provider-engine/subproviders/rpc');
import * as Web3 from 'web3';
import * as Web3_beta from 'web3_beta';
import {constants} from './constants';
import {EmptyWalletSubProvider} from '../../src/subproviders/empty_wallet_subprovider';
export const web3Factory = {
create(): Web3 {
const provider = this.getRpcProvider();
create(hasAddresses: boolean = true): Web3 {
const provider = this.getRpcProvider(hasAddresses);
const web3 = new Web3();
web3.setProvider(provider);
return web3;
},
getRpcProvider(): Web3.Provider {
getRpcProvider(hasAddresses: boolean = true): Web3.Provider {
const provider = new ProviderEngine();
const rpcUrl = `http://${constants.RPC_HOST}:${constants.RPC_PORT}`;
if (!hasAddresses) {
provider.addProvider(new EmptyWalletSubProvider());
}
provider.addProvider(new RpcSubprovider({
rpcUrl,
}));
provider.start();
return provider;
},
getProviderBeta(): Web3.Provider {
const rpcUrl = `http://${constants.RPC_HOST}:${constants.RPC_PORT}`;
const providerBeta = new Web3_beta.providers.HttpProvider(rpcUrl);
return providerBeta;
},
};

16
test/web3_beta_test.ts Normal file
View File

@@ -0,0 +1,16 @@
import * as chai from 'chai';
import {chaiSetup} from './utils/chai_setup';
import 'mocha';
import {ZeroEx, Order, SubscriptionOpts, TokenEvents, ContractEvent} from '../src';
import {web3Factory} from './utils/web3_factory';
chaiSetup.configure();
const expect = chai.expect;
describe('ZeroEx with beta web3', () => {
const web3_beta_provider = web3Factory.getProviderBeta();
const zeroEx = new ZeroEx(web3_beta_provider);
it('is able to make a call using a beta provider', async () => {
await zeroEx.tokenRegistry.getTokenAddressesAsync();
});
});

29
test/web3_wrapper_test.ts Normal file
View File

@@ -0,0 +1,29 @@
import * as chai from 'chai';
import {web3Factory} from './utils/web3_factory';
import {ZeroEx} from '../src/';
import {Web3Wrapper} from '../src/web3_wrapper';
import {constants} from './utils/constants';
chai.config.includeStack = true;
const expect = chai.expect;
describe('Web3Wrapper', () => {
const web3Provider = web3Factory.create().currentProvider;
describe('#getNetworkIdIfExistsAsync', () => {
it('caches network id requests', async () => {
const web3Wrapper = (new ZeroEx(web3Provider) as any)._web3Wrapper as Web3Wrapper;
expect((web3Wrapper as any).networkIdIfExists).to.be.undefined();
const networkIdIfExists = await web3Wrapper.getNetworkIdIfExistsAsync();
expect((web3Wrapper as any).networkIdIfExists).to.be.equal(constants.TESTRPC_NETWORK_ID);
});
it('invalidates network id cache on setProvider call', async () => {
const web3Wrapper = (new ZeroEx(web3Provider) as any)._web3Wrapper as Web3Wrapper;
expect((web3Wrapper as any).networkIdIfExists).to.be.undefined();
const networkIdIfExists = await web3Wrapper.getNetworkIdIfExistsAsync();
expect((web3Wrapper as any).networkIdIfExists).to.be.equal(constants.TESTRPC_NETWORK_ID);
const newProvider = web3Factory.create().currentProvider;
web3Wrapper.setProvider(newProvider);
expect((web3Wrapper as any).networkIdIfExists).to.be.undefined();
});
});
});

View File

@@ -13,6 +13,8 @@
"include": [
"./src/**/*",
"./test/**/*",
"./node_modules/types-bn/index.d.ts",
"./node_modules/types-ethereumjs-util/index.d.ts",
"./node_modules/web3-typescript-typings/index.d.ts",
"./node_modules/chai-typescript-typings/index.d.ts",
"./node_modules/chai-as-promised-typescript-typings/index.d.ts"

View File

@@ -1,16 +1,16 @@
/**
* This is to generate the umd bundle only
*/
const lodash = require('lodash');
const _ = require('lodash');
const webpack = require('webpack');
const path = require('path');
const production = process.env.NODE_ENV === 'production';
let entry = {
'0x': './src/0x.ts',
'index': './src/index.ts',
};
if (production) {
entry = _.assign({}, entry, {'0x.min': './src/0x.ts'});
entry = _.assign({}, entry, {'index.min': './src/index.ts'});
}
module.exports = {

2370
yarn.lock

File diff suppressed because it is too large Load Diff