Compare commits
154 Commits
@0x/contra
...
@0x/assert
Author | SHA1 | Date | |
---|---|---|---|
|
761d0a0f18 | ||
|
ae4b1e74f9 | ||
|
ac44618e58 | ||
|
d634cbf924 | ||
|
21db0e6275 | ||
|
ce426fd3f4 | ||
|
b5d4c91207 | ||
|
b43263be77 | ||
|
207cf7ca24 | ||
|
00e34758c4 | ||
|
dd90aabad6 | ||
|
5bded1946e | ||
|
3642e96154 | ||
|
9da09ee3a6 | ||
|
141c140f53 | ||
|
84b660d2ef | ||
|
6beedba957 | ||
|
d73982819b | ||
|
6ac5bcc907 | ||
|
389d4d10f1 | ||
|
89dcbd0229 | ||
|
ad8caa2b51 | ||
|
9c42241269 | ||
|
38dd45cce2 | ||
|
aa90253c62 | ||
|
41576652dc | ||
|
74830854ca | ||
|
2542b1b44d | ||
|
51f5e60224 | ||
|
bb5885e2bb | ||
|
d51bbb0008 | ||
|
49e898b189 | ||
|
42c4fe5705 | ||
|
4b5f2c36b9 | ||
|
935dca67e6 | ||
|
d431790e19 | ||
|
56310b7bd4 | ||
|
f15e21faad | ||
|
44aa6a2b38 | ||
|
9f32347c01 | ||
|
3d5b229c46 | ||
|
5863ccc0a0 | ||
|
d220a16b99 | ||
|
79784fc8ee | ||
|
a83bc53c6a | ||
|
85de0b91b1 | ||
|
d91c6e5702 | ||
|
ab7689d188 | ||
|
c81455c760 | ||
|
39bfc97a7a | ||
|
88aac78282 | ||
|
863e830d24 | ||
|
6c705728a4 | ||
|
7f00279ffb | ||
|
c198d0079e | ||
|
1135d5a971 | ||
|
e299fa27a0 | ||
|
46e0bc940a | ||
|
9a552012f2 | ||
|
6498d385ee | ||
|
dd00f2016f | ||
|
64d25e6522 | ||
|
1462ab08de | ||
|
a8e93a594d | ||
|
dea30b37ef | ||
|
39571dda0b | ||
|
c7d801b6c2 | ||
|
57731be689 | ||
|
f00524e518 | ||
|
5567c40bae | ||
|
5d1a7613dd | ||
|
fa768dc112 | ||
|
27fb51d37f | ||
|
d02db3864e | ||
|
a26c3036a7 | ||
|
0af346aad8 | ||
|
c3c8ee7292 | ||
|
5fbdfa66d9 | ||
|
15b75715ee | ||
|
1fd92b6cbd | ||
|
2918b5d74e | ||
|
669c5be344 | ||
|
e1b40ec46e | ||
|
15767538eb | ||
|
de2b16c464 | ||
|
d5e6b38450 | ||
|
a636e87a4f | ||
|
50d5b4fa37 | ||
|
f6d26392fb | ||
|
2705bcce15 | ||
|
379a31ece6 | ||
|
daa593d225 | ||
|
ed8340affa | ||
|
b3c1e72577 | ||
|
3da09d140a | ||
|
51f254bbb1 | ||
|
30ee456d4c | ||
|
460d5f2517 | ||
|
5da1fc8445 | ||
|
1166b43946 | ||
|
0a6903c4c3 | ||
|
62fae9af93 | ||
|
509a1c2eb5 | ||
|
8b94bbbc5e | ||
|
bb923d2b7d | ||
|
38adc72954 | ||
|
362c7c57fa | ||
|
6529b7eebf | ||
|
439c98a6e5 | ||
|
32258ef666 | ||
|
176e088d4e | ||
|
4fe57ba025 | ||
|
2818e56932 | ||
|
5428a19617 | ||
|
b58cbca61a | ||
|
5fc6a03784 | ||
|
eb4ad0ba1b | ||
|
72cdd1ea50 | ||
|
18769f0b8f | ||
|
b7d92c3c12 | ||
|
b976101dca | ||
|
8943b670a4 | ||
|
c92ff7c622 | ||
|
301b5e1721 | ||
|
4e50b9b479 | ||
|
f8b7b8cc28 | ||
|
2a6ea74be7 | ||
|
6d6a0c12cd | ||
|
784a03300a | ||
|
392f578567 | ||
|
a91b1d2dd2 | ||
|
400b3d961e | ||
|
4f128470bd | ||
|
fe06f41136 | ||
|
f81a99565e | ||
|
81e146650b | ||
|
bd4e04d331 | ||
|
7663d2c64b | ||
|
443c3c2802 | ||
|
17a546af5d | ||
|
71faf46735 | ||
|
ac28744df6 | ||
|
adaf304b4e | ||
|
16b13f9768 | ||
|
d64bf98dc0 | ||
|
71f57d13fa | ||
|
469c10e45f | ||
|
62def596af | ||
|
aa10844d9e | ||
|
be52079182 | ||
|
255aca8789 | ||
|
117f4a282d | ||
|
e1ea65525f | ||
|
d0a3495b5f |
@@ -37,7 +37,7 @@ jobs:
|
|||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
path: ~/repo/packages/abi-gen/test-cli/output
|
path: ~/repo/packages/abi-gen/test-cli/output
|
||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
path: ~/repo/packages/abi-gen-wrappers/generated_docs
|
path: ~/repo/packages/contract-wrappers/generated_docs
|
||||||
test-contracts-ganache:
|
test-contracts-ganache:
|
||||||
resource_class: medium+
|
resource_class: medium+
|
||||||
docker:
|
docker:
|
||||||
@@ -77,7 +77,7 @@ jobs:
|
|||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
- 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
|
# TODO(dorothy-zbornak): Re-enable after updating this package for
|
||||||
# 3.0. At that time, also remove exclusion from monorepo
|
# 3.0. At that time, also remove exclusion from monorepo
|
||||||
# package.json's test script.
|
# package.json's test script.
|
||||||
@@ -93,8 +93,8 @@ jobs:
|
|||||||
keys:
|
keys:
|
||||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
- run:
|
- run:
|
||||||
command: yarn test:publish:circleci
|
command: yarn test:publish:circleci
|
||||||
no_output_timeout: 1800
|
no_output_timeout: 1800
|
||||||
test-doc-generation:
|
test-doc-generation:
|
||||||
docker:
|
docker:
|
||||||
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||||
@@ -104,8 +104,8 @@ jobs:
|
|||||||
keys:
|
keys:
|
||||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
- run:
|
- run:
|
||||||
command: yarn test:generate_docs:circleci
|
command: yarn test:generate_docs:circleci
|
||||||
no_output_timeout: 1200
|
no_output_timeout: 1200
|
||||||
test-rest:
|
test-rest:
|
||||||
docker:
|
docker:
|
||||||
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||||
@@ -116,23 +116,16 @@ jobs:
|
|||||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
- run: yarn wsrun test:circleci @0x/contracts-test-utils
|
- run: yarn wsrun test:circleci @0x/contracts-test-utils
|
||||||
- run: yarn wsrun test:circleci @0x/abi-gen
|
- run: yarn wsrun test:circleci @0x/abi-gen
|
||||||
# TODO (xianny): Needs to be updated for 3.0
|
- run: yarn wsrun test:circleci @0x/asset-swapper
|
||||||
# - 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/contract-artifacts
|
- run: yarn wsrun test:circleci @0x/contract-artifacts
|
||||||
- run: yarn wsrun test:circleci @0x/assert
|
- run: yarn wsrun test:circleci @0x/assert
|
||||||
- run: yarn wsrun test:circleci @0x/base-contract
|
- 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/connect
|
- run: yarn wsrun test:circleci @0x/contract-wrappers-test
|
||||||
- run: yarn wsrun test:circleci @0x/contract-wrappers
|
|
||||||
- run: yarn wsrun test:circleci @0x/dev-utils
|
- run: yarn wsrun test:circleci @0x/dev-utils
|
||||||
- run: yarn wsrun test:circleci @0x/json-schemas
|
- run: yarn wsrun test:circleci @0x/json-schemas
|
||||||
- run: yarn wsrun test:circleci @0x/order-utils
|
- run: yarn wsrun test:circleci @0x/order-utils
|
||||||
# TODO: Needs to be updated for 3.0. At that time, also remove
|
- run: yarn wsrun test:circleci @0x/orderbook
|
||||||
# exclusion from monorepo package.json's test script.
|
|
||||||
# - run: yarn wsrun test:circleci @0x/orderbook
|
|
||||||
- run: yarn wsrun test:circleci @0x/sol-compiler
|
- run: yarn wsrun test:circleci @0x/sol-compiler
|
||||||
- run: yarn wsrun test:circleci @0x/sol-tracing-utils
|
- run: yarn wsrun test:circleci @0x/sol-tracing-utils
|
||||||
- run: yarn wsrun test:circleci @0x/sol-doc
|
- run: yarn wsrun test:circleci @0x/sol-doc
|
||||||
@@ -149,9 +142,9 @@ jobs:
|
|||||||
paths:
|
paths:
|
||||||
- ~/repo/packages/assert/coverage/lcov.info
|
- ~/repo/packages/assert/coverage/lcov.info
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: coverage-asset-buyer-{{ .Environment.CIRCLE_SHA1 }}
|
key: coverage-asset-swapper-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
paths:
|
paths:
|
||||||
- ~/repo/packages/asset-buyer/coverage/lcov.info
|
- ~/repo/packages/asset-swapper/coverage/lcov.info
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: coverage-base-contract-{{ .Environment.CIRCLE_SHA1 }}
|
key: coverage-base-contract-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
paths:
|
paths:
|
||||||
@@ -161,9 +154,9 @@ jobs:
|
|||||||
paths:
|
paths:
|
||||||
- ~/repo/packages/connect/coverage/lcov.info
|
- ~/repo/packages/connect/coverage/lcov.info
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: coverage-contract-wrappers-{{ .Environment.CIRCLE_SHA1 }}
|
key: coverage-contract-wrappers-test-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
paths:
|
paths:
|
||||||
- ~/repo/packages/contract-wrappers/coverage/lcov.info
|
- ~/repo/packages/contract-wrappers-test/coverage/lcov.info
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: coverage-dev-utils-{{ .Environment.CIRCLE_SHA1 }}
|
key: coverage-dev-utils-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
paths:
|
paths:
|
||||||
@@ -202,9 +195,9 @@ jobs:
|
|||||||
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||||
- image: 0xorg/ganache-cli:4.4.0-beta.1
|
- image: 0xorg/ganache-cli:4.4.0-beta.1
|
||||||
environment:
|
environment:
|
||||||
VERSION: 4.4.0-beta.1
|
VERSION: latest
|
||||||
SNAPSHOT_NAME: 0x_ganache_snapshot-v3-beta
|
SNAPSHOT_NAME: 0x_ganache_snapshot-v3-beta
|
||||||
- image: 0xorg/mesh:6.0.0-beta-0xv3
|
- image: 0xorg/mesh:0xV3
|
||||||
environment:
|
environment:
|
||||||
ETHEREUM_RPC_URL: 'http://localhost:8545'
|
ETHEREUM_RPC_URL: 'http://localhost:8545'
|
||||||
ETHEREUM_NETWORK_ID: '50'
|
ETHEREUM_NETWORK_ID: '50'
|
||||||
@@ -369,7 +362,7 @@ jobs:
|
|||||||
- coverage-assert-{{ .Environment.CIRCLE_SHA1 }}
|
- coverage-assert-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- coverage-asset-buyer-{{ .Environment.CIRCLE_SHA1 }}
|
- coverage-asset-swapper-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- coverage-base-contract-{{ .Environment.CIRCLE_SHA1 }}
|
- coverage-base-contract-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
@@ -378,7 +371,7 @@ jobs:
|
|||||||
- coverage-connect-{{ .Environment.CIRCLE_SHA1 }}
|
- coverage-connect-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- coverage-contract-wrappers-{{ .Environment.CIRCLE_SHA1 }}
|
- coverage-contract-wrappers-test-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- coverage-dev-utils-{{ .Environment.CIRCLE_SHA1 }}
|
- coverage-dev-utils-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
|
3
.github/autolabeler.yml
vendored
3
.github/autolabeler.yml
vendored
@@ -19,13 +19,12 @@ contracts: ['contracts']
|
|||||||
@0x/sol-tracing-utils: ['packages/sol-tracing-utils']
|
@0x/sol-tracing-utils: ['packages/sol-tracing-utils']
|
||||||
@0x/utils: ['packages/utils']
|
@0x/utils: ['packages/utils']
|
||||||
@0x/tslint-config: ['packages/tslint-config']
|
@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/order-utils: ['packages/order-utils']
|
||||||
@0x/assert: ['packages/assert']
|
@0x/assert: ['packages/assert']
|
||||||
@0x/base-contract: ['packages/base-contract']
|
@0x/base-contract: ['packages/base-contract']
|
||||||
@0x/typescript-typings: ['packages/typescript-typings']
|
@0x/typescript-typings: ['packages/typescript-typings']
|
||||||
0x.js: ['packages/0x.js']
|
0x.js: ['packages/0x.js']
|
||||||
@0x/abi-gen-wrappers: ['packages/abi-gen-wrappers']
|
|
||||||
@0x/contract-artifacts: ['packages/contract-artifacts']
|
@0x/contract-artifacts: ['packages/contract-artifacts']
|
||||||
@0x/dev-utils: ['packages/dev-utils']
|
@0x/dev-utils: ['packages/dev-utils']
|
||||||
@0x/contract-wrappers: ['packages/contract-wrappers']
|
@0x/contract-wrappers: ['packages/contract-wrappers']
|
||||||
|
7
.gitignore
vendored
7
.gitignore
vendored
@@ -79,6 +79,8 @@ TODO.md
|
|||||||
.vscode
|
.vscode
|
||||||
|
|
||||||
# generated contract artifacts/
|
# generated contract artifacts/
|
||||||
|
contracts/erc20-bridge-sampler/generated-artifacts/
|
||||||
|
contracts/erc20-bridge-sampler/test/generated-artifacts/
|
||||||
contracts/integrations/generated-artifacts/
|
contracts/integrations/generated-artifacts/
|
||||||
contracts/integrations/test/generated-artifacts/
|
contracts/integrations/test/generated-artifacts/
|
||||||
contracts/staking/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/
|
python-packages/contract_artifacts/src/zero_ex/contract_artifacts/artifacts/
|
||||||
|
|
||||||
# generated truffle contract artifacts/
|
# generated truffle contract artifacts/
|
||||||
|
contracts/erc20-bridge-sampler/build/
|
||||||
contracts/staking/build/
|
contracts/staking/build/
|
||||||
contracts/coordinator/build/
|
contracts/coordinator/build/
|
||||||
contracts/exchange/build/
|
contracts/exchange/build/
|
||||||
@@ -127,6 +130,8 @@ contracts/dev-utils/build/
|
|||||||
|
|
||||||
# generated contract wrappers
|
# generated contract wrappers
|
||||||
packages/python-contract-wrappers/generated/
|
packages/python-contract-wrappers/generated/
|
||||||
|
contracts/erc20-bridge-sampler/generated-wrappers/
|
||||||
|
contracts/erc20-bridge-sampler/test/generated-wrappers/
|
||||||
contracts/integrations/generated-wrappers/
|
contracts/integrations/generated-wrappers/
|
||||||
contracts/integrations/test/generated-wrappers/
|
contracts/integrations/test/generated-wrappers/
|
||||||
contracts/staking/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/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_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/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/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_proxy/__init__.py
|
||||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc721_token/__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/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/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_token/__init__.py
|
||||||
|
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/zrx_vault/__init__.py
|
||||||
|
|
||||||
# solc-bin in sol-compiler
|
# solc-bin in sol-compiler
|
||||||
packages/sol-compiler/solc_bin/
|
packages/sol-compiler/solc_bin/
|
||||||
|
@@ -36,6 +36,10 @@ lib
|
|||||||
/contracts/erc20/test/generated-wrappers
|
/contracts/erc20/test/generated-wrappers
|
||||||
/contracts/erc20/generated-artifacts
|
/contracts/erc20/generated-artifacts
|
||||||
/contracts/erc20/test/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/generated-wrappers
|
||||||
/contracts/erc721/test/generated-wrappers
|
/contracts/erc721/test/generated-wrappers
|
||||||
/contracts/erc721/generated-artifacts
|
/contracts/erc721/generated-artifacts
|
||||||
|
@@ -5,8 +5,8 @@
|
|||||||
# https://git-scm.com/docs/gitignore#_pattern_format
|
# https://git-scm.com/docs/gitignore#_pattern_format
|
||||||
|
|
||||||
# Website
|
# Website
|
||||||
packages/asset-buyer/ @BMillman19 @fragosti @steveklebanoff
|
packages/asset-swapper/ @BMillman19 @fragosti @dave4506
|
||||||
packages/instant/ @BMillman19 @fragosti @steveklebanoff
|
packages/instant/ @BMillman19 @fragosti @dave4506
|
||||||
|
|
||||||
# Dev tools & setup
|
# Dev tools & setup
|
||||||
.circleci/ @LogvinovLeon
|
.circleci/ @LogvinovLeon
|
||||||
|
@@ -61,11 +61,9 @@ These packages are all under development. See [/contracts/README.md](/contracts/
|
|||||||
| [`@0x/order-utils`](/packages/order-utils) | [](https://www.npmjs.com/package/@0x/order-utils) | A set of utilities for generating, parsing, signing and validating 0x orders |
|
| [`@0x/order-utils`](/packages/order-utils) | [](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) | [](https://www.npmjs.com/package/@0x/json-schemas) | 0x-related JSON schemas | |
|
| [`@0x/json-schemas`](/packages/json-schemas) | [](https://www.npmjs.com/package/@0x/json-schemas) | 0x-related JSON schemas | |
|
||||||
| [`@0x/migrations`](/packages/migrations) | [](https://www.npmjs.com/package/@0x/migrations) | Migration tool for deploying 0x smart contracts on private testnets |
|
| [`@0x/migrations`](/packages/migrations) | [](https://www.npmjs.com/package/@0x/migrations) | Migration tool for deploying 0x smart contracts on private testnets |
|
||||||
| [`@0x/contract-artifacts`](/packages/contract-artifacts) | [](https://www.npmjs.com/package/@0x/contract-artifacts) | 0x smart contract compilation artifacts |
|
| [`@0x/contract-artifacts`](/packages/contract-artifacts) | [](https://www.npmjs.com/package/@0x/contract-artifacts) | 0x smart contract compilation artifacts | |
|
||||||
| [`@0x/abi-gen-wrappers`](/packages/abi-gen-wrappers) | [](https://www.npmjs.com/package/@0x/abi-gen-wrappers) | Low-level 0x smart contract wrappers generated using `@0x/abi-gen` |
|
|
||||||
| [`@0x/sra-spec`](/packages/sra-spec) | [](https://www.npmjs.com/package/@0x/sra-spec) | OpenAPI specification for the Standard Relayer API |
|
| [`@0x/sra-spec`](/packages/sra-spec) | [](https://www.npmjs.com/package/@0x/sra-spec) | OpenAPI specification for the Standard Relayer API |
|
||||||
| [`@0x/connect`](/packages/connect) | [](https://www.npmjs.com/package/@0x/connect) | An HTTP/WS client for interacting with the Standard Relayer API |
|
| [`@0x/connect`](/packages/connect) | [](https://www.npmjs.com/package/@0x/connect) | An HTTP/WS client for interacting with the Standard Relayer API |
|
||||||
| [`@0x/asset-buyer`](/packages/asset-buyer) | [](https://www.npmjs.com/package/@0x/asset-buyer) | Convenience package for discovering and buying assets with Ether |
|
|
||||||
| [`@0x/asset-swapper`](/packages/asset-swapper) | [](https://www.npmjs.com/package/@0x/asset-swapper) | Convenience package for discovering and performing swaps for any ERC20 Assets |
|
| [`@0x/asset-swapper`](/packages/asset-swapper) | [](https://www.npmjs.com/package/@0x/asset-swapper) | Convenience package for discovering and performing swaps for any ERC20 Assets |
|
||||||
|
|
||||||
#### Ethereum tooling
|
#### Ethereum tooling
|
||||||
|
@@ -13,4 +13,4 @@
|
|||||||
|
|
||||||
#### Development
|
#### 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.
|
||||||
|
@@ -1,4 +1,73 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"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": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1574238768
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "2.3.0-beta.2",
|
"version": "2.3.0-beta.2",
|
||||||
"changes": [
|
"changes": [
|
||||||
|
@@ -5,6 +5,28 @@ Edit the package's CHANGELOG.json file only.
|
|||||||
|
|
||||||
CHANGELOG
|
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
|
||||||
|
|
||||||
## v2.3.0-beta.2 - _November 17, 2019_
|
## v2.3.0-beta.2 - _November 17, 2019_
|
||||||
|
|
||||||
* Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330)
|
* Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330)
|
||||||
|
@@ -22,6 +22,7 @@ pragma experimental ABIEncoderV2;
|
|||||||
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||||
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
|
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
|
||||||
import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol";
|
import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol";
|
||||||
|
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
|
||||||
import "../interfaces/IERC20Bridge.sol";
|
import "../interfaces/IERC20Bridge.sol";
|
||||||
import "../interfaces/IEth2Dai.sol";
|
import "../interfaces/IEth2Dai.sol";
|
||||||
|
|
||||||
@@ -29,11 +30,9 @@ import "../interfaces/IEth2Dai.sol";
|
|||||||
// solhint-disable space-after-comma
|
// solhint-disable space-after-comma
|
||||||
contract Eth2DaiBridge is
|
contract Eth2DaiBridge is
|
||||||
IERC20Bridge,
|
IERC20Bridge,
|
||||||
IWallet
|
IWallet,
|
||||||
|
DeploymentConstants
|
||||||
{
|
{
|
||||||
/* Mainnet addresses */
|
|
||||||
address constant public ETH2DAI_ADDRESS = 0x39755357759cE0d7f32dC8dC45414CCa409AE24e;
|
|
||||||
|
|
||||||
/// @dev Callback for `IERC20Bridge`. Tries to buy `amount` of
|
/// @dev Callback for `IERC20Bridge`. Tries to buy `amount` of
|
||||||
/// `toTokenAddress` tokens by selling the entirety of the opposing asset
|
/// `toTokenAddress` tokens by selling the entirety of the opposing asset
|
||||||
/// (DAI or WETH) to the Eth2Dai contract, then transfers the bought
|
/// (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));
|
LibERC20Token.approve(fromTokenAddress, address(exchange), uint256(-1));
|
||||||
|
|
||||||
// Try to sell all of this contract's `fromTokenAddress` token balance.
|
// Try to sell all of this contract's `fromTokenAddress` token balance.
|
||||||
uint256 boughtAmount = _getEth2DaiContract().sellAllAmount(
|
uint256 boughtAmount = exchange.sellAllAmount(
|
||||||
address(fromTokenAddress),
|
fromTokenAddress,
|
||||||
IERC20Token(fromTokenAddress).balanceOf(address(this)),
|
IERC20Token(fromTokenAddress).balanceOf(address(this)),
|
||||||
toTokenAddress,
|
toTokenAddress,
|
||||||
amount
|
amount
|
||||||
@@ -93,6 +92,6 @@ contract Eth2DaiBridge is
|
|||||||
view
|
view
|
||||||
returns (IEth2Dai exchange)
|
returns (IEth2Dai exchange)
|
||||||
{
|
{
|
||||||
return IEth2Dai(ETH2DAI_ADDRESS);
|
return IEth2Dai(_getEth2DaiAddress());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
166
contracts/asset-proxy/contracts/src/bridges/KyberBridge.sol
Normal file
166
contracts/asset-proxy/contracts/src/bridges/KyberBridge.sol
Normal 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());
|
||||||
|
}
|
||||||
|
}
|
@@ -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/interfaces/IEtherToken.sol";
|
||||||
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
|
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
|
||||||
import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol";
|
import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol";
|
||||||
|
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
|
||||||
import "../interfaces/IUniswapExchangeFactory.sol";
|
import "../interfaces/IUniswapExchangeFactory.sol";
|
||||||
import "../interfaces/IUniswapExchange.sol";
|
import "../interfaces/IUniswapExchange.sol";
|
||||||
import "../interfaces/IERC20Bridge.sol";
|
import "../interfaces/IERC20Bridge.sol";
|
||||||
@@ -32,12 +33,9 @@ import "../interfaces/IERC20Bridge.sol";
|
|||||||
// solhint-disable not-rely-on-time
|
// solhint-disable not-rely-on-time
|
||||||
contract UniswapBridge is
|
contract UniswapBridge is
|
||||||
IERC20Bridge,
|
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
|
// Struct to hold `bridgeTransferFrom()` local variables in memory and to avoid
|
||||||
// stack overflows.
|
// stack overflows.
|
||||||
struct WithdrawToState {
|
struct WithdrawToState {
|
||||||
@@ -170,7 +168,7 @@ contract UniswapBridge is
|
|||||||
view
|
view
|
||||||
returns (IEtherToken token)
|
returns (IEtherToken token)
|
||||||
{
|
{
|
||||||
return IEtherToken(WETH_ADDRESS);
|
return IEtherToken(_getWETHAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Overridable way to get the uniswap exchange factory contract.
|
/// @dev Overridable way to get the uniswap exchange factory contract.
|
||||||
@@ -180,7 +178,7 @@ contract UniswapBridge is
|
|||||||
view
|
view
|
||||||
returns (IUniswapExchangeFactory factory)
|
returns (IUniswapExchangeFactory factory)
|
||||||
{
|
{
|
||||||
return IUniswapExchangeFactory(UNISWAP_EXCHANGE_FACTORY_ADDRESS);
|
return IUniswapExchangeFactory(_getUniswapExchangeFactoryAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Grants an unlimited allowance to the exchange for its token
|
/// @dev Grants an unlimited allowance to the exchange for its token
|
||||||
@@ -212,7 +210,9 @@ contract UniswapBridge is
|
|||||||
if (fromTokenAddress == address(getWethContract())) {
|
if (fromTokenAddress == address(getWethContract())) {
|
||||||
exchangeTokenAddress = toTokenAddress;
|
exchangeTokenAddress = toTokenAddress;
|
||||||
}
|
}
|
||||||
exchange = getUniswapExchangeFactoryContract().getExchange(exchangeTokenAddress);
|
exchange = IUniswapExchange(
|
||||||
|
getUniswapExchangeFactoryContract().getExchange(exchangeTokenAddress)
|
||||||
|
);
|
||||||
require(address(exchange) != address(0), "NO_UNISWAP_EXCHANGE_FOR_TOKEN");
|
require(address(exchange) != address(0), "NO_UNISWAP_EXCHANGE_FOR_TOKEN");
|
||||||
return exchange;
|
return exchange;
|
||||||
}
|
}
|
||||||
|
@@ -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);
|
||||||
|
}
|
@@ -67,11 +67,4 @@ interface IUniswapExchange {
|
|||||||
)
|
)
|
||||||
external
|
external
|
||||||
returns (uint256 tokensBought);
|
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);
|
|
||||||
}
|
}
|
||||||
|
@@ -28,5 +28,5 @@ interface IUniswapExchangeFactory {
|
|||||||
function getExchange(address tokenAddress)
|
function getExchange(address tokenAddress)
|
||||||
external
|
external
|
||||||
view
|
view
|
||||||
returns (IUniswapExchange);
|
returns (address);
|
||||||
}
|
}
|
||||||
|
322
contracts/asset-proxy/contracts/test/TestKyberBridge.sol
Normal file
322
contracts/asset-proxy/contracts/test/TestKyberBridge.sol
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@@ -407,9 +407,9 @@ contract TestUniswapBridge is
|
|||||||
function getExchange(address tokenAddress)
|
function getExchange(address tokenAddress)
|
||||||
external
|
external
|
||||||
view
|
view
|
||||||
returns (IUniswapExchange)
|
returns (address)
|
||||||
{
|
{
|
||||||
return IUniswapExchange(_testExchanges[tokenAddress]);
|
return address(_testExchanges[tokenAddress]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// @dev Use `wethToken`.
|
// @dev Use `wethToken`.
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@0x/contracts-asset-proxy",
|
"name": "@0x/contracts-asset-proxy",
|
||||||
"version": "2.3.0-beta.2",
|
"version": "3.0.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.12"
|
"node": ">=6.12"
|
||||||
},
|
},
|
||||||
@@ -33,11 +33,13 @@
|
|||||||
"contracts:gen": "contracts-gen generate",
|
"contracts:gen": "contracts-gen generate",
|
||||||
"contracts:copy": "contracts-gen copy",
|
"contracts:copy": "contracts-gen copy",
|
||||||
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol",
|
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol",
|
||||||
"compile:truffle": "truffle compile"
|
"compile:truffle": "truffle compile",
|
||||||
|
"docs:md": "ts-doc-gen --sourceDir='$PROJECT_FILES' --output=$MD_FILE_DIR --fileExtension=mdx --tsconfig=./typedoc-tsconfig.json",
|
||||||
|
"docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"publicInterfaceContracts": "ERC1155Proxy,ERC20Proxy,ERC721Proxy,MultiAssetProxy,StaticCallProxy,ERC20BridgeProxy,Eth2DaiBridge,IAssetData,IAssetProxy,UniswapBridge,TestStaticCallTarget",
|
"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|IUniswapExchange|IUniswapExchangeFactory|MixinAssetProxyDispatcher|MixinAuthorizable|MultiAssetProxy|Ownable|StaticCallProxy|TestERC20Bridge|TestEth2DaiBridge|TestStaticCallTarget|TestUniswapBridge|UniswapBridge).json",
|
"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."
|
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -50,14 +52,15 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/protocol/README.md",
|
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/protocol/README.md",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@0x/abi-gen": "^4.4.0-beta.2",
|
"@0x/abi-gen": "^5.0.0",
|
||||||
"@0x/contracts-gen": "^1.1.0-beta.2",
|
"@0x/contracts-gen": "^2.0.0",
|
||||||
"@0x/contracts-test-utils": "^3.2.0-beta.2",
|
"@0x/contracts-test-utils": "^4.0.0",
|
||||||
"@0x/contracts-utils": "^3.3.0-beta.2",
|
"@0x/contracts-utils": "^4.0.0",
|
||||||
"@0x/dev-utils": "^2.4.0-beta.2",
|
"@0x/dev-utils": "^3.0.0",
|
||||||
"@0x/sol-compiler": "^3.2.0-beta.2",
|
"@0x/sol-compiler": "^4.0.0",
|
||||||
"@0x/tslint-config": "^3.1.0-beta.2",
|
"@0x/ts-doc-gen": "^0.0.22",
|
||||||
"@0x/types": "^2.5.0-beta.2",
|
"@0x/tslint-config": "^4.0.0",
|
||||||
|
"@0x/types": "^3.0.0",
|
||||||
"@types/lodash": "4.14.104",
|
"@types/lodash": "4.14.104",
|
||||||
"@types/mocha": "^5.2.7",
|
"@types/mocha": "^5.2.7",
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
@@ -73,19 +76,20 @@
|
|||||||
"solhint": "^1.4.1",
|
"solhint": "^1.4.1",
|
||||||
"truffle": "^5.0.32",
|
"truffle": "^5.0.32",
|
||||||
"tslint": "5.11.0",
|
"tslint": "5.11.0",
|
||||||
|
"typedoc": "^0.15.0",
|
||||||
"typescript": "3.0.1"
|
"typescript": "3.0.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@0x/base-contract": "^5.5.0-beta.2",
|
"@0x/base-contract": "^6.0.0",
|
||||||
"@0x/contracts-dev-utils": "^0.1.0-beta.2",
|
"@0x/contracts-dev-utils": "^1.0.0",
|
||||||
"@0x/contracts-erc1155": "^1.2.0-beta.2",
|
"@0x/contracts-erc1155": "^2.0.0",
|
||||||
"@0x/contracts-erc20": "^2.3.0-beta.2",
|
"@0x/contracts-erc20": "^3.0.0",
|
||||||
"@0x/contracts-erc721": "^2.2.0-beta.2",
|
"@0x/contracts-erc721": "^3.0.0",
|
||||||
"@0x/order-utils": "^8.5.0-beta.2",
|
"@0x/order-utils": "^9.0.0",
|
||||||
"@0x/typescript-typings": "^4.4.0-beta.2",
|
"@0x/typescript-typings": "^5.0.0",
|
||||||
"@0x/utils": "^4.6.0-beta.2",
|
"@0x/utils": "^5.0.0",
|
||||||
"@0x/web3-wrapper": "^6.1.0-beta.2",
|
"@0x/web3-wrapper": "^7.0.0",
|
||||||
"ethereum-types": "^2.2.0-beta.2",
|
"ethereum-types": "^3.0.0",
|
||||||
"lodash": "^4.17.11"
|
"lodash": "^4.17.11"
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
|
@@ -12,6 +12,7 @@ import * as ERC721Proxy from '../generated-artifacts/ERC721Proxy.json';
|
|||||||
import * as Eth2DaiBridge from '../generated-artifacts/Eth2DaiBridge.json';
|
import * as Eth2DaiBridge from '../generated-artifacts/Eth2DaiBridge.json';
|
||||||
import * as IAssetData from '../generated-artifacts/IAssetData.json';
|
import * as IAssetData from '../generated-artifacts/IAssetData.json';
|
||||||
import * as IAssetProxy from '../generated-artifacts/IAssetProxy.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 MultiAssetProxy from '../generated-artifacts/MultiAssetProxy.json';
|
||||||
import * as StaticCallProxy from '../generated-artifacts/StaticCallProxy.json';
|
import * as StaticCallProxy from '../generated-artifacts/StaticCallProxy.json';
|
||||||
import * as TestStaticCallTarget from '../generated-artifacts/TestStaticCallTarget.json';
|
import * as TestStaticCallTarget from '../generated-artifacts/TestStaticCallTarget.json';
|
||||||
@@ -27,5 +28,6 @@ export const artifacts = {
|
|||||||
IAssetData: IAssetData as ContractArtifact,
|
IAssetData: IAssetData as ContractArtifact,
|
||||||
IAssetProxy: IAssetProxy as ContractArtifact,
|
IAssetProxy: IAssetProxy as ContractArtifact,
|
||||||
UniswapBridge: UniswapBridge as ContractArtifact,
|
UniswapBridge: UniswapBridge as ContractArtifact,
|
||||||
|
KyberBridge: KyberBridge as ContractArtifact,
|
||||||
TestStaticCallTarget: TestStaticCallTarget as ContractArtifact,
|
TestStaticCallTarget: TestStaticCallTarget as ContractArtifact,
|
||||||
};
|
};
|
||||||
|
@@ -1,6 +1,64 @@
|
|||||||
export * from './artifacts';
|
export { artifacts } from './artifacts';
|
||||||
export * from './wrappers';
|
export {
|
||||||
|
ERC1155ProxyContract,
|
||||||
|
ERC20BridgeProxyContract,
|
||||||
|
ERC20ProxyContract,
|
||||||
|
ERC721ProxyContract,
|
||||||
|
Eth2DaiBridgeContract,
|
||||||
|
IAssetDataContract,
|
||||||
|
IAssetProxyContract,
|
||||||
|
MultiAssetProxyContract,
|
||||||
|
StaticCallProxyContract,
|
||||||
|
TestStaticCallTargetContract,
|
||||||
|
UniswapBridgeContract,
|
||||||
|
} from './wrappers';
|
||||||
|
|
||||||
export { ERC20Wrapper } from './erc20_wrapper';
|
export { ERC20Wrapper } from './erc20_wrapper';
|
||||||
export { ERC721Wrapper } from './erc721_wrapper';
|
export { ERC721Wrapper } from './erc721_wrapper';
|
||||||
export { ERC1155ProxyWrapper } from './erc1155_proxy_wrapper';
|
export { ERC1155ProxyWrapper } from './erc1155_proxy_wrapper';
|
||||||
|
export { ERC1155MintableContract, Erc1155Wrapper } from '@0x/contracts-erc1155';
|
||||||
|
export { DummyERC20TokenContract } from '@0x/contracts-erc20';
|
||||||
|
export { DummyERC721TokenContract } from '@0x/contracts-erc721';
|
||||||
|
export {
|
||||||
|
ERC1155HoldingsByOwner,
|
||||||
|
ERC20BalancesByOwner,
|
||||||
|
ERC721TokenIdsByOwner,
|
||||||
|
ERC1155FungibleHoldingsByOwner,
|
||||||
|
ERC1155NonFungibleHoldingsByOwner,
|
||||||
|
} from '@0x/contracts-test-utils';
|
||||||
|
export {
|
||||||
|
TransactionReceiptWithDecodedLogs,
|
||||||
|
Provider,
|
||||||
|
ZeroExProvider,
|
||||||
|
JSONRPCRequestPayload,
|
||||||
|
JSONRPCErrorCallback,
|
||||||
|
TransactionReceiptStatus,
|
||||||
|
JSONRPCResponsePayload,
|
||||||
|
JSONRPCResponseError,
|
||||||
|
ContractArtifact,
|
||||||
|
ContractChains,
|
||||||
|
CompilerOpts,
|
||||||
|
StandardContractOutput,
|
||||||
|
CompilerSettings,
|
||||||
|
ContractChainData,
|
||||||
|
ContractAbi,
|
||||||
|
DevdocOutput,
|
||||||
|
EvmOutput,
|
||||||
|
CompilerSettingsMetadata,
|
||||||
|
OptimizerSettings,
|
||||||
|
OutputField,
|
||||||
|
ParamDescription,
|
||||||
|
EvmBytecodeOutput,
|
||||||
|
AbiDefinition,
|
||||||
|
FunctionAbi,
|
||||||
|
EventAbi,
|
||||||
|
RevertErrorAbi,
|
||||||
|
EventParameter,
|
||||||
|
DataItem,
|
||||||
|
MethodAbi,
|
||||||
|
ConstructorAbi,
|
||||||
|
FallbackAbi,
|
||||||
|
ConstructorStateMutability,
|
||||||
|
TupleDataItem,
|
||||||
|
StateMutability,
|
||||||
|
} from 'ethereum-types';
|
||||||
|
@@ -10,6 +10,7 @@ export * from '../generated-wrappers/erc721_proxy';
|
|||||||
export * from '../generated-wrappers/eth2_dai_bridge';
|
export * from '../generated-wrappers/eth2_dai_bridge';
|
||||||
export * from '../generated-wrappers/i_asset_data';
|
export * from '../generated-wrappers/i_asset_data';
|
||||||
export * from '../generated-wrappers/i_asset_proxy';
|
export * from '../generated-wrappers/i_asset_proxy';
|
||||||
|
export * from '../generated-wrappers/kyber_bridge';
|
||||||
export * from '../generated-wrappers/multi_asset_proxy';
|
export * from '../generated-wrappers/multi_asset_proxy';
|
||||||
export * from '../generated-wrappers/static_call_proxy';
|
export * from '../generated-wrappers/static_call_proxy';
|
||||||
export * from '../generated-wrappers/test_static_call_target';
|
export * from '../generated-wrappers/test_static_call_target';
|
||||||
|
@@ -16,8 +16,10 @@ import * as IAssetProxyDispatcher from '../test/generated-artifacts/IAssetProxyD
|
|||||||
import * as IAuthorizable from '../test/generated-artifacts/IAuthorizable.json';
|
import * as IAuthorizable from '../test/generated-artifacts/IAuthorizable.json';
|
||||||
import * as IERC20Bridge from '../test/generated-artifacts/IERC20Bridge.json';
|
import * as IERC20Bridge from '../test/generated-artifacts/IERC20Bridge.json';
|
||||||
import * as IEth2Dai from '../test/generated-artifacts/IEth2Dai.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 IUniswapExchange from '../test/generated-artifacts/IUniswapExchange.json';
|
||||||
import * as IUniswapExchangeFactory from '../test/generated-artifacts/IUniswapExchangeFactory.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 MixinAssetProxyDispatcher from '../test/generated-artifacts/MixinAssetProxyDispatcher.json';
|
||||||
import * as MixinAuthorizable from '../test/generated-artifacts/MixinAuthorizable.json';
|
import * as MixinAuthorizable from '../test/generated-artifacts/MixinAuthorizable.json';
|
||||||
import * as MultiAssetProxy from '../test/generated-artifacts/MultiAssetProxy.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 StaticCallProxy from '../test/generated-artifacts/StaticCallProxy.json';
|
||||||
import * as TestERC20Bridge from '../test/generated-artifacts/TestERC20Bridge.json';
|
import * as TestERC20Bridge from '../test/generated-artifacts/TestERC20Bridge.json';
|
||||||
import * as TestEth2DaiBridge from '../test/generated-artifacts/TestEth2DaiBridge.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 TestStaticCallTarget from '../test/generated-artifacts/TestStaticCallTarget.json';
|
||||||
import * as TestUniswapBridge from '../test/generated-artifacts/TestUniswapBridge.json';
|
import * as TestUniswapBridge from '../test/generated-artifacts/TestUniswapBridge.json';
|
||||||
import * as UniswapBridge from '../test/generated-artifacts/UniswapBridge.json';
|
import * as UniswapBridge from '../test/generated-artifacts/UniswapBridge.json';
|
||||||
@@ -39,6 +42,7 @@ export const artifacts = {
|
|||||||
MultiAssetProxy: MultiAssetProxy as ContractArtifact,
|
MultiAssetProxy: MultiAssetProxy as ContractArtifact,
|
||||||
StaticCallProxy: StaticCallProxy as ContractArtifact,
|
StaticCallProxy: StaticCallProxy as ContractArtifact,
|
||||||
Eth2DaiBridge: Eth2DaiBridge as ContractArtifact,
|
Eth2DaiBridge: Eth2DaiBridge as ContractArtifact,
|
||||||
|
KyberBridge: KyberBridge as ContractArtifact,
|
||||||
UniswapBridge: UniswapBridge as ContractArtifact,
|
UniswapBridge: UniswapBridge as ContractArtifact,
|
||||||
IAssetData: IAssetData as ContractArtifact,
|
IAssetData: IAssetData as ContractArtifact,
|
||||||
IAssetProxy: IAssetProxy as ContractArtifact,
|
IAssetProxy: IAssetProxy as ContractArtifact,
|
||||||
@@ -46,10 +50,12 @@ export const artifacts = {
|
|||||||
IAuthorizable: IAuthorizable as ContractArtifact,
|
IAuthorizable: IAuthorizable as ContractArtifact,
|
||||||
IERC20Bridge: IERC20Bridge as ContractArtifact,
|
IERC20Bridge: IERC20Bridge as ContractArtifact,
|
||||||
IEth2Dai: IEth2Dai as ContractArtifact,
|
IEth2Dai: IEth2Dai as ContractArtifact,
|
||||||
|
IKyberNetworkProxy: IKyberNetworkProxy as ContractArtifact,
|
||||||
IUniswapExchange: IUniswapExchange as ContractArtifact,
|
IUniswapExchange: IUniswapExchange as ContractArtifact,
|
||||||
IUniswapExchangeFactory: IUniswapExchangeFactory as ContractArtifact,
|
IUniswapExchangeFactory: IUniswapExchangeFactory as ContractArtifact,
|
||||||
TestERC20Bridge: TestERC20Bridge as ContractArtifact,
|
TestERC20Bridge: TestERC20Bridge as ContractArtifact,
|
||||||
TestEth2DaiBridge: TestEth2DaiBridge as ContractArtifact,
|
TestEth2DaiBridge: TestEth2DaiBridge as ContractArtifact,
|
||||||
|
TestKyberBridge: TestKyberBridge as ContractArtifact,
|
||||||
TestStaticCallTarget: TestStaticCallTarget as ContractArtifact,
|
TestStaticCallTarget: TestStaticCallTarget as ContractArtifact,
|
||||||
TestUniswapBridge: TestUniswapBridge as ContractArtifact,
|
TestUniswapBridge: TestUniswapBridge as ContractArtifact,
|
||||||
};
|
};
|
||||||
|
272
contracts/asset-proxy/test/kyber_bridge.ts
Normal file
272
contracts/asset-proxy/test/kyber_bridge.ts
Normal 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,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@@ -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_authorizable';
|
||||||
export * from '../test/generated-wrappers/i_erc20_bridge';
|
export * from '../test/generated-wrappers/i_erc20_bridge';
|
||||||
export * from '../test/generated-wrappers/i_eth2_dai';
|
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';
|
||||||
export * from '../test/generated-wrappers/i_uniswap_exchange_factory';
|
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_asset_proxy_dispatcher';
|
||||||
export * from '../test/generated-wrappers/mixin_authorizable';
|
export * from '../test/generated-wrappers/mixin_authorizable';
|
||||||
export * from '../test/generated-wrappers/multi_asset_proxy';
|
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/static_call_proxy';
|
||||||
export * from '../test/generated-wrappers/test_erc20_bridge';
|
export * from '../test/generated-wrappers/test_erc20_bridge';
|
||||||
export * from '../test/generated-wrappers/test_eth2_dai_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_static_call_target';
|
||||||
export * from '../test/generated-wrappers/test_uniswap_bridge';
|
export * from '../test/generated-wrappers/test_uniswap_bridge';
|
||||||
export * from '../test/generated-wrappers/uniswap_bridge';
|
export * from '../test/generated-wrappers/uniswap_bridge';
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
"generated-artifacts/Eth2DaiBridge.json",
|
"generated-artifacts/Eth2DaiBridge.json",
|
||||||
"generated-artifacts/IAssetData.json",
|
"generated-artifacts/IAssetData.json",
|
||||||
"generated-artifacts/IAssetProxy.json",
|
"generated-artifacts/IAssetProxy.json",
|
||||||
|
"generated-artifacts/KyberBridge.json",
|
||||||
"generated-artifacts/MultiAssetProxy.json",
|
"generated-artifacts/MultiAssetProxy.json",
|
||||||
"generated-artifacts/StaticCallProxy.json",
|
"generated-artifacts/StaticCallProxy.json",
|
||||||
"generated-artifacts/TestStaticCallTarget.json",
|
"generated-artifacts/TestStaticCallTarget.json",
|
||||||
@@ -25,8 +26,10 @@
|
|||||||
"test/generated-artifacts/IAuthorizable.json",
|
"test/generated-artifacts/IAuthorizable.json",
|
||||||
"test/generated-artifacts/IERC20Bridge.json",
|
"test/generated-artifacts/IERC20Bridge.json",
|
||||||
"test/generated-artifacts/IEth2Dai.json",
|
"test/generated-artifacts/IEth2Dai.json",
|
||||||
|
"test/generated-artifacts/IKyberNetworkProxy.json",
|
||||||
"test/generated-artifacts/IUniswapExchange.json",
|
"test/generated-artifacts/IUniswapExchange.json",
|
||||||
"test/generated-artifacts/IUniswapExchangeFactory.json",
|
"test/generated-artifacts/IUniswapExchangeFactory.json",
|
||||||
|
"test/generated-artifacts/KyberBridge.json",
|
||||||
"test/generated-artifacts/MixinAssetProxyDispatcher.json",
|
"test/generated-artifacts/MixinAssetProxyDispatcher.json",
|
||||||
"test/generated-artifacts/MixinAuthorizable.json",
|
"test/generated-artifacts/MixinAuthorizable.json",
|
||||||
"test/generated-artifacts/MultiAssetProxy.json",
|
"test/generated-artifacts/MultiAssetProxy.json",
|
||||||
@@ -34,6 +37,7 @@
|
|||||||
"test/generated-artifacts/StaticCallProxy.json",
|
"test/generated-artifacts/StaticCallProxy.json",
|
||||||
"test/generated-artifacts/TestERC20Bridge.json",
|
"test/generated-artifacts/TestERC20Bridge.json",
|
||||||
"test/generated-artifacts/TestEth2DaiBridge.json",
|
"test/generated-artifacts/TestEth2DaiBridge.json",
|
||||||
|
"test/generated-artifacts/TestKyberBridge.json",
|
||||||
"test/generated-artifacts/TestStaticCallTarget.json",
|
"test/generated-artifacts/TestStaticCallTarget.json",
|
||||||
"test/generated-artifacts/TestUniswapBridge.json",
|
"test/generated-artifacts/TestUniswapBridge.json",
|
||||||
"test/generated-artifacts/UniswapBridge.json"
|
"test/generated-artifacts/UniswapBridge.json"
|
||||||
|
@@ -1,4 +1,80 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"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": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1574238768
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "2.1.0-beta.2",
|
"version": "2.1.0-beta.2",
|
||||||
"changes": [
|
"changes": [
|
||||||
|
@@ -5,6 +5,30 @@ Edit the package's CHANGELOG.json file only.
|
|||||||
|
|
||||||
CHANGELOG
|
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
|
||||||
|
|
||||||
## v2.1.0-beta.2 - _November 17, 2019_
|
## v2.1.0-beta.2 - _November 17, 2019_
|
||||||
|
|
||||||
* Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330)
|
* Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330)
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@0x/contracts-coordinator",
|
"name": "@0x/contracts-coordinator",
|
||||||
"version": "2.1.0-beta.2",
|
"version": "3.0.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.12"
|
"node": ">=6.12"
|
||||||
},
|
},
|
||||||
@@ -33,7 +33,9 @@
|
|||||||
"contracts:gen": "contracts-gen generate",
|
"contracts:gen": "contracts-gen generate",
|
||||||
"contracts:copy": "contracts-gen copy",
|
"contracts:copy": "contracts-gen copy",
|
||||||
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol",
|
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol",
|
||||||
"compile:truffle": "truffle compile"
|
"compile:truffle": "truffle compile",
|
||||||
|
"docs:md": "ts-doc-gen --sourceDir='$PROJECT_FILES' --output=$MD_FILE_DIR --fileExtension=mdx --tsconfig=./typedoc-tsconfig.json",
|
||||||
|
"docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"publicInterfaceContracts": "Coordinator,CoordinatorRegistry,LibCoordinatorApproval,LibCoordinatorRichErrors,LibEIP712CoordinatorDomain,LibConstants",
|
"publicInterfaceContracts": "Coordinator,CoordinatorRegistry,LibCoordinatorApproval,LibCoordinatorRichErrors,LibEIP712CoordinatorDomain,LibConstants",
|
||||||
@@ -50,18 +52,19 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
|
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@0x/abi-gen": "^4.4.0-beta.2",
|
"@0x/abi-gen": "^5.0.0",
|
||||||
"@0x/contracts-asset-proxy": "^2.3.0-beta.2",
|
"@0x/contracts-asset-proxy": "^3.0.0",
|
||||||
"@0x/contracts-dev-utils": "^0.1.0-beta.2",
|
"@0x/contracts-dev-utils": "^1.0.0",
|
||||||
"@0x/contracts-erc20": "^2.3.0-beta.2",
|
"@0x/contracts-erc20": "^3.0.0",
|
||||||
"@0x/contracts-exchange": "^2.2.0-beta.2",
|
"@0x/contracts-exchange": "^3.0.0",
|
||||||
"@0x/contracts-gen": "^1.1.0-beta.2",
|
"@0x/contracts-gen": "^2.0.0",
|
||||||
"@0x/contracts-test-utils": "^3.2.0-beta.2",
|
"@0x/contracts-test-utils": "^4.0.0",
|
||||||
"@0x/dev-utils": "^2.4.0-beta.2",
|
"@0x/dev-utils": "^3.0.0",
|
||||||
"@0x/order-utils": "^8.5.0-beta.2",
|
"@0x/order-utils": "^9.0.0",
|
||||||
"@0x/sol-compiler": "^3.2.0-beta.2",
|
"@0x/sol-compiler": "^4.0.0",
|
||||||
"@0x/tslint-config": "^3.1.0-beta.2",
|
"@0x/ts-doc-gen": "^0.0.22",
|
||||||
"@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/lodash": "4.14.104",
|
||||||
"@types/mocha": "^5.2.7",
|
"@types/mocha": "^5.2.7",
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
@@ -77,15 +80,20 @@
|
|||||||
"solhint": "^1.4.1",
|
"solhint": "^1.4.1",
|
||||||
"truffle": "^5.0.32",
|
"truffle": "^5.0.32",
|
||||||
"tslint": "5.11.0",
|
"tslint": "5.11.0",
|
||||||
|
"typedoc": "^0.15.0",
|
||||||
"typescript": "3.0.1"
|
"typescript": "3.0.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@0x/base-contract": "^5.5.0-beta.2",
|
"@0x/assert": "^3.0.0",
|
||||||
"@0x/contracts-utils": "^3.3.0-beta.2",
|
"@0x/base-contract": "^6.0.0",
|
||||||
"@0x/types": "^2.5.0-beta.2",
|
"@0x/contract-addresses": "^4.0.0",
|
||||||
"@0x/typescript-typings": "^4.4.0-beta.2",
|
"@0x/contracts-utils": "^4.0.0",
|
||||||
"@0x/utils": "^4.6.0-beta.2",
|
"@0x/json-schemas": "^5.0.0",
|
||||||
"ethereum-types": "^2.2.0-beta.2"
|
"@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": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
|
820
contracts/coordinator/src/client/index.ts
Normal file
820
contracts/coordinator/src/client/index.ts
Normal 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
|
@@ -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 { Order } from '@0x/types'; // tslint:disable-line:no-unused-variable
|
||||||
import { BigNumber } from '@0x/utils'; // tslint:disable-line:no-unused-variable
|
import { BigNumber } from '@0x/utils'; // tslint:disable-line:no-unused-variable
|
||||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||||
import * as _ from 'lodash';
|
|
||||||
|
|
||||||
export const assert = {
|
export const assert = {
|
||||||
...sharedAssert,
|
...sharedAssert,
|
@@ -2,10 +2,6 @@ import { Order, SignedOrder, SignedZeroExTransaction } from '@0x/types';
|
|||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
|
|
||||||
export interface CoordinatorServerApprovalResponse {
|
export interface CoordinatorServerApprovalResponse {
|
||||||
signatures: string[];
|
|
||||||
expirationTimeSeconds: BigNumber[];
|
|
||||||
}
|
|
||||||
export interface CoordinatorServerApprovalRawResponse {
|
|
||||||
signatures: string[];
|
signatures: string[];
|
||||||
expirationTimeSeconds: BigNumber;
|
expirationTimeSeconds: BigNumber;
|
||||||
}
|
}
|
||||||
@@ -24,7 +20,7 @@ export interface CoordinatorOutstandingFillSignatures {
|
|||||||
export interface CoordinatorServerResponse {
|
export interface CoordinatorServerResponse {
|
||||||
isError: boolean;
|
isError: boolean;
|
||||||
status: number;
|
status: number;
|
||||||
body?: CoordinatorServerCancellationResponse | CoordinatorServerApprovalRawResponse;
|
body?: CoordinatorServerCancellationResponse | CoordinatorServerApprovalResponse;
|
||||||
error?: any;
|
error?: any;
|
||||||
request: CoordinatorServerRequest;
|
request: CoordinatorServerRequest;
|
||||||
coordinatorOperator: string;
|
coordinatorOperator: string;
|
||||||
@@ -38,12 +34,12 @@ export interface CoordinatorServerRequest {
|
|||||||
|
|
||||||
export class CoordinatorServerError extends Error {
|
export class CoordinatorServerError extends Error {
|
||||||
public message: CoordinatorServerErrorMsg;
|
public message: CoordinatorServerErrorMsg;
|
||||||
public approvedOrders?: SignedOrder[] = [];
|
public approvedOrders?: Order[] = [];
|
||||||
public cancellations?: CoordinatorServerCancellationResponse[] = [];
|
public cancellations?: CoordinatorServerCancellationResponse[] = [];
|
||||||
public errors: CoordinatorServerResponse[];
|
public errors: CoordinatorServerResponse[];
|
||||||
constructor(
|
constructor(
|
||||||
message: CoordinatorServerErrorMsg,
|
message: CoordinatorServerErrorMsg,
|
||||||
approvedOrders: SignedOrder[],
|
approvedOrders: Order[],
|
||||||
cancellations: CoordinatorServerCancellationResponse[],
|
cancellations: CoordinatorServerCancellationResponse[],
|
||||||
errors: CoordinatorServerResponse[],
|
errors: CoordinatorServerResponse[],
|
||||||
) {
|
) {
|
@@ -1,5 +1,66 @@
|
|||||||
export * from './artifacts';
|
export { artifacts } from './artifacts';
|
||||||
export * from './wrappers';
|
export {
|
||||||
export import CoordinatorRevertErrors = require('./revert_errors');
|
CoordinatorContract,
|
||||||
|
CoordinatorRegistryContract,
|
||||||
|
LibConstantsContract,
|
||||||
|
LibCoordinatorApprovalContract,
|
||||||
|
LibCoordinatorRichErrorsContract,
|
||||||
|
LibEIP712CoordinatorDomainContract,
|
||||||
|
} from './wrappers';
|
||||||
|
export { CoordinatorRevertErrors } from '@0x/utils';
|
||||||
|
export { CoordinatorServerCancellationResponse } from './client/index';
|
||||||
export { ApprovalFactory } from './approval_factory';
|
export { ApprovalFactory } from './approval_factory';
|
||||||
export { SignedCoordinatorApproval } from './types';
|
export { SignedCoordinatorApproval } from './types';
|
||||||
|
export {
|
||||||
|
Order,
|
||||||
|
SignedOrder,
|
||||||
|
SignatureType,
|
||||||
|
SignedZeroExTransaction,
|
||||||
|
EIP712DomainWithDefaultSchema,
|
||||||
|
ZeroExTransaction,
|
||||||
|
} from '@0x/types';
|
||||||
|
export { AwaitTransactionSuccessOpts, SendTransactionOpts } from '@0x/base-contract';
|
||||||
|
export {
|
||||||
|
ContractArtifact,
|
||||||
|
ContractChains,
|
||||||
|
CompilerOpts,
|
||||||
|
StandardContractOutput,
|
||||||
|
CompilerSettings,
|
||||||
|
ContractChainData,
|
||||||
|
ContractAbi,
|
||||||
|
DevdocOutput,
|
||||||
|
EvmOutput,
|
||||||
|
CompilerSettingsMetadata,
|
||||||
|
OptimizerSettings,
|
||||||
|
OutputField,
|
||||||
|
ParamDescription,
|
||||||
|
EvmBytecodeOutput,
|
||||||
|
AbiDefinition,
|
||||||
|
FunctionAbi,
|
||||||
|
EventAbi,
|
||||||
|
RevertErrorAbi,
|
||||||
|
EventParameter,
|
||||||
|
DataItem,
|
||||||
|
MethodAbi,
|
||||||
|
ConstructorAbi,
|
||||||
|
FallbackAbi,
|
||||||
|
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';
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import { SignedZeroExTransaction } from '@0x/types';
|
import { SignedZeroExTransaction } from '@0x/types';
|
||||||
|
import { BigNumber } from '@0x/utils';
|
||||||
|
|
||||||
export interface CoordinatorApproval {
|
export interface CoordinatorApproval {
|
||||||
transaction: SignedZeroExTransaction;
|
transaction: SignedZeroExTransaction;
|
||||||
@@ -8,3 +9,9 @@ export interface CoordinatorApproval {
|
|||||||
export interface SignedCoordinatorApproval extends CoordinatorApproval {
|
export interface SignedCoordinatorApproval extends CoordinatorApproval {
|
||||||
signature: string;
|
signature: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface CoordinatorTransaction {
|
||||||
|
salt: BigNumber;
|
||||||
|
signerAddress: string;
|
||||||
|
data: string;
|
||||||
|
}
|
||||||
|
@@ -12,9 +12,7 @@ import {
|
|||||||
} from '@0x/contracts-test-utils';
|
} from '@0x/contracts-test-utils';
|
||||||
import { LibBytesRevertErrors } from '@0x/contracts-utils';
|
import { LibBytesRevertErrors } from '@0x/contracts-utils';
|
||||||
import { SignatureType, SignedOrder } from '@0x/types';
|
import { SignatureType, SignedOrder } from '@0x/types';
|
||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber, CoordinatorRevertErrors } from '@0x/utils';
|
||||||
|
|
||||||
import CoordinatorRevertErrors = require('../src/revert_errors');
|
|
||||||
|
|
||||||
import { ApprovalFactory } from '../src/approval_factory';
|
import { ApprovalFactory } from '../src/approval_factory';
|
||||||
|
|
||||||
|
@@ -3,5 +3,5 @@
|
|||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "lib"
|
"outDir": "lib"
|
||||||
},
|
},
|
||||||
"include": ["./src/**/*"]
|
"include": ["./src/**/*", "./test/**/*"]
|
||||||
}
|
}
|
@@ -1,4 +1,64 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"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": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1574238768
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "0.1.0-beta.2",
|
"version": "0.1.0-beta.2",
|
||||||
"changes": [
|
"changes": [
|
||||||
|
@@ -5,6 +5,26 @@ Edit the package's CHANGELOG.json file only.
|
|||||||
|
|
||||||
CHANGELOG
|
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
|
||||||
|
|
||||||
## v0.1.0-beta.2 - _November 17, 2019_
|
## v0.1.0-beta.2 - _November 17, 2019_
|
||||||
|
|
||||||
* Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330)
|
* Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330)
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@0x/contracts-dev-utils",
|
"name": "@0x/contracts-dev-utils",
|
||||||
"version": "0.1.0-beta.2",
|
"version": "1.0.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.12"
|
"node": ">=6.12"
|
||||||
},
|
},
|
||||||
@@ -22,7 +22,9 @@
|
|||||||
"contracts:copy": "contracts-gen copy",
|
"contracts:copy": "contracts-gen copy",
|
||||||
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol",
|
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol",
|
||||||
"quantify_bytecode": "echo EVM bytecode object lengths:;for i in ./test/generated-artifacts/*.json; do node -e \"console.log('$i\t' + (require('$i').compilerOutput.evm.bytecode.object.length - 2) / 2)\"; done",
|
"quantify_bytecode": "echo EVM bytecode object lengths:;for i in ./test/generated-artifacts/*.json; do node -e \"console.log('$i\t' + (require('$i').compilerOutput.evm.bytecode.object.length - 2) / 2)\"; done",
|
||||||
"compile:truffle": "truffle compile"
|
"compile:truffle": "truffle compile",
|
||||||
|
"docs:md": "ts-doc-gen --sourceDir='$PROJECT_FILES' --output=$MD_FILE_DIR --fileExtension=mdx --tsconfig=./typedoc-tsconfig.json",
|
||||||
|
"docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"publicInterfaceContracts": "DevUtils,LibAssetData,LibTransactionDecoder",
|
"publicInterfaceContracts": "DevUtils,LibAssetData,LibTransactionDecoder",
|
||||||
@@ -39,23 +41,25 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/dev-utils/README.md",
|
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/dev-utils/README.md",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@0x/abi-gen": "^4.4.0-beta.2",
|
"@0x/abi-gen": "^5.0.0",
|
||||||
"@0x/assert": "^2.2.0-beta.2",
|
"@0x/assert": "^3.0.0",
|
||||||
"@0x/contracts-gen": "^1.1.0-beta.2",
|
"@0x/contracts-gen": "^2.0.0",
|
||||||
"@0x/sol-compiler": "^3.2.0-beta.2",
|
"@0x/sol-compiler": "^4.0.0",
|
||||||
"@0x/tslint-config": "^3.1.0-beta.2",
|
"@0x/ts-doc-gen": "^0.0.22",
|
||||||
|
"@0x/tslint-config": "^4.0.0",
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
|
"ethereum-types": "^3.0.0",
|
||||||
"ethers": "~4.0.4",
|
"ethers": "~4.0.4",
|
||||||
"npm-run-all": "^4.1.2",
|
"npm-run-all": "^4.1.2",
|
||||||
"shx": "^0.2.2",
|
"shx": "^0.2.2",
|
||||||
"solhint": "^1.4.1",
|
"solhint": "^1.4.1",
|
||||||
"truffle": "^5.0.32",
|
"truffle": "^5.0.32",
|
||||||
"tslint": "5.11.0",
|
"tslint": "5.11.0",
|
||||||
|
"typedoc": "^0.15.0",
|
||||||
"typescript": "3.0.1"
|
"typescript": "3.0.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@0x/base-contract": "^5.5.0-beta.2",
|
"@0x/base-contract": "^6.0.0"
|
||||||
"ethereum-types": "^2.2.0-beta.2"
|
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
|
@@ -1,2 +1,30 @@
|
|||||||
export * from './artifacts';
|
export { artifacts } from './artifacts';
|
||||||
export * from './wrappers';
|
export { DevUtilsContract, LibAssetDataContract, LibTransactionDecoderContract } from './wrappers';
|
||||||
|
export {
|
||||||
|
ContractArtifact,
|
||||||
|
ContractChains,
|
||||||
|
CompilerOpts,
|
||||||
|
StandardContractOutput,
|
||||||
|
CompilerSettings,
|
||||||
|
ContractChainData,
|
||||||
|
ContractAbi,
|
||||||
|
DevdocOutput,
|
||||||
|
EvmOutput,
|
||||||
|
CompilerSettingsMetadata,
|
||||||
|
OptimizerSettings,
|
||||||
|
OutputField,
|
||||||
|
ParamDescription,
|
||||||
|
EvmBytecodeOutput,
|
||||||
|
AbiDefinition,
|
||||||
|
FunctionAbi,
|
||||||
|
EventAbi,
|
||||||
|
RevertErrorAbi,
|
||||||
|
EventParameter,
|
||||||
|
DataItem,
|
||||||
|
MethodAbi,
|
||||||
|
ConstructorAbi,
|
||||||
|
FallbackAbi,
|
||||||
|
ConstructorStateMutability,
|
||||||
|
TupleDataItem,
|
||||||
|
StateMutability,
|
||||||
|
} from 'ethereum-types';
|
||||||
|
@@ -1,8 +1,7 @@
|
|||||||
{
|
{
|
||||||
"extends": "../../tsconfig",
|
"extends": "../../typedoc-tsconfig",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "lib",
|
"outDir": "lib"
|
||||||
"rootDir": "."
|
|
||||||
},
|
},
|
||||||
"include": ["./src/**/*", "./test/**/*"]
|
"include": ["./src/**/*", "./test/**/*"]
|
||||||
}
|
}
|
@@ -1,4 +1,40 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"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": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1574238768
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "1.2.0-beta.2",
|
"version": "1.2.0-beta.2",
|
||||||
"changes": [
|
"changes": [
|
||||||
|
@@ -5,6 +5,20 @@ Edit the package's CHANGELOG.json file only.
|
|||||||
|
|
||||||
CHANGELOG
|
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
|
||||||
|
|
||||||
## v1.2.0-beta.2 - _November 17, 2019_
|
## v1.2.0-beta.2 - _November 17, 2019_
|
||||||
|
|
||||||
* Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330)
|
* Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330)
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@0x/contracts-erc1155",
|
"name": "@0x/contracts-erc1155",
|
||||||
"version": "1.2.0-beta.2",
|
"version": "2.0.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.12"
|
"node": ">=6.12"
|
||||||
},
|
},
|
||||||
@@ -33,7 +33,9 @@
|
|||||||
"contracts:gen": "contracts-gen generate",
|
"contracts:gen": "contracts-gen generate",
|
||||||
"contracts:copy": "contracts-gen copy",
|
"contracts:copy": "contracts-gen copy",
|
||||||
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol",
|
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol",
|
||||||
"compile:truffle": "truffle compile"
|
"compile:truffle": "truffle compile",
|
||||||
|
"docs:md": "ts-doc-gen --sourceDir='$PROJECT_FILES' --output=$MD_FILE_DIR --fileExtension=mdx --tsconfig=./typedoc-tsconfig.json",
|
||||||
|
"docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"publicInterfaceContracts": "ERC1155,ERC1155Mintable,IERC1155Receiver,DummyERC1155Receiver",
|
"publicInterfaceContracts": "ERC1155,ERC1155Mintable,IERC1155Receiver,DummyERC1155Receiver",
|
||||||
@@ -50,13 +52,15 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md",
|
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@0x/abi-gen": "^4.4.0-beta.2",
|
"@0x/abi-gen": "^5.0.0",
|
||||||
"@0x/contracts-gen": "^1.1.0-beta.2",
|
"@0x/contracts-gen": "^2.0.0",
|
||||||
"@0x/contracts-utils": "^3.3.0-beta.2",
|
"@0x/contracts-utils": "^4.0.0",
|
||||||
"@0x/dev-utils": "^2.4.0-beta.2",
|
"@0x/dev-utils": "^3.0.0",
|
||||||
"@0x/sol-compiler": "^3.2.0-beta.2",
|
"@0x/sol-compiler": "^4.0.0",
|
||||||
"@0x/tslint-config": "^3.1.0-beta.2",
|
"@0x/ts-doc-gen": "^0.0.22",
|
||||||
"@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/lodash": "4.14.104",
|
||||||
"@types/mocha": "^5.2.7",
|
"@types/mocha": "^5.2.7",
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
@@ -64,6 +68,7 @@
|
|||||||
"chai-as-promised": "^7.1.0",
|
"chai-as-promised": "^7.1.0",
|
||||||
"chai-bignumber": "^3.0.0",
|
"chai-bignumber": "^3.0.0",
|
||||||
"dirty-chai": "^2.0.1",
|
"dirty-chai": "^2.0.1",
|
||||||
|
"ethereum-types": "^3.0.0",
|
||||||
"make-promises-safe": "^1.1.0",
|
"make-promises-safe": "^1.1.0",
|
||||||
"mocha": "^6.2.0",
|
"mocha": "^6.2.0",
|
||||||
"npm-run-all": "^4.1.2",
|
"npm-run-all": "^4.1.2",
|
||||||
@@ -71,15 +76,14 @@
|
|||||||
"solhint": "^1.4.1",
|
"solhint": "^1.4.1",
|
||||||
"truffle": "^5.0.32",
|
"truffle": "^5.0.32",
|
||||||
"tslint": "5.11.0",
|
"tslint": "5.11.0",
|
||||||
|
"typedoc": "^0.15.0",
|
||||||
"typescript": "3.0.1"
|
"typescript": "3.0.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@0x/base-contract": "^5.5.0-beta.2",
|
"@0x/base-contract": "^6.0.0",
|
||||||
"@0x/contracts-test-utils": "^3.2.0-beta.2",
|
"@0x/contracts-test-utils": "^4.0.0",
|
||||||
"@0x/typescript-typings": "^4.4.0-beta.2",
|
"@0x/utils": "^5.0.0",
|
||||||
"@0x/utils": "^4.6.0-beta.2",
|
"@0x/web3-wrapper": "^7.0.0",
|
||||||
"@0x/web3-wrapper": "^6.1.0-beta.2",
|
|
||||||
"ethereum-types": "^2.2.0-beta.2",
|
|
||||||
"lodash": "^4.17.11"
|
"lodash": "^4.17.11"
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
|
@@ -1,27 +1,16 @@
|
|||||||
import { LogDecoder } from '@0x/contracts-test-utils';
|
|
||||||
import { BigNumber } from '@0x/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 { LogWithDecodedArgs, Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { ERC1155MintableContract, ERC1155TransferSingleEventArgs } from './wrappers';
|
import { ERC1155MintableContract, ERC1155TransferSingleEventArgs } from './wrappers';
|
||||||
|
|
||||||
import { artifacts } from './artifacts';
|
|
||||||
|
|
||||||
const expect = chai.expect;
|
|
||||||
|
|
||||||
export class Erc1155Wrapper {
|
export class Erc1155Wrapper {
|
||||||
private readonly _erc1155Contract: ERC1155MintableContract;
|
private readonly _erc1155Contract: ERC1155MintableContract;
|
||||||
private readonly _web3Wrapper: Web3Wrapper;
|
|
||||||
private readonly _contractOwner: string;
|
private readonly _contractOwner: string;
|
||||||
private readonly _logDecoder: LogDecoder;
|
|
||||||
|
|
||||||
constructor(contractInstance: ERC1155MintableContract, provider: Provider, contractOwner: string) {
|
constructor(contractInstance: ERC1155MintableContract, provider: Provider, contractOwner: string) {
|
||||||
this._erc1155Contract = contractInstance;
|
this._erc1155Contract = contractInstance;
|
||||||
this._web3Wrapper = new Web3Wrapper(provider);
|
|
||||||
this._contractOwner = contractOwner;
|
this._contractOwner = contractOwner;
|
||||||
this._logDecoder = new LogDecoder(this._web3Wrapper, artifacts);
|
|
||||||
}
|
}
|
||||||
public getContract(): ERC1155MintableContract {
|
public getContract(): ERC1155MintableContract {
|
||||||
return this._erc1155Contract;
|
return this._erc1155Contract;
|
||||||
@@ -40,11 +29,11 @@ export class Erc1155Wrapper {
|
|||||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||||
const spender = delegatedSpender === undefined ? from : delegatedSpender;
|
const spender = delegatedSpender === undefined ? from : delegatedSpender;
|
||||||
const callbackDataHex = callbackData === undefined ? '0x' : callbackData;
|
const callbackDataHex = callbackData === undefined ? '0x' : callbackData;
|
||||||
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(
|
const tx = await this._erc1155Contract
|
||||||
await this._erc1155Contract.safeTransferFrom(from, to, token, value, callbackDataHex).sendTransactionAsync({
|
.safeTransferFrom(from, to, token, value, callbackDataHex)
|
||||||
|
.awaitTransactionSuccessAsync({
|
||||||
from: spender,
|
from: spender,
|
||||||
}),
|
});
|
||||||
);
|
|
||||||
return tx;
|
return tx;
|
||||||
}
|
}
|
||||||
public async safeBatchTransferFromAsync(
|
public async safeBatchTransferFromAsync(
|
||||||
@@ -57,11 +46,9 @@ export class Erc1155Wrapper {
|
|||||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||||
const spender = delegatedSpender === undefined ? from : delegatedSpender;
|
const spender = delegatedSpender === undefined ? from : delegatedSpender;
|
||||||
const callbackDataHex = callbackData === undefined ? '0x' : callbackData;
|
const callbackDataHex = callbackData === undefined ? '0x' : callbackData;
|
||||||
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(
|
const tx = await this._erc1155Contract
|
||||||
await this._erc1155Contract
|
.safeBatchTransferFrom(from, to, tokens, values, callbackDataHex)
|
||||||
.safeBatchTransferFrom(from, to, tokens, values, callbackDataHex)
|
.awaitTransactionSuccessAsync({ from: spender });
|
||||||
.sendTransactionAsync({ from: spender }),
|
|
||||||
);
|
|
||||||
return tx;
|
return tx;
|
||||||
}
|
}
|
||||||
public async mintFungibleTokensAsync(
|
public async mintFungibleTokensAsync(
|
||||||
@@ -70,11 +57,9 @@ export class Erc1155Wrapper {
|
|||||||
): Promise<BigNumber> {
|
): Promise<BigNumber> {
|
||||||
const tokenUri = 'dummyFungibleToken';
|
const tokenUri = 'dummyFungibleToken';
|
||||||
const tokenIsNonFungible = false;
|
const tokenIsNonFungible = false;
|
||||||
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(
|
const tx = await this._erc1155Contract.create(tokenUri, tokenIsNonFungible).awaitTransactionSuccessAsync({
|
||||||
await this._erc1155Contract.create(tokenUri, tokenIsNonFungible).sendTransactionAsync({
|
from: this._contractOwner,
|
||||||
from: this._contractOwner,
|
});
|
||||||
}),
|
|
||||||
);
|
|
||||||
// tslint:disable-next-line no-unnecessary-type-assertion
|
// tslint:disable-next-line no-unnecessary-type-assertion
|
||||||
const createFungibleTokenLog = tx.logs[0] as LogWithDecodedArgs<ERC1155TransferSingleEventArgs>;
|
const createFungibleTokenLog = tx.logs[0] as LogWithDecodedArgs<ERC1155TransferSingleEventArgs>;
|
||||||
const tokenId = createFungibleTokenLog.args.id;
|
const tokenId = createFungibleTokenLog.args.id;
|
||||||
@@ -99,11 +84,9 @@ export class Erc1155Wrapper {
|
|||||||
public async mintNonFungibleTokensAsync(beneficiaries: string[]): Promise<[BigNumber, BigNumber[]]> {
|
public async mintNonFungibleTokensAsync(beneficiaries: string[]): Promise<[BigNumber, BigNumber[]]> {
|
||||||
const tokenUri = 'dummyNonFungibleToken';
|
const tokenUri = 'dummyNonFungibleToken';
|
||||||
const tokenIsNonFungible = true;
|
const tokenIsNonFungible = true;
|
||||||
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(
|
const tx = await this._erc1155Contract.create(tokenUri, tokenIsNonFungible).awaitTransactionSuccessAsync({
|
||||||
await this._erc1155Contract.create(tokenUri, tokenIsNonFungible).sendTransactionAsync({
|
from: this._contractOwner,
|
||||||
from: this._contractOwner,
|
});
|
||||||
}),
|
|
||||||
);
|
|
||||||
// tslint:disable-next-line no-unnecessary-type-assertion
|
// tslint:disable-next-line no-unnecessary-type-assertion
|
||||||
const createFungibleTokenLog = tx.logs[0] as LogWithDecodedArgs<ERC1155TransferSingleEventArgs>;
|
const createFungibleTokenLog = tx.logs[0] as LogWithDecodedArgs<ERC1155TransferSingleEventArgs>;
|
||||||
const token = createFungibleTokenLog.args.id;
|
const token = createFungibleTokenLog.args.id;
|
||||||
@@ -125,11 +108,9 @@ export class Erc1155Wrapper {
|
|||||||
beneficiary: string,
|
beneficiary: string,
|
||||||
isApproved: boolean,
|
isApproved: boolean,
|
||||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||||
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(
|
const tx = await this._erc1155Contract.setApprovalForAll(beneficiary, isApproved).awaitTransactionSuccessAsync({
|
||||||
await this._erc1155Contract.setApprovalForAll(beneficiary, isApproved).sendTransactionAsync({
|
from: owner,
|
||||||
from: owner,
|
});
|
||||||
}),
|
|
||||||
);
|
|
||||||
return tx;
|
return tx;
|
||||||
}
|
}
|
||||||
public async isApprovedForAllAsync(owner: string, beneficiary: string): Promise<boolean> {
|
public async isApprovedForAllAsync(owner: string, beneficiary: string): Promise<boolean> {
|
||||||
@@ -151,7 +132,9 @@ export class Erc1155Wrapper {
|
|||||||
});
|
});
|
||||||
const balances = await this.getBalancesAsync(ownersExtended, tokensExtended);
|
const balances = await this.getBalancesAsync(ownersExtended, tokensExtended);
|
||||||
_.each(balances, (balance: BigNumber, i: number) => {
|
_.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> {
|
public async isNonFungibleItemAsync(tokenId: BigNumber): Promise<boolean> {
|
||||||
|
@@ -1,3 +1,45 @@
|
|||||||
export * from './wrappers';
|
export {
|
||||||
export * from './artifacts';
|
DummyERC1155ReceiverContract,
|
||||||
|
ERC1155Contract,
|
||||||
|
ERC1155MintableContract,
|
||||||
|
IERC1155ReceiverContract,
|
||||||
|
DummyERC1155ReceiverBatchTokenReceivedEventArgs,
|
||||||
|
ERC1155TransferSingleEventArgs,
|
||||||
|
} from './wrappers';
|
||||||
|
export { artifacts } from './artifacts';
|
||||||
export { Erc1155Wrapper } from './erc1155_wrapper';
|
export { Erc1155Wrapper } from './erc1155_wrapper';
|
||||||
|
export {
|
||||||
|
Provider,
|
||||||
|
TransactionReceiptWithDecodedLogs,
|
||||||
|
JSONRPCRequestPayload,
|
||||||
|
JSONRPCResponsePayload,
|
||||||
|
JSONRPCResponseError,
|
||||||
|
JSONRPCErrorCallback,
|
||||||
|
TransactionReceiptStatus,
|
||||||
|
ContractArtifact,
|
||||||
|
ContractChains,
|
||||||
|
CompilerOpts,
|
||||||
|
StandardContractOutput,
|
||||||
|
CompilerSettings,
|
||||||
|
ContractChainData,
|
||||||
|
ContractAbi,
|
||||||
|
DevdocOutput,
|
||||||
|
EvmOutput,
|
||||||
|
CompilerSettingsMetadata,
|
||||||
|
OptimizerSettings,
|
||||||
|
OutputField,
|
||||||
|
ParamDescription,
|
||||||
|
EvmBytecodeOutput,
|
||||||
|
AbiDefinition,
|
||||||
|
FunctionAbi,
|
||||||
|
EventAbi,
|
||||||
|
RevertErrorAbi,
|
||||||
|
EventParameter,
|
||||||
|
DataItem,
|
||||||
|
MethodAbi,
|
||||||
|
ConstructorAbi,
|
||||||
|
FallbackAbi,
|
||||||
|
ConstructorStateMutability,
|
||||||
|
TupleDataItem,
|
||||||
|
StateMutability,
|
||||||
|
} from 'ethereum-types';
|
||||||
|
7
contracts/erc1155/typedoc-tsconfig.json
Normal file
7
contracts/erc1155/typedoc-tsconfig.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../typedoc-tsconfig",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "lib"
|
||||||
|
},
|
||||||
|
"include": ["./src/**/*", "./test/**/*"]
|
||||||
|
}
|
10
contracts/erc20-bridge-sampler/.npmignore
Normal file
10
contracts/erc20-bridge-sampler/.npmignore
Normal 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
|
30
contracts/erc20-bridge-sampler/CHANGELOG.json
Normal file
30
contracts/erc20-bridge-sampler/CHANGELOG.json
Normal 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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
18
contracts/erc20-bridge-sampler/CHANGELOG.md
Normal file
18
contracts/erc20-bridge-sampler/CHANGELOG.md
Normal 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)
|
1
contracts/erc20-bridge-sampler/DEPLOYS.json
Normal file
1
contracts/erc20-bridge-sampler/DEPLOYS.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
[]
|
67
contracts/erc20-bridge-sampler/README.md
Normal file
67
contracts/erc20-bridge-sampler/README.md
Normal 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
|
||||||
|
```
|
26
contracts/erc20-bridge-sampler/compiler.json
Normal file
26
contracts/erc20-bridge-sampler/compiler.json
Normal 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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
@@ -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");
|
||||||
|
}
|
||||||
|
}
|
@@ -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);
|
||||||
|
}
|
41
contracts/erc20-bridge-sampler/contracts/src/IEth2Dai.sol
Normal file
41
contracts/erc20-bridge-sampler/contracts/src/IEth2Dai.sol
Normal 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);
|
||||||
|
}
|
@@ -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);
|
||||||
|
}
|
@@ -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);
|
||||||
|
}
|
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
92
contracts/erc20-bridge-sampler/package.json
Normal file
92
contracts/erc20-bridge-sampler/package.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
13
contracts/erc20-bridge-sampler/src/artifacts.ts
Normal file
13
contracts/erc20-bridge-sampler/src/artifacts.ts
Normal 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,
|
||||||
|
};
|
2
contracts/erc20-bridge-sampler/src/index.ts
Normal file
2
contracts/erc20-bridge-sampler/src/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export * from './wrappers';
|
||||||
|
export * from './artifacts';
|
7
contracts/erc20-bridge-sampler/src/wrappers.ts
Normal file
7
contracts/erc20-bridge-sampler/src/wrappers.ts
Normal 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';
|
23
contracts/erc20-bridge-sampler/test/artifacts.ts
Normal file
23
contracts/erc20-bridge-sampler/test/artifacts.ts
Normal 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,
|
||||||
|
};
|
852
contracts/erc20-bridge-sampler/test/erc20-bridge-sampler.ts
Normal file
852
contracts/erc20-bridge-sampler/test/erc20-bridge-sampler.ts
Normal 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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
12
contracts/erc20-bridge-sampler/test/wrappers.ts
Normal file
12
contracts/erc20-bridge-sampler/test/wrappers.ts
Normal 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';
|
96
contracts/erc20-bridge-sampler/truffle-config.js
Normal file
96
contracts/erc20-bridge-sampler/truffle-config.js
Normal 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 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
17
contracts/erc20-bridge-sampler/tsconfig.json
Normal file
17
contracts/erc20-bridge-sampler/tsconfig.json
Normal 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"]
|
||||||
|
}
|
10
contracts/erc20-bridge-sampler/tslint.json
Normal file
10
contracts/erc20-bridge-sampler/tslint.json
Normal 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"]
|
||||||
|
}
|
||||||
|
}
|
@@ -1,10 +1,54 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"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": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1574238768
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "2.3.0-beta.2",
|
"version": "2.3.0-beta.2",
|
||||||
"changes": [
|
"changes": [
|
||||||
{
|
{
|
||||||
"note": "Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils",
|
"note": "Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils",
|
||||||
"pr": 2330
|
"pr": 2330
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Add `decimals()` to `LibERC20Token`.",
|
||||||
|
"pr": 2344
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"timestamp": 1574030254
|
"timestamp": 1574030254
|
||||||
|
@@ -5,9 +5,25 @@ Edit the package's CHANGELOG.json file only.
|
|||||||
|
|
||||||
CHANGELOG
|
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
|
||||||
|
|
||||||
## v2.3.0-beta.2 - _November 17, 2019_
|
## v2.3.0-beta.2 - _November 17, 2019_
|
||||||
|
|
||||||
* Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330)
|
* 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_
|
## v2.3.0-beta.1 - _November 7, 2019_
|
||||||
|
|
||||||
|
@@ -24,6 +24,7 @@ import "../src/interfaces/IERC20Token.sol";
|
|||||||
|
|
||||||
|
|
||||||
library LibERC20Token {
|
library LibERC20Token {
|
||||||
|
bytes constant private DECIMALS_CALL_DATA = hex"313ce567";
|
||||||
|
|
||||||
/// @dev Calls `IERC20Token(token).approve()`.
|
/// @dev Calls `IERC20Token(token).approve()`.
|
||||||
/// Reverts if `false` is returned or if the return
|
/// Reverts if `false` is returned or if the return
|
||||||
@@ -91,6 +92,21 @@ library LibERC20Token {
|
|||||||
_callWithOptionalBooleanResult(token, callData);
|
_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`
|
/// @dev Executes a call on address `target` with calldata `callData`
|
||||||
/// and asserts that either nothing was returned or a single boolean
|
/// and asserts that either nothing was returned or a single boolean
|
||||||
/// was returned equal to `true`.
|
/// was returned equal to `true`.
|
||||||
|
@@ -69,4 +69,16 @@ contract TestLibERC20Token {
|
|||||||
target.setBehavior(shouldRevert, revertData, returnData);
|
target.setBehavior(shouldRevert, revertData, returnData);
|
||||||
LibERC20Token.transferFrom(address(target), from, to, amount);
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -87,6 +87,14 @@ contract TestLibERC20TokenTarget {
|
|||||||
_execute();
|
_execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function decimals()
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (uint8)
|
||||||
|
{
|
||||||
|
_execute();
|
||||||
|
}
|
||||||
|
|
||||||
function _execute() private view {
|
function _execute() private view {
|
||||||
if (_shouldRevert) {
|
if (_shouldRevert) {
|
||||||
bytes memory revertData = _revertData;
|
bytes memory revertData = _revertData;
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@0x/contracts-erc20",
|
"name": "@0x/contracts-erc20",
|
||||||
"version": "2.3.0-beta.2",
|
"version": "3.0.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.12"
|
"node": ">=6.12"
|
||||||
},
|
},
|
||||||
@@ -32,7 +32,9 @@
|
|||||||
"test:circleci": "yarn test",
|
"test:circleci": "yarn test",
|
||||||
"contracts:gen": "contracts-gen generate",
|
"contracts:gen": "contracts-gen generate",
|
||||||
"contracts:copy": "contracts-gen copy",
|
"contracts:copy": "contracts-gen copy",
|
||||||
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol"
|
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol",
|
||||||
|
"docs:md": "ts-doc-gen --sourceDir='$PROJECT_FILES' --output=$MD_FILE_DIR --fileExtension=mdx --tsconfig=./typedoc-tsconfig.json",
|
||||||
|
"docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"publicInterfaceContracts": "DummyERC20Token,ERC20Token,WETH9,ZRXToken,DummyNoReturnERC20Token,DummyMultipleReturnERC20Token",
|
"publicInterfaceContracts": "DummyERC20Token,ERC20Token,WETH9,ZRXToken,DummyNoReturnERC20Token,DummyMultipleReturnERC20Token",
|
||||||
@@ -49,15 +51,18 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md",
|
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@0x/abi-gen": "^4.4.0-beta.2",
|
"@0x/abi-gen": "^5.0.0",
|
||||||
"@0x/contracts-gen": "^1.1.0-beta.2",
|
"@0x/contracts-gen": "^2.0.0",
|
||||||
"@0x/contracts-test-utils": "^3.2.0-beta.2",
|
"@0x/contracts-test-utils": "^4.0.0",
|
||||||
"@0x/contracts-utils": "^3.3.0-beta.2",
|
"@0x/contracts-utils": "^4.0.0",
|
||||||
"@0x/dev-utils": "^2.4.0-beta.2",
|
"@0x/dev-utils": "^3.0.0",
|
||||||
"@0x/sol-compiler": "^3.2.0-beta.2",
|
"@0x/sol-compiler": "^4.0.0",
|
||||||
"@0x/tslint-config": "^3.1.0-beta.2",
|
"@0x/ts-doc-gen": "^0.0.22",
|
||||||
"@0x/utils": "^4.6.0-beta.2",
|
"@0x/tslint-config": "^4.0.0",
|
||||||
"@0x/web3-wrapper": "^6.1.0-beta.2",
|
"@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/lodash": "4.14.104",
|
||||||
"@types/mocha": "^5.2.7",
|
"@types/mocha": "^5.2.7",
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
@@ -65,6 +70,7 @@
|
|||||||
"chai-as-promised": "^7.1.0",
|
"chai-as-promised": "^7.1.0",
|
||||||
"chai-bignumber": "^3.0.0",
|
"chai-bignumber": "^3.0.0",
|
||||||
"dirty-chai": "^2.0.1",
|
"dirty-chai": "^2.0.1",
|
||||||
|
"ethereum-types": "^3.0.0",
|
||||||
"lodash": "^4.17.11",
|
"lodash": "^4.17.11",
|
||||||
"make-promises-safe": "^1.1.0",
|
"make-promises-safe": "^1.1.0",
|
||||||
"mocha": "^6.2.0",
|
"mocha": "^6.2.0",
|
||||||
@@ -72,13 +78,11 @@
|
|||||||
"shx": "^0.2.2",
|
"shx": "^0.2.2",
|
||||||
"solhint": "^1.4.1",
|
"solhint": "^1.4.1",
|
||||||
"tslint": "5.11.0",
|
"tslint": "5.11.0",
|
||||||
|
"typedoc": "^0.15.0",
|
||||||
"typescript": "3.0.1"
|
"typescript": "3.0.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@0x/base-contract": "^5.5.0-beta.2",
|
"@0x/base-contract": "^6.0.0"
|
||||||
"@0x/types": "^2.5.0-beta.2",
|
|
||||||
"@0x/typescript-typings": "^4.4.0-beta.2",
|
|
||||||
"ethereum-types": "^2.2.0-beta.2"
|
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
|
@@ -1,2 +1,42 @@
|
|||||||
export * from './wrappers';
|
export {
|
||||||
export * from './artifacts';
|
DummyERC20TokenContract,
|
||||||
|
DummyMultipleReturnERC20TokenContract,
|
||||||
|
DummyNoReturnERC20TokenContract,
|
||||||
|
WETH9Contract,
|
||||||
|
ZRXTokenContract,
|
||||||
|
DummyERC20TokenTransferEventArgs,
|
||||||
|
ERC20TokenEventArgs,
|
||||||
|
ERC20TokenEvents,
|
||||||
|
ERC20TokenTransferEventArgs,
|
||||||
|
ERC20TokenApprovalEventArgs,
|
||||||
|
ERC20TokenContract,
|
||||||
|
} from './wrappers';
|
||||||
|
export { artifacts } from './artifacts';
|
||||||
|
export {
|
||||||
|
ContractArtifact,
|
||||||
|
ContractChains,
|
||||||
|
CompilerOpts,
|
||||||
|
StandardContractOutput,
|
||||||
|
CompilerSettings,
|
||||||
|
ContractChainData,
|
||||||
|
ContractAbi,
|
||||||
|
DevdocOutput,
|
||||||
|
EvmOutput,
|
||||||
|
CompilerSettingsMetadata,
|
||||||
|
OptimizerSettings,
|
||||||
|
OutputField,
|
||||||
|
ParamDescription,
|
||||||
|
EvmBytecodeOutput,
|
||||||
|
AbiDefinition,
|
||||||
|
FunctionAbi,
|
||||||
|
EventAbi,
|
||||||
|
RevertErrorAbi,
|
||||||
|
EventParameter,
|
||||||
|
DataItem,
|
||||||
|
MethodAbi,
|
||||||
|
ConstructorAbi,
|
||||||
|
FallbackAbi,
|
||||||
|
ConstructorStateMutability,
|
||||||
|
TupleDataItem,
|
||||||
|
StateMutability,
|
||||||
|
} from 'ethereum-types';
|
||||||
|
@@ -16,6 +16,7 @@ import { artifacts } from './artifacts';
|
|||||||
blockchainTests('LibERC20Token', env => {
|
blockchainTests('LibERC20Token', env => {
|
||||||
let testContract: TestLibERC20TokenContract;
|
let testContract: TestLibERC20TokenContract;
|
||||||
const REVERT_STRING = 'WHOOPSIE';
|
const REVERT_STRING = 'WHOOPSIE';
|
||||||
|
const ENCODED_REVERT = new StringRevertError(REVERT_STRING).encode();
|
||||||
const ENCODED_TRUE = hexLeftPad(1);
|
const ENCODED_TRUE = hexLeftPad(1);
|
||||||
const ENCODED_FALSE = hexLeftPad(0);
|
const ENCODED_FALSE = hexLeftPad(0);
|
||||||
const ENCODED_TWO = hexLeftPad(2);
|
const ENCODED_TWO = hexLeftPad(2);
|
||||||
@@ -31,16 +32,12 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
function encodeRevert(message: string): string {
|
|
||||||
return new StringRevertError(message).encode();
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('approve()', () => {
|
describe('approve()', () => {
|
||||||
it('calls the target with the correct arguments', async () => {
|
it('calls the target with the correct arguments', async () => {
|
||||||
const spender = randomAddress();
|
const spender = randomAddress();
|
||||||
const allowance = getRandomInteger(0, 100e18);
|
const allowance = getRandomInteger(0, 100e18);
|
||||||
const { logs } = await testContract
|
const { logs } = await testContract
|
||||||
.testApprove(false, encodeRevert(REVERT_STRING), ENCODED_TRUE, spender, allowance)
|
.testApprove(false, ENCODED_REVERT, ENCODED_TRUE, spender, allowance)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
expect(logs).to.be.length(1);
|
expect(logs).to.be.length(1);
|
||||||
verifyEventsFromLogs(logs, [{ spender, allowance }], TestLibERC20TokenTargetEvents.ApproveCalled);
|
verifyEventsFromLogs(logs, [{ spender, allowance }], TestLibERC20TokenTargetEvents.ApproveCalled);
|
||||||
@@ -50,7 +47,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const spender = randomAddress();
|
const spender = randomAddress();
|
||||||
const allowance = getRandomInteger(0, 100e18);
|
const allowance = getRandomInteger(0, 100e18);
|
||||||
await testContract
|
await testContract
|
||||||
.testApprove(false, encodeRevert(REVERT_STRING), ENCODED_TRUE, spender, allowance)
|
.testApprove(false, ENCODED_REVERT, ENCODED_TRUE, spender, allowance)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -58,7 +55,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const spender = randomAddress();
|
const spender = randomAddress();
|
||||||
const allowance = getRandomInteger(0, 100e18);
|
const allowance = getRandomInteger(0, 100e18);
|
||||||
await testContract
|
await testContract
|
||||||
.testApprove(false, encodeRevert(REVERT_STRING), constants.NULL_BYTES, spender, allowance)
|
.testApprove(false, ENCODED_REVERT, constants.NULL_BYTES, spender, allowance)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -66,7 +63,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const spender = randomAddress();
|
const spender = randomAddress();
|
||||||
const allowance = getRandomInteger(0, 100e18);
|
const allowance = getRandomInteger(0, 100e18);
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.testApprove(false, encodeRevert(REVERT_STRING), ENCODED_FALSE, spender, allowance)
|
.testApprove(false, ENCODED_REVERT, ENCODED_FALSE, spender, allowance)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
const expectedError = new RawRevertError(ENCODED_FALSE);
|
const expectedError = new RawRevertError(ENCODED_FALSE);
|
||||||
return expect(tx).to.revertWith(expectedError);
|
return expect(tx).to.revertWith(expectedError);
|
||||||
@@ -76,7 +73,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const spender = randomAddress();
|
const spender = randomAddress();
|
||||||
const allowance = getRandomInteger(0, 100e18);
|
const allowance = getRandomInteger(0, 100e18);
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.testApprove(false, encodeRevert(REVERT_STRING), ENCODED_TWO, spender, allowance)
|
.testApprove(false, ENCODED_REVERT, ENCODED_TWO, spender, allowance)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
const expectedError = new RawRevertError(ENCODED_TWO);
|
const expectedError = new RawRevertError(ENCODED_TWO);
|
||||||
return expect(tx).to.revertWith(expectedError);
|
return expect(tx).to.revertWith(expectedError);
|
||||||
@@ -86,7 +83,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const spender = randomAddress();
|
const spender = randomAddress();
|
||||||
const allowance = getRandomInteger(0, 100e18);
|
const allowance = getRandomInteger(0, 100e18);
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.testApprove(false, encodeRevert(REVERT_STRING), ENCODED_SHORT_TRUE, spender, allowance)
|
.testApprove(false, ENCODED_REVERT, ENCODED_SHORT_TRUE, spender, allowance)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
const expectedError = new RawRevertError(ENCODED_SHORT_TRUE);
|
const expectedError = new RawRevertError(ENCODED_SHORT_TRUE);
|
||||||
return expect(tx).to.revertWith(expectedError);
|
return expect(tx).to.revertWith(expectedError);
|
||||||
@@ -96,7 +93,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const spender = randomAddress();
|
const spender = randomAddress();
|
||||||
const allowance = getRandomInteger(0, 100e18);
|
const allowance = getRandomInteger(0, 100e18);
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.testApprove(false, encodeRevert(REVERT_STRING), ENCODED_LONG_TRUE, spender, allowance)
|
.testApprove(false, ENCODED_REVERT, ENCODED_LONG_TRUE, spender, allowance)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
const expectedError = new RawRevertError(ENCODED_LONG_TRUE);
|
const expectedError = new RawRevertError(ENCODED_LONG_TRUE);
|
||||||
return expect(tx).to.revertWith(expectedError);
|
return expect(tx).to.revertWith(expectedError);
|
||||||
@@ -106,7 +103,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const spender = randomAddress();
|
const spender = randomAddress();
|
||||||
const allowance = getRandomInteger(0, 100e18);
|
const allowance = getRandomInteger(0, 100e18);
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.testApprove(true, encodeRevert(REVERT_STRING), ENCODED_TRUE, spender, allowance)
|
.testApprove(true, ENCODED_REVERT, ENCODED_TRUE, spender, allowance)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
return expect(tx).to.revertWith(REVERT_STRING);
|
return expect(tx).to.revertWith(REVERT_STRING);
|
||||||
});
|
});
|
||||||
@@ -126,7 +123,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const to = randomAddress();
|
const to = randomAddress();
|
||||||
const amount = getRandomInteger(0, 100e18);
|
const amount = getRandomInteger(0, 100e18);
|
||||||
const { logs } = await testContract
|
const { logs } = await testContract
|
||||||
.testTransfer(false, encodeRevert(REVERT_STRING), ENCODED_TRUE, to, amount)
|
.testTransfer(false, ENCODED_REVERT, ENCODED_TRUE, to, amount)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
expect(logs).to.be.length(1);
|
expect(logs).to.be.length(1);
|
||||||
verifyEventsFromLogs(logs, [{ to, amount }], TestLibERC20TokenTargetEvents.TransferCalled);
|
verifyEventsFromLogs(logs, [{ to, amount }], TestLibERC20TokenTargetEvents.TransferCalled);
|
||||||
@@ -136,7 +133,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const to = randomAddress();
|
const to = randomAddress();
|
||||||
const amount = getRandomInteger(0, 100e18);
|
const amount = getRandomInteger(0, 100e18);
|
||||||
await testContract
|
await testContract
|
||||||
.testTransfer(false, encodeRevert(REVERT_STRING), ENCODED_TRUE, to, amount)
|
.testTransfer(false, ENCODED_REVERT, ENCODED_TRUE, to, amount)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -144,7 +141,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const to = randomAddress();
|
const to = randomAddress();
|
||||||
const amount = getRandomInteger(0, 100e18);
|
const amount = getRandomInteger(0, 100e18);
|
||||||
await testContract
|
await testContract
|
||||||
.testTransfer(false, encodeRevert(REVERT_STRING), constants.NULL_BYTES, to, amount)
|
.testTransfer(false, ENCODED_REVERT, constants.NULL_BYTES, to, amount)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -152,7 +149,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const to = randomAddress();
|
const to = randomAddress();
|
||||||
const amount = getRandomInteger(0, 100e18);
|
const amount = getRandomInteger(0, 100e18);
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.testTransfer(false, encodeRevert(REVERT_STRING), ENCODED_FALSE, to, amount)
|
.testTransfer(false, ENCODED_REVERT, ENCODED_FALSE, to, amount)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
const expectedError = new RawRevertError(ENCODED_FALSE);
|
const expectedError = new RawRevertError(ENCODED_FALSE);
|
||||||
return expect(tx).to.revertWith(expectedError);
|
return expect(tx).to.revertWith(expectedError);
|
||||||
@@ -162,7 +159,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const to = randomAddress();
|
const to = randomAddress();
|
||||||
const amount = getRandomInteger(0, 100e18);
|
const amount = getRandomInteger(0, 100e18);
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.testTransfer(false, encodeRevert(REVERT_STRING), ENCODED_TWO, to, amount)
|
.testTransfer(false, ENCODED_REVERT, ENCODED_TWO, to, amount)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
const expectedError = new RawRevertError(ENCODED_TWO);
|
const expectedError = new RawRevertError(ENCODED_TWO);
|
||||||
return expect(tx).to.revertWith(expectedError);
|
return expect(tx).to.revertWith(expectedError);
|
||||||
@@ -172,7 +169,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const to = randomAddress();
|
const to = randomAddress();
|
||||||
const amount = getRandomInteger(0, 100e18);
|
const amount = getRandomInteger(0, 100e18);
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.testTransfer(false, encodeRevert(REVERT_STRING), ENCODED_SHORT_TRUE, to, amount)
|
.testTransfer(false, ENCODED_REVERT, ENCODED_SHORT_TRUE, to, amount)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
const expectedError = new RawRevertError(ENCODED_SHORT_TRUE);
|
const expectedError = new RawRevertError(ENCODED_SHORT_TRUE);
|
||||||
return expect(tx).to.revertWith(expectedError);
|
return expect(tx).to.revertWith(expectedError);
|
||||||
@@ -182,7 +179,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const to = randomAddress();
|
const to = randomAddress();
|
||||||
const amount = getRandomInteger(0, 100e18);
|
const amount = getRandomInteger(0, 100e18);
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.testTransfer(false, encodeRevert(REVERT_STRING), ENCODED_LONG_TRUE, to, amount)
|
.testTransfer(false, ENCODED_REVERT, ENCODED_LONG_TRUE, to, amount)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
const expectedError = new RawRevertError(ENCODED_LONG_TRUE);
|
const expectedError = new RawRevertError(ENCODED_LONG_TRUE);
|
||||||
return expect(tx).to.revertWith(expectedError);
|
return expect(tx).to.revertWith(expectedError);
|
||||||
@@ -192,7 +189,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const to = randomAddress();
|
const to = randomAddress();
|
||||||
const amount = getRandomInteger(0, 100e18);
|
const amount = getRandomInteger(0, 100e18);
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.testTransfer(true, encodeRevert(REVERT_STRING), ENCODED_TRUE, to, amount)
|
.testTransfer(true, ENCODED_REVERT, ENCODED_TRUE, to, amount)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
return expect(tx).to.revertWith(REVERT_STRING);
|
return expect(tx).to.revertWith(REVERT_STRING);
|
||||||
});
|
});
|
||||||
@@ -213,7 +210,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const to = randomAddress();
|
const to = randomAddress();
|
||||||
const amount = getRandomInteger(0, 100e18);
|
const amount = getRandomInteger(0, 100e18);
|
||||||
const { logs } = await testContract
|
const { logs } = await testContract
|
||||||
.testTransferFrom(false, encodeRevert(REVERT_STRING), ENCODED_TRUE, owner, to, amount)
|
.testTransferFrom(false, ENCODED_REVERT, ENCODED_TRUE, owner, to, amount)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
expect(logs).to.be.length(1);
|
expect(logs).to.be.length(1);
|
||||||
verifyEventsFromLogs(logs, [{ from: owner, to, amount }], TestLibERC20TokenTargetEvents.TransferFromCalled);
|
verifyEventsFromLogs(logs, [{ from: owner, to, amount }], TestLibERC20TokenTargetEvents.TransferFromCalled);
|
||||||
@@ -224,7 +221,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const to = randomAddress();
|
const to = randomAddress();
|
||||||
const amount = getRandomInteger(0, 100e18);
|
const amount = getRandomInteger(0, 100e18);
|
||||||
await testContract
|
await testContract
|
||||||
.testTransferFrom(false, encodeRevert(REVERT_STRING), ENCODED_TRUE, owner, to, amount)
|
.testTransferFrom(false, ENCODED_REVERT, ENCODED_TRUE, owner, to, amount)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -233,7 +230,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const to = randomAddress();
|
const to = randomAddress();
|
||||||
const amount = getRandomInteger(0, 100e18);
|
const amount = getRandomInteger(0, 100e18);
|
||||||
await testContract
|
await testContract
|
||||||
.testTransferFrom(false, encodeRevert(REVERT_STRING), constants.NULL_BYTES, owner, to, amount)
|
.testTransferFrom(false, ENCODED_REVERT, constants.NULL_BYTES, owner, to, amount)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -242,7 +239,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const to = randomAddress();
|
const to = randomAddress();
|
||||||
const amount = getRandomInteger(0, 100e18);
|
const amount = getRandomInteger(0, 100e18);
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.testTransferFrom(false, encodeRevert(REVERT_STRING), ENCODED_FALSE, owner, to, amount)
|
.testTransferFrom(false, ENCODED_REVERT, ENCODED_FALSE, owner, to, amount)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
const expectedError = new RawRevertError(ENCODED_FALSE);
|
const expectedError = new RawRevertError(ENCODED_FALSE);
|
||||||
return expect(tx).to.revertWith(expectedError);
|
return expect(tx).to.revertWith(expectedError);
|
||||||
@@ -253,7 +250,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const to = randomAddress();
|
const to = randomAddress();
|
||||||
const amount = getRandomInteger(0, 100e18);
|
const amount = getRandomInteger(0, 100e18);
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.testTransferFrom(false, encodeRevert(REVERT_STRING), ENCODED_TWO, owner, to, amount)
|
.testTransferFrom(false, ENCODED_REVERT, ENCODED_TWO, owner, to, amount)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
const expectedError = new RawRevertError(ENCODED_TWO);
|
const expectedError = new RawRevertError(ENCODED_TWO);
|
||||||
return expect(tx).to.revertWith(expectedError);
|
return expect(tx).to.revertWith(expectedError);
|
||||||
@@ -264,7 +261,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const to = randomAddress();
|
const to = randomAddress();
|
||||||
const amount = getRandomInteger(0, 100e18);
|
const amount = getRandomInteger(0, 100e18);
|
||||||
const tx = testContract
|
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();
|
.awaitTransactionSuccessAsync();
|
||||||
const expectedError = new RawRevertError(ENCODED_SHORT_TRUE);
|
const expectedError = new RawRevertError(ENCODED_SHORT_TRUE);
|
||||||
return expect(tx).to.revertWith(expectedError);
|
return expect(tx).to.revertWith(expectedError);
|
||||||
@@ -275,7 +272,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const to = randomAddress();
|
const to = randomAddress();
|
||||||
const amount = getRandomInteger(0, 100e18);
|
const amount = getRandomInteger(0, 100e18);
|
||||||
const tx = testContract
|
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();
|
.awaitTransactionSuccessAsync();
|
||||||
const expectedError = new RawRevertError(ENCODED_LONG_TRUE);
|
const expectedError = new RawRevertError(ENCODED_LONG_TRUE);
|
||||||
return expect(tx).to.revertWith(expectedError);
|
return expect(tx).to.revertWith(expectedError);
|
||||||
@@ -286,7 +283,7 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
const to = randomAddress();
|
const to = randomAddress();
|
||||||
const amount = getRandomInteger(0, 100e18);
|
const amount = getRandomInteger(0, 100e18);
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.testTransferFrom(true, encodeRevert(REVERT_STRING), ENCODED_TRUE, owner, to, amount)
|
.testTransferFrom(true, ENCODED_REVERT, ENCODED_TRUE, owner, to, amount)
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
return expect(tx).to.revertWith(REVERT_STRING);
|
return expect(tx).to.revertWith(REVERT_STRING);
|
||||||
});
|
});
|
||||||
@@ -301,4 +298,39 @@ blockchainTests('LibERC20Token', env => {
|
|||||||
return expect(tx).to.be.rejectedWith('revert');
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
7
contracts/erc20/typedoc-tsconfig.json
Normal file
7
contracts/erc20/typedoc-tsconfig.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../typedoc-tsconfig",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "lib"
|
||||||
|
},
|
||||||
|
"include": ["./src/**/*", "./test/**/*"]
|
||||||
|
}
|
@@ -1,4 +1,36 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"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": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1574238768
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "2.2.0-beta.2",
|
"version": "2.2.0-beta.2",
|
||||||
"changes": [
|
"changes": [
|
||||||
|
@@ -5,6 +5,19 @@ Edit the package's CHANGELOG.json file only.
|
|||||||
|
|
||||||
CHANGELOG
|
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
|
||||||
|
|
||||||
## v2.2.0-beta.2 - _November 17, 2019_
|
## v2.2.0-beta.2 - _November 17, 2019_
|
||||||
|
|
||||||
* Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330)
|
* Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330)
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@0x/contracts-erc721",
|
"name": "@0x/contracts-erc721",
|
||||||
"version": "2.2.0-beta.2",
|
"version": "3.0.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.12"
|
"node": ">=6.12"
|
||||||
},
|
},
|
||||||
@@ -33,7 +33,9 @@
|
|||||||
"contracts:gen": "contracts-gen generate",
|
"contracts:gen": "contracts-gen generate",
|
||||||
"contracts:copy": "contracts-gen copy",
|
"contracts:copy": "contracts-gen copy",
|
||||||
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol",
|
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol",
|
||||||
"compile:truffle": "truffle compile"
|
"compile:truffle": "truffle compile",
|
||||||
|
"docs:md": "ts-doc-gen --sourceDir='$PROJECT_FILES' --output=$MD_FILE_DIR --fileExtension=mdx --tsconfig=./typedoc-tsconfig.json",
|
||||||
|
"docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"publicInterfaceContracts": "DummyERC721Token,ERC721Token,IERC721Receiver,DummyERC721Receiver",
|
"publicInterfaceContracts": "DummyERC721Token,ERC721Token,IERC721Receiver,DummyERC721Receiver",
|
||||||
@@ -50,16 +52,18 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md",
|
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@0x/abi-gen": "^4.4.0-beta.2",
|
"@0x/abi-gen": "^5.0.0",
|
||||||
"@0x/contracts-gen": "^1.1.0-beta.2",
|
"@0x/contracts-gen": "^2.0.0",
|
||||||
"@0x/contracts-test-utils": "^3.2.0-beta.2",
|
"@0x/contracts-test-utils": "^4.0.0",
|
||||||
"@0x/contracts-utils": "^3.3.0-beta.2",
|
"@0x/contracts-utils": "^4.0.0",
|
||||||
"@0x/dev-utils": "^2.4.0-beta.2",
|
"@0x/dev-utils": "^3.0.0",
|
||||||
"@0x/sol-compiler": "^3.2.0-beta.2",
|
"@0x/sol-compiler": "^4.0.0",
|
||||||
"@0x/tslint-config": "^3.1.0-beta.2",
|
"@0x/ts-doc-gen": "^0.0.22",
|
||||||
"@0x/types": "^2.5.0-beta.2",
|
"@0x/tslint-config": "^4.0.0",
|
||||||
"@0x/utils": "^4.6.0-beta.2",
|
"@0x/types": "^3.0.0",
|
||||||
"@0x/web3-wrapper": "^6.1.0-beta.2",
|
"@0x/typescript-typings": "^5.0.0",
|
||||||
|
"@0x/utils": "^5.0.0",
|
||||||
|
"@0x/web3-wrapper": "^7.0.0",
|
||||||
"@types/lodash": "4.14.104",
|
"@types/lodash": "4.14.104",
|
||||||
"@types/mocha": "^5.2.7",
|
"@types/mocha": "^5.2.7",
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
@@ -67,6 +71,7 @@
|
|||||||
"chai-as-promised": "^7.1.0",
|
"chai-as-promised": "^7.1.0",
|
||||||
"chai-bignumber": "^3.0.0",
|
"chai-bignumber": "^3.0.0",
|
||||||
"dirty-chai": "^2.0.1",
|
"dirty-chai": "^2.0.1",
|
||||||
|
"ethereum-types": "^3.0.0",
|
||||||
"lodash": "^4.17.11",
|
"lodash": "^4.17.11",
|
||||||
"make-promises-safe": "^1.1.0",
|
"make-promises-safe": "^1.1.0",
|
||||||
"mocha": "^6.2.0",
|
"mocha": "^6.2.0",
|
||||||
@@ -75,12 +80,11 @@
|
|||||||
"solhint": "^1.4.1",
|
"solhint": "^1.4.1",
|
||||||
"truffle": "^5.0.32",
|
"truffle": "^5.0.32",
|
||||||
"tslint": "5.11.0",
|
"tslint": "5.11.0",
|
||||||
|
"typedoc": "^0.15.0",
|
||||||
"typescript": "3.0.1"
|
"typescript": "3.0.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@0x/base-contract": "^5.5.0-beta.2",
|
"@0x/base-contract": "^6.0.0"
|
||||||
"@0x/typescript-typings": "^4.4.0-beta.2",
|
|
||||||
"ethereum-types": "^2.2.0-beta.2"
|
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
|
@@ -1,2 +1,37 @@
|
|||||||
export * from './wrappers';
|
export {
|
||||||
export * from './artifacts';
|
DummyERC721ReceiverContract,
|
||||||
|
DummyERC721TokenContract,
|
||||||
|
ERC721TokenContract,
|
||||||
|
ERC721TokenEvents,
|
||||||
|
ERC721TokenTransferEventArgs,
|
||||||
|
IERC721ReceiverContract,
|
||||||
|
} from './wrappers';
|
||||||
|
export { artifacts } from './artifacts';
|
||||||
|
export {
|
||||||
|
ContractArtifact,
|
||||||
|
ContractChains,
|
||||||
|
CompilerOpts,
|
||||||
|
StandardContractOutput,
|
||||||
|
CompilerSettings,
|
||||||
|
ContractChainData,
|
||||||
|
ContractAbi,
|
||||||
|
DevdocOutput,
|
||||||
|
EvmOutput,
|
||||||
|
CompilerSettingsMetadata,
|
||||||
|
OptimizerSettings,
|
||||||
|
OutputField,
|
||||||
|
ParamDescription,
|
||||||
|
EvmBytecodeOutput,
|
||||||
|
AbiDefinition,
|
||||||
|
FunctionAbi,
|
||||||
|
EventAbi,
|
||||||
|
RevertErrorAbi,
|
||||||
|
EventParameter,
|
||||||
|
DataItem,
|
||||||
|
MethodAbi,
|
||||||
|
ConstructorAbi,
|
||||||
|
FallbackAbi,
|
||||||
|
ConstructorStateMutability,
|
||||||
|
TupleDataItem,
|
||||||
|
StateMutability,
|
||||||
|
} from 'ethereum-types';
|
||||||
|
7
contracts/erc721/typedoc-tsconfig.json
Normal file
7
contracts/erc721/typedoc-tsconfig.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../typedoc-tsconfig",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "lib"
|
||||||
|
},
|
||||||
|
"include": ["./src/**/*", "./test/**/*"]
|
||||||
|
}
|
@@ -1,4 +1,45 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"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": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1574238768
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "3.1.0-beta.2",
|
"version": "3.1.0-beta.2",
|
||||||
"changes": [
|
"changes": [
|
||||||
|
@@ -5,6 +5,21 @@ Edit the package's CHANGELOG.json file only.
|
|||||||
|
|
||||||
CHANGELOG
|
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
|
||||||
|
|
||||||
## v3.1.0-beta.2 - _November 17, 2019_
|
## v3.1.0-beta.2 - _November 17, 2019_
|
||||||
|
|
||||||
* Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330)
|
* Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330)
|
||||||
|
@@ -32,13 +32,13 @@ contract Forwarder is
|
|||||||
{
|
{
|
||||||
constructor (
|
constructor (
|
||||||
address _exchange,
|
address _exchange,
|
||||||
bytes memory _wethAssetData
|
address _weth
|
||||||
)
|
)
|
||||||
public
|
public
|
||||||
Ownable()
|
Ownable()
|
||||||
LibConstants(
|
LibConstants(
|
||||||
_exchange,
|
_exchange,
|
||||||
_wethAssetData
|
_weth
|
||||||
)
|
)
|
||||||
MixinForwarderCore()
|
MixinForwarderCore()
|
||||||
{}
|
{}
|
||||||
|
@@ -21,9 +21,9 @@ pragma solidity ^0.5.9;
|
|||||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||||
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
|
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
|
||||||
import "@0x/contracts-utils/contracts/src/Ownable.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-erc20/contracts/src/LibERC20Token.sol";
|
||||||
import "@0x/contracts-erc721/contracts/src/interfaces/IERC721Token.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/LibConstants.sol";
|
||||||
import "./libs/LibForwarderRichErrors.sol";
|
import "./libs/LibForwarderRichErrors.sol";
|
||||||
import "./interfaces/IAssets.sol";
|
import "./interfaces/IAssets.sol";
|
||||||
@@ -36,13 +36,10 @@ contract MixinAssets is
|
|||||||
{
|
{
|
||||||
using LibBytes for bytes;
|
using LibBytes for bytes;
|
||||||
|
|
||||||
bytes4 constant internal ERC20_TRANSFER_SELECTOR = bytes4(keccak256("transfer(address,uint256)"));
|
/// @dev Withdraws assets from this contract. It may be used by the owner to withdraw assets
|
||||||
|
/// that were accidentally sent to this contract.
|
||||||
/// @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.
|
|
||||||
/// @param assetData Byte array encoded for the respective asset proxy.
|
/// @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(
|
function withdrawAsset(
|
||||||
bytes calldata assetData,
|
bytes calldata assetData,
|
||||||
uint256 amount
|
uint256 amount
|
||||||
@@ -63,14 +60,16 @@ contract MixinAssets is
|
|||||||
external
|
external
|
||||||
{
|
{
|
||||||
bytes4 proxyId = assetData.readBytes4(0);
|
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.
|
// For now we only care about ERC20, since percentage fees on ERC721 tokens are invalid.
|
||||||
if (proxyId == ERC20_DATA_ID) {
|
if (proxyId == erc20ProxyId) {
|
||||||
address proxyAddress = EXCHANGE.getAssetProxy(ERC20_DATA_ID);
|
address proxyAddress = EXCHANGE.getAssetProxy(erc20ProxyId);
|
||||||
if (proxyAddress == address(0)) {
|
if (proxyAddress == address(0)) {
|
||||||
LibRichErrors.rrevert(LibForwarderRichErrors.UnregisteredAssetProxyError());
|
LibRichErrors.rrevert(LibForwarderRichErrors.UnregisteredAssetProxyError());
|
||||||
}
|
}
|
||||||
IERC20Token assetToken = IERC20Token(assetData.readAddress(16));
|
address token = assetData.readAddress(16);
|
||||||
assetToken.approve(proxyAddress, MAX_UINT);
|
LibERC20Token.approve(token, proxyAddress, MAX_UINT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,9 +84,12 @@ contract MixinAssets is
|
|||||||
{
|
{
|
||||||
bytes4 proxyId = assetData.readBytes4(0);
|
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);
|
_transferERC20Token(assetData, amount);
|
||||||
} else if (proxyId == ERC721_DATA_ID) {
|
} else if (proxyId == IAssetData(address(0)).ERC721Token.selector) {
|
||||||
_transferERC721Token(assetData, amount);
|
_transferERC721Token(assetData, amount);
|
||||||
} else {
|
} else {
|
||||||
LibRichErrors.rrevert(LibForwarderRichErrors.UnsupportedAssetProxyError(
|
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 assetData Byte array encoded for the respective asset proxy.
|
||||||
/// @param amount Amount of asset to transfer to sender.
|
/// @param amount Amount of asset to transfer to sender.
|
||||||
function _transferERC20Token(
|
function _transferERC20Token(
|
||||||
|
@@ -19,12 +19,15 @@
|
|||||||
pragma solidity ^0.5.9;
|
pragma solidity ^0.5.9;
|
||||||
pragma experimental ABIEncoderV2;
|
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/LibRichErrors.sol";
|
||||||
import "@0x/contracts-utils/contracts/src/LibSafeMath.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/LibOrder.sol";
|
||||||
import "@0x/contracts-exchange-libs/contracts/src/LibFillResults.sol";
|
import "@0x/contracts-exchange-libs/contracts/src/LibFillResults.sol";
|
||||||
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
|
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
|
||||||
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.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/LibConstants.sol";
|
||||||
import "./libs/LibForwarderRichErrors.sol";
|
import "./libs/LibForwarderRichErrors.sol";
|
||||||
import "./MixinAssets.sol";
|
import "./MixinAssets.sol";
|
||||||
@@ -34,6 +37,7 @@ contract MixinExchangeWrapper is
|
|||||||
LibConstants,
|
LibConstants,
|
||||||
MixinAssets
|
MixinAssets
|
||||||
{
|
{
|
||||||
|
using LibBytes for bytes;
|
||||||
using LibSafeMath for uint256;
|
using LibSafeMath for uint256;
|
||||||
|
|
||||||
/// @dev Fills the input order.
|
/// @dev Fills the input order.
|
||||||
@@ -88,7 +92,10 @@ contract MixinExchangeWrapper is
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
// No taker fee or percentage fee
|
// 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
|
// Attempt to sell the remaining amount of WETH
|
||||||
LibFillResults.FillResults memory singleFillResults = _fillOrderNoThrow(
|
LibFillResults.FillResults memory singleFillResults = _fillOrderNoThrow(
|
||||||
order,
|
order,
|
||||||
@@ -103,7 +110,7 @@ contract MixinExchangeWrapper is
|
|||||||
makerAssetAcquiredAmount = singleFillResults.makerAssetFilledAmount
|
makerAssetAcquiredAmount = singleFillResults.makerAssetFilledAmount
|
||||||
.safeSub(singleFillResults.takerFeePaid);
|
.safeSub(singleFillResults.takerFeePaid);
|
||||||
// WETH fee
|
// 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.
|
// 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.
|
// This ensures that we reserve enough to pay the taker and protocol fees.
|
||||||
@@ -150,9 +157,10 @@ contract MixinExchangeWrapper is
|
|||||||
uint256 totalMakerAssetAcquiredAmount
|
uint256 totalMakerAssetAcquiredAmount
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
uint256 ordersLength = orders.length;
|
|
||||||
uint256 protocolFee = tx.gasprice.safeMul(EXCHANGE.protocolFeeMultiplier());
|
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++) {
|
for (uint256 i = 0; i != ordersLength; i++) {
|
||||||
// Preemptively skip to avoid division by zero in _marketSellSingleOrder
|
// Preemptively skip to avoid division by zero in _marketSellSingleOrder
|
||||||
if (orders[i].makerAssetAmount == 0 || orders[i].takerAssetAmount == 0) {
|
if (orders[i].makerAssetAmount == 0 || orders[i].takerAssetAmount == 0) {
|
||||||
@@ -164,6 +172,15 @@ contract MixinExchangeWrapper is
|
|||||||
.safeSub(totalWethSpentAmount)
|
.safeSub(totalWethSpentAmount)
|
||||||
.safeSub(protocolFee);
|
.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 wethSpentAmount,
|
||||||
uint256 makerAssetAcquiredAmount
|
uint256 makerAssetAcquiredAmount
|
||||||
@@ -173,6 +190,15 @@ contract MixinExchangeWrapper is
|
|||||||
remainingTakerAssetFillAmount
|
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);
|
_transferAssetToSender(orders[i].makerAssetData, makerAssetAcquiredAmount);
|
||||||
|
|
||||||
totalWethSpentAmount = totalWethSpentAmount
|
totalWethSpentAmount = totalWethSpentAmount
|
||||||
@@ -206,7 +232,10 @@ contract MixinExchangeWrapper is
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
// No taker fee or WETH fee
|
// 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
|
// Calculate the remaining amount of takerAsset to sell
|
||||||
uint256 remainingTakerAssetFillAmount = LibMath.getPartialAmountCeil(
|
uint256 remainingTakerAssetFillAmount = LibMath.getPartialAmountCeil(
|
||||||
order.takerAssetAmount,
|
order.takerAssetAmount,
|
||||||
@@ -228,7 +257,7 @@ contract MixinExchangeWrapper is
|
|||||||
|
|
||||||
makerAssetAcquiredAmount = singleFillResults.makerAssetFilledAmount;
|
makerAssetAcquiredAmount = singleFillResults.makerAssetFilledAmount;
|
||||||
// Percentage fee
|
// Percentage fee
|
||||||
} else if (order.takerFeeAssetData.equals(order.makerAssetData)) {
|
} else if (_areUnderlyingAssetsEqual(order.takerFeeAssetData, order.makerAssetData)) {
|
||||||
// Calculate the remaining amount of takerAsset to sell
|
// Calculate the remaining amount of takerAsset to sell
|
||||||
uint256 remainingTakerAssetFillAmount = LibMath.getPartialAmountCeil(
|
uint256 remainingTakerAssetFillAmount = LibMath.getPartialAmountCeil(
|
||||||
order.takerAssetAmount,
|
order.takerAssetAmount,
|
||||||
@@ -277,6 +306,8 @@ contract MixinExchangeWrapper is
|
|||||||
uint256 totalMakerAssetAcquiredAmount
|
uint256 totalMakerAssetAcquiredAmount
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
bytes4 erc20BridgeProxyId = IAssetData(address(0)).ERC20Bridge.selector;
|
||||||
|
|
||||||
uint256 ordersLength = orders.length;
|
uint256 ordersLength = orders.length;
|
||||||
for (uint256 i = 0; i != ordersLength; i++) {
|
for (uint256 i = 0; i != ordersLength; i++) {
|
||||||
// Preemptively skip to avoid division by zero in _marketBuySingleOrder
|
// Preemptively skip to avoid division by zero in _marketBuySingleOrder
|
||||||
@@ -287,6 +318,15 @@ contract MixinExchangeWrapper is
|
|||||||
uint256 remainingMakerAssetFillAmount = makerAssetBuyAmount
|
uint256 remainingMakerAssetFillAmount = makerAssetBuyAmount
|
||||||
.safeSub(totalMakerAssetAcquiredAmount);
|
.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 wethSpentAmount,
|
||||||
uint256 makerAssetAcquiredAmount
|
uint256 makerAssetAcquiredAmount
|
||||||
@@ -296,6 +336,15 @@ contract MixinExchangeWrapper is
|
|||||||
remainingMakerAssetFillAmount
|
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);
|
_transferAssetToSender(orders[i].makerAssetData, makerAssetAcquiredAmount);
|
||||||
|
|
||||||
totalWethSpentAmount = totalWethSpentAmount
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -24,6 +24,7 @@ import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
|
|||||||
import "@0x/contracts-utils/contracts/src/LibSafeMath.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/LibOrder.sol";
|
||||||
import "@0x/contracts-exchange-libs/contracts/src/LibMath.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/LibConstants.sol";
|
||||||
import "./libs/LibForwarderRichErrors.sol";
|
import "./libs/LibForwarderRichErrors.sol";
|
||||||
import "./interfaces/IAssets.sol";
|
import "./interfaces/IAssets.sol";
|
||||||
@@ -46,7 +47,7 @@ contract MixinForwarderCore is
|
|||||||
constructor ()
|
constructor ()
|
||||||
public
|
public
|
||||||
{
|
{
|
||||||
address proxyAddress = EXCHANGE.getAssetProxy(ERC20_DATA_ID);
|
address proxyAddress = EXCHANGE.getAssetProxy(IAssetData(address(0)).ERC20Token.selector);
|
||||||
if (proxyAddress == address(0)) {
|
if (proxyAddress == address(0)) {
|
||||||
LibRichErrors.rrevert(LibForwarderRichErrors.UnregisteredAssetProxyError());
|
LibRichErrors.rrevert(LibForwarderRichErrors.UnregisteredAssetProxyError());
|
||||||
}
|
}
|
||||||
|
@@ -27,8 +27,6 @@ contract LibConstants {
|
|||||||
|
|
||||||
using LibBytes for bytes;
|
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 MAX_UINT = 2**256 - 1;
|
||||||
uint256 constant internal PERCENTAGE_DENOMINATOR = 10**18;
|
uint256 constant internal PERCENTAGE_DENOMINATOR = 10**18;
|
||||||
uint256 constant internal MAX_FEE_PERCENTAGE = 5 * PERCENTAGE_DENOMINATOR / 100; // 5%
|
uint256 constant internal MAX_FEE_PERCENTAGE = 5 * PERCENTAGE_DENOMINATOR / 100; // 5%
|
||||||
@@ -36,19 +34,15 @@ contract LibConstants {
|
|||||||
// solhint-disable var-name-mixedcase
|
// solhint-disable var-name-mixedcase
|
||||||
IExchange internal EXCHANGE;
|
IExchange internal EXCHANGE;
|
||||||
IEtherToken internal ETHER_TOKEN;
|
IEtherToken internal ETHER_TOKEN;
|
||||||
bytes internal WETH_ASSET_DATA;
|
|
||||||
// solhint-enable var-name-mixedcase
|
// solhint-enable var-name-mixedcase
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
address _exchange,
|
address _exchange,
|
||||||
bytes memory _wethAssetData
|
address _weth
|
||||||
)
|
)
|
||||||
public
|
public
|
||||||
{
|
{
|
||||||
EXCHANGE = IExchange(_exchange);
|
EXCHANGE = IExchange(_exchange);
|
||||||
WETH_ASSET_DATA = _wethAssetData;
|
ETHER_TOKEN = IEtherToken(_weth);
|
||||||
|
|
||||||
address etherToken = _wethAssetData.readAddress(16);
|
|
||||||
ETHER_TOKEN = IEtherToken(etherToken);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -18,8 +18,6 @@
|
|||||||
|
|
||||||
pragma solidity ^0.5.9;
|
pragma solidity ^0.5.9;
|
||||||
|
|
||||||
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
|
|
||||||
|
|
||||||
|
|
||||||
library LibForwarderRichErrors {
|
library LibForwarderRichErrors {
|
||||||
|
|
||||||
|
@@ -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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@0x/contracts-exchange-forwarder",
|
"name": "@0x/contracts-exchange-forwarder",
|
||||||
"version": "3.1.0-beta.2",
|
"version": "4.0.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.12"
|
"node": ">=6.12"
|
||||||
},
|
},
|
||||||
@@ -14,11 +14,12 @@
|
|||||||
"build:ts": "tsc -b",
|
"build:ts": "tsc -b",
|
||||||
"build:ci": "yarn build",
|
"build:ci": "yarn build",
|
||||||
"pre_build": "run-s compile contracts:gen generate_contract_wrappers contracts:copy",
|
"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",
|
"rebuild_and_test": "run-s build test",
|
||||||
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
"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:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html",
|
||||||
"test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha",
|
"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",
|
"compile": "sol-compiler",
|
||||||
"watch": "sol-compiler -w",
|
"watch": "sol-compiler -w",
|
||||||
"clean": "shx rm -rf lib test/generated-artifacts test/generated-wrappers generated-artifacts generated-wrappers",
|
"clean": "shx rm -rf lib test/generated-artifacts test/generated-wrappers generated-artifacts generated-wrappers",
|
||||||
@@ -32,11 +33,13 @@
|
|||||||
"contracts:gen": "contracts-gen generate",
|
"contracts:gen": "contracts-gen generate",
|
||||||
"contracts:copy": "contracts-gen copy",
|
"contracts:copy": "contracts-gen copy",
|
||||||
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol",
|
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol",
|
||||||
"compile:truffle": "truffle compile"
|
"compile:truffle": "truffle compile",
|
||||||
|
"docs:md": "ts-doc-gen --sourceDir='$PROJECT_FILES' --output=$MD_FILE_DIR --fileExtension=mdx --tsconfig=./typedoc-tsconfig.json",
|
||||||
|
"docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"publicInterfaceContracts": "Forwarder",
|
"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."
|
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -49,23 +52,24 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
|
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@0x/abi-gen": "^4.4.0-beta.2",
|
"@0x/abi-gen": "^5.0.0",
|
||||||
"@0x/contracts-asset-proxy": "^2.3.0-beta.2",
|
"@0x/contracts-asset-proxy": "^3.0.0",
|
||||||
"@0x/contracts-dev-utils": "^0.1.0-beta.2",
|
"@0x/contracts-dev-utils": "^1.0.0",
|
||||||
"@0x/contracts-erc20": "^2.3.0-beta.2",
|
"@0x/contracts-erc20": "^3.0.0",
|
||||||
"@0x/contracts-erc721": "^2.2.0-beta.2",
|
"@0x/contracts-erc721": "^3.0.0",
|
||||||
"@0x/contracts-exchange": "^2.2.0-beta.2",
|
"@0x/contracts-exchange": "^3.0.0",
|
||||||
"@0x/contracts-exchange-libs": "^3.1.0-beta.2",
|
"@0x/contracts-exchange-libs": "^4.0.0",
|
||||||
"@0x/contracts-gen": "^1.1.0-beta.2",
|
"@0x/contracts-gen": "^2.0.0",
|
||||||
"@0x/contracts-test-utils": "^3.2.0-beta.2",
|
"@0x/contracts-test-utils": "^4.0.0",
|
||||||
"@0x/contracts-utils": "^3.3.0-beta.2",
|
"@0x/contracts-utils": "^4.0.0",
|
||||||
"@0x/dev-utils": "^2.4.0-beta.2",
|
"@0x/dev-utils": "^3.0.0",
|
||||||
"@0x/order-utils": "^8.5.0-beta.2",
|
"@0x/order-utils": "^9.0.0",
|
||||||
"@0x/sol-compiler": "^3.2.0-beta.2",
|
"@0x/sol-compiler": "^4.0.0",
|
||||||
"@0x/tslint-config": "^3.1.0-beta.2",
|
"@0x/ts-doc-gen": "^0.0.22",
|
||||||
"@0x/types": "^2.5.0-beta.2",
|
"@0x/tslint-config": "^4.0.0",
|
||||||
"@0x/utils": "^4.6.0-beta.2",
|
"@0x/types": "^3.0.0",
|
||||||
"@0x/web3-wrapper": "^6.1.0-beta.2",
|
"@0x/utils": "^5.0.0",
|
||||||
|
"@0x/web3-wrapper": "^7.0.0",
|
||||||
"@types/lodash": "4.14.104",
|
"@types/lodash": "4.14.104",
|
||||||
"@types/mocha": "^5.2.7",
|
"@types/mocha": "^5.2.7",
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
@@ -81,12 +85,13 @@
|
|||||||
"solhint": "^1.4.1",
|
"solhint": "^1.4.1",
|
||||||
"truffle": "^5.0.32",
|
"truffle": "^5.0.32",
|
||||||
"tslint": "5.11.0",
|
"tslint": "5.11.0",
|
||||||
|
"typedoc": "^0.15.0",
|
||||||
"typescript": "3.0.1"
|
"typescript": "3.0.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@0x/base-contract": "^5.5.0-beta.2",
|
"@0x/base-contract": "^6.0.0",
|
||||||
"@0x/typescript-typings": "^4.4.0-beta.2",
|
"@0x/typescript-typings": "^5.0.0",
|
||||||
"ethereum-types": "^2.2.0-beta.2"
|
"ethereum-types": "^3.0.0"
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
|
@@ -1,3 +1,31 @@
|
|||||||
export * from './artifacts';
|
export { artifacts } from './artifacts';
|
||||||
export * from './wrappers';
|
export { ForwarderContract } from './wrappers';
|
||||||
export import ForwarderRevertErrors = require('./revert_errors');
|
export { ExchangeForwarderRevertErrors } from '@0x/utils';
|
||||||
|
export {
|
||||||
|
ContractArtifact,
|
||||||
|
ContractChains,
|
||||||
|
CompilerOpts,
|
||||||
|
StandardContractOutput,
|
||||||
|
CompilerSettings,
|
||||||
|
ContractChainData,
|
||||||
|
ContractAbi,
|
||||||
|
DevdocOutput,
|
||||||
|
EvmOutput,
|
||||||
|
CompilerSettingsMetadata,
|
||||||
|
OptimizerSettings,
|
||||||
|
OutputField,
|
||||||
|
ParamDescription,
|
||||||
|
EvmBytecodeOutput,
|
||||||
|
AbiDefinition,
|
||||||
|
FunctionAbi,
|
||||||
|
EventAbi,
|
||||||
|
RevertErrorAbi,
|
||||||
|
EventParameter,
|
||||||
|
DataItem,
|
||||||
|
MethodAbi,
|
||||||
|
ConstructorAbi,
|
||||||
|
FallbackAbi,
|
||||||
|
ConstructorStateMutability,
|
||||||
|
TupleDataItem,
|
||||||
|
StateMutability,
|
||||||
|
} from 'ethereum-types';
|
||||||
|
@@ -15,6 +15,7 @@ import * as MixinAssets from '../test/generated-artifacts/MixinAssets.json';
|
|||||||
import * as MixinExchangeWrapper from '../test/generated-artifacts/MixinExchangeWrapper.json';
|
import * as MixinExchangeWrapper from '../test/generated-artifacts/MixinExchangeWrapper.json';
|
||||||
import * as MixinForwarderCore from '../test/generated-artifacts/MixinForwarderCore.json';
|
import * as MixinForwarderCore from '../test/generated-artifacts/MixinForwarderCore.json';
|
||||||
import * as MixinWeth from '../test/generated-artifacts/MixinWeth.json';
|
import * as MixinWeth from '../test/generated-artifacts/MixinWeth.json';
|
||||||
|
import * as TestForwarder from '../test/generated-artifacts/TestForwarder.json';
|
||||||
export const artifacts = {
|
export const artifacts = {
|
||||||
Forwarder: Forwarder as ContractArtifact,
|
Forwarder: Forwarder as ContractArtifact,
|
||||||
MixinAssets: MixinAssets as ContractArtifact,
|
MixinAssets: MixinAssets as ContractArtifact,
|
||||||
@@ -26,4 +27,5 @@ export const artifacts = {
|
|||||||
IForwarderCore: IForwarderCore as ContractArtifact,
|
IForwarderCore: IForwarderCore as ContractArtifact,
|
||||||
LibConstants: LibConstants as ContractArtifact,
|
LibConstants: LibConstants as ContractArtifact,
|
||||||
LibForwarderRichErrors: LibForwarderRichErrors as ContractArtifact,
|
LibForwarderRichErrors: LibForwarderRichErrors as ContractArtifact,
|
||||||
|
TestForwarder: TestForwarder as ContractArtifact,
|
||||||
};
|
};
|
||||||
|
184
contracts/exchange-forwarder/test/asset_test.ts
Normal file
184
contracts/exchange-forwarder/test/asset_test.ts
Normal 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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@@ -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_exchange_wrapper';
|
||||||
export * from '../test/generated-wrappers/mixin_forwarder_core';
|
export * from '../test/generated-wrappers/mixin_forwarder_core';
|
||||||
export * from '../test/generated-wrappers/mixin_weth';
|
export * from '../test/generated-wrappers/mixin_weth';
|
||||||
|
export * from '../test/generated-wrappers/test_forwarder';
|
||||||
|
@@ -13,7 +13,8 @@
|
|||||||
"test/generated-artifacts/MixinAssets.json",
|
"test/generated-artifacts/MixinAssets.json",
|
||||||
"test/generated-artifacts/MixinExchangeWrapper.json",
|
"test/generated-artifacts/MixinExchangeWrapper.json",
|
||||||
"test/generated-artifacts/MixinForwarderCore.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"]
|
"exclude": ["./deploy/solc/solc_bin"]
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user