Compare commits

...

143 Commits

Author SHA1 Message Date
xianny
6b0f3570b9 Publish
- @0x/contracts-asset-proxy@3.0.1
 - @0x/contracts-coordinator@3.0.1
 - @0x/contracts-dev-utils@1.0.1
 - @0x/contracts-erc1155@2.0.1
 - @0x/contracts-erc20-bridge-sampler@1.0.1
 - @0x/contracts-erc20@3.0.1
 - @0x/contracts-erc721@3.0.1
 - @0x/contracts-exchange-forwarder@4.0.1
 - @0x/contracts-exchange-libs@4.0.1
 - @0x/contracts-exchange@3.0.1
 - @0x/contracts-extensions@5.1.0
 - @0x/contracts-integrations@2.0.1
 - @0x/contracts-multisig@4.0.1
 - @0x/contracts-staking@2.0.1
 - @0x/contracts-test-utils@5.0.0
 - @0x/contracts-tests@0.0.7
 - @0x/contracts-utils@4.0.1
 - 0x.js@9.0.1
 - @0x/abi-gen@5.0.1
 - @0x/assert@3.0.1
 - @0x/asset-swapper@3.0.1
 - @0x/base-contract@6.0.1
 - @0x/connect@6.0.1
 - @0x/contract-artifacts@3.1.0
 - @0x/contract-wrappers-test@12.2.2
 - @0x/contract-wrappers@13.1.0
 - @0x/contracts-gen@2.0.1
 - @0x/dev-utils@3.0.1
 - @0x/instant@1.0.38
 - @0x/json-schemas@5.0.1
 - @0x/migrations@5.0.1
 - @0x/monorepo-scripts@1.0.44
 - @0x/order-utils@10.0.0
 - @0x/orderbook@1.0.1
 - @0x/sol-compiler@4.0.1
 - @0x/sol-coverage@4.0.1
 - @0x/sol-doc@3.0.1
 - @0x/sol-profiler@4.0.1
 - @0x/sol-resolver@3.0.1
 - @0x/sol-trace@3.0.1
 - @0x/sol-tracing-utils@7.0.1
 - @0x/sra-spec@3.0.1
 - @0x/subproviders@6.0.1
 - @0x/types@3.1.0
 - @0x/utils@5.1.0
 - @0x/web3-wrapper@7.0.1
2019-12-09 14:53:19 -08:00
xianny
71de0d04f3 Updated CHANGELOGS & MD docs 2019-12-09 14:53:05 -08:00
Xianny
99debff5d2 Add syntactic sugar for assetDataUtils (#2388)
* add syntactic sugar for assetDataUtils
2019-12-09 13:55:58 -08:00
Amir Bandeali
3bac6fcb27 Merge pull request #2377 from 0xProject/feat/forwarder/multi-affiliate-fees
Forwarder affiliate fee usability improvements
2019-12-08 19:13:28 -08:00
Amir Bandeali
4b842b81a0 Address PR feedback 2019-12-08 16:28:00 -08:00
Amir Bandeali
e2e4d048ab Update tests to use new Forwarder interface 2019-12-04 21:23:55 -08:00
Amir Bandeali
5574c368cd Allow different ETh fees to be specified for different feeRecipient addresses 2019-12-04 21:23:55 -08:00
Amir Bandeali
0d34f7b92e Add EthFeeLengthMismatchError 2019-12-04 21:23:55 -08:00
Amir Bandeali
5be0632e01 Add tests with multiple fee recipients 2019-12-04 21:23:55 -08:00
Amir Bandeali
79ea0bf9f4 Allow affiliate fee to be split between multiple fee recipient addresses 2019-12-04 21:23:54 -08:00
Amir Bandeali
b1929cb688 Update affiliate fee tests 2019-12-04 21:23:54 -08:00
Amir Bandeali
5ad98700e5 Remove FeePercentageTooLargeError rich revert 2019-12-04 21:22:54 -08:00
Amir Bandeali
a54624b697 Do not return ethFeePaid 2019-12-04 21:22:54 -08:00
Amir Bandeali
ca34c865af Remove max fee percentage for affiliate fees 2019-12-04 21:22:54 -08:00
Amir Bandeali
dde57b1eca Make affiliate fee a flat amount 2019-12-04 21:22:54 -08:00
Amir Bandeali
264b06938e Merge pull request #2378 from 0xProject/feat/bridges/chai-bridge
Implement ChaiBridge
2019-12-04 16:25:46 -08:00
John Johnson
99edb303e2 Merge pull request #2386 from 0xProject/feature/fix-unbound-provider
Fix unbound method in provider standardizer
2019-12-04 15:44:22 -08:00
John Johnson
104cc24dfc Fixing unbound provider (metamask v7.7 incorrectly binds this) 2019-12-04 15:09:00 -08:00
Xianny
fcbcbac889 Remove assetDataUtils everywhere (#2373)
* remove assetDataUtils everywhere

* export IAssetDataContract from @0x/contract-wrappers to allow @0x/instant to decode asset data  synchronously

* export generic function `decodeAssetDataOrThrow` and add ERC20Bridge support

* export `hexUtils` from order-utils instead of contracts-test-utils
2019-12-04 13:08:08 -08:00
mzhu25
b86d19028c Merge pull request #2366 from 0xProject/feature/fuzz/makers-and-takers
Pool Member Fuzz Tests
2019-12-04 11:12:55 -08:00
F. Eugene Aumson
4f17a251d3 Python publish for v3 (#2383)
* Remove pre-release suffixes from version numbers

* For wrapper test, pull latest ganache image first

* For wrapper test, unpin ganache, use beta snapshot

* In docs, advise using beta ganache snapshot

Because we haven't yet published the non-beta snapshot

* Unpin package interdependencies

* unpin tests from beta 0xorg/ganache-cli version

* use beta ganache snapshot

* Set release date in CHANGELOGs

* In testing deployment, stop testing pre-releases

* Include rmtree("build") in all clean commands

* Fix clean not cleaning what it thought it was

* In monorepo script, install pkgs 1st then dev deps

* Stop pinning ganache snapshot version

* In test setup, wait longer for mesh to start up

* Fix broken hyperlinks in docs

* fix missing \n that was breaking doc rendering

* In monorepo script comment, fix typo, and clarify
2019-12-04 08:42:00 -08:00
Michael Zhu
3d79fe2bf4 post-rebase lockfile update 2019-12-03 15:34:59 -08:00
Alex Towle
474399154f Addressed last review comment 2019-12-03 14:41:53 -08:00
Alex Towle
19f5153d0e Addressed some review feedback 2019-12-03 14:41:53 -08:00
Alex Towle
ce11271866 Appease the linter 2019-12-03 14:40:18 -08:00
Alex Towle
86cf353296 Improved the fuzz test 2019-12-03 14:40:07 -08:00
Alex Towle
36df5dc721 Implemented a hacky version of the fillOrder fuzz tests 2019-12-03 14:40:07 -08:00
Alex Towle
1e44a9c942 Made function assertions work with the new wrappers 2019-12-03 14:39:29 -08:00
mzhu25
8685cf9036 Merge pull request #2357 from 0xProject/refactor/integrations/transaction-tests
`@0x/contracts-integrations`: Transaction integration tests
2019-12-03 11:04:11 -08:00
Michael Zhu
2232870b09 address comments 2019-12-03 10:35:59 -08:00
Amir Bandeali
b68acd101e Fix failing tests 2019-12-03 08:46:47 -08:00
Amir Bandeali
173ba9b2b5 Add ChaiBridge unit tests 2019-12-02 16:42:17 -08:00
Amir Bandeali
64ed1f87d3 Rethrow custom error string if draw call fails 2019-12-02 16:42:17 -08:00
Michael Zhu
1ca085ec4a address comments 2019-12-02 15:39:03 -08:00
Michael Zhu
e332b7535c prettier 2019-12-02 15:39:02 -08:00
Michael Zhu
79eb613b3e Use AbiEncoder for methodAbiToFunctionSignature 2019-12-02 15:39:02 -08:00
Michael Zhu
5a79ec28d1 transaction protocol fee integration tests 2019-12-02 15:39:02 -08:00
Michael Zhu
97e65a02c0 fix test nesting 2019-12-02 15:39:02 -08:00
Michael Zhu
e87c786b77 fix dataItemsToABIString 2019-12-02 15:39:02 -08:00
Michael Zhu
251d30d47f refactor transaction integration tests to use new framework 2019-12-02 15:39:02 -08:00
fabioberger
761d0a0f18 Publish
- @0x/contracts-asset-proxy@3.0.0
 - @0x/contracts-coordinator@3.0.0
 - @0x/contracts-dev-utils@1.0.0
 - @0x/contracts-erc1155@2.0.0
 - @0x/contracts-erc20-bridge-sampler@1.0.0
 - @0x/contracts-erc20@3.0.0
 - @0x/contracts-erc721@3.0.0
 - @0x/contracts-exchange-forwarder@4.0.0
 - @0x/contracts-exchange-libs@4.0.0
 - @0x/contracts-exchange@3.0.0
 - @0x/contracts-extensions@5.0.0
 - @0x/contracts-integrations@2.0.0
 - @0x/contracts-multisig@4.0.0
 - @0x/contracts-staking@2.0.0
 - @0x/contracts-test-utils@4.0.0
 - @0x/contracts-tests@0.0.6
 - @0x/contracts-utils@4.0.0
 - 0x.js@9.0.0
 - @0x/abi-gen@5.0.0
 - @0x/assert@3.0.0
 - @0x/asset-swapper@3.0.0
 - @0x/base-contract@6.0.0
 - @0x/connect@6.0.0
 - @0x/contract-addresses@4.0.0
 - @0x/contract-artifacts@3.0.0
 - @0x/contract-wrappers-test@12.2.1
 - @0x/contract-wrappers@13.0.0
 - @0x/contracts-gen@2.0.0
 - @0x/dev-utils@3.0.0
 - ethereum-types@3.0.0
 - @0x/instant@1.0.37
 - @0x/json-schemas@5.0.0
 - @0x/migrations@5.0.0
 - @0x/monorepo-scripts@1.0.43
 - @0x/order-utils@9.0.0
 - @0x/orderbook@1.0.0
 - @0x/sol-compiler@4.0.0
 - @0x/sol-coverage@4.0.0
 - @0x/sol-doc@3.0.0
 - @0x/sol-profiler@4.0.0
 - @0x/sol-resolver@3.0.0
 - @0x/sol-trace@3.0.0
 - @0x/sol-tracing-utils@7.0.0
 - @0x/sra-spec@3.0.0
 - @0x/subproviders@6.0.0
 - @0x/tslint-config@4.0.0
 - @0x/types@3.0.0
 - @0x/typescript-typings@5.0.0
 - @0x/utils@5.0.0
 - @0x/web3-wrapper@7.0.0
2019-12-02 15:31:06 +01:00
fabioberger
ae4b1e74f9 Updated CHANGELOGS & MD docs 2019-12-02 15:30:53 +01:00
Jacob Evans
ac44618e58 Remove DIST_TAG 2019-12-03 00:44:01 +11:00
Jacob Evans
d634cbf924 Major version CHANGELOGs 2019-12-03 00:26:25 +11:00
Jacob Evans
21db0e6275 Publish
- @0x/contracts-asset-proxy@2.3.0-beta.4
 - @0x/contracts-coordinator@2.1.0-beta.4
 - @0x/contracts-dev-utils@0.1.0-beta.4
 - @0x/contracts-erc1155@1.2.0-beta.4
 - @0x/contracts-erc20-bridge-sampler@1.0.0-beta.2
 - @0x/contracts-erc20@2.3.0-beta.4
 - @0x/contracts-erc721@2.2.0-beta.4
 - @0x/contracts-exchange-forwarder@3.1.0-beta.4
 - @0x/contracts-exchange-libs@3.1.0-beta.4
 - @0x/contracts-exchange@2.2.0-beta.4
 - @0x/contracts-extensions@4.1.0-beta.4
 - @0x/contracts-integrations@1.0.3-beta.2
 - @0x/contracts-multisig@3.2.0-beta.4
 - @0x/contracts-staking@1.1.0-beta.4
 - @0x/contracts-test-utils@3.2.0-beta.4
 - @0x/contracts-tests@0.0.5
 - @0x/contracts-utils@3.3.0-beta.4
 - 0x.js@8.0.0-beta.3
 - @0x/abi-gen@4.4.0-beta.4
 - @0x/assert@2.2.0-beta.3
 - @0x/asset-swapper@2.1.0-beta.4
 - @0x/base-contract@5.5.0-beta.4
 - @0x/connect@5.1.0-beta.3
 - @0x/contract-addresses@3.3.0-beta.5
 - @0x/contract-artifacts@2.3.0-beta.4
 - @0x/contract-wrappers-test@12.2.0
 - @0x/contract-wrappers@12.2.0-beta.4
 - @0x/contracts-gen@1.1.0-beta.4
 - @0x/dev-utils@2.4.0-beta.4
 - @0x/instant@1.0.36
 - @0x/json-schemas@4.1.0-beta.3
 - @0x/migrations@4.4.0-beta.4
 - @0x/monorepo-scripts@1.0.42
 - @0x/order-utils@8.5.0-beta.4
 - @0x/orderbook@0.1.0-beta.4
 - @0x/sol-compiler@3.2.0-beta.4
 - @0x/sol-coverage@3.1.0-beta.4
 - @0x/sol-doc@2.1.0-beta.4
 - @0x/sol-profiler@3.2.0-beta.4
 - @0x/sol-resolver@2.1.0-beta.3
 - @0x/sol-trace@2.1.0-beta.4
 - @0x/sol-tracing-utils@6.1.0-beta.4
 - @0x/sra-spec@2.1.0-beta.3
 - @0x/subproviders@5.1.0-beta.3
 - @0x/types@2.5.0-beta.3
 - @0x/utils@4.6.0-beta.3
 - @0x/web3-wrapper@6.1.0-beta.3
2019-12-02 23:38:04 +11:00
Jacob Evans
ce426fd3f4 Updated CHANGELOGS & MD docs 2019-12-02 23:37:43 +11:00
Jacob Evans
b5d4c91207 Update Changelogs to beta 2019-12-02 23:11:48 +11:00
Jacob Evans
b43263be77 Merge pull request #2382 from 0xProject/fix/contract-wrappers-remove-artifacts
Remove contract-artifacts dep from contract-wrappers
2019-12-02 22:09:01 +10:00
Jacob Evans
207cf7ca24 Fix unused export 2019-12-02 22:48:09 +11:00
Jacob Evans
00e34758c4 Remove artifacts dep from contract-wrappers 2019-12-02 18:51:47 +11:00
Amir Bandeali
7a3f878c11 Add ChaiBridge to boilerplate 2019-12-01 15:57:01 -08:00
Amir Bandeali
b8439598bc Remove redundant getters from bridges 2019-12-01 15:55:27 -08:00
Amir Bandeali
7fb0818923 Implement ChaiBridge 2019-12-01 15:54:58 -08:00
Amir Bandeali
a7c435adc4 Add mainnet deployment addresses for Dai, Chai, and ERC20BridgeProxy 2019-12-01 15:40:29 -08:00
Amir Bandeali
dd90aabad6 Merge pull request #2375 from Arctek/fix/coordinator-client
Fix for typo in constructor and gas price to apply to meta transaction.
2019-12-01 13:06:00 -08:00
Joshua Richardson
5bded1946e Fix for typo in constructor and gas price to apply to meta transaction. 2019-12-01 16:14:31 +10:30
Amir Bandeali
3642e96154 Merge pull request #2374 from 0xProject/feat/redeploy-forwarder-3.0
Redeploy 3.0 Forwarder on all networks
2019-11-30 14:17:52 -08:00
Amir Bandeali
9da09ee3a6 Update CHANGELOGs 2019-11-30 13:31:42 -08:00
Amir Bandeali
141c140f53 Update Forwarder artifact and wrapper 2019-11-30 13:31:36 -08:00
Amir Bandeali
84b660d2ef Pass in WETH address into Forwarder constructor 2019-11-29 18:20:07 -08:00
Amir Bandeali
6beedba957 Update Forwarder addresses on all networks 2019-11-29 15:55:49 -08:00
Xianny
d73982819b Deprecate abi-gen-wrappers (#2370)
* generate wrappers in @0x/contract-wrappers and delete abi-gen-wrappers

* trim exports from contract-wrappers

* separate contract-wrappers tests to get rid of dependency cycle

* remove dummy token contracts

* temporarily skip coordinator test until we can upgrade coordinator server
2019-11-27 17:50:24 -08:00
Jacob Evans
6ac5bcc907 Merge pull request #2362 from 0xProject/fix/revert-errors-utils
Re-export the RevertErrors
2019-11-27 12:27:37 +10:00
Jacob Evans
389d4d10f1 Import from @0x/utils 2019-11-27 13:02:37 +11:00
Jacob Evans
89dcbd0229 Fix import of LibBytesRevertErrors 2019-11-27 11:57:56 +11:00
Jacob Evans
ad8caa2b51 Remove moved RevertErrors 2019-11-27 11:52:14 +11:00
Jacob Evans
9c42241269 Re-export the RevertErrors 2019-11-27 11:43:12 +11:00
mzhu25
38dd45cce2 Merge pull request #2356 from 0xProject/feature/forwarder/erc20-bridge-buy
`@0x/contracts-exchange-forwarder`: ERC20Bridge buy support in Forwarder
2019-11-26 15:20:54 -08:00
Michael Zhu
aa90253c62 update TestUniswapExchangeFactory 2019-11-26 14:39:59 -08:00
Michael Zhu
41576652dc address more comments 2019-11-26 14:19:37 -08:00
Michael Zhu
74830854ca update changelogs 2019-11-26 14:16:54 -08:00
Michael Zhu
2542b1b44d address comments and tests 2019-11-26 14:16:54 -08:00
Michael Zhu
51f5e60224 static tests 2019-11-26 14:16:54 -08:00
Michael Zhu
bb5885e2bb integration tests 2019-11-26 14:16:54 -08:00
Michael Zhu
d51bbb0008 Unit tests 2019-11-26 14:16:54 -08:00
Michael Zhu
49e898b189 add ERC20Bridge buy support 2019-11-26 14:16:54 -08:00
F. Eugene Aumson
42c4fe5705 Pre-release version bumps; test fixes for latest mesh/ganache versions (#2363)
* Use pre-release ver's for tests against deployment

* Pre-release version number bumps

* pin sra_client dev deps to prereleases

for testing against deployed package

* middlewares: incl doctest in tests of deployment

* Unpin mesh, use new snapshot, & pay protocol fees

* .gitignore gen'd wrappers for new contracts

* test build_tx() & support for empty TxParams.from_

* fix doc: fill TAKERAssetAmount, not maker...
2019-11-26 13:27:49 -05:00
Jacob Evans
4b5f2c36b9 Merge pull request #2336 from 0xProject/feature/upgrade-instant-v3
Upgrade instant v3
2019-11-26 20:43:58 +10:00
Jacob Evans
935dca67e6 ERC1155 Wrapper without chai 2019-11-26 17:56:30 +11:00
Jacob Evans
d431790e19 Re-export orderHashUtils
Rather than have hacks spread through the codebase
2019-11-26 15:14:36 +11:00
Jacob Evans
56310b7bd4 Revert to abi-gen-wrappers. Clean package.json 2019-11-26 13:58:21 +11:00
Lawrence Forman
f15e21faad Merge pull request #2344 from 0xProject/feat/erc20-bridge-aggregator
ERC20BridgeSampler
2019-11-25 20:33:19 -05:00
Jacob Evans
44aa6a2b38 Clean up package.json dependencies 2019-11-26 11:33:08 +11:00
David Sun
9f32347c01 revert svg loader 2019-11-26 11:33:07 +11:00
David Sun
3d5b229c46 prettier 2019-11-26 11:33:07 +11:00
David Sun
5863ccc0a0 replay @dekz commits 2019-11-26 11:33:07 +11:00
David Sun
d220a16b99 fixed contract-wrappers again 2019-11-26 11:33:06 +11:00
David Sun
79784fc8ee fixed wrappers usage in contract-wrappers 2019-11-26 11:33:06 +11:00
David Sun
a83bc53c6a updated protocol fee utils 2019-11-26 11:33:05 +11:00
David Sun
85de0b91b1 added todo 2019-11-26 11:33:05 +11:00
Jacob Evans
d91c6e5702 Round affiliate fee for non whole amounts 2019-11-26 11:33:05 +11:00
Jacob Evans
ab7689d188 Re-enable affiliate fee 2019-11-26 11:33:05 +11:00
Jacob Evans
c81455c760 Update SwapQuoteUpdater with gas estimator 2019-11-26 11:33:04 +11:00
David Sun
39bfc97a7a fix build issues 2019-11-26 11:33:04 +11:00
David Sun
88aac78282 removed asset-buyer from residual files 2019-11-26 11:33:04 +11:00
David Sun
863e830d24 prettier + lint 2019-11-26 11:32:34 +11:00
David Sun
6c705728a4 passing instant tests 2019-11-26 11:32:34 +11:00
David Sun
7f00279ffb fixed CI tests for swapper 2019-11-26 11:32:34 +11:00
David Sun
c198d0079e prettier + minor changes 2019-11-26 11:32:33 +11:00
David Sun
1135d5a971 updated unit tests 2019-11-26 11:32:33 +11:00
David Sun
e299fa27a0 update to swapper 2019-11-26 11:32:32 +11:00
David Sun
46e0bc940a refactored and added fees 2019-11-26 11:32:32 +11:00
David Sun
9a552012f2 fixed bugs preventing build 2019-11-26 11:32:31 +11:00
David Sun
6498d385ee reworked largely all the asset-buyer legacy code 2019-11-26 11:32:30 +11:00
David Sun
dd00f2016f removed asset-buyer 2019-11-26 11:32:30 +11:00
David Sun
64d25e6522 removed buyer and adding in asset-swapper 2019-11-26 11:32:29 +11:00
Lawrence Forman
1462ab08de @0x/contracts-erc20-bridge-sampler: Clean up linter workaround in tests. 2019-11-25 17:55:12 -05:00
Lawrence Forman
a8e93a594d @0x/contracts-erc20-bridge-sampler: Throw sampling two of the same tokens.
`@0x/contracts-erc20-bridge-sampler`: Address review comments.
2019-11-25 17:48:53 -05:00
Lawrence Forman
dea30b37ef @0x/contracts-erc20-bridge-sampler: Update README and add index.ts. 2019-11-25 17:48:53 -05:00
Lawrence Forman
39571dda0b Add erc20-bridge-sampler to prettierignore 2019-11-25 17:48:53 -05:00
Lawrence Forman
c7d801b6c2 @0x/contracts-erc20-bridge-sampler: Update DEPLOYS.json 2019-11-25 17:48:53 -05:00
Lawrence Forman
57731be689 @0x/contracts-erc20-bridge-sampler: Remove gitkeep files 2019-11-25 17:48:53 -05:00
Lawrence Forman
f00524e518 @0x/contracts-erc20-bridge-sampler: Update README 2019-11-25 17:48:53 -05:00
Lawrence Forman
5567c40bae Update changelogs 2019-11-25 17:48:53 -05:00
Lawrence Forman
5d1a7613dd Add @0x/contracts-erc20-bridge-sampler to CI 2019-11-25 17:48:53 -05:00
Lawrence Forman
fa768dc112 @0x/contracts-erc20-bridge-sampler: Finish off tests. 2019-11-25 17:48:53 -05:00
Lawrence Forman
27fb51d37f @0x/contracts-asset-proxy: Tweak IUniswapExchangeFactory. 2019-11-25 17:48:53 -05:00
Lawrence Forman
d02db3864e @0x/contracts-erc20-bridge-sampler: Fix kyber bug and add test contract. 2019-11-25 17:48:53 -05:00
Lawrence Forman
a26c3036a7 @0x/contracts-erc20-bridge-sampler: Get contracts compiling. 2019-11-25 17:48:53 -05:00
Lawrence Forman
0af346aad8 @0x/contracts-erc20-bridge-aggregator: Create package.
`@0x/contracts-erc20`: Add `decimals()` to `LibERC20Token`.

`@0x/contracts-erc20-bridge-sampler`: Created package.
2019-11-25 17:48:53 -05:00
James Towle
c3c8ee7292 Merge pull request #2367 from 0xProject/feature/staking/authorizable-tests
Added unit tests for Authorizable
2019-11-25 15:59:15 -06:00
David Sun
5fbdfa66d9 Merge pull request #2368 from 0xProject/fix/reenable-builds-for-v3
Reenable CircleCi tests for swapper + orderbook, and fix migrations bug
2019-11-25 16:35:56 -05:00
David Sun
15b75715ee enable tests 2019-11-25 15:14:02 -05:00
Alex Towle
1fd92b6cbd Added unit tests for onlyAuthorized 2019-11-25 14:02:53 -06:00
David Sun
2918b5d74e add coverage for swapper 2019-11-25 14:40:35 -05:00
David Sun
669c5be344 prettier 2019-11-25 14:28:34 -05:00
Jacob Evans
e1b40ec46e Update Mesh fixtures 2019-11-26 00:07:30 +11:00
Jacob Evans
15767538eb Deploy Forwarder after Exchange is configured 2019-11-25 18:20:26 +11:00
David Sun
de2b16c464 fixed migrations 2019-11-25 01:05:24 -05:00
Alex Towle
d5e6b38450 Added unit tests for Authorizable 2019-11-24 22:55:17 -06:00
Xianny
a636e87a4f remove matchOrders; must be executed directly through Exchange contract (#2364) 2019-11-22 14:19:18 -08:00
Xianny
50d5b4fa37 Refactor/3.0/coordinator client (#2348)
* deduplicate migrateOnceAsync() test helper

* move and rename coordinator client to @0x/contracts-coordinator
2019-11-22 12:19:00 -08:00
Jacob Evans
f6d26392fb Merge pull request #2361 from 0xProject/fix/migrations-massage
Massage the migrations to match contract-addresses
2019-11-22 11:36:35 +11:00
Jacob Evans
2705bcce15 Massage the migrations to match contract-addresses 2019-11-22 11:06:07 +11:00
Fabio B
379a31ece6 Merge pull request #2355 from 0xProject/fix/increaseKeepAliveOnGanacheDockerImage
Extend keepAliveTimeout config on Ganache-cli Docker Image
2019-11-21 23:16:18 +00:00
F. Eugene Aumson
daa593d225 Updated DevUtils contract artifact (#2358)
* Add updated DevUtils contract artifact

New contract methods were added in #2321, but this artifact was not
updated.

* fix for breaking change in eth_utils
2019-11-20 20:50:49 -05:00
Amir Bandeali
ed8340affa Merge pull request #2359 from 0xProject/fix/migrations/verify-exchange-registration
Warn if Exchange contract not registered in StakingProxy
2019-11-20 16:45:38 -08:00
Amir Bandeali
b3c1e72577 Warn if Exchange contract not registered in StakingProxy 2019-11-20 16:43:02 -08:00
fabioberger
3da09d140a Extend keepAliveTimeout on Ganache-cli server to 40sec to fix Mesh issue 2019-11-20 17:36:24 +01:00
Lawrence Forman
51f254bbb1 Merge pull request #2352 from 0xProject/feat/asset-proxy/KyberBridge
KyberBridge
2019-11-20 06:20:52 -05:00
Lawrence Forman
30ee456d4c @0x/contracts-asset-proxy: Use DeploymentConstants from @0x/contracts-utils in bridges.
`@0x/contracts-asset-proxy`: Add fallback function to `KyberBridge`.
`@0x/contracts-asset-proxy`: Minor changes to `KyberBridge` contracts based on feedback.
2019-11-20 05:50:15 -05:00
Lawrence Forman
460d5f2517 @0x/contracts-utils: Add DeploymentConstants. 2019-11-20 05:50:15 -05:00
Lawrence Forman
5da1fc8445 @0x/contracts-asset-proxy: Add KyberBridge. 2019-11-20 05:50:15 -05:00
534 changed files with 18967 additions and 18798 deletions

View File

@@ -37,7 +37,7 @@ jobs:
- store_artifacts:
path: ~/repo/packages/abi-gen/test-cli/output
- store_artifacts:
path: ~/repo/packages/abi-gen-wrappers/generated_docs
path: ~/repo/packages/contract-wrappers/generated_docs
test-contracts-ganache:
resource_class: medium+
docker:
@@ -77,7 +77,7 @@ jobs:
- restore_cache:
keys:
- repo-{{ .Environment.CIRCLE_SHA1 }}
- run: yarn wsrun test:circleci @0x/contracts-multisig @0x/contracts-utils @0x/contracts-exchange-libs @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-asset-proxy @0x/contracts-exchange-forwarder @0x/contracts-tests @0x/contracts-staking @0x/contracts-coordinator
- run: yarn wsrun test:circleci @0x/contracts-multisig @0x/contracts-utils @0x/contracts-exchange-libs @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-asset-proxy @0x/contracts-exchange-forwarder @0x/contracts-tests @0x/contracts-staking @0x/contracts-coordinator @0x/contracts-erc20-bridge-sampler
# TODO(dorothy-zbornak): Re-enable after updating this package for
# 3.0. At that time, also remove exclusion from monorepo
# package.json's test script.
@@ -93,8 +93,8 @@ jobs:
keys:
- repo-{{ .Environment.CIRCLE_SHA1 }}
- run:
command: yarn test:publish:circleci
no_output_timeout: 1800
command: yarn test:publish:circleci
no_output_timeout: 1800
test-doc-generation:
docker:
- image: nikolaik/python-nodejs:python3.7-nodejs8
@@ -104,8 +104,8 @@ jobs:
keys:
- repo-{{ .Environment.CIRCLE_SHA1 }}
- run:
command: yarn test:generate_docs:circleci
no_output_timeout: 1200
command: yarn test:generate_docs:circleci
no_output_timeout: 1200
test-rest:
docker:
- image: nikolaik/python-nodejs:python3.7-nodejs8
@@ -116,23 +116,16 @@ jobs:
- repo-{{ .Environment.CIRCLE_SHA1 }}
- run: yarn wsrun test:circleci @0x/contracts-test-utils
- run: yarn wsrun test:circleci @0x/abi-gen
# TODO (xianny): Needs to be updated for 3.0
# - run: yarn wsrun test:circleci @0x/asset-buyer
# TODO: Needs to be updated for 3.0. At that time, also remove
# exclusion from monorepo package.json's test script.
# - run: yarn wsrun test:circleci @0x/asset-swapper
- run: yarn wsrun test:circleci @0x/asset-swapper
- run: yarn wsrun test:circleci @0x/contract-artifacts
- run: yarn wsrun test:circleci @0x/assert
- run: yarn wsrun test:circleci @0x/base-contract
# TODO (xianny): Needs to be updated for 3.0
# - run: yarn wsrun test:circleci @0x/connect
- run: yarn wsrun test:circleci @0x/contract-wrappers
- run: yarn wsrun test:circleci @0x/connect
- run: yarn wsrun test:circleci @0x/contract-wrappers-test
- run: yarn wsrun test:circleci @0x/dev-utils
- run: yarn wsrun test:circleci @0x/json-schemas
- run: yarn wsrun test:circleci @0x/order-utils
# TODO: Needs to be updated for 3.0. At that time, also remove
# exclusion from monorepo package.json's test script.
# - run: yarn wsrun test:circleci @0x/orderbook
- run: yarn wsrun test:circleci @0x/orderbook
- run: yarn wsrun test:circleci @0x/sol-compiler
- run: yarn wsrun test:circleci @0x/sol-tracing-utils
- run: yarn wsrun test:circleci @0x/sol-doc
@@ -149,9 +142,9 @@ jobs:
paths:
- ~/repo/packages/assert/coverage/lcov.info
- save_cache:
key: coverage-asset-buyer-{{ .Environment.CIRCLE_SHA1 }}
key: coverage-asset-swapper-{{ .Environment.CIRCLE_SHA1 }}
paths:
- ~/repo/packages/asset-buyer/coverage/lcov.info
- ~/repo/packages/asset-swapper/coverage/lcov.info
- save_cache:
key: coverage-base-contract-{{ .Environment.CIRCLE_SHA1 }}
paths:
@@ -161,9 +154,9 @@ jobs:
paths:
- ~/repo/packages/connect/coverage/lcov.info
- save_cache:
key: coverage-contract-wrappers-{{ .Environment.CIRCLE_SHA1 }}
key: coverage-contract-wrappers-test-{{ .Environment.CIRCLE_SHA1 }}
paths:
- ~/repo/packages/contract-wrappers/coverage/lcov.info
- ~/repo/packages/contract-wrappers-test/coverage/lcov.info
- save_cache:
key: coverage-dev-utils-{{ .Environment.CIRCLE_SHA1 }}
paths:
@@ -200,11 +193,8 @@ jobs:
working_directory: ~/repo
docker:
- image: nikolaik/python-nodejs:python3.7-nodejs8
- image: 0xorg/ganache-cli:4.4.0-beta.1
environment:
VERSION: 4.4.0-beta.1
SNAPSHOT_NAME: 0x_ganache_snapshot-v3-beta
- image: 0xorg/mesh:6.0.0-beta-0xv3
- image: 0xorg/ganache-cli
- image: 0xorg/mesh:0xV3
environment:
ETHEREUM_RPC_URL: 'http://localhost:8545'
ETHEREUM_NETWORK_ID: '50'
@@ -226,7 +216,7 @@ jobs:
TAKER_FEE_UNIT_AMOUNT: 0
MESH_ENDPOINT: 'ws://localhost:60557'
command: |
sh -c "waitForMesh () { sleep 5; }; waitForMesh && node_modules/.bin/forever ts/lib/index.js"
sh -c "waitForMesh () { sleep 30; }; waitForMesh && node_modules/.bin/forever ts/lib/index.js"
steps:
- checkout
- restore_cache:
@@ -369,7 +359,7 @@ jobs:
- coverage-assert-{{ .Environment.CIRCLE_SHA1 }}
- restore_cache:
keys:
- coverage-asset-buyer-{{ .Environment.CIRCLE_SHA1 }}
- coverage-asset-swapper-{{ .Environment.CIRCLE_SHA1 }}
- restore_cache:
keys:
- coverage-base-contract-{{ .Environment.CIRCLE_SHA1 }}
@@ -378,7 +368,7 @@ jobs:
- coverage-connect-{{ .Environment.CIRCLE_SHA1 }}
- restore_cache:
keys:
- coverage-contract-wrappers-{{ .Environment.CIRCLE_SHA1 }}
- coverage-contract-wrappers-test-{{ .Environment.CIRCLE_SHA1 }}
- restore_cache:
keys:
- coverage-dev-utils-{{ .Environment.CIRCLE_SHA1 }}

View File

@@ -19,13 +19,12 @@ contracts: ['contracts']
@0x/sol-tracing-utils: ['packages/sol-tracing-utils']
@0x/utils: ['packages/utils']
@0x/tslint-config: ['packages/tslint-config']
@0x/asset-buyer: ['packages/asset-buyer']
@0x/asset-swapper: ['packages/asset-swapper']
@0x/order-utils: ['packages/order-utils']
@0x/assert: ['packages/assert']
@0x/base-contract: ['packages/base-contract']
@0x/typescript-typings: ['packages/typescript-typings']
0x.js: ['packages/0x.js']
@0x/abi-gen-wrappers: ['packages/abi-gen-wrappers']
@0x/contract-artifacts: ['packages/contract-artifacts']
@0x/dev-utils: ['packages/dev-utils']
@0x/contract-wrappers: ['packages/contract-wrappers']

7
.gitignore vendored
View File

@@ -79,6 +79,8 @@ TODO.md
.vscode
# generated contract artifacts/
contracts/erc20-bridge-sampler/generated-artifacts/
contracts/erc20-bridge-sampler/test/generated-artifacts/
contracts/integrations/generated-artifacts/
contracts/integrations/test/generated-artifacts/
contracts/staking/generated-artifacts/
@@ -111,6 +113,7 @@ packages/sol-tracing-utils/test/fixtures/artifacts/
python-packages/contract_artifacts/src/zero_ex/contract_artifacts/artifacts/
# generated truffle contract artifacts/
contracts/erc20-bridge-sampler/build/
contracts/staking/build/
contracts/coordinator/build/
contracts/exchange/build/
@@ -127,6 +130,8 @@ contracts/dev-utils/build/
# generated contract wrappers
packages/python-contract-wrappers/generated/
contracts/erc20-bridge-sampler/generated-wrappers/
contracts/erc20-bridge-sampler/test/generated-wrappers/
contracts/integrations/generated-wrappers/
contracts/integrations/test/generated-wrappers/
contracts/staking/generated-wrappers/
@@ -166,6 +171,7 @@ python-packages/contract_wrappers/src/zero_ex/contract_wrappers/dummy_erc721_tok
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/dutch_auction/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc1155_mintable/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc1155_proxy/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc20_bridge_proxy/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc20_proxy/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc721_proxy/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc721_token/__init__.py
@@ -180,6 +186,7 @@ python-packages/contract_wrappers/src/zero_ex/contract_wrappers/staking_proxy/__
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/static_call_proxy/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/weth9/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/zrx_token/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/zrx_vault/__init__.py
# solc-bin in sol-compiler
packages/sol-compiler/solc_bin/

View File

@@ -36,6 +36,10 @@ lib
/contracts/erc20/test/generated-wrappers
/contracts/erc20/generated-artifacts
/contracts/erc20/test/generated-artifacts
/contracts/erc20-bridge-sampler/generated-wrappers
/contracts/erc20-bridge-sampler/test/generated-wrappers
/contracts/erc20-bridge-sampler/generated-artifacts
/contracts/erc20-bridge-sampler/test/generated-artifacts
/contracts/erc721/generated-wrappers
/contracts/erc721/test/generated-wrappers
/contracts/erc721/generated-artifacts

View File

@@ -5,8 +5,8 @@
# https://git-scm.com/docs/gitignore#_pattern_format
# Website
packages/asset-buyer/ @BMillman19 @fragosti @steveklebanoff
packages/instant/ @BMillman19 @fragosti @steveklebanoff
packages/asset-swapper/ @BMillman19 @fragosti @dave4506
packages/instant/ @BMillman19 @fragosti @dave4506
# Dev tools & setup
.circleci/ @LogvinovLeon

View File

@@ -61,11 +61,9 @@ These packages are all under development. See [/contracts/README.md](/contracts/
| [`@0x/order-utils`](/packages/order-utils) | [![npm](https://img.shields.io/npm/v/@0x/order-utils.svg)](https://www.npmjs.com/package/@0x/order-utils) | A set of utilities for generating, parsing, signing and validating 0x orders |
| [`@0x/json-schemas`](/packages/json-schemas) | [![npm](https://img.shields.io/npm/v/@0x/json-schemas.svg)](https://www.npmjs.com/package/@0x/json-schemas) | 0x-related JSON schemas | |
| [`@0x/migrations`](/packages/migrations) | [![npm](https://img.shields.io/npm/v/@0x/migrations.svg)](https://www.npmjs.com/package/@0x/migrations) | Migration tool for deploying 0x smart contracts on private testnets |
| [`@0x/contract-artifacts`](/packages/contract-artifacts) | [![npm](https://img.shields.io/npm/v/@0x/contract-artifacts.svg)](https://www.npmjs.com/package/@0x/contract-artifacts) | 0x smart contract compilation artifacts |
| [`@0x/abi-gen-wrappers`](/packages/abi-gen-wrappers) | [![npm](https://img.shields.io/npm/v/@0x/abi-gen-wrappers.svg)](https://www.npmjs.com/package/@0x/abi-gen-wrappers) | Low-level 0x smart contract wrappers generated using `@0x/abi-gen` |
| [`@0x/contract-artifacts`](/packages/contract-artifacts) | [![npm](https://img.shields.io/npm/v/@0x/contract-artifacts.svg)](https://www.npmjs.com/package/@0x/contract-artifacts) | 0x smart contract compilation artifacts | |
| [`@0x/sra-spec`](/packages/sra-spec) | [![npm](https://img.shields.io/npm/v/@0x/sra-spec.svg)](https://www.npmjs.com/package/@0x/sra-spec) | OpenAPI specification for the Standard Relayer API |
| [`@0x/connect`](/packages/connect) | [![npm](https://img.shields.io/npm/v/@0x/connect.svg)](https://www.npmjs.com/package/@0x/connect) | An HTTP/WS client for interacting with the Standard Relayer API |
| [`@0x/asset-buyer`](/packages/asset-buyer) | [![npm](https://img.shields.io/npm/v/@0x/asset-buyer.svg)](https://www.npmjs.com/package/@0x/asset-buyer) | Convenience package for discovering and buying assets with Ether |
| [`@0x/asset-swapper`](/packages/asset-swapper) | [![npm](https://img.shields.io/npm/v/@0x/asset-swapper.svg)](https://www.npmjs.com/package/@0x/asset-swapper) | Convenience package for discovering and performing swaps for any ERC20 Assets |
#### Ethereum tooling

View File

@@ -13,4 +13,4 @@
#### Development
Building solidity files will update the contract artifact in `{package-name}/generated-artifacts/{contract}.json`, but does not automatically update the `abi-gen-wrappers` package, which are generated from the artifact JSON. To ensure consistency, clean and rebuild `abi-gen-wrappers` after any changes to the artifact JSON.
Building solidity files will update the contract artifact in `{package-name}/generated-artifacts/{contract}.json`, but does not automatically update the `contract-artifacts` or `contract-wrappers` packages, which are generated from the artifact JSON. See `contract-artifacts/README.md` for instructions on updating these packages.

View File

@@ -1,4 +1,73 @@
[
{
"timestamp": 1575931811,
"version": "3.0.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "3.0.0",
"changes": [
{
"note": "Implement `KyberBridge`.",
"pr": 2352
},
{
"note": "Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils",
"pr": 2330
},
{
"note": "ERC20Wrapper and ERC1155ProxyWrapper constructors now require an instance of DevUtilsContract",
"pr": 2034
},
{
"note": "Disallow the zero address from being made an authorized address in MixinAuthorizable, and created an archive directory that includes an old version of Ownable",
"pr": 2019
},
{
"note": "Remove `LibAssetProxyIds` contract",
"pr": 2055
},
{
"note": "Compile and export all contracts, artifacts, and wrappers by default",
"pr": 2055
},
{
"note": "Remove unused dependency on IAuthorizable in IAssetProxy",
"pr": 1910
},
{
"note": "Add `ERC20BridgeProxy`",
"pr": 2220
},
{
"note": "Add `Eth2DaiBridge`",
"pr": 2221
},
{
"note": "Add `UniswapBridge`",
"pr": 2233
},
{
"note": "Replaced `SafeMath` with `LibSafeMath`",
"pr": 2254
}
],
"timestamp": 1575296764
},
{
"version": "2.3.0-beta.4",
"changes": [
{
"note": "Implement `KyberBridge`.",
"pr": 2352
}
],
"timestamp": 1575290197
},
{
"version": "2.3.0-beta.3",
"changes": [

View File

@@ -5,6 +5,28 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.0.1 - _December 9, 2019_
* Dependencies updated
## v3.0.0 - _December 2, 2019_
* Implement `KyberBridge`. (#2352)
* Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330)
* ERC20Wrapper and ERC1155ProxyWrapper constructors now require an instance of DevUtilsContract (#2034)
* Disallow the zero address from being made an authorized address in MixinAuthorizable, and created an archive directory that includes an old version of Ownable (#2019)
* Remove `LibAssetProxyIds` contract (#2055)
* Compile and export all contracts, artifacts, and wrappers by default (#2055)
* Remove unused dependency on IAuthorizable in IAssetProxy (#1910)
* Add `ERC20BridgeProxy` (#2220)
* Add `Eth2DaiBridge` (#2221)
* Add `UniswapBridge` (#2233)
* Replaced `SafeMath` with `LibSafeMath` (#2254)
## v2.3.0-beta.4 - _December 2, 2019_
* Implement `KyberBridge`. (#2352)
## v2.3.0-beta.3 - _November 20, 2019_
* Dependencies updated

View File

@@ -0,0 +1,75 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "../interfaces/IERC20Bridge.sol";
import "../interfaces/IChai.sol";
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
// solhint-disable space-after-comma
contract ChaiBridge is
IERC20Bridge,
DeploymentConstants
{
/// @dev Withdraws `amount` of `from` address's Dai from the Chai contract.
/// Transfers `amount` of Dai to `to` address.
/// @param from Address to transfer asset from.
/// @param to Address to transfer asset to.
/// @param amount Amount of asset to transfer.
/// @return success The magic bytes `0x37708e9b` if successful.
function bridgeTransferFrom(
address /* tokenAddress */,
address from,
address to,
uint256 amount,
bytes calldata /* bridgeData */
)
external
returns (bytes4 success)
{
// Ensure that only the `ERC20BridgeProxy` can call this function.
require(
msg.sender == _getERC20BridgeProxyAddress(),
"ChaiBridge/ONLY_CALLABLE_BY_ERC20_BRIDGE_PROXY"
);
// Withdraw `from` address's Dai.
// NOTE: This contract must be approved to spend Chai on behalf of `from`.
bytes memory drawCalldata = abi.encodeWithSelector(
IChai(address(0)).draw.selector,
from,
amount
);
(bool success,) = _getChaiAddress().call(drawCalldata);
require(
success,
"ChaiBridge/DRAW_DAI_FAILED"
);
// Transfer Dai to `to`
// This will never fail if the `draw` call was successful
IERC20Token(_getDaiAddress()).transfer(to, amount);
return BRIDGE_SUCCESS;
}
}

View File

@@ -22,6 +22,7 @@ pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol";
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
import "../interfaces/IERC20Bridge.sol";
import "../interfaces/IEth2Dai.sol";
@@ -29,11 +30,9 @@ import "../interfaces/IEth2Dai.sol";
// solhint-disable space-after-comma
contract Eth2DaiBridge is
IERC20Bridge,
IWallet
IWallet,
DeploymentConstants
{
/* Mainnet addresses */
address constant public ETH2DAI_ADDRESS = 0x39755357759cE0d7f32dC8dC45414CCa409AE24e;
/// @dev Callback for `IERC20Bridge`. Tries to buy `amount` of
/// `toTokenAddress` tokens by selling the entirety of the opposing asset
/// (DAI or WETH) to the Eth2Dai contract, then transfers the bought
@@ -56,13 +55,13 @@ contract Eth2DaiBridge is
// Decode the bridge data to get the `fromTokenAddress`.
(address fromTokenAddress) = abi.decode(bridgeData, (address));
IEth2Dai exchange = _getEth2DaiContract();
IEth2Dai exchange = IEth2Dai(_getEth2DaiAddress());
// Grant an allowance to the exchange to spend `fromTokenAddress` token.
LibERC20Token.approve(fromTokenAddress, address(exchange), uint256(-1));
// Try to sell all of this contract's `fromTokenAddress` token balance.
uint256 boughtAmount = _getEth2DaiContract().sellAllAmount(
address(fromTokenAddress),
uint256 boughtAmount = exchange.sellAllAmount(
fromTokenAddress,
IERC20Token(fromTokenAddress).balanceOf(address(this)),
toTokenAddress,
amount
@@ -85,14 +84,4 @@ contract Eth2DaiBridge is
{
return LEGACY_WALLET_MAGIC_VALUE;
}
/// @dev Overridable way to get the eth2dai contract.
/// @return exchange The Eth2Dai exchange contract.
function _getEth2DaiContract()
internal
view
returns (IEth2Dai exchange)
{
return IEth2Dai(ETH2DAI_ADDRESS);
}
}

View File

@@ -0,0 +1,146 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
import "@0x/contracts-erc20/contracts/src/interfaces/IEtherToken.sol";
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol";
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
import "../interfaces/IERC20Bridge.sol";
import "../interfaces/IKyberNetworkProxy.sol";
// solhint-disable space-after-comma
contract KyberBridge is
IERC20Bridge,
IWallet,
DeploymentConstants
{
// @dev Structure used internally to get around stack limits.
struct TradeState {
IKyberNetworkProxy kyber;
IEtherToken weth;
address fromTokenAddress;
uint256 fromTokenBalance;
uint256 payableAmount;
}
/// @dev Kyber ETH pseudo-address.
address constant public KYBER_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
/// @dev `bridgeTransferFrom()` failure result.
bytes4 constant private BRIDGE_FAILED = 0x0;
/// @dev Precision of Kyber rates.
uint256 constant private KYBER_RATE_BASE = 10 ** 18;
// solhint-disable no-empty-blocks
/// @dev Payable fallback to receive ETH from Kyber.
function ()
external
payable
{}
/// @dev Callback for `IKyberBridge`. Tries to buy `amount` of
/// `toTokenAddress` tokens by selling the entirety of the opposing asset
/// to the `KyberNetworkProxy` contract, then transfers the bought
/// tokens to `to`.
/// @param toTokenAddress The token to give to `to`.
/// @param to The recipient of the bought tokens.
/// @param amount Minimum amount of `toTokenAddress` tokens to buy.
/// @param bridgeData The abi-encoeded "from" token address.
/// @return success The magic bytes if successful.
function bridgeTransferFrom(
address toTokenAddress,
address /* from */,
address to,
uint256 amount,
bytes calldata bridgeData
)
external
returns (bytes4 success)
{
TradeState memory state;
state.kyber = IKyberNetworkProxy(_getKyberNetworkProxyAddress());
state.weth = IEtherToken(_getWethAddress());
// Decode the bridge data to get the `fromTokenAddress`.
(state.fromTokenAddress) = abi.decode(bridgeData, (address));
state.fromTokenBalance = IERC20Token(state.fromTokenAddress).balanceOf(address(this));
if (state.fromTokenBalance == 0) {
// Return failure if no input tokens.
return BRIDGE_FAILED;
}
if (state.fromTokenAddress == toTokenAddress) {
// Just transfer the tokens if they're the same.
LibERC20Token.transfer(state.fromTokenAddress, to, state.fromTokenBalance);
return BRIDGE_SUCCESS;
} else if (state.fromTokenAddress != address(state.weth)) {
// If the input token is not WETH, grant an allowance to the exchange
// to spend them.
LibERC20Token.approve(state.fromTokenAddress, address(state.kyber), uint256(-1));
} else {
// If the input token is WETH, unwrap it and attach it to the call.
state.fromTokenAddress = KYBER_ETH_ADDRESS;
state.payableAmount = state.fromTokenBalance;
state.weth.withdraw(state.fromTokenBalance);
}
bool isToTokenWeth = toTokenAddress == address(state.weth);
// Try to sell all of this contract's input token balance through
// `KyberNetworkProxy.trade()`.
uint256 boughtAmount = state.kyber.trade.value(state.payableAmount)(
// Input token.
state.fromTokenAddress,
// Sell amount.
state.fromTokenBalance,
// Output token.
isToTokenWeth ? KYBER_ETH_ADDRESS : toTokenAddress,
// Transfer to this contract if converting to ETH, otherwise
// transfer directly to the recipient.
isToTokenWeth ? address(uint160(address(this))) : address(uint160(to)),
// Buy as much as possible.
uint256(-1),
// Compute the minimum conversion rate, which is expressed in units with
// 18 decimal places.
(KYBER_RATE_BASE * amount) / state.fromTokenBalance,
// No affiliate address.
address(0)
);
// Wrap ETH output and transfer to recipient.
if (isToTokenWeth) {
state.weth.deposit.value(boughtAmount)();
state.weth.transfer(to, boughtAmount);
}
return BRIDGE_SUCCESS;
}
/// @dev `SignatureType.Wallet` callback, so that this bridge can be the maker
/// and sign for itself in orders. Always succeeds.
/// @return magicValue Magic success bytes, always.
function isValidSignature(
bytes32,
bytes calldata
)
external
view
returns (bytes4 magicValue)
{
return LEGACY_WALLET_MAGIC_VALUE;
}
}

View File

@@ -23,6 +23,7 @@ import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
import "@0x/contracts-erc20/contracts/src/interfaces/IEtherToken.sol";
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol";
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
import "../interfaces/IUniswapExchangeFactory.sol";
import "../interfaces/IUniswapExchange.sol";
import "../interfaces/IERC20Bridge.sol";
@@ -32,12 +33,9 @@ import "../interfaces/IERC20Bridge.sol";
// solhint-disable not-rely-on-time
contract UniswapBridge is
IERC20Bridge,
IWallet
IWallet,
DeploymentConstants
{
/* Mainnet addresses */
address constant private UNISWAP_EXCHANGE_FACTORY_ADDRESS = 0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95;
address constant private WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
// Struct to hold `bridgeTransferFrom()` local variables in memory and to avoid
// stack overflows.
struct WithdrawToState {
@@ -90,7 +88,7 @@ contract UniswapBridge is
// Get our balance of `fromTokenAddress` token.
state.fromTokenBalance = IERC20Token(fromTokenAddress).balanceOf(address(this));
// Get the weth contract.
state.weth = getWethContract();
state.weth = IEtherToken(_getWethAddress());
// Convert from WETH to a token.
if (fromTokenAddress == address(state.weth)) {
@@ -163,26 +161,6 @@ contract UniswapBridge is
return LEGACY_WALLET_MAGIC_VALUE;
}
/// @dev Overridable way to get the weth contract.
/// @return token The WETH contract.
function getWethContract()
public
view
returns (IEtherToken token)
{
return IEtherToken(WETH_ADDRESS);
}
/// @dev Overridable way to get the uniswap exchange factory contract.
/// @return factory The exchange factory contract.
function getUniswapExchangeFactoryContract()
public
view
returns (IUniswapExchangeFactory factory)
{
return IUniswapExchangeFactory(UNISWAP_EXCHANGE_FACTORY_ADDRESS);
}
/// @dev Grants an unlimited allowance to the exchange for its token
/// on behalf of this contract.
/// @param exchange The Uniswap token exchange.
@@ -209,10 +187,13 @@ contract UniswapBridge is
{
address exchangeTokenAddress = fromTokenAddress;
// Whichever isn't WETH is the exchange token.
if (fromTokenAddress == address(getWethContract())) {
if (fromTokenAddress == _getWethAddress()) {
exchangeTokenAddress = toTokenAddress;
}
exchange = getUniswapExchangeFactoryContract().getExchange(exchangeTokenAddress);
exchange = IUniswapExchange(
IUniswapExchangeFactory(_getUniswapExchangeFactoryAddress())
.getExchange(exchangeTokenAddress)
);
require(address(exchange) != address(0), "NO_UNISWAP_EXCHANGE_FOR_TOKEN");
return exchange;
}

View File

@@ -0,0 +1,33 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
// The actual Chai contract can be found here: https://github.com/dapphub/chai
contract IChai {
/// @dev Withdraws Dai owned by `src`
/// @param src Address that owns Dai.
/// @param wad Amount of Dai to withdraw.
function draw(
address src,
uint256 wad
)
external;
}

View File

@@ -0,0 +1,46 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
interface IKyberNetworkProxy {
/// @dev Sells `sellTokenAddress` tokens for `buyTokenAddress` tokens.
/// @param sellTokenAddress Token to sell.
/// @param sellAmount Amount of tokens to sell.
/// @param buyTokenAddress Token to buy.
/// @param recipientAddress Address to send bought tokens to.
/// @param maxBuyTokenAmount A limit on the amount of tokens to buy.
/// @param minConversionRate The minimal conversion rate. If actual rate
/// is lower, trade is canceled.
/// @param walletId The wallet ID to send part of the fees
/// @return boughtAmount Amount of tokens bought.
function trade(
address sellTokenAddress,
uint256 sellAmount,
address buyTokenAddress,
address payable recipientAddress,
uint256 maxBuyTokenAmount,
uint256 minConversionRate,
address walletId
)
external
payable
returns(uint256 boughtAmount);
}

View File

@@ -67,11 +67,4 @@ interface IUniswapExchange {
)
external
returns (uint256 tokensBought);
/// @dev Retrieves the token that is associated with this exchange.
/// @return tokenAddress The token address.
function toTokenAddress()
external
view
returns (address tokenAddress);
}

View File

@@ -28,5 +28,5 @@ interface IUniswapExchangeFactory {
function getExchange(address tokenAddress)
external
view
returns (IUniswapExchange);
returns (address);
}

View File

@@ -0,0 +1,80 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "../src/bridges/ChaiBridge.sol";
import "@0x/contracts-erc20/contracts/src/ERC20Token.sol";
contract TestChaiDai is
ERC20Token
{
address private constant ALWAYS_REVERT_ADDRESS = address(1);
function draw(
address from,
uint256 amount
)
external
{
if (from == ALWAYS_REVERT_ADDRESS) {
revert();
}
balances[msg.sender] += amount;
}
}
contract TestChaiBridge is
ChaiBridge
{
address public testChaiDai;
address private constant ALWAYS_REVERT_ADDRESS = address(1);
constructor()
public
{
testChaiDai = address(new TestChaiDai());
}
function _getDaiAddress()
internal
view
returns (address)
{
return testChaiDai;
}
function _getChaiAddress()
internal
view
returns (address)
{
return testChaiDai;
}
function _getERC20BridgeProxyAddress()
internal
view
returns (address)
{
return msg.sender == ALWAYS_REVERT_ADDRESS ? address(0) : msg.sender;
}
}

View File

@@ -192,11 +192,11 @@ contract TestEth2DaiBridge is
}
// @dev This contract will double as the Eth2Dai contract.
function _getEth2DaiContract()
function _getEth2DaiAddress()
internal
view
returns (IEth2Dai)
returns (address)
{
return IEth2Dai(address(this));
return address(this);
}
}

View File

@@ -0,0 +1,322 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
import "../src/bridges/KyberBridge.sol";
import "../src/interfaces/IKyberNetworkProxy.sol";
// solhint-disable no-simple-event-func-name
interface ITestContract {
function wethWithdraw(
address payable ownerAddress,
uint256 amount
)
external;
function wethDeposit(
address ownerAddress
)
external
payable;
function tokenTransfer(
address ownerAddress,
address recipientAddress,
uint256 amount
)
external
returns (bool success);
function tokenApprove(
address ownerAddress,
address spenderAddress,
uint256 allowance
)
external
returns (bool success);
function tokenBalanceOf(
address ownerAddress
)
external
view
returns (uint256 balance);
}
/// @dev A minimalist ERC20/WETH token.
contract TestToken {
ITestContract private _testContract;
constructor() public {
_testContract = ITestContract(msg.sender);
}
function approve(address spender, uint256 allowance)
external
returns (bool)
{
return _testContract.tokenApprove(
msg.sender,
spender,
allowance
);
}
function transfer(address recipient, uint256 amount)
external
returns (bool)
{
return _testContract.tokenTransfer(
msg.sender,
recipient,
amount
);
}
function withdraw(uint256 amount)
external
{
return _testContract.wethWithdraw(msg.sender, amount);
}
function deposit()
external
payable
{
return _testContract.wethDeposit.value(msg.value)(msg.sender);
}
function balanceOf(address owner)
external
view
returns (uint256)
{
return _testContract.tokenBalanceOf(owner);
}
}
/// @dev KyberBridge overridden to mock tokens and implement IKyberBridge.
contract TestKyberBridge is
KyberBridge,
ITestContract,
IKyberNetworkProxy
{
event KyberBridgeTrade(
uint256 msgValue,
address sellTokenAddress,
uint256 sellAmount,
address buyTokenAddress,
address payable recipientAddress,
uint256 maxBuyTokenAmount,
uint256 minConversionRate,
address walletId
);
event KyberBridgeWethWithdraw(
address ownerAddress,
uint256 amount
);
event KyberBridgeWethDeposit(
uint256 msgValue,
address ownerAddress,
uint256 amount
);
event KyberBridgeTokenApprove(
address tokenAddress,
address ownerAddress,
address spenderAddress,
uint256 allowance
);
event KyberBridgeTokenTransfer(
address tokenAddress,
address ownerAddress,
address recipientAddress,
uint256 amount
);
IEtherToken public weth;
mapping (address => mapping (address => uint256)) private _tokenBalances;
uint256 private _nextFillAmount;
constructor() public {
weth = IEtherToken(address(new TestToken()));
}
/// @dev Implementation of `IKyberNetworkProxy.trade()`
function trade(
address sellTokenAddress,
uint256 sellAmount,
address buyTokenAddress,
address payable recipientAddress,
uint256 maxBuyTokenAmount,
uint256 minConversionRate,
address walletId
)
external
payable
returns(uint256 boughtAmount)
{
emit KyberBridgeTrade(
msg.value,
sellTokenAddress,
sellAmount,
buyTokenAddress,
recipientAddress,
maxBuyTokenAmount,
minConversionRate,
walletId
);
return _nextFillAmount;
}
function createToken()
external
returns (address tokenAddress)
{
return address(new TestToken());
}
function setNextFillAmount(uint256 amount)
external
payable
{
if (msg.value != 0) {
require(amount == msg.value, "VALUE_AMOUNT_MISMATCH");
grantTokensTo(address(weth), address(this), msg.value);
}
_nextFillAmount = amount;
}
function wethDeposit(
address ownerAddress
)
external
payable
{
require(msg.sender == address(weth), "ONLY_WETH");
grantTokensTo(address(weth), ownerAddress, msg.value);
emit KyberBridgeWethDeposit(
msg.value,
ownerAddress,
msg.value
);
}
function wethWithdraw(
address payable ownerAddress,
uint256 amount
)
external
{
require(msg.sender == address(weth), "ONLY_WETH");
_tokenBalances[address(weth)][ownerAddress] -= amount;
ownerAddress.transfer(amount);
emit KyberBridgeWethWithdraw(
ownerAddress,
amount
);
}
function tokenApprove(
address ownerAddress,
address spenderAddress,
uint256 allowance
)
external
returns (bool success)
{
emit KyberBridgeTokenApprove(
msg.sender,
ownerAddress,
spenderAddress,
allowance
);
return true;
}
function tokenTransfer(
address ownerAddress,
address recipientAddress,
uint256 amount
)
external
returns (bool success)
{
_tokenBalances[msg.sender][ownerAddress] -= amount;
_tokenBalances[msg.sender][recipientAddress] += amount;
emit KyberBridgeTokenTransfer(
msg.sender,
ownerAddress,
recipientAddress,
amount
);
return true;
}
function tokenBalanceOf(
address ownerAddress
)
external
view
returns (uint256 balance)
{
return _tokenBalances[msg.sender][ownerAddress];
}
function grantTokensTo(address tokenAddress, address ownerAddress, uint256 amount)
public
payable
{
_tokenBalances[tokenAddress][ownerAddress] += amount;
if (tokenAddress != address(weth)) {
// Send back ether if not WETH.
msg.sender.transfer(msg.value);
} else {
require(msg.value == amount, "VALUE_AMOUNT_MISMATCH");
}
}
// @dev overridden to point to this contract.
function _getKyberNetworkProxyAddress()
internal
view
returns (address)
{
return address(this);
}
// @dev overridden to point to test WETH.
function _getWethAddress()
internal
view
returns (address)
{
return address(weth);
}
}

View File

@@ -407,26 +407,26 @@ contract TestUniswapBridge is
function getExchange(address tokenAddress)
external
view
returns (IUniswapExchange)
returns (address)
{
return IUniswapExchange(_testExchanges[tokenAddress]);
return address(_testExchanges[tokenAddress]);
}
// @dev Use `wethToken`.
function getWethContract()
public
function _getWethAddress()
internal
view
returns (IEtherToken)
returns (address)
{
return IEtherToken(address(wethToken));
return address(wethToken);
}
// @dev This contract will double as the Uniswap contract.
function getUniswapExchangeFactoryContract()
public
function _getUniswapExchangeFactoryAddress()
internal
view
returns (IUniswapExchangeFactory)
returns (address)
{
return IUniswapExchangeFactory(address(this));
return address(this);
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-asset-proxy",
"version": "2.3.0-beta.3",
"version": "3.0.1",
"engines": {
"node": ">=6.12"
},
@@ -38,8 +38,8 @@
"docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES"
},
"config": {
"publicInterfaceContracts": "ERC1155Proxy,ERC20Proxy,ERC721Proxy,MultiAssetProxy,StaticCallProxy,ERC20BridgeProxy,Eth2DaiBridge,IAssetData,IAssetProxy,UniswapBridge,TestStaticCallTarget",
"abis": "./test/generated-artifacts/@(ERC1155Proxy|ERC20BridgeProxy|ERC20Proxy|ERC721Proxy|Eth2DaiBridge|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|IERC20Bridge|IEth2Dai|IUniswapExchange|IUniswapExchangeFactory|MixinAssetProxyDispatcher|MixinAuthorizable|MultiAssetProxy|Ownable|StaticCallProxy|TestERC20Bridge|TestEth2DaiBridge|TestStaticCallTarget|TestUniswapBridge|UniswapBridge).json",
"publicInterfaceContracts": "ERC1155Proxy,ERC20Proxy,ERC721Proxy,MultiAssetProxy,StaticCallProxy,ERC20BridgeProxy,Eth2DaiBridge,IAssetData,IAssetProxy,UniswapBridge,KyberBridge,ChaiBridge,TestStaticCallTarget",
"abis": "./test/generated-artifacts/@(ChaiBridge|ERC1155Proxy|ERC20BridgeProxy|ERC20Proxy|ERC721Proxy|Eth2DaiBridge|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|IChai|IERC20Bridge|IEth2Dai|IKyberNetworkProxy|IUniswapExchange|IUniswapExchangeFactory|KyberBridge|MixinAssetProxyDispatcher|MixinAuthorizable|MultiAssetProxy|Ownable|StaticCallProxy|TestChaiBridge|TestERC20Bridge|TestEth2DaiBridge|TestKyberBridge|TestStaticCallTarget|TestUniswapBridge|UniswapBridge).json",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
},
"repository": {
@@ -52,15 +52,15 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/protocol/README.md",
"devDependencies": {
"@0x/abi-gen": "^4.4.0-beta.3",
"@0x/contracts-gen": "^1.1.0-beta.3",
"@0x/contracts-test-utils": "^3.2.0-beta.3",
"@0x/contracts-utils": "^3.3.0-beta.3",
"@0x/dev-utils": "^2.4.0-beta.3",
"@0x/sol-compiler": "^3.2.0-beta.3",
"@0x/abi-gen": "^5.0.1",
"@0x/contracts-gen": "^2.0.1",
"@0x/contracts-test-utils": "^5.0.0",
"@0x/contracts-utils": "^4.0.1",
"@0x/dev-utils": "^3.0.1",
"@0x/sol-compiler": "^4.0.1",
"@0x/ts-doc-gen": "^0.0.22",
"@0x/tslint-config": "^3.1.0-beta.2",
"@0x/types": "^2.5.0-beta.2",
"@0x/tslint-config": "^4.0.0",
"@0x/types": "^3.1.0",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "*",
@@ -80,16 +80,16 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^5.5.0-beta.3",
"@0x/contracts-dev-utils": "^0.1.0-beta.3",
"@0x/contracts-erc1155": "^1.2.0-beta.3",
"@0x/contracts-erc20": "^2.3.0-beta.3",
"@0x/contracts-erc721": "^2.2.0-beta.3",
"@0x/order-utils": "^8.5.0-beta.3",
"@0x/typescript-typings": "^4.4.0-beta.2",
"@0x/utils": "^4.6.0-beta.2",
"@0x/web3-wrapper": "^6.1.0-beta.2",
"ethereum-types": "^2.2.0-beta.2",
"@0x/base-contract": "^6.0.1",
"@0x/contracts-dev-utils": "^1.0.1",
"@0x/contracts-erc1155": "^2.0.1",
"@0x/contracts-erc20": "^3.0.1",
"@0x/contracts-erc721": "^3.0.1",
"@0x/order-utils": "^10.0.0",
"@0x/typescript-typings": "^5.0.0",
"@0x/utils": "^5.1.0",
"@0x/web3-wrapper": "^7.0.1",
"ethereum-types": "^3.0.0",
"lodash": "^4.17.11"
},
"publishConfig": {

View File

@@ -5,6 +5,7 @@
*/
import { ContractArtifact } from 'ethereum-types';
import * as ChaiBridge from '../generated-artifacts/ChaiBridge.json';
import * as ERC1155Proxy from '../generated-artifacts/ERC1155Proxy.json';
import * as ERC20BridgeProxy from '../generated-artifacts/ERC20BridgeProxy.json';
import * as ERC20Proxy from '../generated-artifacts/ERC20Proxy.json';
@@ -12,6 +13,7 @@ import * as ERC721Proxy from '../generated-artifacts/ERC721Proxy.json';
import * as Eth2DaiBridge from '../generated-artifacts/Eth2DaiBridge.json';
import * as IAssetData from '../generated-artifacts/IAssetData.json';
import * as IAssetProxy from '../generated-artifacts/IAssetProxy.json';
import * as KyberBridge from '../generated-artifacts/KyberBridge.json';
import * as MultiAssetProxy from '../generated-artifacts/MultiAssetProxy.json';
import * as StaticCallProxy from '../generated-artifacts/StaticCallProxy.json';
import * as TestStaticCallTarget from '../generated-artifacts/TestStaticCallTarget.json';
@@ -27,5 +29,7 @@ export const artifacts = {
IAssetData: IAssetData as ContractArtifact,
IAssetProxy: IAssetProxy as ContractArtifact,
UniswapBridge: UniswapBridge as ContractArtifact,
KyberBridge: KyberBridge as ContractArtifact,
ChaiBridge: ChaiBridge as ContractArtifact,
TestStaticCallTarget: TestStaticCallTarget as ContractArtifact,
};

View File

@@ -16,7 +16,7 @@ export {
export { ERC20Wrapper } from './erc20_wrapper';
export { ERC721Wrapper } from './erc721_wrapper';
export { ERC1155ProxyWrapper } from './erc1155_proxy_wrapper';
export { Erc1155Wrapper, ERC1155MintableContract } from '@0x/contracts-erc1155';
export { ERC1155MintableContract, Erc1155Wrapper } from '@0x/contracts-erc1155';
export { DummyERC20TokenContract } from '@0x/contracts-erc20';
export { DummyERC721TokenContract } from '@0x/contracts-erc721';
export {

View File

@@ -3,6 +3,7 @@
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
* -----------------------------------------------------------------------------
*/
export * from '../generated-wrappers/chai_bridge';
export * from '../generated-wrappers/erc1155_proxy';
export * from '../generated-wrappers/erc20_bridge_proxy';
export * from '../generated-wrappers/erc20_proxy';
@@ -10,6 +11,7 @@ export * from '../generated-wrappers/erc721_proxy';
export * from '../generated-wrappers/eth2_dai_bridge';
export * from '../generated-wrappers/i_asset_data';
export * from '../generated-wrappers/i_asset_proxy';
export * from '../generated-wrappers/kyber_bridge';
export * from '../generated-wrappers/multi_asset_proxy';
export * from '../generated-wrappers/static_call_proxy';
export * from '../generated-wrappers/test_static_call_target';

View File

@@ -5,6 +5,7 @@
*/
import { ContractArtifact } from 'ethereum-types';
import * as ChaiBridge from '../test/generated-artifacts/ChaiBridge.json';
import * as ERC1155Proxy from '../test/generated-artifacts/ERC1155Proxy.json';
import * as ERC20BridgeProxy from '../test/generated-artifacts/ERC20BridgeProxy.json';
import * as ERC20Proxy from '../test/generated-artifacts/ERC20Proxy.json';
@@ -14,17 +15,22 @@ import * as IAssetData from '../test/generated-artifacts/IAssetData.json';
import * as IAssetProxy from '../test/generated-artifacts/IAssetProxy.json';
import * as IAssetProxyDispatcher from '../test/generated-artifacts/IAssetProxyDispatcher.json';
import * as IAuthorizable from '../test/generated-artifacts/IAuthorizable.json';
import * as IChai from '../test/generated-artifacts/IChai.json';
import * as IERC20Bridge from '../test/generated-artifacts/IERC20Bridge.json';
import * as IEth2Dai from '../test/generated-artifacts/IEth2Dai.json';
import * as IKyberNetworkProxy from '../test/generated-artifacts/IKyberNetworkProxy.json';
import * as IUniswapExchange from '../test/generated-artifacts/IUniswapExchange.json';
import * as IUniswapExchangeFactory from '../test/generated-artifacts/IUniswapExchangeFactory.json';
import * as KyberBridge from '../test/generated-artifacts/KyberBridge.json';
import * as MixinAssetProxyDispatcher from '../test/generated-artifacts/MixinAssetProxyDispatcher.json';
import * as MixinAuthorizable from '../test/generated-artifacts/MixinAuthorizable.json';
import * as MultiAssetProxy from '../test/generated-artifacts/MultiAssetProxy.json';
import * as Ownable from '../test/generated-artifacts/Ownable.json';
import * as StaticCallProxy from '../test/generated-artifacts/StaticCallProxy.json';
import * as TestChaiBridge from '../test/generated-artifacts/TestChaiBridge.json';
import * as TestERC20Bridge from '../test/generated-artifacts/TestERC20Bridge.json';
import * as TestEth2DaiBridge from '../test/generated-artifacts/TestEth2DaiBridge.json';
import * as TestKyberBridge from '../test/generated-artifacts/TestKyberBridge.json';
import * as TestStaticCallTarget from '../test/generated-artifacts/TestStaticCallTarget.json';
import * as TestUniswapBridge from '../test/generated-artifacts/TestUniswapBridge.json';
import * as UniswapBridge from '../test/generated-artifacts/UniswapBridge.json';
@@ -38,18 +44,24 @@ export const artifacts = {
ERC721Proxy: ERC721Proxy as ContractArtifact,
MultiAssetProxy: MultiAssetProxy as ContractArtifact,
StaticCallProxy: StaticCallProxy as ContractArtifact,
ChaiBridge: ChaiBridge as ContractArtifact,
Eth2DaiBridge: Eth2DaiBridge as ContractArtifact,
KyberBridge: KyberBridge as ContractArtifact,
UniswapBridge: UniswapBridge as ContractArtifact,
IAssetData: IAssetData as ContractArtifact,
IAssetProxy: IAssetProxy as ContractArtifact,
IAssetProxyDispatcher: IAssetProxyDispatcher as ContractArtifact,
IAuthorizable: IAuthorizable as ContractArtifact,
IChai: IChai as ContractArtifact,
IERC20Bridge: IERC20Bridge as ContractArtifact,
IEth2Dai: IEth2Dai as ContractArtifact,
IKyberNetworkProxy: IKyberNetworkProxy as ContractArtifact,
IUniswapExchange: IUniswapExchange as ContractArtifact,
IUniswapExchangeFactory: IUniswapExchangeFactory as ContractArtifact,
TestChaiBridge: TestChaiBridge as ContractArtifact,
TestERC20Bridge: TestERC20Bridge as ContractArtifact,
TestEth2DaiBridge: TestEth2DaiBridge as ContractArtifact,
TestKyberBridge: TestKyberBridge as ContractArtifact,
TestStaticCallTarget: TestStaticCallTarget as ContractArtifact,
TestUniswapBridge: TestUniswapBridge as ContractArtifact,
};

View File

@@ -0,0 +1,60 @@
import { ERC20TokenContract } from '@0x/contracts-erc20';
import { blockchainTests, constants, expect, randomAddress } from '@0x/contracts-test-utils';
import { AssetProxyId, RevertReason } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { artifacts } from './artifacts';
import { TestChaiBridgeContract } from './wrappers';
blockchainTests.resets('ChaiBridge unit tests', env => {
let chaiBridgeContract: TestChaiBridgeContract;
let testDaiContract: ERC20TokenContract;
let fromAddress: string;
let toAddress: string;
const alwaysRevertAddress = '0x0000000000000000000000000000000000000001';
const amount = new BigNumber(1);
before(async () => {
[fromAddress, toAddress] = await env.getAccountAddressesAsync();
chaiBridgeContract = await TestChaiBridgeContract.deployFrom0xArtifactAsync(
artifacts.TestChaiBridge,
env.provider,
env.txDefaults,
artifacts,
);
const testChaiDaiAddress = await chaiBridgeContract.testChaiDai().callAsync();
testDaiContract = new ERC20TokenContract(testChaiDaiAddress, env.provider, env.txDefaults);
});
describe('bridgeTransferFrom()', () => {
it('fails if not called by ERC20BridgeProxy', async () => {
return expect(
chaiBridgeContract
.bridgeTransferFrom(randomAddress(), fromAddress, toAddress, amount, constants.NULL_BYTES)
.awaitTransactionSuccessAsync({ from: alwaysRevertAddress }),
).to.revertWith(RevertReason.ChaiBridgeOnlyCallableByErc20BridgeProxy);
});
it('returns magic bytes upon success', async () => {
const magicBytes = await chaiBridgeContract
.bridgeTransferFrom(randomAddress(), fromAddress, toAddress, amount, constants.NULL_BYTES)
.callAsync();
expect(magicBytes).to.eq(AssetProxyId.ERC20Bridge);
});
it('should increase the Dai balance of `toAddress` by `amount` if successful', async () => {
const initialBalance = await testDaiContract.balanceOf(toAddress).callAsync();
await chaiBridgeContract
.bridgeTransferFrom(randomAddress(), fromAddress, toAddress, amount, constants.NULL_BYTES)
.awaitTransactionSuccessAsync();
const endBalance = await testDaiContract.balanceOf(toAddress).callAsync();
expect(endBalance).to.bignumber.eq(initialBalance.plus(amount));
});
it('fails if the `chai.draw` call fails', async () => {
return expect(
chaiBridgeContract
.bridgeTransferFrom(randomAddress(), alwaysRevertAddress, toAddress, amount, constants.NULL_BYTES)
.awaitTransactionSuccessAsync(),
).to.revertWith(RevertReason.ChaiBridgeDrawDaiFailed);
});
});
});

View File

@@ -3,15 +3,12 @@ import {
constants,
expect,
getRandomInteger,
hexLeftPad,
hexRightPad,
hexSlice,
Numberish,
randomAddress,
} from '@0x/contracts-test-utils';
import { AuthorizableRevertErrors } from '@0x/contracts-utils';
import { AssetProxyId } from '@0x/types';
import { AbiEncoder, BigNumber, StringRevertError } from '@0x/utils';
import { AbiEncoder, BigNumber, hexUtils, StringRevertError } from '@0x/utils';
import { DecodedLogs } from 'ethereum-types';
import * as _ from 'lodash';
@@ -21,7 +18,7 @@ import { ERC20BridgeProxyContract, TestERC20BridgeContract } from './wrappers';
blockchainTests.resets('ERC20BridgeProxy unit tests', env => {
const PROXY_ID = AssetProxyId.ERC20Bridge;
const BRIDGE_SUCCESS_RETURN_DATA = hexRightPad(PROXY_ID);
const BRIDGE_SUCCESS_RETURN_DATA = hexUtils.rightPad(PROXY_ID);
let owner: string;
let badCaller: string;
let assetProxy: ERC20BridgeProxyContract;
@@ -173,7 +170,7 @@ blockchainTests.resets('ERC20BridgeProxy unit tests', env => {
it('fails if asset data is truncated', async () => {
const opts = createTransferFromOpts();
const truncatedAssetData = hexSlice(encodeAssetData(opts.assetData), 0, -1);
const truncatedAssetData = hexUtils.slice(encodeAssetData(opts.assetData), 0, -1);
const tx = assetProxy
.transferFrom(truncatedAssetData, opts.from, opts.to, new BigNumber(opts.amount))
.awaitTransactionSuccessAsync();
@@ -197,7 +194,7 @@ blockchainTests.resets('ERC20BridgeProxy unit tests', env => {
const tx = transferFromAsync({
assetData: createAssetData({
bridgeData: createBridgeData({
returnData: hexLeftPad('0x1'),
returnData: hexUtils.leftPad('0x1'),
}),
}),
});
@@ -210,7 +207,7 @@ blockchainTests.resets('ERC20BridgeProxy unit tests', env => {
const tx = transferFromAsync({
assetData: createAssetData({
bridgeData: createBridgeData({
returnData: hexRightPad('0x1'),
returnData: hexUtils.rightPad('0x1'),
}),
}),
});

View File

@@ -4,13 +4,11 @@ import {
expect,
filterLogsToArguments,
getRandomInteger,
hexLeftPad,
hexRandom,
Numberish,
randomAddress,
} from '@0x/contracts-test-utils';
import { AssetProxyId } from '@0x/types';
import { BigNumber, RawRevertError } from '@0x/utils';
import { BigNumber, hexUtils, RawRevertError } from '@0x/utils';
import { DecodedLogs } from 'ethereum-types';
import * as _ from 'lodash';
@@ -39,7 +37,9 @@ blockchainTests.resets('Eth2DaiBridge unit tests', env => {
describe('isValidSignature()', () => {
it('returns success bytes', async () => {
const LEGACY_WALLET_MAGIC_VALUE = '0xb0671381';
const result = await testContract.isValidSignature(hexRandom(), hexRandom(_.random(0, 32))).callAsync();
const result = await testContract
.isValidSignature(hexUtils.random(), hexUtils.random(_.random(0, 32)))
.callAsync();
expect(result).to.eq(LEGACY_WALLET_MAGIC_VALUE);
});
});
@@ -71,7 +71,7 @@ blockchainTests.resets('Eth2DaiBridge unit tests', env => {
fillAmount: getRandomInteger(1, 100e18),
fromTokenBalance: getRandomInteger(1, 100e18),
toTokentransferRevertReason: '',
toTokenTransferReturnData: hexLeftPad(1),
toTokenTransferReturnData: hexUtils.leftPad(1),
...opts,
};
}
@@ -111,7 +111,7 @@ blockchainTests.resets('Eth2DaiBridge unit tests', env => {
_opts.toAddress,
new BigNumber(_opts.amount),
// ABI-encode the "from" token address as the bridge data.
hexLeftPad(_opts.fromTokenAddress as string),
hexUtils.leftPad(_opts.fromTokenAddress as string),
);
const result = await bridgeTransferFromFn.callAsync();
const { logs } = await bridgeTransferFromFn.awaitTransactionSuccessAsync();
@@ -179,13 +179,13 @@ blockchainTests.resets('Eth2DaiBridge unit tests', env => {
});
it('fails if `toTokenAddress.transfer()` returns false', async () => {
const opts = createWithdrawToOpts({ toTokenTransferReturnData: hexLeftPad(0) });
const opts = createWithdrawToOpts({ toTokenTransferReturnData: hexUtils.leftPad(0) });
const tx = withdrawToAsync(opts);
return expect(tx).to.revertWith(new RawRevertError(hexLeftPad(0)));
return expect(tx).to.revertWith(new RawRevertError(hexUtils.leftPad(0)));
});
it('succeeds if `toTokenAddress.transfer()` returns true', async () => {
await withdrawToAsync({ toTokenTransferReturnData: hexLeftPad(1) });
await withdrawToAsync({ toTokenTransferReturnData: hexUtils.leftPad(1) });
});
});
});

View File

@@ -0,0 +1,272 @@
import {
blockchainTests,
constants,
expect,
getRandomInteger,
randomAddress,
verifyEventsFromLogs,
} from '@0x/contracts-test-utils';
import { AssetProxyId } from '@0x/types';
import { BigNumber, hexUtils } from '@0x/utils';
import { DecodedLogs } from 'ethereum-types';
import * as _ from 'lodash';
import { artifacts } from './artifacts';
import { TestKyberBridgeContract, TestKyberBridgeEvents } from './wrappers';
blockchainTests.resets('KyberBridge unit tests', env => {
const KYBER_ETH_ADDRESS = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee';
let testContract: TestKyberBridgeContract;
before(async () => {
testContract = await TestKyberBridgeContract.deployFrom0xArtifactAsync(
artifacts.TestKyberBridge,
env.provider,
env.txDefaults,
artifacts,
);
});
describe('isValidSignature()', () => {
it('returns success bytes', async () => {
const LEGACY_WALLET_MAGIC_VALUE = '0xb0671381';
const result = await testContract
.isValidSignature(hexUtils.random(), hexUtils.random(_.random(0, 32)))
.callAsync();
expect(result).to.eq(LEGACY_WALLET_MAGIC_VALUE);
});
});
describe('bridgeTransferFrom()', () => {
let fromTokenAddress: string;
let toTokenAddress: string;
let wethAddress: string;
before(async () => {
wethAddress = await testContract.weth().callAsync();
fromTokenAddress = await testContract.createToken().callAsync();
await testContract.createToken().awaitTransactionSuccessAsync();
toTokenAddress = await testContract.createToken().callAsync();
await testContract.createToken().awaitTransactionSuccessAsync();
});
const STATIC_KYBER_TRADE_ARGS = {
maxBuyTokenAmount: constants.MAX_UINT256,
walletId: constants.NULL_ADDRESS,
};
interface TransferFromOpts {
toTokenAddress: string;
fromTokenAddress: string;
toAddress: string;
// Amount to pass into `bridgeTransferFrom()`
amount: BigNumber;
// Amount to convert in `trade()`.
fillAmount: BigNumber;
// Token balance of the bridge.
fromTokenBalance: BigNumber;
}
interface TransferFromResult {
opts: TransferFromOpts;
result: string;
logs: DecodedLogs;
}
function createTransferFromOpts(opts?: Partial<TransferFromOpts>): TransferFromOpts {
return {
fromTokenAddress,
toTokenAddress,
toAddress: randomAddress(),
amount: getRandomInteger(1, 10e18),
fillAmount: getRandomInteger(1, 10e18),
fromTokenBalance: getRandomInteger(1, 10e18),
...opts,
};
}
async function withdrawToAsync(opts?: Partial<TransferFromOpts>): Promise<TransferFromResult> {
const _opts = createTransferFromOpts(opts);
// Fund the contract with input tokens.
await testContract
.grantTokensTo(_opts.fromTokenAddress, testContract.address, _opts.fromTokenBalance)
.awaitTransactionSuccessAsync({ value: _opts.fromTokenBalance });
// Fund the contract with output tokens.
await testContract.setNextFillAmount(_opts.fillAmount).awaitTransactionSuccessAsync({
value: _opts.toTokenAddress === wethAddress ? _opts.fillAmount : constants.ZERO_AMOUNT,
});
// Call bridgeTransferFrom().
const bridgeTransferFromFn = testContract.bridgeTransferFrom(
// Output token
_opts.toTokenAddress,
// Random maker address.
randomAddress(),
// Recipient address.
_opts.toAddress,
// Transfer amount.
_opts.amount,
// ABI-encode the input token address as the bridge data.
hexUtils.leftPad(_opts.fromTokenAddress),
);
const result = await bridgeTransferFromFn.callAsync();
const { logs } = await bridgeTransferFromFn.awaitTransactionSuccessAsync();
return {
opts: _opts,
result,
logs: (logs as any) as DecodedLogs,
};
}
function getMinimumConversionRate(opts: TransferFromOpts): BigNumber {
return opts.amount
.times(constants.ONE_ETHER)
.div(opts.fromTokenBalance)
.integerValue(BigNumber.ROUND_DOWN);
}
it('returns magic bytes on success', async () => {
const BRIDGE_SUCCESS_RETURN_DATA = AssetProxyId.ERC20Bridge;
const { result } = await withdrawToAsync();
expect(result).to.eq(BRIDGE_SUCCESS_RETURN_DATA);
});
it('can trade token -> token', async () => {
const { opts, logs } = await withdrawToAsync();
verifyEventsFromLogs(
logs,
[
{
sellTokenAddress: opts.fromTokenAddress,
buyTokenAddress: opts.toTokenAddress,
sellAmount: opts.fromTokenBalance,
recipientAddress: opts.toAddress,
minConversionRate: getMinimumConversionRate(opts),
msgValue: constants.ZERO_AMOUNT,
...STATIC_KYBER_TRADE_ARGS,
},
],
TestKyberBridgeEvents.KyberBridgeTrade,
);
});
it('can trade token -> ETH', async () => {
const { opts, logs } = await withdrawToAsync({
toTokenAddress: wethAddress,
});
verifyEventsFromLogs(
logs,
[
{
sellTokenAddress: opts.fromTokenAddress,
buyTokenAddress: KYBER_ETH_ADDRESS,
sellAmount: opts.fromTokenBalance,
recipientAddress: testContract.address,
minConversionRate: getMinimumConversionRate(opts),
msgValue: constants.ZERO_AMOUNT,
...STATIC_KYBER_TRADE_ARGS,
},
],
TestKyberBridgeEvents.KyberBridgeTrade,
);
});
it('can trade ETH -> token', async () => {
const { opts, logs } = await withdrawToAsync({
fromTokenAddress: wethAddress,
});
verifyEventsFromLogs(
logs,
[
{
sellTokenAddress: KYBER_ETH_ADDRESS,
buyTokenAddress: opts.toTokenAddress,
sellAmount: opts.fromTokenBalance,
recipientAddress: opts.toAddress,
minConversionRate: getMinimumConversionRate(opts),
msgValue: opts.fromTokenBalance,
...STATIC_KYBER_TRADE_ARGS,
},
],
TestKyberBridgeEvents.KyberBridgeTrade,
);
});
it('does nothing if bridge has no token balance', async () => {
const { logs } = await withdrawToAsync({
fromTokenBalance: constants.ZERO_AMOUNT,
});
expect(logs).to.be.length(0);
});
it('only transfers the token if trading the same token', async () => {
const { opts, logs } = await withdrawToAsync({
toTokenAddress: fromTokenAddress,
});
verifyEventsFromLogs(
logs,
[
{
tokenAddress: fromTokenAddress,
ownerAddress: testContract.address,
recipientAddress: opts.toAddress,
amount: opts.fromTokenBalance,
},
],
TestKyberBridgeEvents.KyberBridgeTokenTransfer,
);
});
it('grants Kyber an allowance when selling non-WETH', async () => {
const { opts, logs } = await withdrawToAsync();
verifyEventsFromLogs(
logs,
[
{
tokenAddress: opts.fromTokenAddress,
ownerAddress: testContract.address,
spenderAddress: testContract.address,
allowance: constants.MAX_UINT256,
},
],
TestKyberBridgeEvents.KyberBridgeTokenApprove,
);
});
it('does not grant Kyber an allowance when selling WETH', async () => {
const { logs } = await withdrawToAsync({
fromTokenAddress: wethAddress,
});
verifyEventsFromLogs(logs, [], TestKyberBridgeEvents.KyberBridgeTokenApprove);
});
it('withdraws WETH and passes it to Kyber when selling WETH', async () => {
const { opts, logs } = await withdrawToAsync({
fromTokenAddress: wethAddress,
});
expect(logs[0].event).to.eq(TestKyberBridgeEvents.KyberBridgeWethWithdraw);
expect(logs[0].args).to.deep.eq({
ownerAddress: testContract.address,
amount: opts.fromTokenBalance,
});
expect(logs[1].event).to.eq(TestKyberBridgeEvents.KyberBridgeTrade);
expect(logs[1].args.msgValue).to.bignumber.eq(opts.fromTokenBalance);
});
it('wraps WETH and transfers it to the recipient when buyng WETH', async () => {
const { opts, logs } = await withdrawToAsync({
toTokenAddress: wethAddress,
});
expect(logs[0].event).to.eq(TestKyberBridgeEvents.KyberBridgeTokenApprove);
expect(logs[0].args.tokenAddress).to.eq(opts.fromTokenAddress);
expect(logs[1].event).to.eq(TestKyberBridgeEvents.KyberBridgeTrade);
expect(logs[1].args.recipientAddress).to.eq(testContract.address);
expect(logs[2].event).to.eq(TestKyberBridgeEvents.KyberBridgeWethDeposit);
expect(logs[2].args).to.deep.eq({
msgValue: opts.fillAmount,
ownerAddress: testContract.address,
amount: opts.fillAmount,
});
});
});
});

View File

@@ -5,13 +5,11 @@ import {
filterLogs,
filterLogsToArguments,
getRandomInteger,
hexLeftPad,
hexRandom,
Numberish,
randomAddress,
} from '@0x/contracts-test-utils';
import { AssetProxyId } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { BigNumber, hexUtils } from '@0x/utils';
import { DecodedLogs } from 'ethereum-types';
import * as _ from 'lodash';
@@ -46,7 +44,9 @@ blockchainTests.resets('UniswapBridge unit tests', env => {
describe('isValidSignature()', () => {
it('returns success bytes', async () => {
const LEGACY_WALLET_MAGIC_VALUE = '0xb0671381';
const result = await testContract.isValidSignature(hexRandom(), hexRandom(_.random(0, 32))).callAsync();
const result = await testContract
.isValidSignature(hexUtils.random(), hexUtils.random(_.random(0, 32)))
.callAsync();
expect(result).to.eq(LEGACY_WALLET_MAGIC_VALUE);
});
});
@@ -126,7 +126,7 @@ blockchainTests.resets('UniswapBridge unit tests', env => {
// The amount to transfer to "to"
new BigNumber(_opts.amount),
// ABI-encoded "from" token address.
hexLeftPad(_opts.fromTokenAddress),
hexUtils.leftPad(_opts.fromTokenAddress),
);
const result = await bridgeTransferFromFn.callAsync();
const receipt = await bridgeTransferFromFn.awaitTransactionSuccessAsync();
@@ -208,7 +208,7 @@ blockchainTests.resets('UniswapBridge unit tests', env => {
randomAddress(),
randomAddress(),
getRandomInteger(1, 1e18),
hexLeftPad(randomAddress()),
hexUtils.leftPad(randomAddress()),
)
.awaitTransactionSuccessAsync();
return expect(tx).to.eventually.be.rejectedWith('NO_UNISWAP_EXCHANGE_FOR_TOKEN');
@@ -282,7 +282,7 @@ blockchainTests.resets('UniswapBridge unit tests', env => {
randomAddress(),
randomAddress(),
getRandomInteger(1, 1e18),
hexLeftPad(wethTokenAddress),
hexUtils.leftPad(wethTokenAddress),
)
.awaitTransactionSuccessAsync();
return expect(tx).to.eventually.be.rejectedWith('NO_UNISWAP_EXCHANGE_FOR_TOKEN');
@@ -342,7 +342,7 @@ blockchainTests.resets('UniswapBridge unit tests', env => {
randomAddress(),
randomAddress(),
getRandomInteger(1, 1e18),
hexLeftPad(randomAddress()),
hexUtils.leftPad(randomAddress()),
)
.awaitTransactionSuccessAsync();
return expect(tx).to.eventually.be.rejectedWith('NO_UNISWAP_EXCHANGE_FOR_TOKEN');

View File

@@ -3,6 +3,7 @@
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
* -----------------------------------------------------------------------------
*/
export * from '../test/generated-wrappers/chai_bridge';
export * from '../test/generated-wrappers/erc1155_proxy';
export * from '../test/generated-wrappers/erc20_bridge_proxy';
export * from '../test/generated-wrappers/erc20_proxy';
@@ -12,17 +13,22 @@ export * from '../test/generated-wrappers/i_asset_data';
export * from '../test/generated-wrappers/i_asset_proxy';
export * from '../test/generated-wrappers/i_asset_proxy_dispatcher';
export * from '../test/generated-wrappers/i_authorizable';
export * from '../test/generated-wrappers/i_chai';
export * from '../test/generated-wrappers/i_erc20_bridge';
export * from '../test/generated-wrappers/i_eth2_dai';
export * from '../test/generated-wrappers/i_kyber_network_proxy';
export * from '../test/generated-wrappers/i_uniswap_exchange';
export * from '../test/generated-wrappers/i_uniswap_exchange_factory';
export * from '../test/generated-wrappers/kyber_bridge';
export * from '../test/generated-wrappers/mixin_asset_proxy_dispatcher';
export * from '../test/generated-wrappers/mixin_authorizable';
export * from '../test/generated-wrappers/multi_asset_proxy';
export * from '../test/generated-wrappers/ownable';
export * from '../test/generated-wrappers/static_call_proxy';
export * from '../test/generated-wrappers/test_chai_bridge';
export * from '../test/generated-wrappers/test_erc20_bridge';
export * from '../test/generated-wrappers/test_eth2_dai_bridge';
export * from '../test/generated-wrappers/test_kyber_bridge';
export * from '../test/generated-wrappers/test_static_call_target';
export * from '../test/generated-wrappers/test_uniswap_bridge';
export * from '../test/generated-wrappers/uniswap_bridge';

View File

@@ -3,6 +3,7 @@
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
"files": [
"generated-artifacts/ChaiBridge.json",
"generated-artifacts/ERC1155Proxy.json",
"generated-artifacts/ERC20BridgeProxy.json",
"generated-artifacts/ERC20Proxy.json",
@@ -10,10 +11,12 @@
"generated-artifacts/Eth2DaiBridge.json",
"generated-artifacts/IAssetData.json",
"generated-artifacts/IAssetProxy.json",
"generated-artifacts/KyberBridge.json",
"generated-artifacts/MultiAssetProxy.json",
"generated-artifacts/StaticCallProxy.json",
"generated-artifacts/TestStaticCallTarget.json",
"generated-artifacts/UniswapBridge.json",
"test/generated-artifacts/ChaiBridge.json",
"test/generated-artifacts/ERC1155Proxy.json",
"test/generated-artifacts/ERC20BridgeProxy.json",
"test/generated-artifacts/ERC20Proxy.json",
@@ -23,17 +26,22 @@
"test/generated-artifacts/IAssetProxy.json",
"test/generated-artifacts/IAssetProxyDispatcher.json",
"test/generated-artifacts/IAuthorizable.json",
"test/generated-artifacts/IChai.json",
"test/generated-artifacts/IERC20Bridge.json",
"test/generated-artifacts/IEth2Dai.json",
"test/generated-artifacts/IKyberNetworkProxy.json",
"test/generated-artifacts/IUniswapExchange.json",
"test/generated-artifacts/IUniswapExchangeFactory.json",
"test/generated-artifacts/KyberBridge.json",
"test/generated-artifacts/MixinAssetProxyDispatcher.json",
"test/generated-artifacts/MixinAuthorizable.json",
"test/generated-artifacts/MultiAssetProxy.json",
"test/generated-artifacts/Ownable.json",
"test/generated-artifacts/StaticCallProxy.json",
"test/generated-artifacts/TestChaiBridge.json",
"test/generated-artifacts/TestERC20Bridge.json",
"test/generated-artifacts/TestEth2DaiBridge.json",
"test/generated-artifacts/TestKyberBridge.json",
"test/generated-artifacts/TestStaticCallTarget.json",
"test/generated-artifacts/TestUniswapBridge.json",
"test/generated-artifacts/UniswapBridge.json"

View File

@@ -1,4 +1,80 @@
[
{
"timestamp": 1575931811,
"version": "3.0.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "3.0.0",
"changes": [
{
"note": "Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils",
"pr": 2330
},
{
"note": "Introduced new export CoordinatorRevertErrors",
"pr": 2321
},
{
"note": "Added dependency on @0x/contracts-utils",
"pr": 2321
},
{
"note": "Add chainId to domain separator",
"pr": 1742
},
{
"note": "Inherit Exchange domain constants from `exchange-libs` to reduce code duplication",
"pr": 1742
},
{
"note": "Update domain separator",
"pr": 1742
},
{
"note": "Refactor contract to use new ITransactions interface",
"pr": 1753
},
{
"note": "Add verifyingContractIfExists arg to LibEIP712CoordinatorDomain constructor",
"pr": 1753
},
{
"note": "Remove LibZeroExTransaction contract",
"pr": 1753
},
{
"note": "Update tests for arbitrary fee tokens (ZEIP-28).",
"pr": 1819
},
{
"note": "Update for new `marketXOrders` consolidation.",
"pr": 2042
},
{
"note": "Use built in selectors instead of hard coded constants",
"pr": 2055
},
{
"note": "Compile and export all contracts, artifacts, and wrappers by default",
"pr": 2055
}
],
"timestamp": 1575296764
},
{
"version": "2.1.0-beta.4",
"changes": [
{
"note": "Dependencies updated"
}
],
"timestamp": 1575290197
},
{
"version": "2.1.0-beta.3",
"changes": [

View File

@@ -5,6 +5,30 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.0.1 - _December 9, 2019_
* Dependencies updated
## v3.0.0 - _December 2, 2019_
* Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330)
* Introduced new export CoordinatorRevertErrors (#2321)
* Added dependency on @0x/contracts-utils (#2321)
* Add chainId to domain separator (#1742)
* Inherit Exchange domain constants from `exchange-libs` to reduce code duplication (#1742)
* Update domain separator (#1742)
* Refactor contract to use new ITransactions interface (#1753)
* Add verifyingContractIfExists arg to LibEIP712CoordinatorDomain constructor (#1753)
* Remove LibZeroExTransaction contract (#1753)
* Update tests for arbitrary fee tokens (ZEIP-28). (#1819)
* Update for new `marketXOrders` consolidation. (#2042)
* Use built in selectors instead of hard coded constants (#2055)
* Compile and export all contracts, artifacts, and wrappers by default (#2055)
## v2.1.0-beta.4 - _December 2, 2019_
* Dependencies updated
## v2.1.0-beta.3 - _November 20, 2019_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-coordinator",
"version": "2.1.0-beta.3",
"version": "3.0.1",
"engines": {
"node": ">=6.12"
},
@@ -52,19 +52,19 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
"devDependencies": {
"@0x/abi-gen": "^4.4.0-beta.3",
"@0x/contracts-asset-proxy": "^2.3.0-beta.3",
"@0x/contracts-dev-utils": "^0.1.0-beta.3",
"@0x/contracts-erc20": "^2.3.0-beta.3",
"@0x/contracts-exchange": "^2.2.0-beta.3",
"@0x/contracts-gen": "^1.1.0-beta.3",
"@0x/contracts-test-utils": "^3.2.0-beta.3",
"@0x/dev-utils": "^2.4.0-beta.3",
"@0x/order-utils": "^8.5.0-beta.3",
"@0x/sol-compiler": "^3.2.0-beta.3",
"@0x/abi-gen": "^5.0.1",
"@0x/contracts-asset-proxy": "^3.0.1",
"@0x/contracts-dev-utils": "^1.0.1",
"@0x/contracts-erc20": "^3.0.1",
"@0x/contracts-exchange": "^3.0.1",
"@0x/contracts-gen": "^2.0.1",
"@0x/contracts-test-utils": "^5.0.0",
"@0x/dev-utils": "^3.0.1",
"@0x/order-utils": "^10.0.0",
"@0x/sol-compiler": "^4.0.1",
"@0x/ts-doc-gen": "^0.0.22",
"@0x/tslint-config": "^3.1.0-beta.2",
"@0x/web3-wrapper": "^6.1.0-beta.2",
"@0x/tslint-config": "^4.0.0",
"@0x/web3-wrapper": "^7.0.1",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "*",
@@ -84,12 +84,16 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^5.5.0-beta.3",
"@0x/contracts-utils": "^3.3.0-beta.3",
"@0x/types": "^2.5.0-beta.2",
"@0x/typescript-typings": "^4.4.0-beta.2",
"@0x/utils": "^4.6.0-beta.2",
"ethereum-types": "^2.2.0-beta.2"
"@0x/assert": "^3.0.1",
"@0x/base-contract": "^6.0.1",
"@0x/contract-addresses": "^4.0.0",
"@0x/contracts-utils": "^4.0.1",
"@0x/json-schemas": "^5.0.1",
"@0x/types": "^3.1.0",
"@0x/typescript-typings": "^5.0.0",
"@0x/utils": "^5.1.0",
"ethereum-types": "^3.0.0",
"http-status-codes": "^1.3.2"
},
"publishConfig": {
"access": "public"

View File

@@ -1,5 +1,6 @@
import { hexConcat, signingUtils } from '@0x/contracts-test-utils';
import { signingUtils } from '@0x/contracts-test-utils';
import { SignatureType, SignedZeroExTransaction } from '@0x/types';
import { hexUtils } from '@0x/utils';
import { hashUtils } from './hash_utils';
import { SignedCoordinatorApproval } from './types';
@@ -27,7 +28,7 @@ export class ApprovalFactory {
const signedApproval = {
txOrigin,
transaction,
signature: hexConcat(signatureBuff),
signature: hexUtils.concat(signatureBuff),
};
return signedApproval;
}

View File

@@ -0,0 +1,820 @@
import { SendTransactionOpts } from '@0x/base-contract';
import { getContractAddressesForChainOrThrow } from '@0x/contract-addresses';
import { ExchangeContract } from '@0x/contracts-exchange';
import { ExchangeFunctionName } from '@0x/contracts-test-utils';
import { devConstants } from '@0x/dev-utils';
import { schemas } from '@0x/json-schemas';
import { generatePseudoRandomSalt, signatureUtils } from '@0x/order-utils';
import { Order, SignedOrder, SignedZeroExTransaction, ZeroExTransaction } from '@0x/types';
import { BigNumber, fetchAsync } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { CallData, ContractAbi, SupportedProvider, TxData } from 'ethereum-types';
import * as HttpStatus from 'http-status-codes';
import { flatten } from 'lodash';
import { artifacts } from '../artifacts';
import { CoordinatorContract, CoordinatorRegistryContract } from '../wrappers';
import { assert } from './utils/assert';
import {
CoordinatorServerApprovalResponse,
CoordinatorServerCancellationResponse,
CoordinatorServerError,
CoordinatorServerErrorMsg,
CoordinatorServerResponse,
} from './utils/coordinator_server_types';
import { decorators } from './utils/decorators';
export { CoordinatorServerErrorMsg, CoordinatorServerCancellationResponse };
const DEFAULT_TX_DATA = {
gas: devConstants.GAS_LIMIT,
gasPrice: new BigNumber(1),
value: new BigNumber(150000), // DEFAULT_PROTOCOL_FEE_MULTIPLIER
};
// tx expiration time will be set to (now + default_approval - time_buffer)
const DEFAULT_APPROVAL_EXPIRATION_TIME_SECONDS = 90;
const DEFAULT_EXPIRATION_TIME_BUFFER_SECONDS = 30;
/**
* This class includes all the functionality related to filling or cancelling orders through
* the 0x V2 Coordinator extension contract.
*/
export class CoordinatorClient {
public abi: ContractAbi = artifacts.Coordinator.compilerOutput.abi;
public chainId: number;
public address: string;
public exchangeAddress: string;
public registryAddress: string;
private readonly _web3Wrapper: Web3Wrapper;
private readonly _contractInstance: CoordinatorContract;
private readonly _registryInstance: CoordinatorRegistryContract;
private readonly _exchangeInstance: ExchangeContract;
private readonly _feeRecipientToEndpoint: { [feeRecipient: string]: string } = {};
private readonly _txDefaults: CallData = DEFAULT_TX_DATA;
/**
* Validates that the 0x transaction has been approved by all of the feeRecipients that correspond to each order in the transaction's Exchange calldata.
* Throws an error if the transaction approvals are not valid. Will not detect failures that would occur when the transaction is executed on the Exchange contract.
* @param transaction 0x transaction containing salt, signerAddress, and data.
* @param txOrigin Required signer of Ethereum transaction calling this function.
* @param transactionSignature Proof that the transaction has been signed by the signer.
* @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order in the transaction's Exchange calldata.
*/
@decorators.asyncZeroExErrorHandler
public async assertValidCoordinatorApprovalsOrThrowAsync(
transaction: ZeroExTransaction,
txOrigin: string,
transactionSignature: string,
approvalSignatures: string[],
): Promise<void> {
assert.doesConformToSchema('transaction', transaction, schemas.zeroExTransactionSchema);
assert.isETHAddressHex('txOrigin', txOrigin);
assert.isHexString('transactionSignature', transactionSignature);
for (const approvalSignature of approvalSignatures) {
assert.isHexString('approvalSignature', approvalSignature);
}
return this._contractInstance
.assertValidCoordinatorApprovals(transaction, txOrigin, transactionSignature, approvalSignatures)
.callAsync();
}
/**
* Instantiate CoordinatorClient
* @param web3Wrapper Web3Wrapper instance to use.
* @param chainId Desired chainId.
* @param address The address of the Coordinator contract. If undefined, will
* default to the known address corresponding to the chainId.
* @param exchangeAddress The address of the Exchange contract. If undefined, will
* default to the known address corresponding to the chainId.
* @param registryAddress The address of the CoordinatorRegistry contract. If undefined, will
* default to the known address corresponding to the chainId.
*/
constructor(
address: string,
provider: SupportedProvider,
chainId: number,
txDefaults?: Partial<TxData>,
exchangeAddress?: string,
registryAddress?: string,
) {
this.chainId = chainId;
const contractAddresses = getContractAddressesForChainOrThrow(this.chainId);
this.address = address === undefined ? contractAddresses.coordinator : address;
this.exchangeAddress = exchangeAddress === undefined ? contractAddresses.exchange : exchangeAddress;
this.registryAddress = registryAddress === undefined ? contractAddresses.coordinatorRegistry : registryAddress;
this._web3Wrapper = new Web3Wrapper(provider);
this._txDefaults = { ...txDefaults, ...DEFAULT_TX_DATA };
this._contractInstance = new CoordinatorContract(
this.address,
this._web3Wrapper.getProvider(),
this._web3Wrapper.getContractDefaults(),
);
this._registryInstance = new CoordinatorRegistryContract(
this.registryAddress,
this._web3Wrapper.getProvider(),
this._web3Wrapper.getContractDefaults(),
);
this._exchangeInstance = new ExchangeContract(
this.exchangeAddress,
this._web3Wrapper.getProvider(),
this._web3Wrapper.getContractDefaults(),
);
}
/**
* Fills a signed order with an amount denominated in baseUnits of the taker asset. Under-the-hood, this
* method uses the `feeRecipientAddress` of the order to look up the coordinator server endpoint registered in the
* coordinator registry contract. It requests an approval from that coordinator server before
* submitting the order and approval as a 0x transaction to the coordinator extension contract. The coordinator extension
* contract validates approvals and then fills the order via the Exchange contract.
* @param order An object that conforms to the Order interface.
* @param takerAssetFillAmount The amount of the order (in taker asset baseUnits) that you wish to fill.
* @param signature Signature corresponding to the order.
* @param txData Transaction data. The `from` field should be the user Ethereum address who would like
* to fill these orders. Must be available via the Provider supplied at instantiation.
* @param sendTxOpts Optional arguments for sending the transaction.
* @return Transaction hash.
*/
@decorators.asyncZeroExErrorHandler
public async fillOrderAsync(
order: Order,
takerAssetFillAmount: BigNumber,
signature: string,
txData: TxData,
sendTxOpts: Partial<SendTransactionOpts> = { shouldValidate: true },
): Promise<string> {
assert.doesConformToSchema('order', order, schemas.orderSchema);
assert.isValidBaseUnitAmount('takerAssetFillAmount', takerAssetFillAmount);
return this._executeTxThroughCoordinatorAsync(
ExchangeFunctionName.FillOrder,
txData,
sendTxOpts,
[order],
order,
takerAssetFillAmount,
signature,
);
}
/**
* Attempts to fill a specific amount of an order. If the entire amount specified cannot be filled,
* the fill order is abandoned.
* @param order An object that conforms to the Order interface.
* @param takerAssetFillAmount The amount of the order (in taker asset baseUnits) that you wish to fill.
* @param signature Signature corresponding to the order.
* @param txData Transaction data. The `from` field should be the user Ethereum address who would like
* to fill these orders. Must be available via the Provider supplied at instantiation.
* @param sendTxOpts Optional arguments for sending the transaction.
* @return Transaction hash.
*/
@decorators.asyncZeroExErrorHandler
public async fillOrKillOrderAsync(
order: Order,
takerAssetFillAmount: BigNumber,
signature: string,
txData: TxData,
sendTxOpts: Partial<SendTransactionOpts> = { shouldValidate: true },
): Promise<string> {
assert.doesConformToSchema('order', order, schemas.orderSchema);
assert.isValidBaseUnitAmount('takerAssetFillAmount', takerAssetFillAmount);
return this._executeTxThroughCoordinatorAsync(
ExchangeFunctionName.FillOrKillOrder,
txData,
sendTxOpts,
[order],
order,
takerAssetFillAmount,
signature,
);
}
/**
* Batch version of fillOrderAsync. Executes multiple fills atomically in a single transaction.
* If any `feeRecipientAddress` in the batch is not registered to a coordinator server through the CoordinatorRegistryContract, the whole batch fails.
* @param orders An array of orders to fill.
* @param takerAssetFillAmounts The amounts of the orders (in taker asset baseUnits) that you wish to fill.
* @param signatures Signatures corresponding to the orders.
* @param txData Transaction data. The `from` field should be the user Ethereum address who would like
* to fill these orders. Must be available via the Provider supplied at instantiation.
* @param sendTxOpts Optional arguments for sending the transaction.
* @return Transaction hash.
*/
@decorators.asyncZeroExErrorHandler
public async batchFillOrdersAsync(
orders: Order[],
takerAssetFillAmounts: BigNumber[],
signatures: string[],
txData: TxData,
sendTxOpts?: Partial<SendTransactionOpts>,
): Promise<string> {
return this._batchFillAsync(
ExchangeFunctionName.BatchFillOrders,
orders,
takerAssetFillAmounts,
signatures,
txData,
sendTxOpts,
);
}
/**
* No throw version of batchFillOrdersAsync
* @param orders An array of orders to fill.
* @param takerAssetFillAmounts The amounts of the orders (in taker asset baseUnits) that you wish to fill.
* @param signatures Signatures corresponding to the orders.
* @param txData Transaction data. The `from` field should be the user Ethereum address who would like
* to fill these orders. Must be available via the Provider supplied at instantiation.
* @param sendTxOpts Optional arguments for sending the transaction.
* @return Transaction hash.
*/
public async batchFillOrdersNoThrowAsync(
orders: Order[],
takerAssetFillAmounts: BigNumber[],
signatures: string[],
txData: TxData,
sendTxOpts?: Partial<SendTransactionOpts>,
): Promise<string> {
return this._batchFillAsync(
ExchangeFunctionName.BatchFillOrdersNoThrow,
orders,
takerAssetFillAmounts,
signatures,
txData,
sendTxOpts,
);
}
/**
* Batch version of fillOrKillOrderAsync. Executes multiple fills atomically in a single transaction.
* @param orders An array of orders to fill.
* @param takerAssetFillAmounts The amounts of the orders (in taker asset baseUnits) that you wish to fill.
* @param signatures Signatures corresponding to the orders.
* @param txData Transaction data. The `from` field should be the user Ethereum address who would like
* to fill these orders. Must be available via the Provider supplied at instantiation.
* @param sendTxOpts Optional arguments for sending the transaction.
* @return Transaction hash.
*/
@decorators.asyncZeroExErrorHandler
public async batchFillOrKillOrdersAsync(
orders: Order[],
takerAssetFillAmounts: BigNumber[],
signatures: string[],
txData: TxData,
sendTxOpts?: Partial<SendTransactionOpts>,
): Promise<string> {
return this._batchFillAsync(
ExchangeFunctionName.BatchFillOrKillOrders,
orders,
takerAssetFillAmounts,
signatures,
txData,
sendTxOpts,
);
}
/**
* Executes multiple calls of fillOrder until total amount of makerAsset is bought by taker.
* If any fill reverts, the error is caught and ignored. Finally, reverts if < makerAssetFillAmount has been bought.
* NOTE: This function does not enforce that the makerAsset is the same for each order.
* @param orders Array of order specifications.
* @param makerAssetFillAmount Desired amount of makerAsset to buy.
* @param signatures Proofs that orders have been signed by makers.
* @param txData Transaction data. The `from` field should be the user Ethereum address who would like
* to fill these orders. Must be available via the Provider supplied at instantiation.
* @param sendTxOpts Optional arguments for sending the transaction.
* @return Transaction hash.
*/
@decorators.asyncZeroExErrorHandler
public async marketBuyOrdersFillOrKillAsync(
orders: Order[],
makerAssetFillAmount: BigNumber,
signatures: string[],
txData: TxData,
sendTxOpts: SendTransactionOpts = { shouldValidate: true },
): Promise<string> {
return this._marketBuySellOrdersAsync(
ExchangeFunctionName.MarketBuyOrdersFillOrKill,
orders,
makerAssetFillAmount,
signatures,
txData,
sendTxOpts,
);
}
/**
* No throw version of marketBuyOrdersFillOrKillAsync
* @param orders An array of orders to fill.
* @param makerAssetFillAmount Maker asset fill amount.
* @param signatures Signatures corresponding to the orders.
* @param txData Transaction data. The `from` field should be the user Ethereum address who would like
* to fill these orders. Must be available via the Provider supplied at instantiation.
* @param sendTxOpts Optional arguments for sending the transaction.
* @return Transaction hash.
*/
@decorators.asyncZeroExErrorHandler
public async marketBuyOrdersNoThrowAsync(
orders: Order[],
makerAssetFillAmount: BigNumber,
signatures: string[],
txData: TxData,
sendTxOpts: SendTransactionOpts = { shouldValidate: true },
): Promise<string> {
return this._marketBuySellOrdersAsync(
ExchangeFunctionName.MarketBuyOrdersNoThrow,
orders,
makerAssetFillAmount,
signatures,
txData,
sendTxOpts,
);
}
/**
* Executes multiple calls of fillOrder until total amount of takerAsset is sold by taker.
* If any fill reverts, the error is caught and ignored. Finally, reverts if < takerAssetFillAmount has been sold.
* NOTE: This function does not enforce that the takerAsset is the same for each order.
* @param orders Array of order specifications.
* @param takerAssetFillAmount Desired amount of takerAsset to sell.
* @param signatures Proofs that orders have been signed by makers.
* @param txData Transaction data. The `from` field should be the user Ethereum address who would like
* to fill these orders. Must be available via the Provider supplied at instantiation.
* @param sendTxOpts Optional arguments for sending the transaction.
* @return Transaction hash.
*/
@decorators.asyncZeroExErrorHandler
public async marketSellOrdersFillOrKillAsync(
orders: Order[],
takerAssetFillAmount: BigNumber,
signatures: string[],
txData: TxData,
sendTxOpts: SendTransactionOpts = { shouldValidate: true },
): Promise<string> {
return this._marketBuySellOrdersAsync(
ExchangeFunctionName.MarketSellOrdersFillOrKill,
orders,
takerAssetFillAmount,
signatures,
txData,
sendTxOpts,
);
}
/**
* No throw version of marketSellOrdersAsync
* @param orders An array of orders to fill.
* @param takerAssetFillAmount Taker asset fill amount.
* @param signatures Signatures corresponding to the orders.
* @param txData Transaction data. The `from` field should be the user Ethereum address who would like
* to fill these orders. Must be available via the Provider supplied at instantiation.
* @param sendTxOpts Optional arguments for sending the transaction.
* @return Transaction hash.
*/
@decorators.asyncZeroExErrorHandler
public async marketSellOrdersNoThrowAsync(
orders: Order[],
takerAssetFillAmount: BigNumber,
signatures: string[],
txData: TxData,
sendTxOpts: SendTransactionOpts = { shouldValidate: true },
): Promise<string> {
return this._marketBuySellOrdersAsync(
ExchangeFunctionName.MarketSellOrdersNoThrow,
orders,
takerAssetFillAmount,
signatures,
txData,
sendTxOpts,
);
}
/**
* Cancels an order on-chain by submitting an Ethereum transaction.
* @param order An object that conforms to the Order interface. The order you would like to cancel.
* @param txData Transaction data. The `from` field should be the maker's Ethereum address. Must be available
* via the Provider supplied at instantiation.
* @param sendTxOpts Optional arguments for sending the transaction.
* @return Transaction hash.
*/
@decorators.asyncZeroExErrorHandler
public async hardCancelOrderAsync(
order: Order,
txData: TxData,
sendTxOpts: SendTransactionOpts = { shouldValidate: true },
): Promise<string> {
assert.doesConformToSchema('order', order, schemas.orderSchema);
return this._executeTxThroughCoordinatorAsync(
ExchangeFunctionName.CancelOrder,
txData,
sendTxOpts,
[order],
order,
);
}
/**
* Batch version of hardCancelOrderAsync. Cancels orders on-chain by submitting an Ethereum transaction.
* Executes multiple cancels atomically in a single transaction.
* @param orders An array of orders to cancel.
* @param txData Transaction data. The `from` field should be the maker's Ethereum address. Must be available
* via the Provider supplied at instantiation.
* @param sendTxOpts Optional arguments for sending the transaction.
* @return Transaction hash.
*/
@decorators.asyncZeroExErrorHandler
public async batchHardCancelOrdersAsync(
orders: Order[],
txData: TxData,
sendTxOpts: SendTransactionOpts = { shouldValidate: true },
): Promise<string> {
assert.doesConformToSchema('orders', orders, schemas.ordersSchema);
return this._executeTxThroughCoordinatorAsync(
ExchangeFunctionName.BatchCancelOrders,
txData,
sendTxOpts,
orders,
orders,
);
}
/**
* Cancels orders on-chain by submitting an Ethereum transaction.
* Cancels all orders created by makerAddress with a salt less than or equal to the targetOrderEpoch
* and senderAddress equal to coordinator extension contract address.
* @param targetOrderEpoch Target order epoch.
* @param txData Transaction data. The `from` field should be the maker's Ethereum address. Must be available
* via the Provider supplied at instantiation.
* @param sendTxOpts Optional arguments for sending the transaction.
* @return Transaction hash.
*/
@decorators.asyncZeroExErrorHandler
public async hardCancelOrdersUpToAsync(
targetOrderEpoch: BigNumber,
txData: TxData,
sendTxOpts: SendTransactionOpts = { shouldValidate: true },
): Promise<string> {
assert.isBigNumber('targetOrderEpoch', targetOrderEpoch);
return this._executeTxThroughCoordinatorAsync(
ExchangeFunctionName.CancelOrdersUpTo,
txData,
sendTxOpts,
[],
targetOrderEpoch,
);
}
/**
* Soft cancel a given order.
* Soft cancels are recorded only on coordinator operator servers and do not involve an Ethereum transaction.
* See [soft cancels](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/coordinator-specification.md#soft-cancels).
* @param order An object that conforms to the Order or SignedOrder interface. The order you would like to cancel.
* @return CoordinatorServerCancellationResponse. See [Cancellation Response](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/coordinator-specification.md#response).
*/
@decorators.asyncZeroExErrorHandler
public async softCancelAsync(order: Order): Promise<CoordinatorServerCancellationResponse> {
assert.doesConformToSchema('order', order, schemas.orderSchema);
assert.isETHAddressHex('feeRecipientAddress', order.feeRecipientAddress);
assert.isSenderAddressAsync('makerAddress', order.makerAddress, this._web3Wrapper);
const data = this._exchangeInstance.cancelOrder(order).getABIEncodedTransactionData();
const transaction = await this._generateSignedZeroExTransactionAsync(data, order.makerAddress);
const endpoint = await this._getServerEndpointOrThrowAsync(order);
const response = await this._executeServerRequestAsync(transaction, order.makerAddress, endpoint);
if (response.isError) {
const approvedOrders = new Array();
const cancellations = new Array();
const errors = [
{
...response,
orders: [order],
},
];
throw new CoordinatorServerError(
CoordinatorServerErrorMsg.CancellationFailed,
approvedOrders,
cancellations,
errors,
);
} else {
return response.body as CoordinatorServerCancellationResponse;
}
}
/**
* Batch version of softCancelOrderAsync. Requests multiple soft cancels
* @param orders An array of orders to cancel.
* @return CoordinatorServerCancellationResponse. See [Cancellation Response](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/coordinator-specification.md#response).
*/
@decorators.asyncZeroExErrorHandler
public async batchSoftCancelAsync(orders: SignedOrder[]): Promise<CoordinatorServerCancellationResponse[]> {
assert.doesConformToSchema('orders', orders, schemas.ordersSchema);
const makerAddress = getMakerAddressOrThrow(orders);
assert.isSenderAddressAsync('makerAddress', makerAddress, this._web3Wrapper);
const data = this._exchangeInstance.batchCancelOrders(orders).getABIEncodedTransactionData();
const transaction = await this._generateSignedZeroExTransactionAsync(data, makerAddress);
// make server requests
const errorResponses: CoordinatorServerResponse[] = [];
const successResponses: CoordinatorServerCancellationResponse[] = [];
const serverEndpointsToOrders = await this._mapServerEndpointsToOrdersAsync(orders);
for (const endpoint of Object.keys(serverEndpointsToOrders)) {
const response = await this._executeServerRequestAsync(transaction, makerAddress, endpoint);
if (response.isError) {
errorResponses.push(response);
} else {
successResponses.push(response.body as CoordinatorServerCancellationResponse);
}
}
// if no errors
if (errorResponses.length === 0) {
return successResponses;
} else {
// lookup orders with errors
const errorsWithOrders = errorResponses.map(resp => {
const endpoint = resp.coordinatorOperator;
const _orders = serverEndpointsToOrders[endpoint];
return {
...resp,
orders: _orders,
};
});
const approvedOrders = new Array();
const cancellations = successResponses;
// return errors and approvals
throw new CoordinatorServerError(
CoordinatorServerErrorMsg.CancellationFailed,
approvedOrders,
cancellations,
errorsWithOrders,
);
}
}
/**
* Recovers the address of a signer given a hash and signature.
* @param hash Any 32 byte hash.
* @param signature Proof that the hash has been signed by signer.
* @returns Signer address.
*/
@decorators.asyncZeroExErrorHandler
public async getSignerAddressAsync(hash: string, signature: string): Promise<string> {
assert.isHexString('hash', hash);
assert.isHexString('signature', signature);
const signerAddress = await this._contractInstance.getSignerAddress(hash, signature).callAsync();
return signerAddress;
}
private async _marketBuySellOrdersAsync(
exchangeFn: ExchangeFunctionName,
orders: Order[],
assetFillAmount: BigNumber,
signatures: string[],
txData: TxData,
sendTxOpts: SendTransactionOpts = { shouldValidate: true },
): Promise<string> {
assert.doesConformToSchema('orders', orders, schemas.ordersSchema);
assert.isBigNumber('assetFillAmount', assetFillAmount);
return this._executeTxThroughCoordinatorAsync(
exchangeFn,
txData,
sendTxOpts,
orders,
orders,
assetFillAmount,
signatures,
);
}
private async _batchFillAsync(
exchangeFn: ExchangeFunctionName,
orders: Order[],
takerAssetFillAmounts: BigNumber[],
signatures: string[],
txData: TxData,
sendTxOpts: SendTransactionOpts = { shouldValidate: true },
): Promise<string> {
assert.doesConformToSchema('orders', orders, schemas.ordersSchema);
takerAssetFillAmounts.forEach(takerAssetFillAmount =>
assert.isValidBaseUnitAmount('takerAssetFillAmount', takerAssetFillAmount),
);
return this._executeTxThroughCoordinatorAsync(
exchangeFn,
txData,
sendTxOpts,
orders,
orders,
takerAssetFillAmounts,
signatures,
);
}
private async _executeTxThroughCoordinatorAsync(
exchangeFn: ExchangeFunctionName,
txData: TxData,
sendTxOpts: Partial<SendTransactionOpts>,
ordersNeedingApprovals: Order[],
...args: any[] // tslint:disable-line:trailing-comma
): Promise<string> {
assert.isETHAddressHex('takerAddress', txData.from);
await assert.isSenderAddressAsync('takerAddress', txData.from, this._web3Wrapper);
// get ABI encoded transaction data for the desired exchange method
const data = (this._exchangeInstance as any)[exchangeFn](...args).getABIEncodedTransactionData();
// generate and sign a ZeroExTransaction
const signedZrxTx = await this._generateSignedZeroExTransactionAsync(data, txData.from, txData.gasPrice);
// get approval signatures from registered coordinator operators
const approvalSignatures = await this._getApprovalsAsync(signedZrxTx, ordersNeedingApprovals, txData.from);
// execute the transaction through the Coordinator Contract
const txDataWithDefaults = {
...this._txDefaults,
...txData, // override defaults
};
const txHash = this._contractInstance
.executeTransaction(signedZrxTx, txData.from, signedZrxTx.signature, approvalSignatures)
.sendTransactionAsync(txDataWithDefaults, sendTxOpts);
return txHash;
}
private async _generateSignedZeroExTransactionAsync(
data: string,
signerAddress: string,
gasPrice?: BigNumber | string | number,
): Promise<SignedZeroExTransaction> {
const transaction: ZeroExTransaction = {
salt: generatePseudoRandomSalt(),
signerAddress,
data,
domain: {
verifyingContract: this.exchangeAddress,
chainId: await this._web3Wrapper.getChainIdAsync(),
},
expirationTimeSeconds: new BigNumber(
Math.floor(Date.now() / 1000) +
DEFAULT_APPROVAL_EXPIRATION_TIME_SECONDS -
DEFAULT_EXPIRATION_TIME_BUFFER_SECONDS,
),
gasPrice: gasPrice ? new BigNumber(gasPrice) : new BigNumber(1),
};
const signedZrxTx = await signatureUtils.ecSignTransactionAsync(
this._web3Wrapper.getProvider(),
transaction,
transaction.signerAddress,
);
return signedZrxTx;
}
private async _getApprovalsAsync(
transaction: SignedZeroExTransaction,
orders: Order[],
txOrigin: string,
): Promise<string[]> {
const coordinatorOrders = orders.filter(o => o.senderAddress === this.address);
if (coordinatorOrders.length === 0) {
return [];
}
const serverEndpointsToOrders = await this._mapServerEndpointsToOrdersAsync(coordinatorOrders);
// make server requests
const errorResponses: CoordinatorServerResponse[] = [];
const approvalResponses: CoordinatorServerResponse[] = [];
for (const endpoint of Object.keys(serverEndpointsToOrders)) {
const response = await this._executeServerRequestAsync(transaction, txOrigin, endpoint);
if (response.isError) {
errorResponses.push(response);
} else {
approvalResponses.push(response);
}
}
// if no errors
if (errorResponses.length === 0) {
// concatenate all approval responses
return approvalResponses.reduce(
(accumulator, response) =>
accumulator.concat((response.body as CoordinatorServerApprovalResponse).signatures),
[] as string[],
);
} else {
// format errors and approvals
// concatenate approvals
const notCoordinatorOrders = orders.filter(o => o.senderAddress !== this.address);
const approvedOrdersNested = approvalResponses.map(resp => {
const endpoint = resp.coordinatorOperator;
return serverEndpointsToOrders[endpoint];
});
const approvedOrders = flatten(approvedOrdersNested.concat(notCoordinatorOrders));
// lookup orders with errors
const errorsWithOrders = errorResponses.map(resp => {
const endpoint = resp.coordinatorOperator;
return {
...resp,
orders: serverEndpointsToOrders[endpoint],
};
});
// throw informative error
const cancellations = new Array();
throw new CoordinatorServerError(
CoordinatorServerErrorMsg.FillFailed,
approvedOrders,
cancellations,
errorsWithOrders,
);
}
}
private async _getServerEndpointOrThrowAsync(order: Order): Promise<string> {
const cached = this._feeRecipientToEndpoint[order.feeRecipientAddress];
const endpoint =
cached !== undefined
? cached
: await _fetchServerEndpointOrThrowAsync(order.feeRecipientAddress, this._registryInstance);
return endpoint;
async function _fetchServerEndpointOrThrowAsync(
feeRecipient: string,
registryInstance: CoordinatorRegistryContract,
): Promise<string> {
const coordinatorOperatorEndpoint = await registryInstance.getCoordinatorEndpoint(feeRecipient).callAsync();
if (coordinatorOperatorEndpoint === '' || coordinatorOperatorEndpoint === undefined) {
throw new Error(
`No Coordinator server endpoint found in Coordinator Registry for feeRecipientAddress: ${feeRecipient}. Registry contract address: [${
registryInstance.address
}] Order: [${JSON.stringify(order)}]`,
);
}
return coordinatorOperatorEndpoint;
}
}
private async _executeServerRequestAsync(
signedTransaction: SignedZeroExTransaction,
txOrigin: string,
endpoint: string,
): Promise<CoordinatorServerResponse> {
const requestPayload = {
signedTransaction,
txOrigin,
};
const response = await fetchAsync(`${endpoint}/v2/request_transaction?chainId=${this.chainId}`, {
body: JSON.stringify(requestPayload),
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=utf-8',
},
});
const isError = response.status !== HttpStatus.OK;
const isValidationError = response.status === HttpStatus.BAD_REQUEST;
const json = isError && !isValidationError ? undefined : await response.json();
const result = {
isError,
status: response.status,
body: isError ? undefined : json,
error: isError ? json : undefined,
request: requestPayload,
coordinatorOperator: endpoint,
};
return result;
}
private async _mapServerEndpointsToOrdersAsync(
coordinatorOrders: Order[],
): Promise<{ [endpoint: string]: Order[] }> {
const groupByFeeRecipient: { [feeRecipient: string]: Order[] } = {};
for (const order of coordinatorOrders) {
const feeRecipient = order.feeRecipientAddress;
if (groupByFeeRecipient[feeRecipient] === undefined) {
groupByFeeRecipient[feeRecipient] = [] as Order[];
}
groupByFeeRecipient[feeRecipient].push(order);
}
const serverEndpointsToOrders: { [endpoint: string]: Order[] } = {};
for (const orders of Object.values(groupByFeeRecipient)) {
const endpoint = await this._getServerEndpointOrThrowAsync(orders[0]);
if (serverEndpointsToOrders[endpoint] === undefined) {
serverEndpointsToOrders[endpoint] = [];
}
serverEndpointsToOrders[endpoint] = serverEndpointsToOrders[endpoint].concat(orders);
}
return serverEndpointsToOrders;
}
}
function getMakerAddressOrThrow(orders: Array<Order | SignedOrder>): string {
const uniqueMakerAddresses = new Set(orders.map(o => o.makerAddress));
if (uniqueMakerAddresses.size > 1) {
throw new Error(`All orders in a batch must have the same makerAddress`);
}
return orders[0].makerAddress;
}
// tslint:disable:max-file-line-count

View File

@@ -4,7 +4,6 @@ import { Schema } from '@0x/json-schemas'; // tslint:disable-line:no-unused-vari
import { Order } from '@0x/types'; // tslint:disable-line:no-unused-variable
import { BigNumber } from '@0x/utils'; // tslint:disable-line:no-unused-variable
import { Web3Wrapper } from '@0x/web3-wrapper';
import * as _ from 'lodash';
export const assert = {
...sharedAssert,

View File

@@ -2,10 +2,6 @@ import { Order, SignedOrder, SignedZeroExTransaction } from '@0x/types';
import { BigNumber } from '@0x/utils';
export interface CoordinatorServerApprovalResponse {
signatures: string[];
expirationTimeSeconds: BigNumber[];
}
export interface CoordinatorServerApprovalRawResponse {
signatures: string[];
expirationTimeSeconds: BigNumber;
}
@@ -24,7 +20,7 @@ export interface CoordinatorOutstandingFillSignatures {
export interface CoordinatorServerResponse {
isError: boolean;
status: number;
body?: CoordinatorServerCancellationResponse | CoordinatorServerApprovalRawResponse;
body?: CoordinatorServerCancellationResponse | CoordinatorServerApprovalResponse;
error?: any;
request: CoordinatorServerRequest;
coordinatorOperator: string;
@@ -38,12 +34,12 @@ export interface CoordinatorServerRequest {
export class CoordinatorServerError extends Error {
public message: CoordinatorServerErrorMsg;
public approvedOrders?: SignedOrder[] = [];
public approvedOrders?: Order[] = [];
public cancellations?: CoordinatorServerCancellationResponse[] = [];
public errors: CoordinatorServerResponse[];
constructor(
message: CoordinatorServerErrorMsg,
approvedOrders: SignedOrder[],
approvedOrders: Order[],
cancellations: CoordinatorServerCancellationResponse[],
errors: CoordinatorServerResponse[],
) {

View File

@@ -1,7 +1,6 @@
import { hexConcat } from '@0x/contracts-test-utils';
import { eip712Utils } from '@0x/order-utils';
import { SignedZeroExTransaction } from '@0x/types';
import { signTypedDataUtils } from '@0x/utils';
import { hexUtils, signTypedDataUtils } from '@0x/utils';
export const hashUtils = {
async getApprovalHashBufferAsync(
@@ -22,7 +21,9 @@ export const hashUtils = {
verifyingContract: string,
txOrigin: string,
): Promise<string> {
const hashHex = hexConcat(await hashUtils.getApprovalHashBufferAsync(transaction, verifyingContract, txOrigin));
const hashHex = hexUtils.concat(
await hashUtils.getApprovalHashBufferAsync(transaction, verifyingContract, txOrigin),
);
return hashHex;
},
};

View File

@@ -7,10 +7,19 @@ export {
LibCoordinatorRichErrorsContract,
LibEIP712CoordinatorDomainContract,
} from './wrappers';
export import CoordinatorRevertErrors = require('./revert_errors');
export { CoordinatorRevertErrors } from '@0x/utils';
export { CoordinatorServerCancellationResponse } from './client/index';
export { ApprovalFactory } from './approval_factory';
export { SignedCoordinatorApproval } from './types';
export { SignatureType, SignedZeroExTransaction, EIP712DomainWithDefaultSchema } from '@0x/types';
export {
Order,
SignedOrder,
SignatureType,
SignedZeroExTransaction,
EIP712DomainWithDefaultSchema,
ZeroExTransaction,
} from '@0x/types';
export { AwaitTransactionSuccessOpts, SendTransactionOpts } from '@0x/base-contract';
export {
ContractArtifact,
ContractChains,
@@ -38,4 +47,20 @@ export {
ConstructorStateMutability,
TupleDataItem,
StateMutability,
SupportedProvider,
TxData,
TxDataPayable,
Web3JsProvider,
GanacheProvider,
EIP1193Provider,
ZeroExProvider,
EIP1193Event,
JSONRPCRequestPayload,
JSONRPCErrorCallback,
Web3JsV1Provider,
Web3JsV2Provider,
Web3JsV3Provider,
JSONRPCResponsePayload,
JSONRPCResponseError,
} from 'ethereum-types';
export { CoordinatorClient, CoordinatorServerErrorMsg } from './client/index';

View File

@@ -1,4 +1,5 @@
import { SignedZeroExTransaction } from '@0x/types';
import { BigNumber } from '@0x/utils';
export interface CoordinatorApproval {
transaction: SignedZeroExTransaction;
@@ -8,3 +9,9 @@ export interface CoordinatorApproval {
export interface SignedCoordinatorApproval extends CoordinatorApproval {
signature: string;
}
export interface CoordinatorTransaction {
salt: BigNumber;
signerAddress: string;
data: string;
}

View File

@@ -4,17 +4,13 @@ import {
constants,
ExchangeFunctionName,
expect,
hexConcat,
hexSlice,
randomAddress,
TransactionFactory,
transactionHashUtils,
} from '@0x/contracts-test-utils';
import { LibBytesRevertErrors } from '@0x/contracts-utils';
import { SignatureType, SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
import CoordinatorRevertErrors = require('../src/revert_errors');
import { BigNumber, CoordinatorRevertErrors, hexUtils } from '@0x/utils';
import { ApprovalFactory } from '../src/approval_factory';
@@ -91,8 +87,8 @@ blockchainTests.resets('Mixins tests', env => {
it('should revert with with the Illegal signature type', async () => {
const data = constants.NULL_BYTES;
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
transaction.signature = hexConcat(
hexSlice(transaction.signature, 0, transaction.signature.length - 1),
transaction.signature = hexUtils.concat(
hexUtils.slice(transaction.signature, 0, transaction.signature.length - 1),
SignatureType.Illegal,
);
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
@@ -107,7 +103,7 @@ blockchainTests.resets('Mixins tests', env => {
it('should revert with with the Invalid signature type', async () => {
const data = constants.NULL_BYTES;
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
transaction.signature = hexConcat(SignatureType.Invalid);
transaction.signature = hexUtils.concat(SignatureType.Invalid);
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
expect(mixins.getSignerAddress(transactionHash, transaction.signature).callAsync()).to.revertWith(
new CoordinatorRevertErrors.SignatureError(
@@ -120,8 +116,8 @@ blockchainTests.resets('Mixins tests', env => {
it('should revert with with a signature type that equals `NSignatureTypes`', async () => {
const data = constants.NULL_BYTES;
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
transaction.signature = hexConcat(
hexSlice(transaction.signature, 0, transaction.signature.length - 1),
transaction.signature = hexUtils.concat(
hexUtils.slice(transaction.signature, 0, transaction.signature.length - 1),
SignatureType.NSignatureTypes,
);
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
@@ -136,8 +132,8 @@ blockchainTests.resets('Mixins tests', env => {
it("should revert with with a signature type that isn't supported", async () => {
const data = constants.NULL_BYTES;
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
transaction.signature = hexConcat(
hexSlice(transaction.signature, 0, transaction.signature.length - 1),
transaction.signature = hexUtils.concat(
hexUtils.slice(transaction.signature, 0, transaction.signature.length - 1),
SignatureType.Wallet,
);
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
@@ -298,10 +294,10 @@ blockchainTests.resets('Mixins tests', env => {
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
const approval = await approvalFactory1.newSignedApprovalAsync(transaction, transactionSignerAddress);
const signature = hexConcat(
hexSlice(approval.signature, 0, 2),
const signature = hexUtils.concat(
hexUtils.slice(approval.signature, 0, 2),
'0xFFFFFFFF',
hexSlice(approval.signature, 6),
hexUtils.slice(approval.signature, 6),
);
const tx = mixins
.assertValidCoordinatorApprovals(transaction, transactionSignerAddress, transaction.signature, [
@@ -434,10 +430,10 @@ blockchainTests.resets('Mixins tests', env => {
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
const approval = await approvalFactory1.newSignedApprovalAsync(transaction, transactionSignerAddress);
const signature = hexConcat(
hexSlice(approval.signature, 0, 2),
const signature = hexUtils.concat(
hexUtils.slice(approval.signature, 0, 2),
'0xFFFFFFFF',
hexSlice(approval.signature, 6),
hexUtils.slice(approval.signature, 6),
);
const tx = mixins
.assertValidCoordinatorApprovals(transaction, transactionSignerAddress, transaction.signature, [
@@ -456,10 +452,10 @@ blockchainTests.resets('Mixins tests', env => {
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
const approval1 = await approvalFactory1.newSignedApprovalAsync(transaction, transactionSignerAddress);
const approval2 = await approvalFactory2.newSignedApprovalAsync(transaction, transactionSignerAddress);
const approvalSignature2 = hexConcat(
hexSlice(approval2.signature, 0, 2),
const approvalSignature2 = hexUtils.concat(
hexUtils.slice(approval2.signature, 0, 2),
'0xFFFFFFFF',
hexSlice(approval2.signature, 6),
hexUtils.slice(approval2.signature, 6),
);
const tx = mixins
.assertValidCoordinatorApprovals(transaction, transactionSignerAddress, transaction.signature, [
@@ -478,10 +474,10 @@ blockchainTests.resets('Mixins tests', env => {
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
const approval2 = await approvalFactory2.newSignedApprovalAsync(transaction, transactionSignerAddress);
const approvalSignature2 = hexConcat(
hexSlice(approval2.signature, 0, 2),
const approvalSignature2 = hexUtils.concat(
hexUtils.slice(approval2.signature, 0, 2),
'0xFFFFFFFF',
hexSlice(approval2.signature, 6),
hexUtils.slice(approval2.signature, 6),
);
const tx = mixins
.assertValidCoordinatorApprovals(transaction, approvalSignerAddress1, transaction.signature, [

View File

@@ -1,4 +1,64 @@
[
{
"timestamp": 1575931811,
"version": "1.0.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "1.0.0",
"changes": [
{
"note": "Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils",
"pr": 2330
},
{
"note": "Add new method getOrderHash() to DevUtils contract",
"pr": 2321
},
{
"note": "Add new method getTransactionHash() to DevUtils contract",
"pr": 2321
},
{
"note": "Add `encodeStaticCallAssetData` and `decodeStaticCallAssetData` in LibAssetData",
"pr": 2034
},
{
"note": "Add `revertIfInvalidAssetData` in LibAssetData",
"pr": 2034
},
{
"note": "Use built in selectors instead of hard coded constants",
"pr": 2055
},
{
"note": "Compile and export all contracts, artifacts, and wrappers by default",
"pr": 2055
},
{
"note": "Add `marketBuy/SellOrdersNoThrow` and `marketBuy/SellOrdersFillOrKill` to `LibTransactionDecoder`.",
"pr": 2075
},
{
"note": "`run_mocha` package script runs with `UNLIMITED_CONTRACT_SIZE=true` environment variable.",
"pr": 2075
}
],
"timestamp": 1575296764
},
{
"version": "0.1.0-beta.4",
"changes": [
{
"note": "Dependencies updated"
}
],
"timestamp": 1575290197
},
{
"version": "0.1.0-beta.3",
"changes": [

View File

@@ -5,6 +5,26 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v1.0.1 - _December 9, 2019_
* Dependencies updated
## v1.0.0 - _December 2, 2019_
* Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330)
* Add new method getOrderHash() to DevUtils contract (#2321)
* Add new method getTransactionHash() to DevUtils contract (#2321)
* Add `encodeStaticCallAssetData` and `decodeStaticCallAssetData` in LibAssetData (#2034)
* Add `revertIfInvalidAssetData` in LibAssetData (#2034)
* Use built in selectors instead of hard coded constants (#2055)
* Compile and export all contracts, artifacts, and wrappers by default (#2055)
* Add `marketBuy/SellOrdersNoThrow` and `marketBuy/SellOrdersFillOrKill` to `LibTransactionDecoder`. (#2075)
* `run_mocha` package script runs with `UNLIMITED_CONTRACT_SIZE=true` environment variable. (#2075)
## v0.1.0-beta.4 - _December 2, 2019_
* Dependencies updated
## v0.1.0-beta.3 - _November 20, 2019_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-dev-utils",
"version": "0.1.0-beta.3",
"version": "1.0.1",
"engines": {
"node": ">=6.12"
},
@@ -41,13 +41,14 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/dev-utils/README.md",
"devDependencies": {
"@0x/abi-gen": "^4.4.0-beta.3",
"@0x/assert": "^2.2.0-beta.2",
"@0x/contracts-gen": "^1.1.0-beta.3",
"@0x/sol-compiler": "^3.2.0-beta.3",
"@0x/abi-gen": "^5.0.1",
"@0x/assert": "^3.0.1",
"@0x/contracts-gen": "^2.0.1",
"@0x/sol-compiler": "^4.0.1",
"@0x/ts-doc-gen": "^0.0.22",
"@0x/tslint-config": "^3.1.0-beta.2",
"@0x/tslint-config": "^4.0.0",
"@types/node": "*",
"ethereum-types": "^3.0.0",
"ethers": "~4.0.4",
"npm-run-all": "^4.1.2",
"shx": "^0.2.2",
@@ -58,8 +59,7 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^5.5.0-beta.3",
"ethereum-types": "^2.2.0-beta.2"
"@0x/base-contract": "^6.0.1"
},
"publishConfig": {
"access": "public"

View File

@@ -1,4 +1,40 @@
[
{
"timestamp": 1575931811,
"version": "2.0.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "2.0.0",
"changes": [
{
"note": "Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils",
"pr": 2330
},
{
"note": "Add `mintKnownFungibleTokensAsync()`, `isNonFungibleItemAsync()`, `isFungibleItemAsync()`, `getOwnerOfAsync()`, `getBalanceAsync()` to `Erc1155Wrapper`.",
"pr": 1819
},
{
"note": "Replaced `SafeMath` with `LibSafeMath`",
"pr": 2254
}
],
"timestamp": 1575296764
},
{
"version": "1.2.0-beta.4",
"changes": [
{
"note": "Dependencies updated"
}
],
"timestamp": 1575290197
},
{
"version": "1.2.0-beta.3",
"changes": [

View File

@@ -5,6 +5,20 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v2.0.1 - _December 9, 2019_
* Dependencies updated
## v2.0.0 - _December 2, 2019_
* Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330)
* Add `mintKnownFungibleTokensAsync()`, `isNonFungibleItemAsync()`, `isFungibleItemAsync()`, `getOwnerOfAsync()`, `getBalanceAsync()` to `Erc1155Wrapper`. (#1819)
* Replaced `SafeMath` with `LibSafeMath` (#2254)
## v1.2.0-beta.4 - _December 2, 2019_
* Dependencies updated
## v1.2.0-beta.3 - _November 20, 2019_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-erc1155",
"version": "1.2.0-beta.3",
"version": "2.0.1",
"engines": {
"node": ">=6.12"
},
@@ -52,14 +52,15 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md",
"devDependencies": {
"@0x/abi-gen": "^4.4.0-beta.3",
"@0x/contracts-gen": "^1.1.0-beta.3",
"@0x/contracts-utils": "^3.3.0-beta.3",
"@0x/dev-utils": "^2.4.0-beta.3",
"@0x/sol-compiler": "^3.2.0-beta.3",
"@0x/abi-gen": "^5.0.1",
"@0x/contracts-gen": "^2.0.1",
"@0x/contracts-utils": "^4.0.1",
"@0x/dev-utils": "^3.0.1",
"@0x/sol-compiler": "^4.0.1",
"@0x/ts-doc-gen": "^0.0.22",
"@0x/tslint-config": "^3.1.0-beta.2",
"@0x/types": "^2.5.0-beta.2",
"@0x/tslint-config": "^4.0.0",
"@0x/types": "^3.1.0",
"@0x/typescript-typings": "^5.0.0",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "*",
@@ -67,6 +68,7 @@
"chai-as-promised": "^7.1.0",
"chai-bignumber": "^3.0.0",
"dirty-chai": "^2.0.1",
"ethereum-types": "^3.0.0",
"make-promises-safe": "^1.1.0",
"mocha": "^6.2.0",
"npm-run-all": "^4.1.2",
@@ -78,12 +80,10 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^5.5.0-beta.3",
"@0x/contracts-test-utils": "^3.2.0-beta.3",
"@0x/typescript-typings": "^4.4.0-beta.2",
"@0x/utils": "^4.6.0-beta.2",
"@0x/web3-wrapper": "^6.1.0-beta.2",
"ethereum-types": "^2.2.0-beta.2",
"@0x/base-contract": "^6.0.1",
"@0x/contracts-test-utils": "^5.0.0",
"@0x/utils": "^5.1.0",
"@0x/web3-wrapper": "^7.0.1",
"lodash": "^4.17.11"
},
"publishConfig": {

View File

@@ -1,27 +1,16 @@
import { LogDecoder } from '@0x/contracts-test-utils';
import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import * as chai from 'chai';
import { LogWithDecodedArgs, Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
import * as _ from 'lodash';
import { ERC1155MintableContract, ERC1155TransferSingleEventArgs } from './wrappers';
import { artifacts } from './artifacts';
const expect = chai.expect;
export class Erc1155Wrapper {
private readonly _erc1155Contract: ERC1155MintableContract;
private readonly _web3Wrapper: Web3Wrapper;
private readonly _contractOwner: string;
private readonly _logDecoder: LogDecoder;
constructor(contractInstance: ERC1155MintableContract, provider: Provider, contractOwner: string) {
this._erc1155Contract = contractInstance;
this._web3Wrapper = new Web3Wrapper(provider);
this._contractOwner = contractOwner;
this._logDecoder = new LogDecoder(this._web3Wrapper, artifacts);
}
public getContract(): ERC1155MintableContract {
return this._erc1155Contract;
@@ -40,11 +29,11 @@ export class Erc1155Wrapper {
): Promise<TransactionReceiptWithDecodedLogs> {
const spender = delegatedSpender === undefined ? from : delegatedSpender;
const callbackDataHex = callbackData === undefined ? '0x' : callbackData;
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(
await this._erc1155Contract.safeTransferFrom(from, to, token, value, callbackDataHex).sendTransactionAsync({
const tx = await this._erc1155Contract
.safeTransferFrom(from, to, token, value, callbackDataHex)
.awaitTransactionSuccessAsync({
from: spender,
}),
);
});
return tx;
}
public async safeBatchTransferFromAsync(
@@ -57,11 +46,9 @@ export class Erc1155Wrapper {
): Promise<TransactionReceiptWithDecodedLogs> {
const spender = delegatedSpender === undefined ? from : delegatedSpender;
const callbackDataHex = callbackData === undefined ? '0x' : callbackData;
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(
await this._erc1155Contract
.safeBatchTransferFrom(from, to, tokens, values, callbackDataHex)
.sendTransactionAsync({ from: spender }),
);
const tx = await this._erc1155Contract
.safeBatchTransferFrom(from, to, tokens, values, callbackDataHex)
.awaitTransactionSuccessAsync({ from: spender });
return tx;
}
public async mintFungibleTokensAsync(
@@ -70,11 +57,9 @@ export class Erc1155Wrapper {
): Promise<BigNumber> {
const tokenUri = 'dummyFungibleToken';
const tokenIsNonFungible = false;
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(
await this._erc1155Contract.create(tokenUri, tokenIsNonFungible).sendTransactionAsync({
from: this._contractOwner,
}),
);
const tx = await this._erc1155Contract.create(tokenUri, tokenIsNonFungible).awaitTransactionSuccessAsync({
from: this._contractOwner,
});
// tslint:disable-next-line no-unnecessary-type-assertion
const createFungibleTokenLog = tx.logs[0] as LogWithDecodedArgs<ERC1155TransferSingleEventArgs>;
const tokenId = createFungibleTokenLog.args.id;
@@ -99,11 +84,9 @@ export class Erc1155Wrapper {
public async mintNonFungibleTokensAsync(beneficiaries: string[]): Promise<[BigNumber, BigNumber[]]> {
const tokenUri = 'dummyNonFungibleToken';
const tokenIsNonFungible = true;
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(
await this._erc1155Contract.create(tokenUri, tokenIsNonFungible).sendTransactionAsync({
from: this._contractOwner,
}),
);
const tx = await this._erc1155Contract.create(tokenUri, tokenIsNonFungible).awaitTransactionSuccessAsync({
from: this._contractOwner,
});
// tslint:disable-next-line no-unnecessary-type-assertion
const createFungibleTokenLog = tx.logs[0] as LogWithDecodedArgs<ERC1155TransferSingleEventArgs>;
const token = createFungibleTokenLog.args.id;
@@ -125,11 +108,9 @@ export class Erc1155Wrapper {
beneficiary: string,
isApproved: boolean,
): Promise<TransactionReceiptWithDecodedLogs> {
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(
await this._erc1155Contract.setApprovalForAll(beneficiary, isApproved).sendTransactionAsync({
from: owner,
}),
);
const tx = await this._erc1155Contract.setApprovalForAll(beneficiary, isApproved).awaitTransactionSuccessAsync({
from: owner,
});
return tx;
}
public async isApprovedForAllAsync(owner: string, beneficiary: string): Promise<boolean> {
@@ -151,7 +132,9 @@ export class Erc1155Wrapper {
});
const balances = await this.getBalancesAsync(ownersExtended, tokensExtended);
_.each(balances, (balance: BigNumber, i: number) => {
expect(balance, `${ownersExtended[i]}${tokensExtended[i]}`).to.be.bignumber.equal(expectedBalances[i]);
if (!balance.isEqualTo(expectedBalances[i])) {
throw new Error(`${ownersExtended[i]}${tokensExtended[i]} balance not equal ${expectedBalances[i]}`);
}
});
}
public async isNonFungibleItemAsync(tokenId: BigNumber): Promise<boolean> {

View File

@@ -0,0 +1,10 @@
# Blacklist all files
.*
*
# Whitelist lib
!lib/**/*
# Whitelist Solidity contracts
!contracts/src/**/*
# Blacklist tests in lib
/lib/test/*
# Package specific ignore

View File

@@ -0,0 +1,39 @@
[
{
"timestamp": 1575931811,
"version": "1.0.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "1.0.0",
"changes": [
{
"note": "Created package.",
"pr": 2344
}
],
"timestamp": 1575296764
},
{
"version": "1.0.0-beta.2",
"changes": [
{
"note": "Dependencies updated"
}
],
"timestamp": 1575290197
},
{
"version": "1.0.0-beta.1",
"changes": [
{
"note": "Created package.",
"pr": 2344
}
]
}
]

View File

@@ -0,0 +1,22 @@
<!--
changelogUtils.file is auto-generated using the monorepo-scripts package. Don't edit directly.
Edit the package's CHANGELOG.json file only.
-->
CHANGELOG
## v1.0.1 - _December 9, 2019_
* Dependencies updated
## v1.0.0 - _December 2, 2019_
* Created package. (#2344)
## v1.0.0-beta.2 - _December 2, 2019_
* Dependencies updated
## v1.0.0-beta.1 - _Invalid date_
* Created package. (#2344)

View File

@@ -0,0 +1 @@
[]

View File

@@ -0,0 +1,67 @@
## ERC20BridgeSampler
This package contains contracts used in DEX aggregation.
This is an MVP implementation, which agnostically samples DEXes for off-chain sorting and order generation. It is entirely read-only and never not touches any funds.
## Installation
**Install**
```bash
npm install @0x/contracts-erc20-bridge-sampler --save
```
## Contributing
We strongly recommend that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository.
For proposals regarding the 0x protocol's smart contract architecture, message format, or additional functionality, go to the [0x Improvement Proposals (ZEIPs)](https://github.com/0xProject/ZEIPs) repository and follow the contribution guidelines provided therein.
Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started.
### Install Dependencies
If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them:
```bash
yarn config set workspaces-experimental true
```
Then install dependencies
```bash
yarn install
```
### Build
To build this package and all other monorepo packages that it depends on, run the following from the monorepo root directory:
```bash
PKG=@0x/contracts-erc20-bridge-sampler yarn build
```
Or continuously rebuild on change:
```bash
PKG=@0x/contracts-erc20-bridge-sampler yarn watch
```
### Clean
```bash
yarn clean
```
### Lint
```bash
yarn lint
```
### Run Tests
```bash
yarn test
```

View File

@@ -0,0 +1,26 @@
{
"artifactsDir": "./test/generated-artifacts",
"contractsDir": "./contracts",
"useDockerisedSolc": false,
"isOfflineMode": false,
"compilerSettings": {
"evmVersion": "constantinople",
"optimizer": {
"enabled": true,
"runs": 1000000,
"details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
},
"outputSelection": {
"*": {
"*": [
"abi",
"devdoc",
"evm.bytecode.object",
"evm.bytecode.sourceMap",
"evm.deployedBytecode.object",
"evm.deployedBytecode.sourceMap"
]
}
}
}
}

View File

@@ -0,0 +1,92 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IUniswapExchangeFactory.sol";
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
import "./IEth2Dai.sol";
import "./IKyberNetwork.sol";
contract DeploymentConstants {
/// @dev Address of the 0x Exchange contract.
address constant public EXCHANGE_ADDRESS = 0x080bf510FCbF18b91105470639e9561022937712;
/// @dev Address of the Eth2Dai MatchingMarket contract.
address constant public ETH2DAI_ADDRESS = 0x39755357759cE0d7f32dC8dC45414CCa409AE24e;
/// @dev Address of the UniswapExchangeFactory contract.
address constant public UNISWAP_EXCHANGE_FACTORY_ADDRESS = 0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95;
/// @dev Address of the KyberNeworkProxy contract.
address constant public KYBER_NETWORK_PROXY_ADDRESS = 0x818E6FECD516Ecc3849DAf6845e3EC868087B755;
/// @dev Address of the WETH contract.
address constant public WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
/// @dev Kyber ETH pseudo-address.
address constant public KYBER_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
/// @dev An overridable way to retrieve the 0x Exchange contract.
/// @return zeroex The 0x Exchange contract.
function _getExchangeContract()
internal
view
returns (IExchange zeroex)
{
return IExchange(EXCHANGE_ADDRESS);
}
/// @dev An overridable way to retrieve the Eth2Dai exchange contract.
/// @return eth2dai The Eth2Dai exchange contract.
function _getEth2DaiContract()
internal
view
returns (IEth2Dai eth2dai)
{
return IEth2Dai(ETH2DAI_ADDRESS);
}
/// @dev An overridable way to retrieve the Uniswap exchange factory contract.
/// @return uniswap The UniswapExchangeFactory contract.
function _getUniswapExchangeFactoryContract()
internal
view
returns (IUniswapExchangeFactory uniswap)
{
return IUniswapExchangeFactory(UNISWAP_EXCHANGE_FACTORY_ADDRESS);
}
/// @dev An overridable way to retrieve the Kyber network proxy contract.
/// @return kyber The KyberNeworkProxy contract.
function _getKyberNetworkContract()
internal
view
returns (IKyberNetwork kyber)
{
return IKyberNetwork(KYBER_NETWORK_PROXY_ADDRESS);
}
/// @dev An overridable way to retrieve the WETH contract address.
/// @return weth The WETH contract address.
function _getWETHAddress()
internal
view
returns (address weth)
{
return WETH_ADDRESS;
}
}

View File

@@ -0,0 +1,451 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IUniswapExchangeFactory.sol";
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "./IERC20BridgeSampler.sol";
import "./IEth2Dai.sol";
import "./IKyberNetwork.sol";
import "./IUniswapExchangeQuotes.sol";
import "./DeploymentConstants.sol";
contract ERC20BridgeSampler is
IERC20BridgeSampler,
DeploymentConstants
{
bytes4 constant internal ERC20_PROXY_ID = 0xf47261b0; // bytes4(keccak256("ERC20Token(address)"));
/// @dev Query native orders and sample sell quotes on multiple DEXes at once.
/// @param orders Native orders to query.
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
/// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return orderInfos `OrderInfo`s for each order in `orders`.
/// @return makerTokenAmountsBySource Maker amounts bought for each source at
/// each taker token amount. First indexed by source index, then sample
/// index.
function queryOrdersAndSampleSells(
LibOrder.Order[] memory orders,
address[] memory sources,
uint256[] memory takerTokenAmounts
)
public
view
returns (
LibOrder.OrderInfo[] memory orderInfos,
uint256[][] memory makerTokenAmountsBySource
)
{
require(orders.length != 0, "EMPTY_ORDERS");
orderInfos = queryOrders(orders);
makerTokenAmountsBySource = sampleSells(
sources,
_assetDataToTokenAddress(orders[0].takerAssetData),
_assetDataToTokenAddress(orders[0].makerAssetData),
takerTokenAmounts
);
}
/// @dev Query native orders and sample buy quotes on multiple DEXes at once.
/// @param orders Native orders to query.
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
/// @param makerTokenAmounts Maker token buy amount for each sample.
/// @return orderInfos `OrderInfo`s for each order in `orders`.
/// @return takerTokenAmountsBySource Taker amounts sold for each source at
/// each maker token amount. First indexed by source index, then sample
/// index.
function queryOrdersAndSampleBuys(
LibOrder.Order[] memory orders,
address[] memory sources,
uint256[] memory makerTokenAmounts
)
public
view
returns (
LibOrder.OrderInfo[] memory orderInfos,
uint256[][] memory makerTokenAmountsBySource
)
{
require(orders.length != 0, "EMPTY_ORDERS");
orderInfos = queryOrders(orders);
makerTokenAmountsBySource = sampleBuys(
sources,
_assetDataToTokenAddress(orders[0].takerAssetData),
_assetDataToTokenAddress(orders[0].makerAssetData),
makerTokenAmounts
);
}
/// @dev Queries the status of several native orders.
/// @param orders Native orders to query.
/// @return orderInfos Order info for each respective order.
function queryOrders(LibOrder.Order[] memory orders)
public
view
returns (LibOrder.OrderInfo[] memory orderInfos)
{
uint256 numOrders = orders.length;
orderInfos = new LibOrder.OrderInfo[](numOrders);
for (uint256 i = 0; i < numOrders; i++) {
orderInfos[i] = _getExchangeContract().getOrderInfo(orders[i]);
}
}
/// @dev Sample sell quotes on multiple DEXes at once.
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return makerTokenAmountsBySource Maker amounts bought for each source at
/// each taker token amount. First indexed by source index, then sample
/// index.
function sampleSells(
address[] memory sources,
address takerToken,
address makerToken,
uint256[] memory takerTokenAmounts
)
public
view
returns (uint256[][] memory makerTokenAmountsBySource)
{
uint256 numSources = sources.length;
makerTokenAmountsBySource = new uint256[][](numSources);
for (uint256 i = 0; i < numSources; i++) {
makerTokenAmountsBySource[i] = _sampleSellSource(
sources[i],
takerToken,
makerToken,
takerTokenAmounts
);
}
}
/// @dev Query native orders and sample buy quotes on multiple DEXes at once.
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param makerTokenAmounts Maker token buy amount for each sample.
/// @return takerTokenAmountsBySource Taker amounts sold for each source at
/// each maker token amount. First indexed by source index, then sample
/// index.
function sampleBuys(
address[] memory sources,
address takerToken,
address makerToken,
uint256[] memory makerTokenAmounts
)
public
view
returns (uint256[][] memory takerTokenAmountsBySource)
{
uint256 numSources = sources.length;
takerTokenAmountsBySource = new uint256[][](numSources);
for (uint256 i = 0; i < numSources; i++) {
takerTokenAmountsBySource[i] = _sampleBuySource(
sources[i],
takerToken,
makerToken,
makerTokenAmounts
);
}
}
/// @dev Sample sell quotes from Kyber.
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return makerTokenAmounts Maker amounts bought at each taker token
/// amount.
function sampleSellsFromKyberNetwork(
address takerToken,
address makerToken,
uint256[] memory takerTokenAmounts
)
public
view
returns (uint256[] memory makerTokenAmounts)
{
_assertValidPair(makerToken, takerToken);
address _takerToken = takerToken == _getWETHAddress() ? KYBER_ETH_ADDRESS : takerToken;
address _makerToken = makerToken == _getWETHAddress() ? KYBER_ETH_ADDRESS : makerToken;
uint256 takerTokenDecimals = _getTokenDecimals(takerToken);
uint256 makerTokenDecimals = _getTokenDecimals(makerToken);
uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples);
for (uint256 i = 0; i < numSamples; i++) {
(uint256 rate,) = _getKyberNetworkContract().getExpectedRate(
_takerToken,
_makerToken,
takerTokenAmounts[i]
);
makerTokenAmounts[i] =
rate *
takerTokenAmounts[i] *
10 ** makerTokenDecimals /
10 ** takerTokenDecimals /
10 ** 18;
}
}
/// @dev Sample sell quotes from Eth2Dai/Oasis.
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return makerTokenAmounts Maker amounts bought at each taker token
/// amount.
function sampleSellsFromEth2Dai(
address takerToken,
address makerToken,
uint256[] memory takerTokenAmounts
)
public
view
returns (uint256[] memory makerTokenAmounts)
{
_assertValidPair(makerToken, takerToken);
uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples);
for (uint256 i = 0; i < numSamples; i++) {
makerTokenAmounts[i] = _getEth2DaiContract().getBuyAmount(
makerToken,
takerToken,
takerTokenAmounts[i]
);
}
}
/// @dev Sample buy quotes from Eth2Dai/Oasis.
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param takerTokenAmounts Maker token sell amount for each sample.
/// @return takerTokenAmounts Taker amounts sold at each maker token
/// amount.
function sampleBuysFromEth2Dai(
address takerToken,
address makerToken,
uint256[] memory makerTokenAmounts
)
public
view
returns (uint256[] memory takerTokenAmounts)
{
_assertValidPair(makerToken, takerToken);
uint256 numSamples = makerTokenAmounts.length;
takerTokenAmounts = new uint256[](numSamples);
for (uint256 i = 0; i < numSamples; i++) {
takerTokenAmounts[i] = _getEth2DaiContract().getPayAmount(
takerToken,
makerToken,
makerTokenAmounts[i]
);
}
}
/// @dev Sample sell quotes from Uniswap.
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return makerTokenAmounts Maker amounts bought at each taker token
/// amount.
function sampleSellsFromUniswap(
address takerToken,
address makerToken,
uint256[] memory takerTokenAmounts
)
public
view
returns (uint256[] memory makerTokenAmounts)
{
_assertValidPair(makerToken, takerToken);
uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples);
IUniswapExchangeQuotes takerTokenExchange = takerToken == _getWETHAddress() ?
IUniswapExchangeQuotes(0) : _getUniswapExchange(takerToken);
IUniswapExchangeQuotes makerTokenExchange = makerToken == _getWETHAddress() ?
IUniswapExchangeQuotes(0) : _getUniswapExchange(makerToken);
for (uint256 i = 0; i < numSamples; i++) {
if (makerToken == _getWETHAddress()) {
makerTokenAmounts[i] = takerTokenExchange.getTokenToEthInputPrice(
takerTokenAmounts[i]
);
} else if (takerToken == _getWETHAddress()) {
makerTokenAmounts[i] = makerTokenExchange.getEthToTokenInputPrice(
takerTokenAmounts[i]
);
} else {
uint256 ethBought = takerTokenExchange.getTokenToEthInputPrice(
takerTokenAmounts[i]
);
makerTokenAmounts[i] = makerTokenExchange.getEthToTokenInputPrice(
ethBought
);
}
}
}
/// @dev Sample buy quotes from Uniswap.
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param makerTokenAmounts Maker token sell amount for each sample.
/// @return takerTokenAmounts Taker amounts sold at each maker token
/// amount.
function sampleBuysFromUniswap(
address takerToken,
address makerToken,
uint256[] memory makerTokenAmounts
)
public
view
returns (uint256[] memory takerTokenAmounts)
{
_assertValidPair(makerToken, takerToken);
uint256 numSamples = makerTokenAmounts.length;
takerTokenAmounts = new uint256[](numSamples);
IUniswapExchangeQuotes takerTokenExchange = takerToken == _getWETHAddress() ?
IUniswapExchangeQuotes(0) : _getUniswapExchange(takerToken);
IUniswapExchangeQuotes makerTokenExchange = makerToken == _getWETHAddress() ?
IUniswapExchangeQuotes(0) : _getUniswapExchange(makerToken);
for (uint256 i = 0; i < numSamples; i++) {
if (makerToken == _getWETHAddress()) {
takerTokenAmounts[i] = takerTokenExchange.getTokenToEthOutputPrice(
makerTokenAmounts[i]
);
} else if (takerToken == _getWETHAddress()) {
takerTokenAmounts[i] = makerTokenExchange.getEthToTokenOutputPrice(
makerTokenAmounts[i]
);
} else {
uint256 ethSold = makerTokenExchange.getEthToTokenOutputPrice(
makerTokenAmounts[i]
);
takerTokenAmounts[i] = takerTokenExchange.getTokenToEthOutputPrice(
ethSold
);
}
}
}
/// @dev Overridable way to get token decimals.
/// @param tokenAddress Address of the token.
/// @return decimals The decimal places for the token.
function _getTokenDecimals(address tokenAddress)
internal
view
returns (uint8 decimals)
{
return LibERC20Token.decimals(tokenAddress);
}
/// @dev Samples a supported sell source, defined by its address.
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return makerTokenAmounts Maker amounts bought at each taker token
/// amount.
function _sampleSellSource(
address source,
address takerToken,
address makerToken,
uint256[] memory takerTokenAmounts
)
private
view
returns (uint256[] memory makerTokenAmounts)
{
if (source == address(_getEth2DaiContract())) {
return sampleSellsFromEth2Dai(takerToken, makerToken, takerTokenAmounts);
}
if (source == address(_getUniswapExchangeFactoryContract())) {
return sampleSellsFromUniswap(takerToken, makerToken, takerTokenAmounts);
}
if (source == address(_getKyberNetworkContract())) {
return sampleSellsFromKyberNetwork(takerToken, makerToken, takerTokenAmounts);
}
revert("UNSUPPORTED_SOURCE");
}
/// @dev Samples a supported buy source, defined by its address.
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param makerTokenAmounts Maker token sell amount for each sample.
/// @return takerTokenAmounts Taker amounts sold at each maker token
/// amount.
function _sampleBuySource(
address source,
address takerToken,
address makerToken,
uint256[] memory makerTokenAmounts
)
private
view
returns (uint256[] memory takerTokenAmounts)
{
if (source == address(_getEth2DaiContract())) {
return sampleBuysFromEth2Dai(takerToken, makerToken, makerTokenAmounts);
}
if (source == address(_getUniswapExchangeFactoryContract())) {
return sampleBuysFromUniswap(takerToken, makerToken, makerTokenAmounts);
}
revert("UNSUPPORTED_SOURCE");
}
/// @dev Retrive an existing Uniswap exchange contract.
/// Throws if the exchange does not exist.
/// @param tokenAddress Address of the token contract.
/// @return exchange `IUniswapExchangeQuotes` for the token.
function _getUniswapExchange(address tokenAddress)
private
view
returns (IUniswapExchangeQuotes exchange)
{
exchange = IUniswapExchangeQuotes(
address(_getUniswapExchangeFactoryContract().getExchange(tokenAddress))
);
require(address(exchange) != address(0), "UNSUPPORTED_UNISWAP_EXCHANGE");
}
/// @dev Extract the token address from ERC20 proxy asset data.
/// @param assetData ERC20 asset data.
/// @return tokenAddress The decoded token address.
function _assetDataToTokenAddress(bytes memory assetData)
private
pure
returns (address tokenAddress)
{
require(assetData.length == 36, "INVALID_ASSET_DATA");
bytes4 selector;
assembly {
selector := and(mload(add(assetData, 0x20)), 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000)
tokenAddress := mload(add(assetData, 0x24))
}
require(selector == ERC20_PROXY_ID, "UNSUPPORTED_ASSET_PROXY");
}
function _assertValidPair(address makerToken, address takerToken)
private
pure
{
require(makerToken != takerToken, "INVALID_TOKEN_PAIR");
}
}

View File

@@ -0,0 +1,111 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
interface IERC20BridgeSampler {
/// @dev Query native orders and sample sell orders on multiple DEXes at once.
/// @param orders Native orders to query.
/// @param sources Address of each DEX. Passing in an unknown DEX will throw.
/// @param takerTokenAmounts Taker sell amount for each sample.
/// @return orderInfos `OrderInfo`s for each order in `orders`.
/// @return makerTokenAmountsBySource Maker amounts bought for each source at
/// each taker token amount. First indexed by source index, then sample
/// index.
function queryOrdersAndSampleSells(
LibOrder.Order[] calldata orders,
address[] calldata sources,
uint256[] calldata takerTokenAmounts
)
external
view
returns (
LibOrder.OrderInfo[] memory orderInfos,
uint256[][] memory makerTokenAmountsBySource
);
/// @dev Query native orders and sample buy orders on multiple DEXes at once.
/// @param orders Native orders to query.
/// @param sources Address of each DEX. Passing in an unknown DEX will throw.
/// @param makerTokenAmounts Maker sell amount for each sample.
/// @return orderInfos `OrderInfo`s for each order in `orders`.
/// @return takerTokenAmountsBySource Taker amounts sold for each source at
/// each maker token amount. First indexed by source index, then sample
/// index.
function queryOrdersAndSampleBuys(
LibOrder.Order[] calldata orders,
address[] calldata sources,
uint256[] calldata makerTokenAmounts
)
external
view
returns (
LibOrder.OrderInfo[] memory orderInfos,
uint256[][] memory makerTokenAmountsBySource
);
/// @dev Queries the status of several native orders.
/// @param orders Native orders to query.
/// @return orderInfos Order info for each respective order.
function queryOrders(LibOrder.Order[] calldata orders)
external
view
returns (LibOrder.OrderInfo[] memory orderInfos);
/// @dev Sample sell quotes on multiple DEXes at once.
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return makerTokenAmountsBySource Maker amounts bought for each source at
/// each taker token amount. First indexed by source index, then sample
/// index.
function sampleSells(
address[] calldata sources,
address takerToken,
address makerToken,
uint256[] calldata takerTokenAmounts
)
external
view
returns (uint256[][] memory makerTokenAmountsBySource);
/// @dev Query native orders and sample buy quotes on multiple DEXes at once.
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param makerTokenAmounts Maker token buy amount for each sample.
/// @return takerTokenAmountsBySource Taker amounts sold for each source at
/// each maker token amount. First indexed by source index, then sample
/// index.
function sampleBuys(
address[] calldata sources,
address takerToken,
address makerToken,
uint256[] calldata makerTokenAmounts
)
external
view
returns (uint256[][] memory takerTokenAmountsBySource);
}

View File

@@ -0,0 +1,41 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
interface IEth2Dai {
function getBuyAmount(
address buyToken,
address payToken,
uint256 payAmount
)
external
view
returns (uint256 buyAmount);
function getPayAmount(
address payToken,
address buyToken,
uint256 buyAmount
)
external
view
returns (uint256 payAmount);
}

View File

@@ -0,0 +1,32 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
interface IKyberNetwork {
function getExpectedRate(
address fromToken,
address toToken,
uint256 fromAmount
)
external
view
returns (uint256 expectedRate, uint256 slippageRate);
}

View File

@@ -0,0 +1,51 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
interface IUniswapExchangeQuotes {
function getEthToTokenInputPrice(
uint256 ethSold
)
external
view
returns (uint256 tokensBought);
function getEthToTokenOutputPrice(
uint256 tokensBought
)
external
view
returns (uint256 ethSold);
function getTokenToEthInputPrice(
uint256 tokensSold
)
external
view
returns (uint256 ethBought);
function getTokenToEthOutputPrice(
uint256 ethBought
)
external
view
returns (uint256 tokensSold);
}

View File

@@ -0,0 +1,349 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IUniswapExchangeFactory.sol";
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "../src/ERC20BridgeSampler.sol";
import "../src/IEth2Dai.sol";
import "../src/IKyberNetwork.sol";
library LibDeterministicQuotes {
address private constant WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
uint256 private constant RATE_DENOMINATOR = 1 ether;
uint256 private constant MIN_RATE = RATE_DENOMINATOR / 100;
uint256 private constant MAX_RATE = 100 * RATE_DENOMINATOR;
uint8 private constant MIN_DECIMALS = 4;
uint8 private constant MAX_DECIMALS = 20;
function getDeterministicSellQuote(
bytes32 salt,
address sellToken,
address buyToken,
uint256 sellAmount
)
internal
pure
returns (uint256 buyAmount)
{
uint256 sellBase = uint256(10) ** getDeterministicTokenDecimals(sellToken);
uint256 buyBase = uint256(10) ** getDeterministicTokenDecimals(buyToken);
uint256 rate = getDeterministicRate(salt, sellToken, buyToken);
return sellAmount * rate * buyBase / sellBase / RATE_DENOMINATOR;
}
function getDeterministicBuyQuote(
bytes32 salt,
address sellToken,
address buyToken,
uint256 buyAmount
)
internal
pure
returns (uint256 sellAmount)
{
uint256 sellBase = uint256(10) ** getDeterministicTokenDecimals(sellToken);
uint256 buyBase = uint256(10) ** getDeterministicTokenDecimals(buyToken);
uint256 rate = getDeterministicRate(salt, sellToken, buyToken);
return buyAmount * RATE_DENOMINATOR * sellBase / rate / buyBase;
}
function getDeterministicTokenDecimals(address token)
internal
pure
returns (uint8 decimals)
{
if (token == WETH_ADDRESS) {
return 18;
}
bytes32 seed = keccak256(abi.encodePacked(token));
return uint8(uint256(seed) % (MAX_DECIMALS - MIN_DECIMALS)) + MIN_DECIMALS;
}
function getDeterministicRate(bytes32 salt, address sellToken, address buyToken)
internal
pure
returns (uint256 rate)
{
bytes32 seed = keccak256(abi.encodePacked(salt, sellToken, buyToken));
return uint256(seed) % (MAX_RATE - MIN_RATE) + MIN_RATE;
}
}
contract TestERC20BridgeSamplerUniswapExchange is
IUniswapExchangeQuotes,
DeploymentConstants
{
bytes32 constant private BASE_SALT = 0x1d6a6a0506b0b4a554b907a4c29d9f4674e461989d9c1921feb17b26716385ab;
address public tokenAddress;
bytes32 public salt;
constructor(address _tokenAddress) public {
tokenAddress = _tokenAddress;
salt = keccak256(abi.encodePacked(BASE_SALT, _tokenAddress));
}
// Deterministic `IUniswapExchangeQuotes.getEthToTokenInputPrice()`.
function getEthToTokenInputPrice(
uint256 ethSold
)
external
view
returns (uint256 tokensBought)
{
return LibDeterministicQuotes.getDeterministicSellQuote(
salt,
tokenAddress,
WETH_ADDRESS,
ethSold
);
}
// Deterministic `IUniswapExchangeQuotes.getEthToTokenOutputPrice()`.
function getEthToTokenOutputPrice(
uint256 tokensBought
)
external
view
returns (uint256 ethSold)
{
return LibDeterministicQuotes.getDeterministicBuyQuote(
salt,
WETH_ADDRESS,
tokenAddress,
tokensBought
);
}
// Deterministic `IUniswapExchangeQuotes.getTokenToEthInputPrice()`.
function getTokenToEthInputPrice(
uint256 tokensSold
)
external
view
returns (uint256 ethBought)
{
return LibDeterministicQuotes.getDeterministicSellQuote(
salt,
tokenAddress,
WETH_ADDRESS,
tokensSold
);
}
// Deterministic `IUniswapExchangeQuotes.getTokenToEthOutputPrice()`.
function getTokenToEthOutputPrice(
uint256 ethBought
)
external
view
returns (uint256 tokensSold)
{
return LibDeterministicQuotes.getDeterministicBuyQuote(
salt,
WETH_ADDRESS,
tokenAddress,
ethBought
);
}
}
contract TestERC20BridgeSamplerKyberNetwork is
IKyberNetwork,
DeploymentConstants
{
bytes32 constant private SALT = 0x0ff3ca9d46195c39f9a12afb74207b4970349fb3cfb1e459bbf170298d326bc7;
address constant public ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
// Deterministic `IKyberNetwork.getExpectedRate()`.
function getExpectedRate(
address fromToken,
address toToken,
uint256
)
external
view
returns (uint256 expectedRate, uint256)
{
fromToken = fromToken == ETH_ADDRESS ? WETH_ADDRESS : fromToken;
toToken = toToken == ETH_ADDRESS ? WETH_ADDRESS : toToken;
expectedRate = LibDeterministicQuotes.getDeterministicRate(
SALT,
fromToken,
toToken
);
}
}
contract TestERC20BridgeSamplerEth2Dai is
IEth2Dai
{
bytes32 constant private SALT = 0xb713b61bb9bb2958a0f5d1534b21e94fc68c4c0c034b0902ed844f2f6cd1b4f7;
// Deterministic `IEth2Dai.getBuyAmount()`.
function getBuyAmount(
address buyToken,
address payToken,
uint256 payAmount
)
external
view
returns (uint256 buyAmount)
{
return LibDeterministicQuotes.getDeterministicSellQuote(
SALT,
payToken,
buyToken,
payAmount
);
}
// Deterministic `IEth2Dai.getPayAmount()`.
function getPayAmount(
address payToken,
address buyToken,
uint256 buyAmount
)
external
view
returns (uint256 payAmount)
{
return LibDeterministicQuotes.getDeterministicBuyQuote(
SALT,
payToken,
buyToken,
buyAmount
);
}
}
contract TestERC20BridgeSamplerUniswapExchangeFactory is
IUniswapExchangeFactory
{
mapping (address => IUniswapExchangeQuotes) private _exchangesByToken;
// Creates Uniswap exchange contracts for tokens.
function createTokenExchanges(address[] calldata tokenAddresses)
external
{
for (uint256 i = 0; i < tokenAddresses.length; i++) {
address tokenAddress = tokenAddresses[i];
_exchangesByToken[tokenAddress] =
new TestERC20BridgeSamplerUniswapExchange(tokenAddress);
}
}
// `IUniswapExchangeFactory.getExchange()`.
function getExchange(address tokenAddress)
external
view
returns (address)
{
return address(_exchangesByToken[tokenAddress]);
}
}
contract TestERC20BridgeSampler is
ERC20BridgeSampler
{
TestERC20BridgeSamplerUniswapExchangeFactory public uniswap;
TestERC20BridgeSamplerEth2Dai public eth2Dai;
TestERC20BridgeSamplerKyberNetwork public kyber;
constructor() public {
uniswap = new TestERC20BridgeSamplerUniswapExchangeFactory();
eth2Dai = new TestERC20BridgeSamplerEth2Dai();
kyber = new TestERC20BridgeSamplerKyberNetwork();
}
// Creates Uniswap exchange contracts for tokens.
function createTokenExchanges(address[] calldata tokenAddresses)
external
{
uniswap.createTokenExchanges(tokenAddresses);
}
// `IExchange.getOrderInfo()`, overridden to return deterministic order infos.
function getOrderInfo(LibOrder.Order memory order)
public
pure
returns (LibOrder.OrderInfo memory orderInfo)
{
// The order hash is just the hash of the salt.
bytes32 orderHash = keccak256(abi.encode(order.salt));
// Everything else is derived from the hash.
orderInfo.orderHash = orderHash;
orderInfo.orderStatus = uint8(uint256(orderHash) % uint8(-1));
orderInfo.orderTakerAssetFilledAmount = uint256(orderHash) % order.takerAssetAmount;
}
// Overriden to return deterministic decimals.
function _getTokenDecimals(address tokenAddress)
internal
view
returns (uint8 decimals)
{
return LibDeterministicQuotes.getDeterministicTokenDecimals(tokenAddress);
}
// Overriden to point to a this contract.
function _getExchangeContract()
internal
view
returns (IExchange zeroex)
{
return IExchange(address(this));
}
// Overriden to point to a custom contract.
function _getEth2DaiContract()
internal
view
returns (IEth2Dai eth2dai_)
{
return eth2Dai;
}
// Overriden to point to a custom contract.
function _getUniswapExchangeFactoryContract()
internal
view
returns (IUniswapExchangeFactory uniswap_)
{
return uniswap;
}
// Overriden to point to a custom contract.
function _getKyberNetworkContract()
internal
view
returns (IKyberNetwork kyber_)
{
return kyber;
}
}

View File

@@ -0,0 +1,92 @@
{
"name": "@0x/contracts-erc20-bridge-sampler",
"version": "1.0.1",
"engines": {
"node": ">=6.12"
},
"description": "Sampler contracts for the 0x asset-swapper",
"main": "lib/src/index.js",
"directories": {
"test": "test"
},
"scripts": {
"build": "yarn pre_build && tsc -b",
"build:ci": "yarn build",
"pre_build": "run-s compile contracts:gen generate_contract_wrappers contracts:copy",
"test": "yarn run_mocha",
"rebuild_and_test": "run-s build test",
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
"test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html",
"test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha",
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
"compile": "sol-compiler",
"watch": "sol-compiler -w",
"clean": "shx rm -rf lib test/generated-artifacts test/generated-wrappers generated-artifacts generated-wrappers",
"generate_contract_wrappers": "abi-gen --debug --abis ${npm_package_config_abis} --output test/generated-wrappers --backend ethers",
"lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./test/generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude ./test/generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
"fix": "tslint --fix --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude ./test/generated-wrappers/**/* --exclude ./test/generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
"coverage:report:text": "istanbul report text",
"coverage:report:html": "istanbul report html && open coverage/index.html",
"profiler:report:html": "istanbul report html && open coverage/index.html",
"coverage:report:lcov": "istanbul report lcov",
"test:circleci": "yarn test",
"contracts:gen": "contracts-gen generate",
"contracts:copy": "contracts-gen copy",
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol",
"compile:truffle": "truffle compile"
},
"config": {
"publicInterfaceContracts": "ERC20BridgeSampler,IERC20BridgeSampler",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
"abis": "./test/generated-artifacts/@(DeploymentConstants|ERC20BridgeSampler|IERC20BridgeSampler|IEth2Dai|IKyberNetwork|IUniswapExchangeQuotes|TestERC20BridgeSampler).json"
},
"repository": {
"type": "git",
"url": "https://github.com/0xProject/0x-monorepo.git"
},
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/0xProject/0x-monorepo/issues"
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/protocol/README.md",
"devDependencies": {
"@0x/abi-gen": "^5.0.1",
"@0x/contracts-asset-proxy": "^3.0.1",
"@0x/contracts-erc20": "^3.0.1",
"@0x/contracts-exchange": "^3.0.1",
"@0x/contracts-exchange-libs": "^4.0.1",
"@0x/contracts-gen": "^2.0.1",
"@0x/contracts-test-utils": "^5.0.0",
"@0x/contracts-utils": "^4.0.1",
"@0x/dev-utils": "^3.0.1",
"@0x/sol-compiler": "^4.0.1",
"@0x/tslint-config": "^4.0.0",
"@0x/web3-wrapper": "^7.0.1",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "*",
"chai": "^4.0.1",
"chai-as-promised": "^7.1.0",
"chai-bignumber": "^3.0.0",
"dirty-chai": "^2.0.1",
"make-promises-safe": "^1.1.0",
"mocha": "^6.2.0",
"npm-run-all": "^4.1.2",
"shx": "^0.2.2",
"solhint": "^1.4.1",
"truffle": "^5.0.32",
"tslint": "5.11.0",
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^6.0.1",
"@0x/types": "^3.1.0",
"@0x/typescript-typings": "^5.0.0",
"@0x/utils": "^5.1.0",
"ethereum-types": "^3.0.0",
"lodash": "^4.17.11"
},
"publishConfig": {
"access": "public"
}
}

View File

@@ -0,0 +1,13 @@
/*
* -----------------------------------------------------------------------------
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
* -----------------------------------------------------------------------------
*/
import { ContractArtifact } from 'ethereum-types';
import * as ERC20BridgeSampler from '../generated-artifacts/ERC20BridgeSampler.json';
import * as IERC20BridgeSampler from '../generated-artifacts/IERC20BridgeSampler.json';
export const artifacts = {
ERC20BridgeSampler: ERC20BridgeSampler as ContractArtifact,
IERC20BridgeSampler: IERC20BridgeSampler as ContractArtifact,
};

View File

@@ -0,0 +1,2 @@
export * from './wrappers';
export * from './artifacts';

View File

@@ -0,0 +1,7 @@
/*
* -----------------------------------------------------------------------------
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
* -----------------------------------------------------------------------------
*/
export * from '../generated-wrappers/erc20_bridge_sampler';
export * from '../generated-wrappers/i_erc20_bridge_sampler';

View File

@@ -0,0 +1,23 @@
/*
* -----------------------------------------------------------------------------
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
* -----------------------------------------------------------------------------
*/
import { ContractArtifact } from 'ethereum-types';
import * as DeploymentConstants from '../test/generated-artifacts/DeploymentConstants.json';
import * as ERC20BridgeSampler from '../test/generated-artifacts/ERC20BridgeSampler.json';
import * as IERC20BridgeSampler from '../test/generated-artifacts/IERC20BridgeSampler.json';
import * as IEth2Dai from '../test/generated-artifacts/IEth2Dai.json';
import * as IKyberNetwork from '../test/generated-artifacts/IKyberNetwork.json';
import * as IUniswapExchangeQuotes from '../test/generated-artifacts/IUniswapExchangeQuotes.json';
import * as TestERC20BridgeSampler from '../test/generated-artifacts/TestERC20BridgeSampler.json';
export const artifacts = {
DeploymentConstants: DeploymentConstants as ContractArtifact,
ERC20BridgeSampler: ERC20BridgeSampler as ContractArtifact,
IERC20BridgeSampler: IERC20BridgeSampler as ContractArtifact,
IEth2Dai: IEth2Dai as ContractArtifact,
IKyberNetwork: IKyberNetwork as ContractArtifact,
IUniswapExchangeQuotes: IUniswapExchangeQuotes as ContractArtifact,
TestERC20BridgeSampler: TestERC20BridgeSampler as ContractArtifact,
};

View File

@@ -0,0 +1,847 @@
import {
blockchainTests,
constants,
expect,
getRandomInteger,
getRandomPortion,
randomAddress,
} from '@0x/contracts-test-utils';
import { Order, OrderInfo } from '@0x/types';
import { BigNumber, hexUtils } from '@0x/utils';
import * as _ from 'lodash';
import { artifacts } from './artifacts';
import { TestERC20BridgeSamplerContract } from './wrappers';
blockchainTests('erc20-bridge-sampler', env => {
let testContract: TestERC20BridgeSamplerContract;
let allSources: { [name: string]: string };
const RATE_DENOMINATOR = constants.ONE_ETHER;
const MIN_RATE = new BigNumber('0.01');
const MAX_RATE = new BigNumber('100');
const MIN_DECIMALS = 4;
const MAX_DECIMALS = 20;
const WETH_ADDRESS = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2';
const KYBER_SALT = '0x0ff3ca9d46195c39f9a12afb74207b4970349fb3cfb1e459bbf170298d326bc7';
const ETH2DAI_SALT = '0xb713b61bb9bb2958a0f5d1534b21e94fc68c4c0c034b0902ed844f2f6cd1b4f7';
const UNISWAP_BASE_SALT = '0x1d6a6a0506b0b4a554b907a4c29d9f4674e461989d9c1921feb17b26716385ab';
const ERC20_PROXY_ID = '0xf47261b0';
const INVALID_ASSET_PROXY_ASSET_DATA = hexUtils.concat('0xf47261b1', hexUtils.leftPad(randomAddress()));
const INVALID_ASSET_DATA = hexUtils.random(37);
const SELL_SOURCES = ['Eth2Dai', 'Kyber', 'Uniswap'];
const BUY_SOURCES = ['Eth2Dai', 'Uniswap'];
const EMPTY_ORDERS_ERROR = 'EMPTY_ORDERS';
const UNSUPPORTED_ASSET_PROXY_ERROR = 'UNSUPPORTED_ASSET_PROXY';
const INVALID_ASSET_DATA_ERROR = 'INVALID_ASSET_DATA';
const UNSUPPORTED_UNISWAP_EXCHANGE_ERROR = 'UNSUPPORTED_UNISWAP_EXCHANGE';
const UNSUPPORTED_SOURCE_ERROR = 'UNSUPPORTED_SOURCE';
const INVALID_TOKEN_PAIR_ERROR = 'INVALID_TOKEN_PAIR';
before(async () => {
testContract = await TestERC20BridgeSamplerContract.deployFrom0xArtifactAsync(
artifacts.TestERC20BridgeSampler,
env.provider,
env.txDefaults,
{},
);
allSources = _.zipObject(
['Uniswap', 'Eth2Dai', 'Kyber'],
[
await testContract.uniswap().callAsync(),
await testContract.eth2Dai().callAsync(),
await testContract.kyber().callAsync(),
],
);
});
function getPackedHash(...args: string[]): string {
return hexUtils.hash(hexUtils.concat(...args.map(a => hexUtils.toHex(a))));
}
function getUniswapExchangeSalt(tokenAddress: string): string {
return getPackedHash(UNISWAP_BASE_SALT, tokenAddress);
}
function getDeterministicRate(salt: string, sellToken: string, buyToken: string): BigNumber {
const hash = getPackedHash(salt, sellToken, buyToken);
const _minRate = RATE_DENOMINATOR.times(MIN_RATE);
const _maxRate = RATE_DENOMINATOR.times(MAX_RATE);
return new BigNumber(hash)
.mod(_maxRate.minus(_minRate))
.plus(_minRate)
.div(RATE_DENOMINATOR);
}
function getDeterministicTokenDecimals(token: string): number {
if (token === WETH_ADDRESS) {
return 18;
}
// HACK(dorothy-zbornak): Linter will complain about the addition not being
// between two numbers, even though they are.
// tslint:disable-next-line restrict-plus-operands
return new BigNumber(getPackedHash(token)).mod(MAX_DECIMALS - MIN_DECIMALS).toNumber() + MIN_DECIMALS;
}
function getDeterministicSellQuote(
salt: string,
sellToken: string,
buyToken: string,
sellAmount: BigNumber,
): BigNumber {
const sellBase = new BigNumber(10).pow(getDeterministicTokenDecimals(sellToken));
const buyBase = new BigNumber(10).pow(getDeterministicTokenDecimals(buyToken));
const rate = getDeterministicRate(salt, sellToken, buyToken);
return sellAmount
.times(rate)
.times(buyBase)
.dividedToIntegerBy(sellBase);
}
function getDeterministicBuyQuote(
salt: string,
sellToken: string,
buyToken: string,
buyAmount: BigNumber,
): BigNumber {
const sellBase = new BigNumber(10).pow(getDeterministicTokenDecimals(sellToken));
const buyBase = new BigNumber(10).pow(getDeterministicTokenDecimals(buyToken));
const rate = getDeterministicRate(salt, sellToken, buyToken);
return buyAmount
.times(sellBase)
.dividedToIntegerBy(rate)
.dividedToIntegerBy(buyBase);
}
function areAddressesEqual(a: string, b: string): boolean {
return a.toLowerCase() === b.toLowerCase();
}
function getDeterministicUniswapSellQuote(sellToken: string, buyToken: string, sellAmount: BigNumber): BigNumber {
if (areAddressesEqual(buyToken, WETH_ADDRESS)) {
return getDeterministicSellQuote(getUniswapExchangeSalt(sellToken), sellToken, WETH_ADDRESS, sellAmount);
}
if (areAddressesEqual(sellToken, WETH_ADDRESS)) {
return getDeterministicSellQuote(getUniswapExchangeSalt(buyToken), buyToken, WETH_ADDRESS, sellAmount);
}
const ethBought = getDeterministicSellQuote(
getUniswapExchangeSalt(sellToken),
sellToken,
WETH_ADDRESS,
sellAmount,
);
return getDeterministicSellQuote(getUniswapExchangeSalt(buyToken), buyToken, WETH_ADDRESS, ethBought);
}
function getDeterministicUniswapBuyQuote(sellToken: string, buyToken: string, buyAmount: BigNumber): BigNumber {
if (areAddressesEqual(buyToken, WETH_ADDRESS)) {
return getDeterministicBuyQuote(getUniswapExchangeSalt(sellToken), WETH_ADDRESS, sellToken, buyAmount);
}
if (areAddressesEqual(sellToken, WETH_ADDRESS)) {
return getDeterministicBuyQuote(getUniswapExchangeSalt(buyToken), WETH_ADDRESS, buyToken, buyAmount);
}
const ethSold = getDeterministicBuyQuote(getUniswapExchangeSalt(buyToken), WETH_ADDRESS, buyToken, buyAmount);
return getDeterministicBuyQuote(getUniswapExchangeSalt(sellToken), WETH_ADDRESS, sellToken, ethSold);
}
function getDeterministicSellQuotes(
sellToken: string,
buyToken: string,
sources: string[],
sampleAmounts: BigNumber[],
): BigNumber[][] {
const quotes: BigNumber[][] = [];
for (const source of sources) {
const sampleOutputs = [];
for (const amount of sampleAmounts) {
if (source === 'Kyber' || source === 'Eth2Dai') {
sampleOutputs.push(
getDeterministicSellQuote(
source === 'Kyber' ? KYBER_SALT : ETH2DAI_SALT,
sellToken,
buyToken,
amount,
),
);
} else if (source === 'Uniswap') {
sampleOutputs.push(getDeterministicUniswapSellQuote(sellToken, buyToken, amount));
}
}
quotes.push(sampleOutputs);
}
return quotes;
}
function getDeterministicBuyQuotes(
sellToken: string,
buyToken: string,
sources: string[],
sampleAmounts: BigNumber[],
): BigNumber[][] {
const quotes: BigNumber[][] = [];
for (const source of sources) {
const sampleOutputs = [];
for (const amount of sampleAmounts) {
if (source === 'Eth2Dai') {
sampleOutputs.push(getDeterministicBuyQuote(ETH2DAI_SALT, sellToken, buyToken, amount));
} else if (source === 'Uniswap') {
sampleOutputs.push(getDeterministicUniswapBuyQuote(sellToken, buyToken, amount));
}
}
quotes.push(sampleOutputs);
}
return quotes;
}
function getDeterministicOrderInfo(order: Order): OrderInfo {
const hash = getPackedHash(hexUtils.leftPad(order.salt, 32));
return {
orderHash: hash,
orderStatus: new BigNumber(hash).mod(255).toNumber(),
orderTakerAssetFilledAmount: new BigNumber(hash).mod(order.takerAssetAmount),
};
}
function getERC20AssetData(tokenAddress: string): string {
return hexUtils.concat(ERC20_PROXY_ID, hexUtils.leftPad(tokenAddress));
}
function getSampleAmounts(tokenAddress: string, count?: number): BigNumber[] {
const tokenDecimals = getDeterministicTokenDecimals(tokenAddress);
const _upperLimit = getRandomPortion(getRandomInteger(1, 1000).times(10 ** tokenDecimals));
const _count = count || _.random(1, 16);
const d = _upperLimit.div(_count);
return _.times(_count, i => d.times((i + 1) / _count).integerValue());
}
function createOrder(makerToken: string, takerToken: string): Order {
return {
chainId: 1337,
exchangeAddress: randomAddress(),
makerAddress: randomAddress(),
takerAddress: randomAddress(),
senderAddress: randomAddress(),
feeRecipientAddress: randomAddress(),
makerAssetAmount: getRandomInteger(1, 1e18),
takerAssetAmount: getRandomInteger(1, 1e18),
makerFee: getRandomInteger(1, 1e18),
takerFee: getRandomInteger(1, 1e18),
makerAssetData: getERC20AssetData(makerToken),
takerAssetData: getERC20AssetData(takerToken),
makerFeeAssetData: getERC20AssetData(randomAddress()),
takerFeeAssetData: getERC20AssetData(randomAddress()),
salt: new BigNumber(hexUtils.random()),
expirationTimeSeconds: getRandomInteger(0, 2 ** 32),
};
}
function createOrders(makerToken: string, takerToken: string, count?: number): Order[] {
return _.times(count || _.random(1, 16), () => createOrder(makerToken, takerToken));
}
describe('queryOrders()', () => {
const MAKER_TOKEN = randomAddress();
const TAKER_TOKEN = randomAddress();
it('returns the results of `getOrderInfo()` for each order', async () => {
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN);
const expected = orders.map(getDeterministicOrderInfo);
const actual = await testContract.queryOrders(orders).callAsync();
expect(actual).to.deep.eq(expected);
});
it('returns empty for no orders', async () => {
const actual = await testContract.queryOrders([]).callAsync();
expect(actual).to.deep.eq([]);
});
});
describe('queryOrdersAndSampleSells()', () => {
const MAKER_TOKEN = randomAddress();
const TAKER_TOKEN = randomAddress();
before(async () => {
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
});
it('returns the results of `getOrderInfo()` for each order', async () => {
const takerTokenAmounts = getSampleAmounts(TAKER_TOKEN);
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN);
const expectedOrderInfos = orders.map(getDeterministicOrderInfo);
const [orderInfos] = await testContract
.queryOrdersAndSampleSells(orders, SELL_SOURCES.map(n => allSources[n]), takerTokenAmounts)
.callAsync();
expect(orderInfos).to.deep.eq(expectedOrderInfos);
});
it('can return quotes for all sources', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const expectedQuotes = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, SELL_SOURCES, sampleAmounts);
const [, quotes] = await testContract
.queryOrdersAndSampleSells(
createOrders(MAKER_TOKEN, TAKER_TOKEN),
SELL_SOURCES.map(n => allSources[n]),
sampleAmounts,
)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('throws if no orders are passed in', async () => {
const tx = testContract
.queryOrdersAndSampleSells([], SELL_SOURCES.map(n => allSources[n]), getSampleAmounts(TAKER_TOKEN))
.callAsync();
return expect(tx).to.revertWith(EMPTY_ORDERS_ERROR);
});
it('throws with an unsupported source', async () => {
const tx = testContract
.queryOrdersAndSampleSells(
createOrders(MAKER_TOKEN, TAKER_TOKEN),
[...SELL_SOURCES.map(n => allSources[n]), randomAddress()],
getSampleAmounts(TAKER_TOKEN),
)
.callAsync();
return expect(tx).to.revertWith(UNSUPPORTED_SOURCE_ERROR);
});
it('throws with non-ERC20 maker asset data', async () => {
const tx = testContract
.queryOrdersAndSampleSells(
createOrders(MAKER_TOKEN, TAKER_TOKEN).map(o => ({
...o,
makerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
})),
SELL_SOURCES.map(n => allSources[n]),
getSampleAmounts(TAKER_TOKEN),
)
.callAsync();
return expect(tx).to.revertWith(UNSUPPORTED_ASSET_PROXY_ERROR);
});
it('throws with non-ERC20 taker asset data', async () => {
const tx = testContract
.queryOrdersAndSampleSells(
createOrders(MAKER_TOKEN, TAKER_TOKEN).map(o => ({
...o,
takerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
})),
SELL_SOURCES.map(n => allSources[n]),
getSampleAmounts(TAKER_TOKEN),
)
.callAsync();
return expect(tx).to.revertWith(UNSUPPORTED_ASSET_PROXY_ERROR);
});
it('throws with invalid maker asset data', async () => {
const tx = testContract
.queryOrdersAndSampleSells(
createOrders(MAKER_TOKEN, TAKER_TOKEN).map(o => ({
...o,
makerAssetData: INVALID_ASSET_DATA,
})),
SELL_SOURCES.map(n => allSources[n]),
getSampleAmounts(TAKER_TOKEN),
)
.callAsync();
return expect(tx).to.revertWith(INVALID_ASSET_DATA_ERROR);
});
it('throws with invalid taker asset data', async () => {
const tx = testContract
.queryOrdersAndSampleSells(
createOrders(MAKER_TOKEN, TAKER_TOKEN).map(o => ({
...o,
takerAssetData: INVALID_ASSET_DATA,
})),
SELL_SOURCES.map(n => allSources[n]),
getSampleAmounts(TAKER_TOKEN),
)
.callAsync();
return expect(tx).to.revertWith(INVALID_ASSET_DATA_ERROR);
});
});
describe('queryOrdersAndSampleBuys()', () => {
const MAKER_TOKEN = randomAddress();
const TAKER_TOKEN = randomAddress();
before(async () => {
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
});
it('returns the results of `getOrderInfo()` for each order', async () => {
const takerTokenAmounts = getSampleAmounts(MAKER_TOKEN);
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN);
const expectedOrderInfos = orders.map(getDeterministicOrderInfo);
const [orderInfos] = await testContract
.queryOrdersAndSampleBuys(orders, BUY_SOURCES.map(n => allSources[n]), takerTokenAmounts)
.callAsync();
expect(orderInfos).to.deep.eq(expectedOrderInfos);
});
it('can return quotes for all sources', async () => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const expectedQuotes = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, BUY_SOURCES, sampleAmounts);
const [, quotes] = await testContract
.queryOrdersAndSampleBuys(
createOrders(MAKER_TOKEN, TAKER_TOKEN),
BUY_SOURCES.map(n => allSources[n]),
sampleAmounts,
)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('throws if no orders are passed in', async () => {
const tx = testContract
.queryOrdersAndSampleBuys([], BUY_SOURCES.map(n => allSources[n]), getSampleAmounts(MAKER_TOKEN))
.callAsync();
return expect(tx).to.revertWith(EMPTY_ORDERS_ERROR);
});
it('throws with an unsupported source', async () => {
const tx = testContract
.queryOrdersAndSampleBuys(
createOrders(MAKER_TOKEN, TAKER_TOKEN),
[...BUY_SOURCES.map(n => allSources[n]), randomAddress()],
getSampleAmounts(MAKER_TOKEN),
)
.callAsync();
return expect(tx).to.revertWith(UNSUPPORTED_SOURCE_ERROR);
});
it('throws if kyber is passed in as a source', async () => {
const sources = [...BUY_SOURCES, 'Kyber'];
const tx = testContract
.queryOrdersAndSampleBuys(
createOrders(MAKER_TOKEN, TAKER_TOKEN),
sources.map(n => allSources[n]),
getSampleAmounts(MAKER_TOKEN),
)
.callAsync();
return expect(tx).to.revertWith(UNSUPPORTED_SOURCE_ERROR);
});
it('throws with non-ERC20 maker asset data', async () => {
const tx = testContract
.queryOrdersAndSampleBuys(
createOrders(MAKER_TOKEN, TAKER_TOKEN).map(o => ({
...o,
makerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
})),
BUY_SOURCES.map(n => allSources[n]),
getSampleAmounts(MAKER_TOKEN),
)
.callAsync();
return expect(tx).to.revertWith(UNSUPPORTED_ASSET_PROXY_ERROR);
});
it('throws with non-ERC20 taker asset data', async () => {
const tx = testContract
.queryOrdersAndSampleBuys(
createOrders(MAKER_TOKEN, TAKER_TOKEN).map(o => ({
...o,
takerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
})),
BUY_SOURCES.map(n => allSources[n]),
getSampleAmounts(MAKER_TOKEN),
)
.callAsync();
return expect(tx).to.revertWith(UNSUPPORTED_ASSET_PROXY_ERROR);
});
it('throws with invalid maker asset data', async () => {
const tx = testContract
.queryOrdersAndSampleBuys(
createOrders(MAKER_TOKEN, TAKER_TOKEN).map(o => ({
...o,
makerAssetData: INVALID_ASSET_DATA,
})),
BUY_SOURCES.map(n => allSources[n]),
getSampleAmounts(MAKER_TOKEN),
)
.callAsync();
return expect(tx).to.revertWith(INVALID_ASSET_DATA_ERROR);
});
it('throws with invalid taker asset data', async () => {
const tx = testContract
.queryOrdersAndSampleBuys(
createOrders(MAKER_TOKEN, TAKER_TOKEN).map(o => ({
...o,
takerAssetData: INVALID_ASSET_DATA,
})),
BUY_SOURCES.map(n => allSources[n]),
getSampleAmounts(MAKER_TOKEN),
)
.callAsync();
return expect(tx).to.revertWith(INVALID_ASSET_DATA_ERROR);
});
});
describe('sampleSells()', () => {
const MAKER_TOKEN = randomAddress();
const TAKER_TOKEN = randomAddress();
before(async () => {
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
});
it('returns empty quotes with no sample amounts', async () => {
const emptyQuotes = _.times(SELL_SOURCES.length, () => []);
const quotes = await testContract
.sampleSells(SELL_SOURCES.map(n => allSources[n]), TAKER_TOKEN, MAKER_TOKEN, [])
.callAsync();
expect(quotes).to.deep.eq(emptyQuotes);
});
it('can return quotes for all sources', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const expectedQuotes = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, SELL_SOURCES, sampleAmounts);
const quotes = await testContract
.sampleSells(SELL_SOURCES.map(n => allSources[n]), TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('can return quotes for some sources', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const sources = _.sampleSize(SELL_SOURCES, 1);
const expectedQuotes = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, sources, sampleAmounts);
const quotes = await testContract
.sampleSells(sources.map(n => allSources[n]), TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('throws with an unsupported source', async () => {
const tx = testContract
.sampleSells(
[...SELL_SOURCES.map(n => allSources[n]), randomAddress()],
TAKER_TOKEN,
MAKER_TOKEN,
getSampleAmounts(TAKER_TOKEN),
)
.callAsync();
return expect(tx).to.revertWith(UNSUPPORTED_SOURCE_ERROR);
});
});
describe('sampleBuys()', () => {
const MAKER_TOKEN = randomAddress();
const TAKER_TOKEN = randomAddress();
before(async () => {
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
});
it('returns empty quotes with no sample amounts', async () => {
const emptyQuotes = _.times(BUY_SOURCES.length, () => []);
const quotes = await testContract
.sampleBuys(BUY_SOURCES.map(n => allSources[n]), TAKER_TOKEN, MAKER_TOKEN, [])
.callAsync();
expect(quotes).to.deep.eq(emptyQuotes);
});
it('can return quotes for all sources', async () => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const expectedQuotes = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, BUY_SOURCES, sampleAmounts);
const quotes = await testContract
.sampleBuys(BUY_SOURCES.map(n => allSources[n]), TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('can return quotes for some sources', async () => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const sources = _.sampleSize(BUY_SOURCES, 1);
const expectedQuotes = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, sources, sampleAmounts);
const quotes = await testContract
.sampleBuys(sources.map(n => allSources[n]), TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('throws with an unsupported source', async () => {
const tx = testContract
.sampleBuys(
[...BUY_SOURCES.map(n => allSources[n]), randomAddress()],
TAKER_TOKEN,
MAKER_TOKEN,
getSampleAmounts(MAKER_TOKEN),
)
.callAsync();
return expect(tx).to.revertWith(UNSUPPORTED_SOURCE_ERROR);
});
it('throws if kyber is passed in as a source', async () => {
const sources = [...BUY_SOURCES, 'Kyber'];
const tx = testContract
.sampleBuys(sources.map(n => allSources[n]), TAKER_TOKEN, MAKER_TOKEN, getSampleAmounts(MAKER_TOKEN))
.callAsync();
return expect(tx).to.revertWith(UNSUPPORTED_SOURCE_ERROR);
});
});
describe('sampleSellsFromKyberNetwork()', () => {
const MAKER_TOKEN = randomAddress();
const TAKER_TOKEN = randomAddress();
before(async () => {
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
});
it('throws if tokens are the same', async () => {
const tx = testContract.sampleSellsFromKyberNetwork(MAKER_TOKEN, MAKER_TOKEN, []).callAsync();
return expect(tx).to.revertWith(INVALID_TOKEN_PAIR_ERROR);
});
it('can return no quotes', async () => {
const quotes = await testContract.sampleSellsFromKyberNetwork(TAKER_TOKEN, MAKER_TOKEN, []).callAsync();
expect(quotes).to.deep.eq([]);
});
it('can return many quotes', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Kyber'], sampleAmounts);
const quotes = await testContract
.sampleSellsFromKyberNetwork(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('can quote token -> ETH', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Kyber'], sampleAmounts);
const quotes = await testContract
.sampleSellsFromKyberNetwork(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('can quote ETH -> token', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicSellQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Kyber'], sampleAmounts);
const quotes = await testContract
.sampleSellsFromKyberNetwork(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
});
describe('sampleSellsFromEth2Dai()', () => {
const MAKER_TOKEN = randomAddress();
const TAKER_TOKEN = randomAddress();
before(async () => {
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
});
it('throws if tokens are the same', async () => {
const tx = testContract.sampleSellsFromEth2Dai(MAKER_TOKEN, MAKER_TOKEN, []).callAsync();
return expect(tx).to.revertWith(INVALID_TOKEN_PAIR_ERROR);
});
it('can return no quotes', async () => {
const quotes = await testContract.sampleSellsFromEth2Dai(TAKER_TOKEN, MAKER_TOKEN, []).callAsync();
expect(quotes).to.deep.eq([]);
});
it('can return many quotes', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Eth2Dai'], sampleAmounts);
const quotes = await testContract
.sampleSellsFromEth2Dai(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('can quote token -> ETH', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Eth2Dai'], sampleAmounts);
const quotes = await testContract
.sampleSellsFromEth2Dai(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('can quote ETH -> token', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicSellQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Eth2Dai'], sampleAmounts);
const quotes = await testContract
.sampleSellsFromEth2Dai(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
});
describe('sampleBuysFromEth2Dai()', () => {
const MAKER_TOKEN = randomAddress();
const TAKER_TOKEN = randomAddress();
before(async () => {
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
});
it('throws if tokens are the same', async () => {
const tx = testContract.sampleBuysFromEth2Dai(MAKER_TOKEN, MAKER_TOKEN, []).callAsync();
return expect(tx).to.revertWith(INVALID_TOKEN_PAIR_ERROR);
});
it('can return no quotes', async () => {
const quotes = await testContract.sampleBuysFromEth2Dai(TAKER_TOKEN, MAKER_TOKEN, []).callAsync();
expect(quotes).to.deep.eq([]);
});
it('can return many quotes', async () => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Eth2Dai'], sampleAmounts);
const quotes = await testContract
.sampleBuysFromEth2Dai(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('can quote token -> ETH', async () => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Eth2Dai'], sampleAmounts);
const quotes = await testContract
.sampleBuysFromEth2Dai(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('can quote ETH -> token', async () => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const [expectedQuotes] = getDeterministicBuyQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Eth2Dai'], sampleAmounts);
const quotes = await testContract
.sampleBuysFromEth2Dai(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
});
describe('sampleSellsFromUniswap()', () => {
const MAKER_TOKEN = randomAddress();
const TAKER_TOKEN = randomAddress();
before(async () => {
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
});
it('throws if tokens are the same', async () => {
const tx = testContract.sampleSellsFromUniswap(MAKER_TOKEN, MAKER_TOKEN, []).callAsync();
return expect(tx).to.revertWith(INVALID_TOKEN_PAIR_ERROR);
});
it('can return no quotes', async () => {
const quotes = await testContract.sampleSellsFromUniswap(TAKER_TOKEN, MAKER_TOKEN, []).callAsync();
expect(quotes).to.deep.eq([]);
});
it('can return many quotes', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Uniswap'], sampleAmounts);
const quotes = await testContract
.sampleSellsFromUniswap(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('can quote token -> ETH', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Uniswap'], sampleAmounts);
const quotes = await testContract
.sampleSellsFromUniswap(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('can quote ETH -> token', async () => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const [expectedQuotes] = getDeterministicSellQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Uniswap'], sampleAmounts);
const quotes = await testContract
.sampleSellsFromUniswap(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('throws if no exchange exists for the maker token', async () => {
const nonExistantToken = randomAddress();
const tx = testContract
.sampleSellsFromUniswap(TAKER_TOKEN, nonExistantToken, getSampleAmounts(TAKER_TOKEN))
.callAsync();
return expect(tx).to.revertWith(UNSUPPORTED_UNISWAP_EXCHANGE_ERROR);
});
it('throws if no exchange exists for the taker token', async () => {
const nonExistantToken = randomAddress();
const tx = testContract
.sampleSellsFromUniswap(nonExistantToken, MAKER_TOKEN, getSampleAmounts(nonExistantToken))
.callAsync();
return expect(tx).to.revertWith(UNSUPPORTED_UNISWAP_EXCHANGE_ERROR);
});
});
describe('sampleBuysFromUniswap()', () => {
const MAKER_TOKEN = randomAddress();
const TAKER_TOKEN = randomAddress();
before(async () => {
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
});
it('throws if tokens are the same', async () => {
const tx = testContract.sampleBuysFromUniswap(MAKER_TOKEN, MAKER_TOKEN, []).callAsync();
return expect(tx).to.revertWith(INVALID_TOKEN_PAIR_ERROR);
});
it('can return no quotes', async () => {
const quotes = await testContract.sampleBuysFromUniswap(TAKER_TOKEN, MAKER_TOKEN, []).callAsync();
expect(quotes).to.deep.eq([]);
});
it('can return many quotes', async () => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Uniswap'], sampleAmounts);
const quotes = await testContract
.sampleBuysFromUniswap(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('can quote token -> ETH', async () => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Uniswap'], sampleAmounts);
const quotes = await testContract
.sampleBuysFromUniswap(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('can quote ETH -> token', async () => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const [expectedQuotes] = getDeterministicBuyQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Uniswap'], sampleAmounts);
const quotes = await testContract
.sampleBuysFromUniswap(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
it('throws if no exchange exists for the maker token', async () => {
const nonExistantToken = randomAddress();
const tx = testContract
.sampleBuysFromUniswap(TAKER_TOKEN, nonExistantToken, getSampleAmounts(TAKER_TOKEN))
.callAsync();
return expect(tx).to.revertWith(UNSUPPORTED_UNISWAP_EXCHANGE_ERROR);
});
it('throws if no exchange exists for the taker token', async () => {
const nonExistantToken = randomAddress();
const tx = testContract
.sampleBuysFromUniswap(nonExistantToken, MAKER_TOKEN, getSampleAmounts(nonExistantToken))
.callAsync();
return expect(tx).to.revertWith(UNSUPPORTED_UNISWAP_EXCHANGE_ERROR);
});
});
});

View File

@@ -0,0 +1,12 @@
/*
* -----------------------------------------------------------------------------
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
* -----------------------------------------------------------------------------
*/
export * from '../test/generated-wrappers/deployment_constants';
export * from '../test/generated-wrappers/erc20_bridge_sampler';
export * from '../test/generated-wrappers/i_erc20_bridge_sampler';
export * from '../test/generated-wrappers/i_eth2_dai';
export * from '../test/generated-wrappers/i_kyber_network';
export * from '../test/generated-wrappers/i_uniswap_exchange_quotes';
export * from '../test/generated-wrappers/test_erc20_bridge_sampler';

View File

@@ -0,0 +1,96 @@
/**
* Use this file to configure your truffle project. It's seeded with some
* common settings for different networks and features like migrations,
* compilation and testing. Uncomment the ones you need or modify
* them to suit your project as necessary.
*
* More information about configuration can be found at:
*
* truffleframework.com/docs/advanced/configuration
*
* To deploy via Infura you'll need a wallet provider (like truffle-hdwallet-provider)
* to sign your transactions before they're sent to a remote public node. Infura accounts
* are available for free at: infura.io/register.
*
* You'll also need a mnemonic - the twelve word phrase the wallet uses to generate
* public/private key pairs. If you're publishing your code to GitHub make sure you load this
* phrase from a file you've .gitignored so it doesn't accidentally become public.
*
*/
// const HDWalletProvider = require('truffle-hdwallet-provider');
// const infuraKey = "fj4jll3k.....";
//
// const fs = require('fs');
// const mnemonic = fs.readFileSync(".secret").toString().trim();
module.exports = {
/**
* Networks define how you connect to your ethereum client and let you set the
* defaults web3 uses to send transactions. If you don't specify one truffle
* will spin up a development blockchain for you on port 9545 when you
* run `develop` or `test`. You can ask a truffle command to use a specific
* network from the command line, e.g
*
* $ truffle test --network <network-name>
*/
networks: {
// Useful for testing. The `development` name is special - truffle uses it by default
// if it's defined here and no other network is specified at the command line.
// You should run a client (like ganache-cli, geth or parity) in a separate terminal
// tab if you use this network and you must also set the `host`, `port` and `network_id`
// options below to some value.
//
// development: {
// host: "127.0.0.1", // Localhost (default: none)
// port: 8545, // Standard Ethereum port (default: none)
// network_id: "*", // Any network (default: none)
// },
// Another network with more advanced options...
// advanced: {
// port: 8777, // Custom port
// network_id: 1342, // Custom network
// gas: 8500000, // Gas sent with each transaction (default: ~6700000)
// gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei)
// from: <address>, // Account to send txs from (default: accounts[0])
// websockets: true // Enable EventEmitter interface for web3 (default: false)
// },
// Useful for deploying to a public network.
// NB: It's important to wrap the provider as a function.
// ropsten: {
// provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/v3/YOUR-PROJECT-ID`),
// network_id: 3, // Ropsten's id
// gas: 5500000, // Ropsten has a lower block limit than mainnet
// confirmations: 2, // # of confs to wait between deployments. (default: 0)
// timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50)
// skipDryRun: true // Skip dry run before migrations? (default: false for public nets )
// },
// Useful for private networks
// private: {
// provider: () => new HDWalletProvider(mnemonic, `https://network.io`),
// network_id: 2111, // This network is yours, in the cloud.
// production: true // Treats this network as if it was a public net. (default: false)
// }
},
// Set default mocha options here, use special reporters etc.
mocha: {
// timeout: 100000
},
// Configure your compilers
compilers: {
solc: {
version: '0.5.9',
settings: {
evmVersion: 'constantinople',
optimizer: {
enabled: true,
runs: 1000000,
details: { yul: true, deduplicate: true, cse: true, constantOptimizer: true },
},
},
},
},
};

View File

@@ -0,0 +1,17 @@
{
"extends": "../../tsconfig",
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
"files": [
"generated-artifacts/ERC20BridgeSampler.json",
"generated-artifacts/IERC20BridgeSampler.json",
"test/generated-artifacts/DeploymentConstants.json",
"test/generated-artifacts/ERC20BridgeSampler.json",
"test/generated-artifacts/IERC20BridgeSampler.json",
"test/generated-artifacts/IEth2Dai.json",
"test/generated-artifacts/IKyberNetwork.json",
"test/generated-artifacts/IUniswapExchangeQuotes.json",
"test/generated-artifacts/TestERC20BridgeSampler.json"
],
"exclude": ["./deploy/solc/solc_bin"]
}

View File

@@ -0,0 +1,10 @@
{
"extends": ["@0x/tslint-config"],
"rules": {
"custom-no-magic-numbers": false,
"max-file-line-count": false
},
"linterOptions": {
"exclude": ["src/artifacts.ts", "test/artifacts.ts"]
}
}

View File

@@ -1,4 +1,44 @@
[
{
"timestamp": 1575931811,
"version": "3.0.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "3.0.0",
"changes": [
{
"note": "Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils",
"pr": 2330
},
{
"note": "Add `decimals()` to `LibERC20Token`.",
"pr": 2344
},
{
"note": "Create `LibERC20Token`",
"pr": 2309
},
{
"note": "Replaced `SafeMath` with `LibSafeMath`",
"pr": 2254
}
],
"timestamp": 1575296764
},
{
"version": "2.3.0-beta.4",
"changes": [
{
"note": "Dependencies updated"
}
],
"timestamp": 1575290197
},
{
"version": "2.3.0-beta.3",
"changes": [
@@ -14,6 +54,10 @@
{
"note": "Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils",
"pr": 2330
},
{
"note": "Add `decimals()` to `LibERC20Token`.",
"pr": 2344
}
],
"timestamp": 1574030254

View File

@@ -5,6 +5,21 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.0.1 - _December 9, 2019_
* Dependencies updated
## v3.0.0 - _December 2, 2019_
* Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330)
* Add `decimals()` to `LibERC20Token`. (#2344)
* Create `LibERC20Token` (#2309)
* Replaced `SafeMath` with `LibSafeMath` (#2254)
## v2.3.0-beta.4 - _December 2, 2019_
* Dependencies updated
## v2.3.0-beta.3 - _November 20, 2019_
* Dependencies updated
@@ -12,6 +27,7 @@ CHANGELOG
## v2.3.0-beta.2 - _November 17, 2019_
* Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330)
* Add `decimals()` to `LibERC20Token`. (#2344)
## v2.3.0-beta.1 - _November 7, 2019_

View File

@@ -24,6 +24,7 @@ import "../src/interfaces/IERC20Token.sol";
library LibERC20Token {
bytes constant private DECIMALS_CALL_DATA = hex"313ce567";
/// @dev Calls `IERC20Token(token).approve()`.
/// Reverts if `false` is returned or if the return
@@ -91,6 +92,21 @@ library LibERC20Token {
_callWithOptionalBooleanResult(token, callData);
}
/// @dev Retrieves the number of decimals for a token.
/// Returns `18` if the call reverts.
/// @return The number of decimals places for the token.
function decimals(address token)
internal
view
returns (uint8 tokenDecimals)
{
tokenDecimals = 18;
(bool didSucceed, bytes memory resultData) = token.staticcall(DECIMALS_CALL_DATA);
if (didSucceed && resultData.length == 32) {
tokenDecimals = uint8(LibBytes.readUint256(resultData, 0));
}
}
/// @dev Executes a call on address `target` with calldata `callData`
/// and asserts that either nothing was returned or a single boolean
/// was returned equal to `true`.

View File

@@ -69,4 +69,16 @@ contract TestLibERC20Token {
target.setBehavior(shouldRevert, revertData, returnData);
LibERC20Token.transferFrom(address(target), from, to, amount);
}
function testDecimals(
bool shouldRevert,
bytes calldata revertData,
bytes calldata returnData
)
external
returns (uint8)
{
target.setBehavior(shouldRevert, revertData, returnData);
return LibERC20Token.decimals(address(target));
}
}

View File

@@ -87,6 +87,14 @@ contract TestLibERC20TokenTarget {
_execute();
}
function decimals()
external
view
returns (uint8)
{
_execute();
}
function _execute() private view {
if (_shouldRevert) {
bytes memory revertData = _revertData;

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-erc20",
"version": "2.3.0-beta.3",
"version": "3.0.1",
"engines": {
"node": ">=6.12"
},
@@ -51,16 +51,18 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md",
"devDependencies": {
"@0x/abi-gen": "^4.4.0-beta.3",
"@0x/contracts-gen": "^1.1.0-beta.3",
"@0x/contracts-test-utils": "^3.2.0-beta.3",
"@0x/contracts-utils": "^3.3.0-beta.3",
"@0x/dev-utils": "^2.4.0-beta.3",
"@0x/sol-compiler": "^3.2.0-beta.3",
"@0x/abi-gen": "^5.0.1",
"@0x/contracts-gen": "^2.0.1",
"@0x/contracts-test-utils": "^5.0.0",
"@0x/contracts-utils": "^4.0.1",
"@0x/dev-utils": "^3.0.1",
"@0x/sol-compiler": "^4.0.1",
"@0x/ts-doc-gen": "^0.0.22",
"@0x/tslint-config": "^3.1.0-beta.2",
"@0x/utils": "^4.6.0-beta.2",
"@0x/web3-wrapper": "^6.1.0-beta.2",
"@0x/tslint-config": "^4.0.0",
"@0x/types": "^3.1.0",
"@0x/typescript-typings": "^5.0.0",
"@0x/utils": "^5.1.0",
"@0x/web3-wrapper": "^7.0.1",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "*",
@@ -68,6 +70,7 @@
"chai-as-promised": "^7.1.0",
"chai-bignumber": "^3.0.0",
"dirty-chai": "^2.0.1",
"ethereum-types": "^3.0.0",
"lodash": "^4.17.11",
"make-promises-safe": "^1.1.0",
"mocha": "^6.2.0",
@@ -79,10 +82,7 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^5.5.0-beta.3",
"@0x/types": "^2.5.0-beta.2",
"@0x/typescript-typings": "^4.4.0-beta.2",
"ethereum-types": "^2.2.0-beta.2"
"@0x/base-contract": "^6.0.1"
},
"publishConfig": {
"access": "public"

View File

@@ -3,11 +3,10 @@ import {
constants,
expect,
getRandomInteger,
hexLeftPad,
randomAddress,
verifyEventsFromLogs,
} from '@0x/contracts-test-utils';
import { RawRevertError, StringRevertError } from '@0x/utils';
import { hexUtils, RawRevertError, StringRevertError } from '@0x/utils';
import { TestLibERC20TokenContract, TestLibERC20TokenTargetEvents } from './wrappers';
@@ -16,11 +15,12 @@ import { artifacts } from './artifacts';
blockchainTests('LibERC20Token', env => {
let testContract: TestLibERC20TokenContract;
const REVERT_STRING = 'WHOOPSIE';
const ENCODED_TRUE = hexLeftPad(1);
const ENCODED_FALSE = hexLeftPad(0);
const ENCODED_TWO = hexLeftPad(2);
const ENCODED_SHORT_TRUE = hexLeftPad(2, 31);
const ENCODED_LONG_TRUE = hexLeftPad(2, 33);
const ENCODED_REVERT = new StringRevertError(REVERT_STRING).encode();
const ENCODED_TRUE = hexUtils.leftPad(1);
const ENCODED_FALSE = hexUtils.leftPad(0);
const ENCODED_TWO = hexUtils.leftPad(2);
const ENCODED_SHORT_TRUE = hexUtils.leftPad(2, 31);
const ENCODED_LONG_TRUE = hexUtils.leftPad(2, 33);
before(async () => {
testContract = await TestLibERC20TokenContract.deployFrom0xArtifactAsync(
@@ -31,16 +31,12 @@ blockchainTests('LibERC20Token', env => {
);
});
function encodeRevert(message: string): string {
return new StringRevertError(message).encode();
}
describe('approve()', () => {
it('calls the target with the correct arguments', async () => {
const spender = randomAddress();
const allowance = getRandomInteger(0, 100e18);
const { logs } = await testContract
.testApprove(false, encodeRevert(REVERT_STRING), ENCODED_TRUE, spender, allowance)
.testApprove(false, ENCODED_REVERT, ENCODED_TRUE, spender, allowance)
.awaitTransactionSuccessAsync();
expect(logs).to.be.length(1);
verifyEventsFromLogs(logs, [{ spender, allowance }], TestLibERC20TokenTargetEvents.ApproveCalled);
@@ -50,7 +46,7 @@ blockchainTests('LibERC20Token', env => {
const spender = randomAddress();
const allowance = getRandomInteger(0, 100e18);
await testContract
.testApprove(false, encodeRevert(REVERT_STRING), ENCODED_TRUE, spender, allowance)
.testApprove(false, ENCODED_REVERT, ENCODED_TRUE, spender, allowance)
.awaitTransactionSuccessAsync();
});
@@ -58,7 +54,7 @@ blockchainTests('LibERC20Token', env => {
const spender = randomAddress();
const allowance = getRandomInteger(0, 100e18);
await testContract
.testApprove(false, encodeRevert(REVERT_STRING), constants.NULL_BYTES, spender, allowance)
.testApprove(false, ENCODED_REVERT, constants.NULL_BYTES, spender, allowance)
.awaitTransactionSuccessAsync();
});
@@ -66,7 +62,7 @@ blockchainTests('LibERC20Token', env => {
const spender = randomAddress();
const allowance = getRandomInteger(0, 100e18);
const tx = testContract
.testApprove(false, encodeRevert(REVERT_STRING), ENCODED_FALSE, spender, allowance)
.testApprove(false, ENCODED_REVERT, ENCODED_FALSE, spender, allowance)
.awaitTransactionSuccessAsync();
const expectedError = new RawRevertError(ENCODED_FALSE);
return expect(tx).to.revertWith(expectedError);
@@ -76,7 +72,7 @@ blockchainTests('LibERC20Token', env => {
const spender = randomAddress();
const allowance = getRandomInteger(0, 100e18);
const tx = testContract
.testApprove(false, encodeRevert(REVERT_STRING), ENCODED_TWO, spender, allowance)
.testApprove(false, ENCODED_REVERT, ENCODED_TWO, spender, allowance)
.awaitTransactionSuccessAsync();
const expectedError = new RawRevertError(ENCODED_TWO);
return expect(tx).to.revertWith(expectedError);
@@ -86,7 +82,7 @@ blockchainTests('LibERC20Token', env => {
const spender = randomAddress();
const allowance = getRandomInteger(0, 100e18);
const tx = testContract
.testApprove(false, encodeRevert(REVERT_STRING), ENCODED_SHORT_TRUE, spender, allowance)
.testApprove(false, ENCODED_REVERT, ENCODED_SHORT_TRUE, spender, allowance)
.awaitTransactionSuccessAsync();
const expectedError = new RawRevertError(ENCODED_SHORT_TRUE);
return expect(tx).to.revertWith(expectedError);
@@ -96,7 +92,7 @@ blockchainTests('LibERC20Token', env => {
const spender = randomAddress();
const allowance = getRandomInteger(0, 100e18);
const tx = testContract
.testApprove(false, encodeRevert(REVERT_STRING), ENCODED_LONG_TRUE, spender, allowance)
.testApprove(false, ENCODED_REVERT, ENCODED_LONG_TRUE, spender, allowance)
.awaitTransactionSuccessAsync();
const expectedError = new RawRevertError(ENCODED_LONG_TRUE);
return expect(tx).to.revertWith(expectedError);
@@ -106,7 +102,7 @@ blockchainTests('LibERC20Token', env => {
const spender = randomAddress();
const allowance = getRandomInteger(0, 100e18);
const tx = testContract
.testApprove(true, encodeRevert(REVERT_STRING), ENCODED_TRUE, spender, allowance)
.testApprove(true, ENCODED_REVERT, ENCODED_TRUE, spender, allowance)
.awaitTransactionSuccessAsync();
return expect(tx).to.revertWith(REVERT_STRING);
});
@@ -126,7 +122,7 @@ blockchainTests('LibERC20Token', env => {
const to = randomAddress();
const amount = getRandomInteger(0, 100e18);
const { logs } = await testContract
.testTransfer(false, encodeRevert(REVERT_STRING), ENCODED_TRUE, to, amount)
.testTransfer(false, ENCODED_REVERT, ENCODED_TRUE, to, amount)
.awaitTransactionSuccessAsync();
expect(logs).to.be.length(1);
verifyEventsFromLogs(logs, [{ to, amount }], TestLibERC20TokenTargetEvents.TransferCalled);
@@ -136,7 +132,7 @@ blockchainTests('LibERC20Token', env => {
const to = randomAddress();
const amount = getRandomInteger(0, 100e18);
await testContract
.testTransfer(false, encodeRevert(REVERT_STRING), ENCODED_TRUE, to, amount)
.testTransfer(false, ENCODED_REVERT, ENCODED_TRUE, to, amount)
.awaitTransactionSuccessAsync();
});
@@ -144,7 +140,7 @@ blockchainTests('LibERC20Token', env => {
const to = randomAddress();
const amount = getRandomInteger(0, 100e18);
await testContract
.testTransfer(false, encodeRevert(REVERT_STRING), constants.NULL_BYTES, to, amount)
.testTransfer(false, ENCODED_REVERT, constants.NULL_BYTES, to, amount)
.awaitTransactionSuccessAsync();
});
@@ -152,7 +148,7 @@ blockchainTests('LibERC20Token', env => {
const to = randomAddress();
const amount = getRandomInteger(0, 100e18);
const tx = testContract
.testTransfer(false, encodeRevert(REVERT_STRING), ENCODED_FALSE, to, amount)
.testTransfer(false, ENCODED_REVERT, ENCODED_FALSE, to, amount)
.awaitTransactionSuccessAsync();
const expectedError = new RawRevertError(ENCODED_FALSE);
return expect(tx).to.revertWith(expectedError);
@@ -162,7 +158,7 @@ blockchainTests('LibERC20Token', env => {
const to = randomAddress();
const amount = getRandomInteger(0, 100e18);
const tx = testContract
.testTransfer(false, encodeRevert(REVERT_STRING), ENCODED_TWO, to, amount)
.testTransfer(false, ENCODED_REVERT, ENCODED_TWO, to, amount)
.awaitTransactionSuccessAsync();
const expectedError = new RawRevertError(ENCODED_TWO);
return expect(tx).to.revertWith(expectedError);
@@ -172,7 +168,7 @@ blockchainTests('LibERC20Token', env => {
const to = randomAddress();
const amount = getRandomInteger(0, 100e18);
const tx = testContract
.testTransfer(false, encodeRevert(REVERT_STRING), ENCODED_SHORT_TRUE, to, amount)
.testTransfer(false, ENCODED_REVERT, ENCODED_SHORT_TRUE, to, amount)
.awaitTransactionSuccessAsync();
const expectedError = new RawRevertError(ENCODED_SHORT_TRUE);
return expect(tx).to.revertWith(expectedError);
@@ -182,7 +178,7 @@ blockchainTests('LibERC20Token', env => {
const to = randomAddress();
const amount = getRandomInteger(0, 100e18);
const tx = testContract
.testTransfer(false, encodeRevert(REVERT_STRING), ENCODED_LONG_TRUE, to, amount)
.testTransfer(false, ENCODED_REVERT, ENCODED_LONG_TRUE, to, amount)
.awaitTransactionSuccessAsync();
const expectedError = new RawRevertError(ENCODED_LONG_TRUE);
return expect(tx).to.revertWith(expectedError);
@@ -192,7 +188,7 @@ blockchainTests('LibERC20Token', env => {
const to = randomAddress();
const amount = getRandomInteger(0, 100e18);
const tx = testContract
.testTransfer(true, encodeRevert(REVERT_STRING), ENCODED_TRUE, to, amount)
.testTransfer(true, ENCODED_REVERT, ENCODED_TRUE, to, amount)
.awaitTransactionSuccessAsync();
return expect(tx).to.revertWith(REVERT_STRING);
});
@@ -213,7 +209,7 @@ blockchainTests('LibERC20Token', env => {
const to = randomAddress();
const amount = getRandomInteger(0, 100e18);
const { logs } = await testContract
.testTransferFrom(false, encodeRevert(REVERT_STRING), ENCODED_TRUE, owner, to, amount)
.testTransferFrom(false, ENCODED_REVERT, ENCODED_TRUE, owner, to, amount)
.awaitTransactionSuccessAsync();
expect(logs).to.be.length(1);
verifyEventsFromLogs(logs, [{ from: owner, to, amount }], TestLibERC20TokenTargetEvents.TransferFromCalled);
@@ -224,7 +220,7 @@ blockchainTests('LibERC20Token', env => {
const to = randomAddress();
const amount = getRandomInteger(0, 100e18);
await testContract
.testTransferFrom(false, encodeRevert(REVERT_STRING), ENCODED_TRUE, owner, to, amount)
.testTransferFrom(false, ENCODED_REVERT, ENCODED_TRUE, owner, to, amount)
.awaitTransactionSuccessAsync();
});
@@ -233,7 +229,7 @@ blockchainTests('LibERC20Token', env => {
const to = randomAddress();
const amount = getRandomInteger(0, 100e18);
await testContract
.testTransferFrom(false, encodeRevert(REVERT_STRING), constants.NULL_BYTES, owner, to, amount)
.testTransferFrom(false, ENCODED_REVERT, constants.NULL_BYTES, owner, to, amount)
.awaitTransactionSuccessAsync();
});
@@ -242,7 +238,7 @@ blockchainTests('LibERC20Token', env => {
const to = randomAddress();
const amount = getRandomInteger(0, 100e18);
const tx = testContract
.testTransferFrom(false, encodeRevert(REVERT_STRING), ENCODED_FALSE, owner, to, amount)
.testTransferFrom(false, ENCODED_REVERT, ENCODED_FALSE, owner, to, amount)
.awaitTransactionSuccessAsync();
const expectedError = new RawRevertError(ENCODED_FALSE);
return expect(tx).to.revertWith(expectedError);
@@ -253,7 +249,7 @@ blockchainTests('LibERC20Token', env => {
const to = randomAddress();
const amount = getRandomInteger(0, 100e18);
const tx = testContract
.testTransferFrom(false, encodeRevert(REVERT_STRING), ENCODED_TWO, owner, to, amount)
.testTransferFrom(false, ENCODED_REVERT, ENCODED_TWO, owner, to, amount)
.awaitTransactionSuccessAsync();
const expectedError = new RawRevertError(ENCODED_TWO);
return expect(tx).to.revertWith(expectedError);
@@ -264,7 +260,7 @@ blockchainTests('LibERC20Token', env => {
const to = randomAddress();
const amount = getRandomInteger(0, 100e18);
const tx = testContract
.testTransferFrom(false, encodeRevert(REVERT_STRING), ENCODED_SHORT_TRUE, owner, to, amount)
.testTransferFrom(false, ENCODED_REVERT, ENCODED_SHORT_TRUE, owner, to, amount)
.awaitTransactionSuccessAsync();
const expectedError = new RawRevertError(ENCODED_SHORT_TRUE);
return expect(tx).to.revertWith(expectedError);
@@ -275,7 +271,7 @@ blockchainTests('LibERC20Token', env => {
const to = randomAddress();
const amount = getRandomInteger(0, 100e18);
const tx = testContract
.testTransferFrom(false, encodeRevert(REVERT_STRING), ENCODED_LONG_TRUE, owner, to, amount)
.testTransferFrom(false, ENCODED_REVERT, ENCODED_LONG_TRUE, owner, to, amount)
.awaitTransactionSuccessAsync();
const expectedError = new RawRevertError(ENCODED_LONG_TRUE);
return expect(tx).to.revertWith(expectedError);
@@ -286,7 +282,7 @@ blockchainTests('LibERC20Token', env => {
const to = randomAddress();
const amount = getRandomInteger(0, 100e18);
const tx = testContract
.testTransferFrom(true, encodeRevert(REVERT_STRING), ENCODED_TRUE, owner, to, amount)
.testTransferFrom(true, ENCODED_REVERT, ENCODED_TRUE, owner, to, amount)
.awaitTransactionSuccessAsync();
return expect(tx).to.revertWith(REVERT_STRING);
});
@@ -301,4 +297,39 @@ blockchainTests('LibERC20Token', env => {
return expect(tx).to.be.rejectedWith('revert');
});
});
describe('decimals()', () => {
const DEFAULT_DECIMALS = 18;
const ENCODED_ZERO = hexUtils.leftPad(0);
const ENCODED_SHORT_ZERO = hexUtils.leftPad(0, 31);
const ENCODED_LONG_ZERO = hexUtils.leftPad(0, 33);
const randomDecimals = () => Math.floor(Math.random() * 256) + 1;
it('returns the number of decimals defined by the token', async () => {
const decimals = randomDecimals();
const encodedDecimals = hexUtils.leftPad(decimals);
const result = await testContract.testDecimals(false, ENCODED_REVERT, encodedDecimals).callAsync();
return expect(result).to.bignumber.eq(decimals);
});
it('returns 0 if the token returns 0', async () => {
const result = await testContract.testDecimals(false, ENCODED_REVERT, ENCODED_ZERO).callAsync();
return expect(result).to.bignumber.eq(0);
});
it('returns 18 if the token returns less than 32 bytes', async () => {
const result = await testContract.testDecimals(false, ENCODED_REVERT, ENCODED_SHORT_ZERO).callAsync();
return expect(result).to.bignumber.eq(DEFAULT_DECIMALS);
});
it('returns 18 if the token returns greater than 32 bytes', async () => {
const result = await testContract.testDecimals(false, ENCODED_REVERT, ENCODED_LONG_ZERO).callAsync();
return expect(result).to.bignumber.eq(DEFAULT_DECIMALS);
});
it('returns 18 if the token reverts', async () => {
const result = await testContract.testDecimals(true, ENCODED_REVERT, ENCODED_ZERO).callAsync();
return expect(result).to.bignumber.eq(DEFAULT_DECIMALS);
});
});
});

View File

@@ -1,4 +1,36 @@
[
{
"timestamp": 1575931811,
"version": "3.0.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "3.0.0",
"changes": [
{
"note": "Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils",
"pr": 2330
},
{
"note": "Replaced `SafeMath` with `LibSafeMath`",
"pr": 2254
}
],
"timestamp": 1575296764
},
{
"version": "2.2.0-beta.4",
"changes": [
{
"note": "Dependencies updated"
}
],
"timestamp": 1575290197
},
{
"version": "2.2.0-beta.3",
"changes": [

View File

@@ -5,6 +5,19 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.0.1 - _December 9, 2019_
* Dependencies updated
## v3.0.0 - _December 2, 2019_
* Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330)
* Replaced `SafeMath` with `LibSafeMath` (#2254)
## v2.2.0-beta.4 - _December 2, 2019_
* Dependencies updated
## v2.2.0-beta.3 - _November 20, 2019_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-erc721",
"version": "2.2.0-beta.3",
"version": "3.0.1",
"engines": {
"node": ">=6.12"
},
@@ -52,17 +52,18 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md",
"devDependencies": {
"@0x/abi-gen": "^4.4.0-beta.3",
"@0x/contracts-gen": "^1.1.0-beta.3",
"@0x/contracts-test-utils": "^3.2.0-beta.3",
"@0x/contracts-utils": "^3.3.0-beta.3",
"@0x/dev-utils": "^2.4.0-beta.3",
"@0x/sol-compiler": "^3.2.0-beta.3",
"@0x/abi-gen": "^5.0.1",
"@0x/contracts-gen": "^2.0.1",
"@0x/contracts-test-utils": "^5.0.0",
"@0x/contracts-utils": "^4.0.1",
"@0x/dev-utils": "^3.0.1",
"@0x/sol-compiler": "^4.0.1",
"@0x/ts-doc-gen": "^0.0.22",
"@0x/tslint-config": "^3.1.0-beta.2",
"@0x/types": "^2.5.0-beta.2",
"@0x/utils": "^4.6.0-beta.2",
"@0x/web3-wrapper": "^6.1.0-beta.2",
"@0x/tslint-config": "^4.0.0",
"@0x/types": "^3.1.0",
"@0x/typescript-typings": "^5.0.0",
"@0x/utils": "^5.1.0",
"@0x/web3-wrapper": "^7.0.1",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "*",
@@ -70,6 +71,7 @@
"chai-as-promised": "^7.1.0",
"chai-bignumber": "^3.0.0",
"dirty-chai": "^2.0.1",
"ethereum-types": "^3.0.0",
"lodash": "^4.17.11",
"make-promises-safe": "^1.1.0",
"mocha": "^6.2.0",
@@ -82,9 +84,7 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^5.5.0-beta.3",
"@0x/typescript-typings": "^4.4.0-beta.2",
"ethereum-types": "^2.2.0-beta.2"
"@0x/base-contract": "^6.0.1"
},
"publishConfig": {
"access": "public"

View File

@@ -2,6 +2,8 @@ export {
DummyERC721ReceiverContract,
DummyERC721TokenContract,
ERC721TokenContract,
ERC721TokenEvents,
ERC721TokenTransferEventArgs,
IERC721ReceiverContract,
} from './wrappers';
export { artifacts } from './artifacts';

View File

@@ -1,4 +1,45 @@
[
{
"timestamp": 1575931811,
"version": "4.0.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "4.0.0",
"changes": [
{
"note": "Added buy support for ERC20Bridge",
"pr": 2356
},
{
"note": "Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils",
"pr": 2330
},
{
"note": "Introduced new export ForwarderRevertErrors",
"pr": 2321
},
{
"note": "Use `LibERC20Token` in `MixinAssets`",
"pr": 2309
}
],
"timestamp": 1575296764
},
{
"version": "3.1.0-beta.4",
"changes": [
{
"note": "Added buy support for ERC20Bridge",
"pr": 2356
}
],
"timestamp": 1575290197
},
{
"version": "3.1.0-beta.3",
"changes": [

View File

@@ -5,6 +5,21 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v4.0.1 - _December 9, 2019_
* Dependencies updated
## v4.0.0 - _December 2, 2019_
* Added buy support for ERC20Bridge (#2356)
* Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330)
* Introduced new export ForwarderRevertErrors (#2321)
* Use `LibERC20Token` in `MixinAssets` (#2309)
## v3.1.0-beta.4 - _December 2, 2019_
* Added buy support for ERC20Bridge (#2356)
## v3.1.0-beta.3 - _November 20, 2019_
* Dependencies updated

View File

@@ -32,13 +32,13 @@ contract Forwarder is
{
constructor (
address _exchange,
bytes memory _wethAssetData
address _weth
)
public
Ownable()
LibConstants(
_exchange,
_wethAssetData
_weth
)
MixinForwarderCore()
{}

View File

@@ -21,9 +21,9 @@ pragma solidity ^0.5.9;
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
import "@0x/contracts-utils/contracts/src/Ownable.sol";
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
import "@0x/contracts-erc721/contracts/src/interfaces/IERC721Token.sol";
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetData.sol";
import "./libs/LibConstants.sol";
import "./libs/LibForwarderRichErrors.sol";
import "./interfaces/IAssets.sol";
@@ -36,13 +36,10 @@ contract MixinAssets is
{
using LibBytes for bytes;
bytes4 constant internal ERC20_TRANSFER_SELECTOR = bytes4(keccak256("transfer(address,uint256)"));
/// @dev Withdraws assets from this contract. The contract formerly required a ZRX balance in order
/// to function optimally, and this function allows the ZRX to be withdrawn by owner.
/// It may also be used to withdraw assets that were accidentally sent to this contract.
/// @dev Withdraws assets from this contract. It may be used by the owner to withdraw assets
/// that were accidentally sent to this contract.
/// @param assetData Byte array encoded for the respective asset proxy.
/// @param amount Amount of ERC20 token to withdraw.
/// @param amount Amount of the asset to withdraw.
function withdrawAsset(
bytes calldata assetData,
uint256 amount
@@ -63,14 +60,16 @@ contract MixinAssets is
external
{
bytes4 proxyId = assetData.readBytes4(0);
bytes4 erc20ProxyId = IAssetData(address(0)).ERC20Token.selector;
// For now we only care about ERC20, since percentage fees on ERC721 tokens are invalid.
if (proxyId == ERC20_DATA_ID) {
address proxyAddress = EXCHANGE.getAssetProxy(ERC20_DATA_ID);
if (proxyId == erc20ProxyId) {
address proxyAddress = EXCHANGE.getAssetProxy(erc20ProxyId);
if (proxyAddress == address(0)) {
LibRichErrors.rrevert(LibForwarderRichErrors.UnregisteredAssetProxyError());
}
IERC20Token assetToken = IERC20Token(assetData.readAddress(16));
assetToken.approve(proxyAddress, MAX_UINT);
address token = assetData.readAddress(16);
LibERC20Token.approve(token, proxyAddress, MAX_UINT);
}
}
@@ -85,9 +84,12 @@ contract MixinAssets is
{
bytes4 proxyId = assetData.readBytes4(0);
if (proxyId == ERC20_DATA_ID) {
if (
proxyId == IAssetData(address(0)).ERC20Token.selector ||
proxyId == IAssetData(address(0)).ERC20Bridge.selector
) {
_transferERC20Token(assetData, amount);
} else if (proxyId == ERC721_DATA_ID) {
} else if (proxyId == IAssetData(address(0)).ERC721Token.selector) {
_transferERC721Token(assetData, amount);
} else {
LibRichErrors.rrevert(LibForwarderRichErrors.UnsupportedAssetProxyError(
@@ -96,7 +98,7 @@ contract MixinAssets is
}
}
/// @dev Decodes ERC20 assetData and transfers given amount to sender.
/// @dev Decodes ERC20 or ERC20Bridge assetData and transfers given amount to sender.
/// @param assetData Byte array encoded for the respective asset proxy.
/// @param amount Amount of asset to transfer to sender.
function _transferERC20Token(

View File

@@ -19,12 +19,15 @@
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibFillResults.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetData.sol";
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
import "./libs/LibConstants.sol";
import "./libs/LibForwarderRichErrors.sol";
import "./MixinAssets.sol";
@@ -34,6 +37,7 @@ contract MixinExchangeWrapper is
LibConstants,
MixinAssets
{
using LibBytes for bytes;
using LibSafeMath for uint256;
/// @dev Fills the input order.
@@ -88,7 +92,10 @@ contract MixinExchangeWrapper is
)
{
// No taker fee or percentage fee
if (order.takerFee == 0 || order.takerFeeAssetData.equals(order.makerAssetData)) {
if (
order.takerFee == 0 ||
_areUnderlyingAssetsEqual(order.takerFeeAssetData, order.makerAssetData)
) {
// Attempt to sell the remaining amount of WETH
LibFillResults.FillResults memory singleFillResults = _fillOrderNoThrow(
order,
@@ -102,8 +109,9 @@ contract MixinExchangeWrapper is
// Subtract fee from makerAssetFilledAmount for the net amount acquired.
makerAssetAcquiredAmount = singleFillResults.makerAssetFilledAmount
.safeSub(singleFillResults.takerFeePaid);
// WETH fee
} else if (order.takerFeeAssetData.equals(order.takerAssetData)) {
} else if (_areUnderlyingAssetsEqual(order.takerFeeAssetData, order.takerAssetData)) {
// We will first sell WETH as the takerAsset, then use it to pay the takerFee.
// This ensures that we reserve enough to pay the taker and protocol fees.
@@ -125,6 +133,7 @@ contract MixinExchangeWrapper is
.safeAdd(singleFillResults.protocolFeePaid);
makerAssetAcquiredAmount = singleFillResults.makerAssetFilledAmount;
// Unsupported fee
} else {
LibRichErrors.rrevert(LibForwarderRichErrors.UnsupportedFeeError(order.takerFeeAssetData));
@@ -139,7 +148,7 @@ contract MixinExchangeWrapper is
/// @param signatures Proofs that orders have been signed by makers.
/// @return totalWethSpentAmount Total amount of WETH spent on the given orders.
/// @return totalMakerAssetAcquiredAmount Total amount of maker asset acquired from the given orders.
function _marketSellWeth(
function _marketSellNoThrow(
LibOrder.Order[] memory orders,
uint256 wethSellAmount,
bytes[] memory signatures
@@ -150,9 +159,10 @@ contract MixinExchangeWrapper is
uint256 totalMakerAssetAcquiredAmount
)
{
uint256 ordersLength = orders.length;
uint256 protocolFee = tx.gasprice.safeMul(EXCHANGE.protocolFeeMultiplier());
bytes4 erc20BridgeProxyId = IAssetData(address(0)).ERC20Bridge.selector;
uint256 ordersLength = orders.length;
for (uint256 i = 0; i != ordersLength; i++) {
// Preemptively skip to avoid division by zero in _marketSellSingleOrder
if (orders[i].makerAssetAmount == 0 || orders[i].takerAssetAmount == 0) {
@@ -164,6 +174,15 @@ contract MixinExchangeWrapper is
.safeSub(totalWethSpentAmount)
.safeSub(protocolFee);
// If the maker asset is ERC20Bridge, take a snapshot of the Forwarder contract's balance.
bytes4 makerAssetProxyId = orders[i].makerAssetData.readBytes4(0);
address tokenAddress;
uint256 balanceBefore;
if (makerAssetProxyId == erc20BridgeProxyId) {
tokenAddress = orders[i].makerAssetData.readAddress(16);
balanceBefore = IERC20Token(tokenAddress).balanceOf(address(this));
}
(
uint256 wethSpentAmount,
uint256 makerAssetAcquiredAmount
@@ -173,6 +192,15 @@ contract MixinExchangeWrapper is
remainingTakerAssetFillAmount
);
// Account for the ERC20Bridge transfering more of the maker asset than expected.
if (makerAssetProxyId == erc20BridgeProxyId) {
uint256 balanceAfter = IERC20Token(tokenAddress).balanceOf(address(this));
makerAssetAcquiredAmount = LibSafeMath.max256(
balanceAfter.safeSub(balanceBefore),
makerAssetAcquiredAmount
);
}
_transferAssetToSender(orders[i].makerAssetData, makerAssetAcquiredAmount);
totalWethSpentAmount = totalWethSpentAmount
@@ -206,7 +234,10 @@ contract MixinExchangeWrapper is
)
{
// No taker fee or WETH fee
if (order.takerFee == 0 || order.takerFeeAssetData.equals(order.takerAssetData)) {
if (
order.takerFee == 0 ||
_areUnderlyingAssetsEqual(order.takerFeeAssetData, order.takerAssetData)
) {
// Calculate the remaining amount of takerAsset to sell
uint256 remainingTakerAssetFillAmount = LibMath.getPartialAmountCeil(
order.takerAssetAmount,
@@ -227,8 +258,9 @@ contract MixinExchangeWrapper is
.safeAdd(singleFillResults.protocolFeePaid);
makerAssetAcquiredAmount = singleFillResults.makerAssetFilledAmount;
// Percentage fee
} else if (order.takerFeeAssetData.equals(order.makerAssetData)) {
} else if (_areUnderlyingAssetsEqual(order.takerFeeAssetData, order.makerAssetData)) {
// Calculate the remaining amount of takerAsset to sell
uint256 remainingTakerAssetFillAmount = LibMath.getPartialAmountCeil(
order.takerAssetAmount,
@@ -249,6 +281,7 @@ contract MixinExchangeWrapper is
// Subtract fee from makerAssetFilledAmount for the net amount acquired.
makerAssetAcquiredAmount = singleFillResults.makerAssetFilledAmount
.safeSub(singleFillResults.takerFeePaid);
// Unsupported fee
} else {
LibRichErrors.rrevert(LibForwarderRichErrors.UnsupportedFeeError(order.takerFeeAssetData));
@@ -266,7 +299,7 @@ contract MixinExchangeWrapper is
/// @param signatures Proofs that orders have been signed by makers.
/// @return totalWethSpentAmount Total amount of WETH spent on the given orders.
/// @return totalMakerAssetAcquiredAmount Total amount of maker asset acquired from the given orders.
function _marketBuyExactAmountWithWeth(
function _marketBuyFillOrKill(
LibOrder.Order[] memory orders,
uint256 makerAssetBuyAmount,
bytes[] memory signatures
@@ -277,6 +310,8 @@ contract MixinExchangeWrapper is
uint256 totalMakerAssetAcquiredAmount
)
{
bytes4 erc20BridgeProxyId = IAssetData(address(0)).ERC20Bridge.selector;
uint256 ordersLength = orders.length;
for (uint256 i = 0; i != ordersLength; i++) {
// Preemptively skip to avoid division by zero in _marketBuySingleOrder
@@ -287,6 +322,15 @@ contract MixinExchangeWrapper is
uint256 remainingMakerAssetFillAmount = makerAssetBuyAmount
.safeSub(totalMakerAssetAcquiredAmount);
// If the maker asset is ERC20Bridge, take a snapshot of the Forwarder contract's balance.
bytes4 makerAssetProxyId = orders[i].makerAssetData.readBytes4(0);
address tokenAddress;
uint256 balanceBefore;
if (makerAssetProxyId == erc20BridgeProxyId) {
tokenAddress = orders[i].makerAssetData.readAddress(16);
balanceBefore = IERC20Token(tokenAddress).balanceOf(address(this));
}
(
uint256 wethSpentAmount,
uint256 makerAssetAcquiredAmount
@@ -296,6 +340,15 @@ contract MixinExchangeWrapper is
remainingMakerAssetFillAmount
);
// Account for the ERC20Bridge transfering more of the maker asset than expected.
if (makerAssetProxyId == erc20BridgeProxyId) {
uint256 balanceAfter = IERC20Token(tokenAddress).balanceOf(address(this));
makerAssetAcquiredAmount = LibSafeMath.max256(
balanceAfter.safeSub(balanceBefore),
makerAssetAcquiredAmount
);
}
_transferAssetToSender(orders[i].makerAssetData, makerAssetAcquiredAmount);
totalWethSpentAmount = totalWethSpentAmount
@@ -316,4 +369,36 @@ contract MixinExchangeWrapper is
));
}
}
/// @dev Checks whether one asset is effectively equal to another asset.
/// This is the case if they have the same ERC20Proxy/ERC20BridgeProxy asset data, or if
/// one is the ERC20Bridge equivalent of the other.
/// @param assetData1 Byte array encoded for the takerFee asset proxy.
/// @param assetData2 Byte array encoded for the maker asset proxy.
/// @return areEqual Whether or not the underlying assets are equal.
function _areUnderlyingAssetsEqual(
bytes memory assetData1,
bytes memory assetData2
)
internal
pure
returns (bool)
{
bytes4 assetProxyId1 = assetData1.readBytes4(0);
bytes4 assetProxyId2 = assetData2.readBytes4(0);
bytes4 erc20ProxyId = IAssetData(address(0)).ERC20Token.selector;
bytes4 erc20BridgeProxyId = IAssetData(address(0)).ERC20Bridge.selector;
if (
(assetProxyId1 == erc20ProxyId || assetProxyId1 == erc20BridgeProxyId) &&
(assetProxyId2 == erc20ProxyId || assetProxyId2 == erc20BridgeProxyId)
) {
// Compare the underlying token addresses.
address token1 = assetData1.readAddress(16);
address token2 = assetData2.readAddress(16);
return (token1 == token2);
} else {
return false;
}
}
}

View File

@@ -24,6 +24,7 @@ import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetData.sol";
import "./libs/LibConstants.sol";
import "./libs/LibForwarderRichErrors.sol";
import "./interfaces/IAssets.sol";
@@ -46,7 +47,7 @@ contract MixinForwarderCore is
constructor ()
public
{
address proxyAddress = EXCHANGE.getAssetProxy(ERC20_DATA_ID);
address proxyAddress = EXCHANGE.getAssetProxy(IAssetData(address(0)).ERC20Token.selector);
if (proxyAddress == address(0)) {
LibRichErrors.rrevert(LibForwarderRichErrors.UnregisteredAssetProxyError());
}
@@ -62,53 +63,42 @@ contract MixinForwarderCore is
/// as possible, accounting for order and forwarder fees.
/// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset.
/// @param signatures Proofs that orders have been created by makers.
/// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.
/// @param feeRecipient Address that will receive ETH when orders are filled.
/// @param ethFeeAmounts Amounts of ETH, denominated in Wei, that are paid to corresponding feeRecipients.
/// @param feeRecipients Addresses that will receive ETH when orders are filled.
/// @return wethSpentAmount Amount of WETH spent on the given set of orders.
/// @return makerAssetAcquiredAmount Amount of maker asset acquired from the given set of orders.
/// @return ethFeePaid Amount of ETH spent on the given forwarder fee.
function marketSellOrdersWithEth(
LibOrder.Order[] memory orders,
bytes[] memory signatures,
uint256 feePercentage,
address payable feeRecipient
uint256[] memory ethFeeAmounts,
address payable[] memory feeRecipients
)
public
payable
returns (
uint256 wethSpentAmount,
uint256 makerAssetAcquiredAmount,
uint256 ethFeePaid
uint256 makerAssetAcquiredAmount
)
{
// Convert ETH to WETH.
_convertEthToWeth();
// Calculate amount of WETH that won't be spent on the forwarder fee.
uint256 wethSellAmount = LibMath.getPartialAmountFloor(
PERCENTAGE_DENOMINATOR,
feePercentage.safeAdd(PERCENTAGE_DENOMINATOR),
msg.value
// Pay ETH affiliate fees to all feeRecipient addresses
uint256 wethRemaining = _transferEthFeesAndWrapRemaining(
ethFeeAmounts,
feeRecipients
);
// Spends up to wethSellAmount to fill orders, transfers purchased assets to msg.sender,
// Spends up to wethRemaining to fill orders, transfers purchased assets to msg.sender,
// and pays WETH order fees.
(
wethSpentAmount,
makerAssetAcquiredAmount
) = _marketSellWeth(
) = _marketSellNoThrow(
orders,
wethSellAmount,
wethRemaining,
signatures
);
// Transfer feePercentage of total ETH spent on orders to feeRecipient.
// Refund remaining ETH to msg.sender.
ethFeePaid = _transferEthFeeAndRefund(
wethSpentAmount,
feePercentage,
feeRecipient
);
_transferEthRefund(wethRemaining, wethSpentAmount);
}
/// @dev Attempt to buy makerAssetBuyAmount of makerAsset by selling ETH provided with transaction.
@@ -118,45 +108,41 @@ contract MixinForwarderCore is
/// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset.
/// @param makerAssetBuyAmount Desired amount of makerAsset to purchase.
/// @param signatures Proofs that orders have been created by makers.
/// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.
/// @param feeRecipient Address that will receive ETH when orders are filled.
/// @param ethFeeAmounts Amounts of ETH, denominated in Wei, that are paid to corresponding feeRecipients.
/// @param feeRecipients Addresses that will receive ETH when orders are filled.
/// @return wethSpentAmount Amount of WETH spent on the given set of orders.
/// @return makerAssetAcquiredAmount Amount of maker asset acquired from the given set of orders.
/// @return ethFeePaid Amount of ETH spent on the given forwarder fee.
function marketBuyOrdersWithEth(
LibOrder.Order[] memory orders,
uint256 makerAssetBuyAmount,
bytes[] memory signatures,
uint256 feePercentage,
address payable feeRecipient
uint256[] memory ethFeeAmounts,
address payable[] memory feeRecipients
)
public
payable
returns (
uint256 wethSpentAmount,
uint256 makerAssetAcquiredAmount,
uint256 ethFeePaid
uint256 makerAssetAcquiredAmount
)
{
// Convert ETH to WETH.
_convertEthToWeth();
// Pay ETH affiliate fees to all feeRecipient addresses
uint256 wethRemaining = _transferEthFeesAndWrapRemaining(
ethFeeAmounts,
feeRecipients
);
// Attempts to fill the desired amount of makerAsset and trasnfer purchased assets to msg.sender.
(
wethSpentAmount,
makerAssetAcquiredAmount
) = _marketBuyExactAmountWithWeth(
) = _marketBuyFillOrKill(
orders,
makerAssetBuyAmount,
signatures
);
// Transfer feePercentage of total ETH spent on orders to feeRecipient.
// Refund remaining ETH to msg.sender.
ethFeePaid = _transferEthFeeAndRefund(
wethSpentAmount,
feePercentage,
feeRecipient
);
_transferEthRefund(wethRemaining, wethSpentAmount);
}
}

View File

@@ -20,7 +20,6 @@ pragma solidity ^0.5.9;
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
import "./libs/LibConstants.sol";
import "./libs/LibForwarderRichErrors.sol";
@@ -42,39 +41,61 @@ contract MixinWeth is
}
}
/// @dev Converts message call's ETH value into WETH.
function _convertEthToWeth()
internal
{
if (msg.value == 0) {
LibRichErrors.rrevert(LibForwarderRichErrors.MsgValueCannotEqualZeroError());
}
ETHER_TOKEN.deposit.value(msg.value)();
}
/// @dev Transfers feePercentage of WETH spent on primary orders to feeRecipient.
/// Refunds any excess ETH to msg.sender.
/// @param wethSpent Amount of WETH spent when filling orders.
/// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.
/// @param feeRecipient Address that will receive ETH when orders are filled.
/// @return ethFee Amount paid to feeRecipient as a percentage fee on the total WETH sold.
function _transferEthFeeAndRefund(
uint256 wethSpent,
uint256 feePercentage,
address payable feeRecipient
/// @dev Transfers ETH denominated fees to all feeRecipient addresses
/// @param ethFeeAmounts Amounts of ETH, denominated in Wei, that are paid to corresponding feeRecipients.
/// @param feeRecipients Addresses that will receive ETH when orders are filled.
/// @return ethRemaining msg.value minus the amount of ETH spent on affiliate fees.
function _transferEthFeesAndWrapRemaining(
uint256[] memory ethFeeAmounts,
address payable[] memory feeRecipients
)
internal
returns (uint256 ethFee)
returns (uint256 ethRemaining)
{
// Ensure feePercentage is less than 5%.
if (feePercentage > MAX_FEE_PERCENTAGE) {
LibRichErrors.rrevert(LibForwarderRichErrors.FeePercentageTooLargeError(
feePercentage
uint256 feesLen = ethFeeAmounts.length;
// ethFeeAmounts len must equal feeRecipients len
if (feesLen != feeRecipients.length) {
LibRichErrors.rrevert(LibForwarderRichErrors.EthFeeLengthMismatchError(
feesLen,
feeRecipients.length
));
}
// This function is always called before any other function, so we assume that
// the ETH remaining is the entire msg.value.
ethRemaining = msg.value;
for (uint256 i = 0; i != feesLen; i++) {
uint256 ethFeeAmount = ethFeeAmounts[i];
// Ensure there is enough ETH to pay the fee
if (ethRemaining < ethFeeAmount) {
LibRichErrors.rrevert(LibForwarderRichErrors.InsufficientEthForFeeError(
ethFeeAmount,
ethRemaining
));
}
// Decrease ethRemaining and transfer fee to corresponding feeRecipient
ethRemaining = ethRemaining.safeSub(ethFeeAmount);
feeRecipients[i].transfer(ethFeeAmount);
}
// Convert remaining ETH to WETH.
ETHER_TOKEN.deposit.value(ethRemaining)();
return ethRemaining;
}
/// @dev Refunds any excess ETH to msg.sender.
/// @param initialWethAmount Amount of WETH available after transferring affiliate fees.
/// @param wethSpent Amount of WETH spent when filling orders.
function _transferEthRefund(
uint256 initialWethAmount,
uint256 wethSpent
)
internal
{
// Ensure that no extra WETH owned by this contract has been spent.
if (wethSpent > msg.value) {
if (wethSpent > initialWethAmount) {
LibRichErrors.rrevert(LibForwarderRichErrors.OverspentWethError(
wethSpent,
msg.value
@@ -82,38 +103,15 @@ contract MixinWeth is
}
// Calculate amount of WETH that hasn't been spent.
uint256 wethRemaining = msg.value.safeSub(wethSpent);
// Calculate ETH fee to pay to feeRecipient.
ethFee = LibMath.getPartialAmountFloor(
feePercentage,
PERCENTAGE_DENOMINATOR,
wethSpent
);
// Ensure fee is less than amount of WETH remaining.
if (ethFee > wethRemaining) {
LibRichErrors.rrevert(LibForwarderRichErrors.InsufficientEthForFeeError(
ethFee,
wethRemaining
));
}
uint256 wethRemaining = initialWethAmount.safeSub(wethSpent);
// Do nothing if no WETH remaining
if (wethRemaining > 0) {
// Convert remaining WETH to ETH
ETHER_TOKEN.withdraw(wethRemaining);
// Pay ETH to feeRecipient
if (ethFee > 0) {
feeRecipient.transfer(ethFee);
}
// Refund remaining ETH to msg.sender.
uint256 ethRefund = wethRemaining.safeSub(ethFee);
if (ethRefund > 0) {
msg.sender.transfer(ethRefund);
}
// Transfer remaining ETH to sender
msg.sender.transfer(wethRemaining);
}
}
}

View File

@@ -29,23 +29,21 @@ contract IForwarderCore {
/// as possible, accounting for order and forwarder fees.
/// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset.
/// @param signatures Proofs that orders have been created by makers.
/// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.
/// @param feeRecipient Address that will receive ETH when orders are filled.
/// @param ethFeeAmounts Amounts of ETH, denominated in Wei, that are paid to corresponding feeRecipients.
/// @param feeRecipients Addresses that will receive ETH when orders are filled.
/// @return wethSpentAmount Amount of WETH spent on the given set of orders.
/// @return makerAssetAcquiredAmount Amount of maker asset acquired from the given set of orders.
/// @return ethFeePaid Amount of ETH spent on the given forwarder fee.
function marketSellOrdersWithEth(
LibOrder.Order[] memory orders,
bytes[] memory signatures,
uint256 feePercentage,
address payable feeRecipient
uint256[] memory ethFeeAmounts,
address payable[] memory feeRecipients
)
public
payable
returns (
uint256 wethSpentAmount,
uint256 makerAssetAcquiredAmount,
uint256 ethFeePaid
uint256 makerAssetAcquiredAmount
);
/// @dev Attempt to buy makerAssetBuyAmount of makerAsset by selling ETH provided with transaction.
@@ -55,23 +53,21 @@ contract IForwarderCore {
/// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset.
/// @param makerAssetBuyAmount Desired amount of makerAsset to purchase.
/// @param signatures Proofs that orders have been created by makers.
/// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.
/// @param feeRecipient Address that will receive ETH when orders are filled.
/// @param ethFeeAmounts Amounts of ETH, denominated in Wei, that are paid to corresponding feeRecipients.
/// @param feeRecipients Addresses that will receive ETH when orders are filled.
/// @return wethSpentAmount Amount of WETH spent on the given set of orders.
/// @return makerAssetAcquiredAmount Amount of maker asset acquired from the given set of orders.
/// @return ethFeePaid Amount of ETH spent on the given forwarder fee.
function marketBuyOrdersWithEth(
LibOrder.Order[] memory orders,
uint256 makerAssetBuyAmount,
bytes[] memory signatures,
uint256 feePercentage,
address payable feeRecipient
uint256[] memory ethFeeAmounts,
address payable[] memory feeRecipients
)
public
payable
returns (
uint256 wethSpentAmount,
uint256 makerAssetAcquiredAmount,
uint256 ethFeePaid
uint256 makerAssetAcquiredAmount
);
}

View File

@@ -27,28 +27,20 @@ contract LibConstants {
using LibBytes for bytes;
bytes4 constant internal ERC20_DATA_ID = bytes4(keccak256("ERC20Token(address)"));
bytes4 constant internal ERC721_DATA_ID = bytes4(keccak256("ERC721Token(address,uint256)"));
uint256 constant internal MAX_UINT = 2**256 - 1;
uint256 constant internal PERCENTAGE_DENOMINATOR = 10**18;
uint256 constant internal MAX_FEE_PERCENTAGE = 5 * PERCENTAGE_DENOMINATOR / 100; // 5%
// solhint-disable var-name-mixedcase
IExchange internal EXCHANGE;
IEtherToken internal ETHER_TOKEN;
bytes internal WETH_ASSET_DATA;
// solhint-enable var-name-mixedcase
constructor (
address _exchange,
bytes memory _wethAssetData
address _weth
)
public
{
EXCHANGE = IExchange(_exchange);
WETH_ASSET_DATA = _wethAssetData;
address etherToken = _wethAssetData.readAddress(16);
ETHER_TOKEN = IEtherToken(etherToken);
ETHER_TOKEN = IEtherToken(_weth);
}
}

View File

@@ -18,8 +18,6 @@
pragma solidity ^0.5.9;
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
library LibForwarderRichErrors {
@@ -39,10 +37,6 @@ library LibForwarderRichErrors {
bytes4 internal constant UNSUPPORTED_FEE_ERROR_SELECTOR =
0x31360af1;
// bytes4(keccak256("FeePercentageTooLargeError(uint256)"))
bytes4 internal constant FEE_PERCENTAGE_TOO_LARGE_ERROR_SELECTOR =
0x1174fb80;
// bytes4(keccak256("InsufficientEthForFeeError(uint256,uint256)"))
bytes4 internal constant INSUFFICIENT_ETH_FOR_FEE_ERROR_SELECTOR =
0xecf40fd9;
@@ -55,13 +49,13 @@ library LibForwarderRichErrors {
bytes4 internal constant DEFAULT_FUNCTION_WETH_CONTRACT_ONLY_ERROR_SELECTOR =
0x08b18698;
// bytes4(keccak256("MsgValueCannotEqualZeroError()"))
bytes4 internal constant MSG_VALUE_CANNOT_EQUAL_ZERO_ERROR_SELECTOR =
0x8c0e562b;
// bytes4(keccak256("Erc721AmountMustEqualOneError(uint256)"))
bytes4 internal constant ERC721_AMOUNT_MUST_EQUAL_ONE_ERROR_SELECTOR =
0xbaffa474;
// bytes4(keccak256("EthFeeLengthMismatchError(uint256,uint256)"))
bytes4 internal constant ETH_FEE_LENGTH_MISMATCH_ERROR_SELECTOR =
0x3ecb6ceb;
// solhint-disable func-name-mixedcase
function UnregisteredAssetProxyError()
@@ -113,19 +107,6 @@ library LibForwarderRichErrors {
);
}
function FeePercentageTooLargeError(
uint256 feePercentage
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
FEE_PERCENTAGE_TOO_LARGE_ERROR_SELECTOR,
feePercentage
);
}
function InsufficientEthForFeeError(
uint256 ethFeeRequired,
uint256 ethAvailable
@@ -169,14 +150,6 @@ library LibForwarderRichErrors {
);
}
function MsgValueCannotEqualZeroError()
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(MSG_VALUE_CANNOT_EQUAL_ZERO_ERROR_SELECTOR);
}
function Erc721AmountMustEqualOneError(
uint256 amount
)
@@ -190,4 +163,18 @@ library LibForwarderRichErrors {
);
}
function EthFeeLengthMismatchError(
uint256 ethFeesLength,
uint256 feeRecipientsLength
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
ETH_FEE_LENGTH_MISMATCH_ERROR_SELECTOR,
ethFeesLength,
feeRecipientsLength
);
}
}

View File

@@ -0,0 +1,63 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "../src/MixinExchangeWrapper.sol";
import "../src/libs/LibConstants.sol";
contract TestForwarder is
LibConstants,
MixinExchangeWrapper
{
// solhint-disable no-empty-blocks
constructor ()
public
LibConstants(
address(0),
address(0)
)
{}
function areUnderlyingAssetsEqual(
bytes memory assetData1,
bytes memory assetData2
)
public
returns (bool)
{
return _areUnderlyingAssetsEqual(
assetData1,
assetData2
);
}
function transferAssetToSender(
bytes memory assetData,
uint256 amount
)
public
{
_transferAssetToSender(
assetData,
amount
);
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-exchange-forwarder",
"version": "3.1.0-beta.3",
"version": "4.0.1",
"engines": {
"node": ">=6.12"
},
@@ -14,11 +14,12 @@
"build:ts": "tsc -b",
"build:ci": "yarn build",
"pre_build": "run-s compile contracts:gen generate_contract_wrappers contracts:copy",
"test": "echo !!! Tests have been relocated to @0x/contracts-integrations !!!",
"test": "yarn run_mocha",
"rebuild_and_test": "run-s build test",
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
"test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html",
"test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha",
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
"compile": "sol-compiler",
"watch": "sol-compiler -w",
"clean": "shx rm -rf lib test/generated-artifacts test/generated-wrappers generated-artifacts generated-wrappers",
@@ -38,7 +39,7 @@
},
"config": {
"publicInterfaceContracts": "Forwarder",
"abis": "./test/generated-artifacts/@(Forwarder|IAssets|IForwarder|IForwarderCore|LibConstants|LibForwarderRichErrors|MixinAssets|MixinExchangeWrapper|MixinForwarderCore|MixinWeth).json",
"abis": "./test/generated-artifacts/@(Forwarder|IAssets|IForwarder|IForwarderCore|LibConstants|LibForwarderRichErrors|MixinAssets|MixinExchangeWrapper|MixinForwarderCore|MixinWeth|TestForwarder).json",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
},
"repository": {
@@ -51,24 +52,24 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
"devDependencies": {
"@0x/abi-gen": "^4.4.0-beta.3",
"@0x/contracts-asset-proxy": "^2.3.0-beta.3",
"@0x/contracts-dev-utils": "^0.1.0-beta.3",
"@0x/contracts-erc20": "^2.3.0-beta.3",
"@0x/contracts-erc721": "^2.2.0-beta.3",
"@0x/contracts-exchange": "^2.2.0-beta.3",
"@0x/contracts-exchange-libs": "^3.1.0-beta.3",
"@0x/contracts-gen": "^1.1.0-beta.3",
"@0x/contracts-test-utils": "^3.2.0-beta.3",
"@0x/contracts-utils": "^3.3.0-beta.3",
"@0x/dev-utils": "^2.4.0-beta.3",
"@0x/order-utils": "^8.5.0-beta.3",
"@0x/sol-compiler": "^3.2.0-beta.3",
"@0x/abi-gen": "^5.0.1",
"@0x/contracts-asset-proxy": "^3.0.1",
"@0x/contracts-dev-utils": "^1.0.1",
"@0x/contracts-erc20": "^3.0.1",
"@0x/contracts-erc721": "^3.0.1",
"@0x/contracts-exchange": "^3.0.1",
"@0x/contracts-exchange-libs": "^4.0.1",
"@0x/contracts-gen": "^2.0.1",
"@0x/contracts-test-utils": "^5.0.0",
"@0x/contracts-utils": "^4.0.1",
"@0x/dev-utils": "^3.0.1",
"@0x/order-utils": "^10.0.0",
"@0x/sol-compiler": "^4.0.1",
"@0x/ts-doc-gen": "^0.0.22",
"@0x/tslint-config": "^3.1.0-beta.2",
"@0x/types": "^2.5.0-beta.2",
"@0x/utils": "^4.6.0-beta.2",
"@0x/web3-wrapper": "^6.1.0-beta.2",
"@0x/tslint-config": "^4.0.0",
"@0x/types": "^3.1.0",
"@0x/utils": "^5.1.0",
"@0x/web3-wrapper": "^7.0.1",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "*",
@@ -88,9 +89,9 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^5.5.0-beta.3",
"@0x/typescript-typings": "^4.4.0-beta.2",
"ethereum-types": "^2.2.0-beta.2"
"@0x/base-contract": "^6.0.1",
"@0x/typescript-typings": "^5.0.0",
"ethereum-types": "^3.0.0"
},
"publishConfig": {
"access": "public"

View File

@@ -1,6 +1,6 @@
export { artifacts } from './artifacts';
export { ForwarderContract } from './wrappers';
export import ForwarderRevertErrors = require('./revert_errors');
export { ExchangeForwarderRevertErrors } from '@0x/utils';
export {
ContractArtifact,
ContractChains,

View File

@@ -15,6 +15,7 @@ import * as MixinAssets from '../test/generated-artifacts/MixinAssets.json';
import * as MixinExchangeWrapper from '../test/generated-artifacts/MixinExchangeWrapper.json';
import * as MixinForwarderCore from '../test/generated-artifacts/MixinForwarderCore.json';
import * as MixinWeth from '../test/generated-artifacts/MixinWeth.json';
import * as TestForwarder from '../test/generated-artifacts/TestForwarder.json';
export const artifacts = {
Forwarder: Forwarder as ContractArtifact,
MixinAssets: MixinAssets as ContractArtifact,
@@ -26,4 +27,5 @@ export const artifacts = {
IForwarderCore: IForwarderCore as ContractArtifact,
LibConstants: LibConstants as ContractArtifact,
LibForwarderRichErrors: LibForwarderRichErrors as ContractArtifact,
TestForwarder: TestForwarder as ContractArtifact,
};

Some files were not shown because too many files have changed in this diff Show More