Compare commits

...

99 Commits

Author SHA1 Message Date
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
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
474 changed files with 13263 additions and 14971 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:
@@ -202,9 +195,9 @@ jobs:
- image: nikolaik/python-nodejs:python3.7-nodejs8
- image: 0xorg/ganache-cli:4.4.0-beta.1
environment:
VERSION: 4.4.0-beta.1
VERSION: latest
SNAPSHOT_NAME: 0x_ganache_snapshot-v3-beta
- image: 0xorg/mesh:6.0.0-beta-0xv3
- image: 0xorg/mesh:0xV3
environment:
ETHEREUM_RPC_URL: 'http://localhost:8545'
ETHEREUM_NETWORK_ID: '50'
@@ -369,7 +362,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 +371,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,64 @@
[
{
"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,24 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## 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

@@ -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
@@ -61,8 +60,8 @@ contract Eth2DaiBridge is
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
@@ -93,6 +92,6 @@ contract Eth2DaiBridge is
view
returns (IEth2Dai exchange)
{
return IEth2Dai(ETH2DAI_ADDRESS);
return IEth2Dai(_getEth2DaiAddress());
}
}

View File

@@ -0,0 +1,166 @@
/*
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 = _getKyberContract();
state.weth = _getWETHContract();
// 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;
}
/// @dev Overridable way to get the `KyberNetworkProxy` contract.
/// @return kyber The `IKyberNetworkProxy` contract.
function _getKyberContract()
internal
view
returns (IKyberNetworkProxy kyber)
{
return IKyberNetworkProxy(_getKyberNetworkProxyAddress());
}
/// @dev Overridable way to get the WETH contract.
/// @return weth The WETH contract.
function _getWETHContract()
internal
view
returns (IEtherToken weth)
{
return IEtherToken(_getWETHAddress());
}
}

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 {
@@ -170,7 +168,7 @@ contract UniswapBridge is
view
returns (IEtherToken token)
{
return IEtherToken(WETH_ADDRESS);
return IEtherToken(_getWETHAddress());
}
/// @dev Overridable way to get the uniswap exchange factory contract.
@@ -180,7 +178,7 @@ contract UniswapBridge is
view
returns (IUniswapExchangeFactory factory)
{
return IUniswapExchangeFactory(UNISWAP_EXCHANGE_FACTORY_ADDRESS);
return IUniswapExchangeFactory(_getUniswapExchangeFactoryAddress());
}
/// @dev Grants an unlimited allowance to the exchange for its token
@@ -212,7 +210,9 @@ contract UniswapBridge is
if (fromTokenAddress == address(getWethContract())) {
exchangeTokenAddress = toTokenAddress;
}
exchange = getUniswapExchangeFactoryContract().getExchange(exchangeTokenAddress);
exchange = IUniswapExchange(
getUniswapExchangeFactoryContract().getExchange(exchangeTokenAddress)
);
require(address(exchange) != address(0), "NO_UNISWAP_EXCHANGE_FOR_TOKEN");
return exchange;
}

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,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 _getKyberContract()
internal
view
returns (IKyberNetworkProxy kyber)
{
return IKyberNetworkProxy(address(this));
}
// @dev overridden to point to test WETH.
function _getWETHContract()
internal
view
returns (IEtherToken weth_)
{
return weth;
}
}

View File

@@ -407,9 +407,9 @@ contract TestUniswapBridge is
function getExchange(address tokenAddress)
external
view
returns (IUniswapExchange)
returns (address)
{
return IUniswapExchange(_testExchanges[tokenAddress]);
return address(_testExchanges[tokenAddress]);
}
// @dev Use `wethToken`.

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-asset-proxy",
"version": "2.3.0-beta.3",
"version": "3.0.0",
"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,TestStaticCallTarget",
"abis": "./test/generated-artifacts/@(ERC1155Proxy|ERC20BridgeProxy|ERC20Proxy|ERC721Proxy|Eth2DaiBridge|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|IERC20Bridge|IEth2Dai|IKyberNetworkProxy|IUniswapExchange|IUniswapExchangeFactory|KyberBridge|MixinAssetProxyDispatcher|MixinAuthorizable|MultiAssetProxy|Ownable|StaticCallProxy|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.0",
"@0x/contracts-gen": "^2.0.0",
"@0x/contracts-test-utils": "^4.0.0",
"@0x/contracts-utils": "^4.0.0",
"@0x/dev-utils": "^3.0.0",
"@0x/sol-compiler": "^4.0.0",
"@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.0.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.0",
"@0x/contracts-dev-utils": "^1.0.0",
"@0x/contracts-erc1155": "^2.0.0",
"@0x/contracts-erc20": "^3.0.0",
"@0x/contracts-erc721": "^3.0.0",
"@0x/order-utils": "^9.0.0",
"@0x/typescript-typings": "^5.0.0",
"@0x/utils": "^5.0.0",
"@0x/web3-wrapper": "^7.0.0",
"ethereum-types": "^3.0.0",
"lodash": "^4.17.11"
},
"publishConfig": {

View File

@@ -12,6 +12,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 +28,6 @@ export const artifacts = {
IAssetData: IAssetData as ContractArtifact,
IAssetProxy: IAssetProxy as ContractArtifact,
UniswapBridge: UniswapBridge as ContractArtifact,
KyberBridge: KyberBridge 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

@@ -10,6 +10,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

@@ -16,8 +16,10 @@ import * as IAssetProxyDispatcher from '../test/generated-artifacts/IAssetProxyD
import * as IAuthorizable from '../test/generated-artifacts/IAuthorizable.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';
@@ -25,6 +27,7 @@ import * as Ownable from '../test/generated-artifacts/Ownable.json';
import * as StaticCallProxy from '../test/generated-artifacts/StaticCallProxy.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';
@@ -39,6 +42,7 @@ export const artifacts = {
MultiAssetProxy: MultiAssetProxy as ContractArtifact,
StaticCallProxy: StaticCallProxy as ContractArtifact,
Eth2DaiBridge: Eth2DaiBridge as ContractArtifact,
KyberBridge: KyberBridge as ContractArtifact,
UniswapBridge: UniswapBridge as ContractArtifact,
IAssetData: IAssetData as ContractArtifact,
IAssetProxy: IAssetProxy as ContractArtifact,
@@ -46,10 +50,12 @@ export const artifacts = {
IAuthorizable: IAuthorizable as ContractArtifact,
IERC20Bridge: IERC20Bridge as ContractArtifact,
IEth2Dai: IEth2Dai as ContractArtifact,
IKyberNetworkProxy: IKyberNetworkProxy as ContractArtifact,
IUniswapExchange: IUniswapExchange as ContractArtifact,
IUniswapExchangeFactory: IUniswapExchangeFactory 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,272 @@
import {
blockchainTests,
constants,
expect,
getRandomInteger,
hexLeftPad,
hexRandom,
randomAddress,
verifyEventsFromLogs,
} from '@0x/contracts-test-utils';
import { AssetProxyId } from '@0x/types';
import { BigNumber } 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(hexRandom(), hexRandom(_.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.
hexLeftPad(_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

@@ -14,8 +14,10 @@ export * from '../test/generated-wrappers/i_asset_proxy_dispatcher';
export * from '../test/generated-wrappers/i_authorizable';
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';
@@ -23,6 +25,7 @@ export * from '../test/generated-wrappers/ownable';
export * from '../test/generated-wrappers/static_call_proxy';
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

@@ -10,6 +10,7 @@
"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",
@@ -25,8 +26,10 @@
"test/generated-artifacts/IAuthorizable.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",
@@ -34,6 +37,7 @@
"test/generated-artifacts/StaticCallProxy.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,71 @@
[
{
"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,26 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## 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.0",
"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.0",
"@0x/contracts-asset-proxy": "^3.0.0",
"@0x/contracts-dev-utils": "^1.0.0",
"@0x/contracts-erc20": "^3.0.0",
"@0x/contracts-exchange": "^3.0.0",
"@0x/contracts-gen": "^2.0.0",
"@0x/contracts-test-utils": "^4.0.0",
"@0x/dev-utils": "^3.0.0",
"@0x/order-utils": "^9.0.0",
"@0x/sol-compiler": "^4.0.0",
"@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.0",
"@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.0",
"@0x/base-contract": "^6.0.0",
"@0x/contract-addresses": "^4.0.0",
"@0x/contracts-utils": "^4.0.0",
"@0x/json-schemas": "^5.0.0",
"@0x/types": "^3.0.0",
"@0x/typescript-typings": "^5.0.0",
"@0x/utils": "^5.0.0",
"ethereum-types": "^3.0.0",
"http-status-codes": "^1.3.2"
},
"publishConfig": {
"access": "public"

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

@@ -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

@@ -12,9 +12,7 @@ import {
} 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 } from '@0x/utils';
import { ApprovalFactory } from '../src/approval_factory';

View File

@@ -1,4 +1,55 @@
[
{
"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,22 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## 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.0",
"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.0",
"@0x/assert": "^3.0.0",
"@0x/contracts-gen": "^2.0.0",
"@0x/sol-compiler": "^4.0.0",
"@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.0"
},
"publishConfig": {
"access": "public"

View File

@@ -1,4 +1,31 @@
[
{
"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,16 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## 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.0",
"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.0",
"@0x/contracts-gen": "^2.0.0",
"@0x/contracts-utils": "^4.0.0",
"@0x/dev-utils": "^3.0.0",
"@0x/sol-compiler": "^4.0.0",
"@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.0.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.0",
"@0x/contracts-test-utils": "^4.0.0",
"@0x/utils": "^5.0.0",
"@0x/web3-wrapper": "^7.0.0",
"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,30 @@
[
{
"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,18 @@
<!--
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.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.0",
"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.0",
"@0x/contracts-asset-proxy": "^3.0.0",
"@0x/contracts-erc20": "^3.0.0",
"@0x/contracts-exchange": "^3.0.0",
"@0x/contracts-exchange-libs": "^4.0.0",
"@0x/contracts-gen": "^2.0.0",
"@0x/contracts-test-utils": "^4.0.0",
"@0x/contracts-utils": "^4.0.0",
"@0x/dev-utils": "^3.0.0",
"@0x/sol-compiler": "^4.0.0",
"@0x/tslint-config": "^4.0.0",
"@0x/web3-wrapper": "^7.0.0",
"@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.0",
"@0x/types": "^3.0.0",
"@0x/typescript-typings": "^5.0.0",
"@0x/utils": "^5.0.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,852 @@
import {
blockchainTests,
constants,
expect,
getRandomInteger,
getRandomPortion,
hexConcat,
hexHash,
hexLeftPad,
hexRandom,
randomAddress,
toHex,
} from '@0x/contracts-test-utils';
import { Order, OrderInfo } from '@0x/types';
import { BigNumber } 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 = hexConcat('0xf47261b1', hexLeftPad(randomAddress()));
const INVALID_ASSET_DATA = hexRandom(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 hexHash(hexConcat(...args.map(a => 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(toHex(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 hexConcat(ERC20_PROXY_ID, hexLeftPad(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(hexRandom()),
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,35 @@
[
{
"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 +45,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,17 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## 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 +23,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.0",
"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.0",
"@0x/contracts-gen": "^2.0.0",
"@0x/contracts-test-utils": "^4.0.0",
"@0x/contracts-utils": "^4.0.0",
"@0x/dev-utils": "^3.0.0",
"@0x/sol-compiler": "^4.0.0",
"@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.0.0",
"@0x/typescript-typings": "^5.0.0",
"@0x/utils": "^5.0.0",
"@0x/web3-wrapper": "^7.0.0",
"@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.0"
},
"publishConfig": {
"access": "public"

View File

@@ -16,6 +16,7 @@ import { artifacts } from './artifacts';
blockchainTests('LibERC20Token', env => {
let testContract: TestLibERC20TokenContract;
const REVERT_STRING = 'WHOOPSIE';
const ENCODED_REVERT = new StringRevertError(REVERT_STRING).encode();
const ENCODED_TRUE = hexLeftPad(1);
const ENCODED_FALSE = hexLeftPad(0);
const ENCODED_TWO = hexLeftPad(2);
@@ -31,16 +32,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 +47,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 +55,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 +63,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 +73,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 +83,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 +93,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 +103,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 +123,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 +133,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 +141,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 +149,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 +159,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 +169,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 +179,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 +189,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 +210,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 +221,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 +230,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 +239,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 +250,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 +261,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 +272,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 +283,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 +298,39 @@ blockchainTests('LibERC20Token', env => {
return expect(tx).to.be.rejectedWith('revert');
});
});
describe('decimals()', () => {
const DEFAULT_DECIMALS = 18;
const ENCODED_ZERO = hexLeftPad(0);
const ENCODED_SHORT_ZERO = hexLeftPad(0, 31);
const ENCODED_LONG_ZERO = hexLeftPad(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 = hexLeftPad(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,27 @@
[
{
"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,15 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## 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.0",
"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.0",
"@0x/contracts-gen": "^2.0.0",
"@0x/contracts-test-utils": "^4.0.0",
"@0x/contracts-utils": "^4.0.0",
"@0x/dev-utils": "^3.0.0",
"@0x/sol-compiler": "^4.0.0",
"@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.0.0",
"@0x/typescript-typings": "^5.0.0",
"@0x/utils": "^5.0.0",
"@0x/web3-wrapper": "^7.0.0",
"@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.0"
},
"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,36 @@
[
{
"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,17 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## 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,
@@ -103,7 +110,7 @@ contract MixinExchangeWrapper is
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.
@@ -150,9 +157,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 +172,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 +190,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 +232,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,
@@ -228,7 +257,7 @@ contract MixinExchangeWrapper is
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,
@@ -277,6 +306,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 +318,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 +336,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 +365,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());
}

View File

@@ -27,8 +27,6 @@ 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%
@@ -36,19 +34,15 @@ contract LibConstants {
// 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 {

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.0",
"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.0",
"@0x/contracts-asset-proxy": "^3.0.0",
"@0x/contracts-dev-utils": "^1.0.0",
"@0x/contracts-erc20": "^3.0.0",
"@0x/contracts-erc721": "^3.0.0",
"@0x/contracts-exchange": "^3.0.0",
"@0x/contracts-exchange-libs": "^4.0.0",
"@0x/contracts-gen": "^2.0.0",
"@0x/contracts-test-utils": "^4.0.0",
"@0x/contracts-utils": "^4.0.0",
"@0x/dev-utils": "^3.0.0",
"@0x/order-utils": "^9.0.0",
"@0x/sol-compiler": "^4.0.0",
"@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.0.0",
"@0x/utils": "^5.0.0",
"@0x/web3-wrapper": "^7.0.0",
"@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.0",
"@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,
};

View File

@@ -0,0 +1,184 @@
import { IAssetDataContract } from '@0x/contracts-asset-proxy';
import {
artifacts as ERC20Artifacts,
DummyERC20TokenContract,
ERC20TokenEvents,
ERC20TokenTransferEventArgs,
} from '@0x/contracts-erc20';
import {
artifacts as ERC721Artifacts,
DummyERC721TokenContract,
ERC721TokenEvents,
ERC721TokenTransferEventArgs,
} from '@0x/contracts-erc721';
import {
blockchainTests,
constants,
expect,
getRandomInteger,
hexRandom,
hexSlice,
randomAddress,
verifyEventsFromLogs,
} from '@0x/contracts-test-utils';
import { BigNumber, ExchangeForwarderRevertErrors } from '@0x/utils';
import { artifacts } from './artifacts';
import { TestForwarderContract } from './wrappers';
blockchainTests('Supported asset type unit tests', env => {
let forwarder: TestForwarderContract;
let assetDataEncoder: IAssetDataContract;
let bridgeAddress: string;
let bridgeData: string;
let receiver: string;
let erc20Token: DummyERC20TokenContract;
let erc721Token: DummyERC721TokenContract;
let nftId: BigNumber;
let erc20AssetData: string;
let erc721AssetData: string;
let erc20BridgeAssetData: string;
before(async () => {
[receiver] = await env.getAccountAddressesAsync();
assetDataEncoder = new IAssetDataContract(constants.NULL_ADDRESS, env.provider);
forwarder = await TestForwarderContract.deployFrom0xArtifactAsync(
artifacts.TestForwarder,
env.provider,
env.txDefaults,
{ ...artifacts, ...ERC20Artifacts, ...ERC721Artifacts },
);
erc20Token = await DummyERC20TokenContract.deployFrom0xArtifactAsync(
ERC20Artifacts.DummyERC20Token,
env.provider,
env.txDefaults,
ERC20Artifacts,
constants.DUMMY_TOKEN_NAME,
constants.DUMMY_TOKEN_SYMBOL,
constants.DUMMY_TOKEN_DECIMALS,
constants.DUMMY_TOKEN_TOTAL_SUPPLY,
);
erc20AssetData = assetDataEncoder.ERC20Token(erc20Token.address).getABIEncodedTransactionData();
erc721Token = await DummyERC721TokenContract.deployFrom0xArtifactAsync(
ERC721Artifacts.DummyERC721Token,
env.provider,
env.txDefaults,
ERC721Artifacts,
constants.DUMMY_TOKEN_NAME,
constants.DUMMY_TOKEN_SYMBOL,
);
nftId = getRandomInteger(constants.ZERO_AMOUNT, constants.MAX_UINT256);
erc721AssetData = assetDataEncoder.ERC721Token(erc721Token.address, nftId).getABIEncodedTransactionData();
bridgeAddress = randomAddress();
bridgeData = hexRandom();
erc20BridgeAssetData = assetDataEncoder
.ERC20Bridge(erc20Token.address, bridgeAddress, bridgeData)
.getABIEncodedTransactionData();
});
describe('_areUnderlyingAssetsEqual', () => {
it('returns true if assetData1 == assetData2 are ERC20', async () => {
const result = await forwarder.areUnderlyingAssetsEqual(erc20AssetData, erc20AssetData).callAsync();
expect(result).to.be.true();
});
it('returns true if assetData1 == assetData2 are ERC20Bridge', async () => {
const result = await forwarder
.areUnderlyingAssetsEqual(erc20BridgeAssetData, erc20BridgeAssetData)
.callAsync();
expect(result).to.be.true();
});
it('returns true if assetData2 is the ERC20Bridge equivalent of assetData1', async () => {
const result = await forwarder.areUnderlyingAssetsEqual(erc20AssetData, erc20BridgeAssetData).callAsync();
expect(result).to.be.true();
});
it('returns false if assetData1 != assetData2 are ERC20', async () => {
const differentERC20AssetData = assetDataEncoder.ERC20Token(randomAddress()).getABIEncodedTransactionData();
const result = await forwarder
.areUnderlyingAssetsEqual(erc20AssetData, differentERC20AssetData)
.callAsync();
expect(result).to.be.false();
});
it('returns false if assetData1 is ERC20 and assetData2 is ERC721', async () => {
const result = await forwarder.areUnderlyingAssetsEqual(erc20AssetData, erc721AssetData).callAsync();
expect(result).to.be.false();
});
it('returns false if assetData2 is ERC20Bridge, but for a different token than assetData1', async () => {
const mismatchedErc20BridgeAssetData = assetDataEncoder
.ERC20Bridge(randomAddress(), bridgeAddress, bridgeData)
.getABIEncodedTransactionData();
const result = await forwarder
.areUnderlyingAssetsEqual(erc20AssetData, mismatchedErc20BridgeAssetData)
.callAsync();
expect(result).to.be.false();
});
it('returns false if assetData1 == assetData2 are ERC721', async () => {
const result = await forwarder.areUnderlyingAssetsEqual(erc721AssetData, erc721AssetData).callAsync();
expect(result).to.be.false();
});
});
describe('_transferAssetToSender', () => {
const TRANSFER_AMOUNT = new BigNumber(1);
before(async () => {
await erc20Token
.setBalance(forwarder.address, constants.INITIAL_ERC20_BALANCE)
.awaitTransactionSuccessAsync();
await erc721Token.mint(forwarder.address, nftId).awaitTransactionSuccessAsync();
});
it('transfers an ERC20 token given ERC20 assetData', async () => {
const txReceipt = await forwarder
.transferAssetToSender(erc20AssetData, TRANSFER_AMOUNT)
.awaitTransactionSuccessAsync({ from: receiver });
verifyEventsFromLogs<ERC20TokenTransferEventArgs>(
txReceipt.logs,
[{ _from: forwarder.address, _to: receiver, _value: TRANSFER_AMOUNT }],
ERC20TokenEvents.Transfer,
);
});
it('transfers an ERC721 token given ERC721 assetData and amount == 1', async () => {
const txReceipt = await forwarder
.transferAssetToSender(erc721AssetData, TRANSFER_AMOUNT)
.awaitTransactionSuccessAsync({ from: receiver });
verifyEventsFromLogs<ERC721TokenTransferEventArgs>(
txReceipt.logs,
[{ _from: forwarder.address, _to: receiver, _tokenId: nftId }],
ERC721TokenEvents.Transfer,
);
});
it('reverts if attempting to transfer an ERC721 token with amount != 1', async () => {
const invalidAmount = new BigNumber(2);
const tx = forwarder
.transferAssetToSender(erc721AssetData, invalidAmount)
.awaitTransactionSuccessAsync({ from: receiver });
const expectedError = new ExchangeForwarderRevertErrors.Erc721AmountMustEqualOneError(invalidAmount);
return expect(tx).to.revertWith(expectedError);
});
it('transfers an ERC20 token given ERC20Bridge assetData', async () => {
const txReceipt = await forwarder
.transferAssetToSender(erc20BridgeAssetData, TRANSFER_AMOUNT)
.awaitTransactionSuccessAsync({ from: receiver });
verifyEventsFromLogs<ERC20TokenTransferEventArgs>(
txReceipt.logs,
[{ _from: forwarder.address, _to: receiver, _value: TRANSFER_AMOUNT }],
ERC20TokenEvents.Transfer,
);
});
it('reverts if assetData is unsupported', async () => {
const randomBytes = hexRandom();
const tx = forwarder
.transferAssetToSender(randomBytes, TRANSFER_AMOUNT)
.awaitTransactionSuccessAsync({ from: receiver });
const expectedError = new ExchangeForwarderRevertErrors.UnsupportedAssetProxyError(
hexSlice(randomBytes, 0, 4),
);
return expect(tx).to.revertWith(expectedError);
});
});
});

View File

@@ -13,3 +13,4 @@ export * from '../test/generated-wrappers/mixin_assets';
export * from '../test/generated-wrappers/mixin_exchange_wrapper';
export * from '../test/generated-wrappers/mixin_forwarder_core';
export * from '../test/generated-wrappers/mixin_weth';
export * from '../test/generated-wrappers/test_forwarder';

View File

@@ -13,7 +13,8 @@
"test/generated-artifacts/MixinAssets.json",
"test/generated-artifacts/MixinExchangeWrapper.json",
"test/generated-artifacts/MixinForwarderCore.json",
"test/generated-artifacts/MixinWeth.json"
"test/generated-artifacts/MixinWeth.json",
"test/generated-artifacts/TestForwarder.json"
],
"exclude": ["./deploy/solc/solc_bin"]
}

View File

@@ -1,4 +1,135 @@
[
{
"version": "4.0.0",
"changes": [
{
"note": "Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils",
"pr": 2330
},
{
"note": "Introduced new export LibMathRevertErrors",
"pr": 2321
},
{
"note": "Break up `LibEIP712` into reusable components",
"pr": 1742
},
{
"note": "Add `chainId` to EIP712 domain schema",
"pr": 1742
},
{
"note": "Rename `verifyingContract` to `verifyingContractAddress` in domain schema",
"pr": 1742
},
{
"note": "Add LibZeroExTransaction contract",
"pr": 1753
},
{
"note": "Add verifyingContractIfExists arg to LibEIP712ExchangeDomain constructor",
"pr": 1753
},
{
"note": "Remove LibEIP712ExchangeDomainConstants and LibEIP712 contracts",
"pr": 1753
},
{
"note": "Add `LibExchangeRichErrorDecoder` contract.",
"pr": 1790
},
{
"note": "Break out types/interaces from `MExchangeRichErrors` into `MExchangeRichErrorTypes`.",
"pr": 1790
},
{
"note": "Reorder some revert error parameters for consistency",
"pr": 1790
},
{
"note": "Add new `Order` fields for arbitrary fee tokens (ZEIP-28).",
"pr": 1819
},
{
"note": "Remove `LibAbiEncoder` and `LibConstants`.",
"pr": 1819
},
{
"note": "Add `generate-exchange-selectors` package script.",
"pr": 1819
},
{
"note": "Add `expirationTimeSeconds` to `ZeroExTransaction` struct",
"pr": 1823
},
{
"note": "Add reference functions for `LibMath` and `LibFillResults`",
"pr": 2031
},
{
"note": "Move in revamped `LibMath` tests from the `contracts-exchange` package.",
"pr": 2031
},
{
"note": "Move in revamped `LibFillResults` tests from the `contracts-exchange` package.",
"pr": 2031
},
{
"note": "Remove unecessary zero-denominator checks in `LibMath`.",
"pr": 2031
},
{
"note": "Fix coverage hooks.",
"pr": 2031
},
{
"note": "Regenerate selectors.",
"pr": 2042
},
{
"note": "Convert `LibFillResults`, `LibOrder`, `LibZeroExTransaction`, and `LibMath` to libraries",
"pr": 2055
},
{
"note": "Remove `LibExchangeSelectors`",
"pr": 2055
},
{
"note": "Add `LibExchangeRichErrors`",
"pr": 2055
},
{
"note": "Add `calculateFillResults` and `calculateMatchedFillResults` to `LibFillResults`",
"pr": 2055
},
{
"note": "Remove `_hashEIP712ExchangeMessage` from `LibEIP712ExchangeDomain`",
"pr": 2055
},
{
"note": "Compile and export all contracts, artifacts, and wrappers by default",
"pr": 2055
},
{
"note": "Update `IncompleteFillError` to take an `errorCode`, `expectedAssetFillAmount`, and `actualAssetFillAmount` fields.",
"pr": 2075
},
{
"note": "Move `IWallet.sol` from `asset-proxy` and `exchange` packages to here.",
"pr": 2233
}
],
"timestamp": 1575296764
},
{
"version": "3.1.0-beta.4",
"changes": [
{
"note": "Dependencies updated"
}
],
"timestamp": 1575290197
},
{
"version": "3.1.0-beta.3",
"changes": [

View File

@@ -5,6 +5,42 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v4.0.0 - _December 2, 2019_
* Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330)
* Introduced new export LibMathRevertErrors (#2321)
* Break up `LibEIP712` into reusable components (#1742)
* Add `chainId` to EIP712 domain schema (#1742)
* Rename `verifyingContract` to `verifyingContractAddress` in domain schema (#1742)
* Add LibZeroExTransaction contract (#1753)
* Add verifyingContractIfExists arg to LibEIP712ExchangeDomain constructor (#1753)
* Remove LibEIP712ExchangeDomainConstants and LibEIP712 contracts (#1753)
* Add `LibExchangeRichErrorDecoder` contract. (#1790)
* Break out types/interaces from `MExchangeRichErrors` into `MExchangeRichErrorTypes`. (#1790)
* Reorder some revert error parameters for consistency (#1790)
* Add new `Order` fields for arbitrary fee tokens (ZEIP-28). (#1819)
* Remove `LibAbiEncoder` and `LibConstants`. (#1819)
* Add `generate-exchange-selectors` package script. (#1819)
* Add `expirationTimeSeconds` to `ZeroExTransaction` struct (#1823)
* Add reference functions for `LibMath` and `LibFillResults` (#2031)
* Move in revamped `LibMath` tests from the `contracts-exchange` package. (#2031)
* Move in revamped `LibFillResults` tests from the `contracts-exchange` package. (#2031)
* Remove unecessary zero-denominator checks in `LibMath`. (#2031)
* Fix coverage hooks. (#2031)
* Regenerate selectors. (#2042)
* Convert `LibFillResults`, `LibOrder`, `LibZeroExTransaction`, and `LibMath` to libraries (#2055)
* Remove `LibExchangeSelectors` (#2055)
* Add `LibExchangeRichErrors` (#2055)
* Add `calculateFillResults` and `calculateMatchedFillResults` to `LibFillResults` (#2055)
* Remove `_hashEIP712ExchangeMessage` from `LibEIP712ExchangeDomain` (#2055)
* Compile and export all contracts, artifacts, and wrappers by default (#2055)
* Update `IncompleteFillError` to take an `errorCode`, `expectedAssetFillAmount`, and `actualAssetFillAmount` fields. (#2075)
* Move `IWallet.sol` from `asset-proxy` and `exchange` packages to here. (#2233)
## v3.1.0-beta.4 - _December 2, 2019_
* Dependencies updated
## v3.1.0-beta.3 - _November 20, 2019_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-exchange-libs",
"version": "3.1.0-beta.3",
"version": "4.0.0",
"engines": {
"node": ">=6.12"
},
@@ -52,15 +52,15 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/libs/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/dev-utils": "^2.4.0-beta.3",
"@0x/sol-compiler": "^3.2.0-beta.3",
"@0x/subproviders": "^5.1.0-beta.2",
"@0x/abi-gen": "^5.0.0",
"@0x/contracts-gen": "^2.0.0",
"@0x/contracts-test-utils": "^4.0.0",
"@0x/dev-utils": "^3.0.0",
"@0x/sol-compiler": "^4.0.0",
"@0x/subproviders": "^6.0.0",
"@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.0",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "*",
@@ -81,13 +81,13 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^5.5.0-beta.3",
"@0x/contracts-utils": "^3.3.0-beta.3",
"@0x/order-utils": "^8.5.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/base-contract": "^6.0.0",
"@0x/contracts-utils": "^4.0.0",
"@0x/order-utils": "^9.0.0",
"@0x/types": "^3.0.0",
"@0x/typescript-typings": "^5.0.0",
"@0x/utils": "^5.0.0",
"ethereum-types": "^3.0.0"
},
"publishConfig": {
"access": "public"

View File

@@ -8,7 +8,7 @@ export {
LibOrderContract,
LibZeroExTransactionContract,
} from './wrappers';
export import LibMathRevertErrors = require('./lib_math_revert_errors');
export { LibMathRevertErrors } from '@0x/utils';
import * as ReferenceFunctionsToExport from './reference_functions';
export import ReferenceFunctions = ReferenceFunctionsToExport;

View File

@@ -1,8 +1,6 @@
import { ReferenceFunctions } from '@0x/contracts-utils';
import { FillResults, Order } from '@0x/types';
import { BigNumber } from '@0x/utils';
import LibMathRevertErrors = require('./lib_math_revert_errors');
import { BigNumber, LibMathRevertErrors } from '@0x/utils';
const { safeAdd, safeSub, safeMul, safeDiv } = ReferenceFunctions;

View File

@@ -9,11 +9,10 @@ import {
} from '@0x/contracts-test-utils';
import { SafeMathRevertErrors } from '@0x/contracts-utils';
import { FillResults, MatchedFillResults, Order } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { BigNumber, LibMathRevertErrors } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import * as _ from 'lodash';
import LibMathRevertErrors = require('../src/lib_math_revert_errors');
import { addFillResults, calculateFillResults, getPartialAmountFloor } from '../src/reference_functions';
import { artifacts } from './artifacts';

View File

@@ -7,9 +7,8 @@ import {
uint256Values,
} from '@0x/contracts-test-utils';
import { SafeMathRevertErrors } from '@0x/contracts-utils';
import { BigNumber } from '@0x/utils';
import { BigNumber, LibMathRevertErrors } from '@0x/utils';
import LibMathRevertErrors = require('../src/lib_math_revert_errors');
import {
getPartialAmountCeil,
getPartialAmountFloor,

View File

@@ -1,9 +1,8 @@
import { constants, describe, expect } from '@0x/contracts-test-utils';
import { SafeMathRevertErrors } from '@0x/contracts-utils';
import { BigNumber } from '@0x/utils';
import { BigNumber, LibMathRevertErrors } from '@0x/utils';
import * as _ from 'lodash';
import LibMathRevertErrors = require('../src/lib_math_revert_errors');
import {
addFillResults,
getPartialAmountCeil,

View File

@@ -1,4 +1,235 @@
[
{
"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 ExchangeRevertErrors",
"pr": 2321
},
{
"note": "Round up in `marketBuyOrdersNoThrow()` so `marketBuyOrdersFillOrKill()` doesn't throw up.",
"pr": 2338
},
{
"note": "LocalBalanceStore.create and constructor now require an instance of DevUtilsContract",
"pr": 2304
},
{
"note": "In LocalBalanceStore, `transferAsset` is now `transferAssetAsync`",
"pr": 2304
},
{
"note": "Test utility classes AssetWrapper, MatchOrderTester, and OrderFactoryFromScenario constructors now require an instance of DevUtilsContract",
"pr": 2304
},
{
"note": "In OrderFactoryFromScenario, `generateOrder` is now `generateOrderAsync`",
"pr": 2304
},
{
"note": "Use new/cheaper reentrancy guard/mutex",
"pr": 1699
},
{
"note": "Update domain separator",
"pr": 1742
},
{
"note": "Refactor `executeTransaction` to take `ZeroExTransaction` struct as input",
"pr": 1753
},
{
"note": "Refactor example contracts that use `executeTransaction`",
"pr": 1753
},
{
"note": "Upgrade all string reverts to rich reverts",
"pr": 1761
},
{
"note": "Add support for `SignatureType.OrderValidator` for orders",
"pr": 1774
},
{
"note": "Add support for `SignatureType.WalletOrderValidator` for orders",
"pr": 1774
},
{
"note": "Add a `bytes` return value to `executeTransaction`, which is equal to the encoded return data of the underlying Exchange function call",
"pr": 1793
},
{
"note": "Implement `batchExecuteTransactions`",
"pr": 1793
},
{
"note": "Refactor preSign to be compatible with `executeTransaction`",
"pr": 1793
},
{
"note": "Remove ZRX fees in lieu of arbitrary maker and taker fee tokens.",
"pr": 1819
},
{
"note": "Incorporate Multi-asset and ERC1155 tests into `fillOrder` and `matchOrders` tests",
"pr": 1819
},
{
"note": "Swap fill order from maker -> taker to taker -> maker",
"pr": 1819
},
{
"note": "Avoid redundant transfer in `fillOrder()` and `matchOrders()` when maker/taker is the same as feeRecipient and assets are the same",
"pr": 1819
},
{
"note": "Implement `cancelOrderNoThrow` and `batchCancelOrdersNoThrow` functions",
"pr": 1827
},
{
"note": "`executeTransaction` will now revert if the input transaction is expired",
"pr": 1832
},
{
"note": "Log an `TransactionExecuted` event when an `executeTransaction` call is successful",
"pr": 1832
},
{
"note": "Return a FillResults array for batch fill variants",
"pr": 1834
},
{
"note": "Add `MixinTransferSimulator` contract for simulating multiple transfers on-chain",
"pr": 1868
},
{
"note": "Add `EIP1271Wallet` signature type",
"pr": 1885
},
{
"note": "Remove `WalletOrderValidator` and `OrderValidator` signature types",
"pr": 1885
},
{
"note": "Make the regular `Validator` signature type have EIP1271 behavior",
"pr": 1885
},
{
"note": "Always check signature types that are validated via contract (not just on first fill).",
"pr": 1885
},
{
"note": "Remove unecessary rich revert error types.",
"pr": 1885
},
{
"note": "Add `IEIP1271Wallet` interface",
"pr": 1885
},
{
"note": "Add `validatorAddress` field to `SignatureValidatorError` rich reverts",
"pr": 1885
},
{
"note": "Make `calculateMatchedFillResults` public",
"pr": 1885
},
{
"note": "Updated RichErrors to the library pattern",
"pr": 1913
},
{
"note": "Rewrote _dispatchTransferFrom in Solidity",
"pr": 2020
},
{
"note": "Add `TestIsolatedExchange` contract and `IsolatedExchangeWrapper` test class",
"pr": 2031
},
{
"note": "Add `ReferenceFunctions` as package export.",
"pr": 2031
},
{
"note": "Remove `TestExchangeMath.sol`. Exchange math functions are now tested in the `exchange-libs` package and reference implementations are available there as well.",
"pr": 2031
},
{
"note": "Remove functions from `TestExchangeInternals.sol` that are no longer tested in this package.",
"pr": 2031
},
{
"note": "Remove `_assertValidFill()`",
"pr": 2031
},
{
"note": "Add `wrapper_unit_tests` tests and `TestWrapperFunctions` contract",
"pr": 2042
},
{
"note": "Disallow `signerAddress == 0` in signature validation functions.",
"pr": 2042
},
{
"note": "Update `Wallet` signature type behavior to be in line with v2.1.",
"pr": 2042
},
{
"note": "Add (semi) automated reentrancy tests and remove manual ones",
"pr": 2042
},
{
"note": "Refactor to use new `LibFillResults`, `LibOrder`, `LibZeroExTransaction`, and `LibMath` to libraries",
"pr": 2055
},
{
"note": "Remove `LibExchangeRichErrors` and `IExchangeRichErrors`",
"pr": 2055
},
{
"note": "Use built in selectors instead of `LibExchangeSelectors` constants",
"pr": 2055
},
{
"note": "Move `calculateFillResults` and `calculateMatchedFillResults` to `LibFillResults` in `exchange-libs` package",
"pr": 2055
},
{
"note": "Compile and export all contracts, artifacts, and wrappers by default",
"pr": 2055
},
{
"note": "Rename `marketSellOrders` and `marketBuyOrders` back to `marketSellOrdersNoThrow` and `marketBuyOrdersNoThrow`.",
"pr": 2075
},
{
"note": "Introduce new `marketSellOrdersFillOrKill` and `marketBuyOrdersFillOrKill` functions.",
"pr": 2075
},
{
"note": "Use `abi.decode()` in `LibExchangeRichErrorDecoder` over `LibBytes`.",
"pr": 2075
},
{
"note": "Overridden functions in `ReentrancyTester` now return sane values.",
"pr": 2075
}
],
"timestamp": 1575296764
},
{
"version": "2.2.0-beta.4",
"changes": [
{
"note": "Dependencies updated"
}
],
"timestamp": 1575290197
},
{
"version": "2.2.0-beta.3",
"changes": [

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