Compare commits
393 Commits
@0x/contra
...
@0x/dev-ut
Author | SHA1 | Date | |
---|---|---|---|
|
7bf710f153 | ||
|
e50fd77973 | ||
|
71cdc8a28f | ||
|
549f7bc6ee | ||
|
92ec1d6923 | ||
|
56529180e9 | ||
|
f14b6f2ba2 | ||
|
4fced276c4 | ||
|
7590471d62 | ||
|
0b6bcf6739 | ||
|
3a5ce86ed9 | ||
|
7b298939e2 | ||
|
f1f6aa7d80 | ||
|
7ce7dd7252 | ||
|
01aa556ede | ||
|
48ad39c1c7 | ||
|
9cab034448 | ||
|
0d7a22634c | ||
|
3e3bc5c06d | ||
|
2a81e468c7 | ||
|
0ba67a363e | ||
|
deae846864 | ||
|
3a0d48ad77 | ||
|
81d4803b4d | ||
|
e936c7c507 | ||
|
cd14a45414 | ||
|
44262bf747 | ||
|
0fbbabe208 | ||
|
b39189fde3 | ||
|
18fc1d78f4 | ||
|
d44a91d10b | ||
|
5e9829b2d8 | ||
|
2176deae36 | ||
|
7c5035aa39 | ||
|
5707993995 | ||
|
f81697527c | ||
|
967eff4c58 | ||
|
87ed0071c4 | ||
|
0fed48630c | ||
|
7b3e7c98ac | ||
|
09f44b0375 | ||
|
ebfa62637e | ||
|
cb2cc05cac | ||
|
f9c9131d81 | ||
|
1be9a1cbc7 | ||
|
b7fda8ecf0 | ||
|
db16392821 | ||
|
7127f541c3 | ||
|
32793cc008 | ||
|
fbaf55cb25 | ||
|
b8d51fc4e8 | ||
|
0c6e05d220 | ||
|
4066c17a0f | ||
|
429f2bb8dd | ||
|
112f4fc4f0 | ||
|
ecfbd6280f | ||
|
bf84409839 | ||
|
a7ce72cae0 | ||
|
28402ff7d8 | ||
|
e1d213d1a3 | ||
|
c610dd96f5 | ||
|
2ba3818b65 | ||
|
0e1a5a375a | ||
|
cfc3daeb65 | ||
|
6359f1950e | ||
|
d2f581853d | ||
|
030cb285da | ||
|
af45409959 | ||
|
d9a9bc35e3 | ||
|
c911c3352c | ||
|
654abbac25 | ||
|
46d5f42c9d | ||
|
1509da1215 | ||
|
98a99d96aa | ||
|
d62d81af17 | ||
|
dba67eb927 | ||
|
548c30d6a0 | ||
|
511cd90c44 | ||
|
8fb2d8da88 | ||
|
2fce332ed7 | ||
|
b23f1eb145 | ||
|
f694072b5a | ||
|
10de266e0f | ||
|
71811ed04f | ||
|
e9638ef95e | ||
|
5226bb5596 | ||
|
d9e13d6b99 | ||
|
dede076835 | ||
|
c929782e0d | ||
|
a43c7e36f8 | ||
|
b95cec9c32 | ||
|
9a7d9abe3c | ||
|
c77b620453 | ||
|
b348084e2a | ||
|
ff1811ef90 | ||
|
39cf4a7576 | ||
|
21b67625a6 | ||
|
fb0311e675 | ||
|
8a14b4afff | ||
|
e42701599a | ||
|
352b1b43f2 | ||
|
2922ebd095 | ||
|
a9e72085dd | ||
|
ed3d194c90 | ||
|
125c53827b | ||
|
38781807cd | ||
|
c046943269 | ||
|
fe36cd86bb | ||
|
401410089c | ||
|
fa23337519 | ||
|
ae5421f76e | ||
|
a2e2b429d5 | ||
|
ac852b23b3 | ||
|
e12389a002 | ||
|
da18e42d2a | ||
|
93bdaba8ee | ||
|
4cb08a61d5 | ||
|
133692ca92 | ||
|
4bcc4b3cf8 | ||
|
4eb20ca4b6 | ||
|
69f7343be5 | ||
|
19b28a58d6 | ||
|
3c33a93b9c | ||
|
f2e16dfb21 | ||
|
0f689e8051 | ||
|
5b44e6ef64 | ||
|
49e4ade66f | ||
|
3bae27d039 | ||
|
a77f0c606d | ||
|
f1de64dcaf | ||
|
feb672c3cd | ||
|
a90b463a7d | ||
|
5f5f25c978 | ||
|
351ea8bc1c | ||
|
b34edcbf87 | ||
|
160519e1fe | ||
|
44d626e12e | ||
|
efb9dc51c9 | ||
|
deb51434fd | ||
|
501070bfb0 | ||
|
712958d8c8 | ||
|
4d8d944fe5 | ||
|
0042e42160 | ||
|
85509ea251 | ||
|
6063854d06 | ||
|
456f8a90b1 | ||
|
980246d07a | ||
|
141c30b173 | ||
|
22f9b03fce | ||
|
be2db504b9 | ||
|
cac6f5234f | ||
|
eb6b32b6ee | ||
|
07acc9529e | ||
|
f7f0152573 | ||
|
153533f1d5 | ||
|
11622c586a | ||
|
7396bb508a | ||
|
7df6530f3a | ||
|
80787456fa | ||
|
4446ac1ca3 | ||
|
220039ab00 | ||
|
12f2250ab5 | ||
|
0c33aa16a1 | ||
|
72908b02fe | ||
|
223aa04424 | ||
|
e53248cca6 | ||
|
c11d661b39 | ||
|
4212a08337 | ||
|
c0553fa9eb | ||
|
7f26fafed7 | ||
|
3e9309c003 | ||
|
1017707475 | ||
|
e8ff5da209 | ||
|
03ed3ac1b0 | ||
|
2c97208e74 | ||
|
a458e81f8d | ||
|
83289bc801 | ||
|
ba2ac6a7b5 | ||
|
245b6da577 | ||
|
8875f924b0 | ||
|
ad7868ebe1 | ||
|
5effc6ec90 | ||
|
4cc9ceabd2 | ||
|
464c918134 | ||
|
2456adcb68 | ||
|
45bc967f30 | ||
|
d6d4d29257 | ||
|
33fdfdc8c0 | ||
|
80c8712b2a | ||
|
957e3eb93c | ||
|
110e1afa8e | ||
|
1da8f68871 | ||
|
513ddb4cca | ||
|
3bdfcb8542 | ||
|
aee758eca2 | ||
|
47ef7fffce | ||
|
58d6256607 | ||
|
b854fcdb72 | ||
|
27ca75d94f | ||
|
bb15f78af0 | ||
|
ccc9e18132 | ||
|
d55108ab60 | ||
|
84adbcb683 | ||
|
0cb5e4553b | ||
|
264407b707 | ||
|
eb5ec58453 | ||
|
39c2a757be | ||
|
8cdc05f582 | ||
|
5f4778c852 | ||
|
70add44fc1 | ||
|
fa617d2e9d | ||
|
3c795d365d | ||
|
93872ad7a3 | ||
|
227676c150 | ||
|
5f23833b43 | ||
|
121d51b414 | ||
|
0e196a59d9 | ||
|
63bfd23310 | ||
|
aadcf8fed0 | ||
|
37390e03b4 | ||
|
98fc780ade | ||
|
1670b21123 | ||
|
5602aacbd2 | ||
|
6ddbea207f | ||
|
261bced822 | ||
|
a80d1f861c | ||
|
c541340ef5 | ||
|
434af0ae64 | ||
|
c6379ca1d4 | ||
|
cdbcada49b | ||
|
282930cb9b | ||
|
b09c751942 | ||
|
b2047b90b3 | ||
|
0c8aec30bd | ||
|
fafaa3e69b | ||
|
d19bb3de8d | ||
|
481136166c | ||
|
4df81a0b9e | ||
|
da1e9c2d97 | ||
|
4b5a02c246 | ||
|
0e1572cfd7 | ||
|
343caa1ff6 | ||
|
403fb3826d | ||
|
3cdccb7b4e | ||
|
d9be78cdb4 | ||
|
16dc8255e1 | ||
|
2086bfff25 | ||
|
b9a68c0b8e | ||
|
74647f0e88 | ||
|
6e29c8bea9 | ||
|
b36373ab81 | ||
|
dc437aacf1 | ||
|
e19dae9b28 | ||
|
424cbd4831 | ||
|
350feed993 | ||
|
7ca4aa0ff4 | ||
|
bf00e67245 | ||
|
c5a6e49681 | ||
|
a509af2875 | ||
|
1f789bf396 | ||
|
0ccbc7d3d2 | ||
|
f3c6f26f85 | ||
|
338c358df2 | ||
|
4bdaa48303 | ||
|
36267746da | ||
|
7775541eed | ||
|
5029be4c83 | ||
|
277dbacf68 | ||
|
0fdfb03f3c | ||
|
17d81bf014 | ||
|
568e9be07e | ||
|
99b0a95f8a | ||
|
0fdd31892a | ||
|
7a682604f9 | ||
|
ce96e35d7d | ||
|
b7fb5394a7 | ||
|
aaa62beca7 | ||
|
2b58a7242b | ||
|
5ed919df79 | ||
|
baf6372179 | ||
|
1a9063a55b | ||
|
87999f402f | ||
|
109d466260 | ||
|
37597eca75 | ||
|
5fd767b739 | ||
|
0e7a473116 | ||
|
05bf55dca8 | ||
|
cc12ad8d86 | ||
|
847a7f470c | ||
|
95bebd6d1d | ||
|
8179a964ea | ||
|
d0805d4bbb | ||
|
f901c401b7 | ||
|
6ccadcb928 | ||
|
58b6c25d5c | ||
|
9cbab26202 | ||
|
74bcb468ca | ||
|
07a6ba88f4 | ||
|
49dff49993 | ||
|
68207aac80 | ||
|
64652b2ff0 | ||
|
659e8991de | ||
|
9f495b6dc9 | ||
|
d8498134ad | ||
|
17b2320b0d | ||
|
36c457f483 | ||
|
b8ec7f5e26 | ||
|
dbc5c0d5d8 | ||
|
fa886aa849 | ||
|
32e1ae2b18 | ||
|
61f03b0ea2 | ||
|
c47825a820 | ||
|
2d7cb63c7b | ||
|
c3d4c13936 | ||
|
3f51b9322f | ||
|
b604e2bd4e | ||
|
bcd92473d1 | ||
|
6f5bf9d146 | ||
|
490094939f | ||
|
d0d7d2772f | ||
|
59091896b4 | ||
|
3eb1429a25 | ||
|
bc7504c469 | ||
|
99dc4b8e07 | ||
|
807904bb86 | ||
|
7b8c8348f4 | ||
|
9c5ac26170 | ||
|
5db065644c | ||
|
0cc3a99169 | ||
|
2b1545145b | ||
|
49b7c9c8a4 | ||
|
114119f7c8 | ||
|
b4ef866c10 | ||
|
15f75a08d5 | ||
|
29f9c5473d | ||
|
ea1528aea6 | ||
|
18ce19a84d | ||
|
b0fd78d68d | ||
|
8186d6277e | ||
|
599af2b1a9 | ||
|
82de5adbe9 | ||
|
82b0f85258 | ||
|
cded58c30d | ||
|
da17b49b81 | ||
|
4728cdfa7c | ||
|
d97b30dcee | ||
|
fa8e8ad52d | ||
|
7495ac8111 | ||
|
08619e2c06 | ||
|
5d4bbd5b37 | ||
|
9d2aef5006 | ||
|
bcc3e5ebb0 | ||
|
e5df51a83a | ||
|
97fb843f01 | ||
|
8afb044877 | ||
|
7f869868c9 | ||
|
31275228ac | ||
|
7181be8768 | ||
|
3b446e887a | ||
|
431196c391 | ||
|
9608d8fb46 | ||
|
d574ca1628 | ||
|
8be60e2ff5 | ||
|
3c0fd540b1 | ||
|
23a7dc9a8a | ||
|
f97e6892b6 | ||
|
7742901e5c | ||
|
de68cb25d0 | ||
|
77d7afe505 | ||
|
68fb6c2c27 | ||
|
a47c031ad1 | ||
|
817049456c | ||
|
e1a061789f | ||
|
28573ba772 | ||
|
880b9413f6 | ||
|
9a7c4b21a9 | ||
|
ac56038eca | ||
|
4dd2fb6903 | ||
|
0baec61f06 | ||
|
d4751788d1 | ||
|
ae68c061d1 | ||
|
b0387245f0 | ||
|
c2d06a4a23 | ||
|
270108abb7 | ||
|
e76c33232d | ||
|
58ff2dc492 | ||
|
13c45a0e96 | ||
|
90cb86911a | ||
|
be7b1a1bd4 | ||
|
132a3c6705 | ||
|
5e73257557 | ||
|
e83346fbbb | ||
|
f56839ec6b |
@@ -38,17 +38,7 @@ jobs:
|
||||
path: ~/repo/packages/abi-gen/test-cli/output
|
||||
- store_artifacts:
|
||||
path: ~/repo/packages/contract-wrappers/generated_docs
|
||||
test-contracts-ganache:
|
||||
resource_class: medium+
|
||||
docker:
|
||||
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: yarn wsrun test:circleci @0x/contracts-multisig @0x/contracts-utils @0x/contracts-exchange-libs @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-extensions @0x/contracts-asset-proxy @0x/contracts-exchange @0x/contracts-exchange-forwarder @0x/contracts-coordinator @0x/contracts-staking
|
||||
test-exchange-ganache-3.0:
|
||||
test-exchange-ganache:
|
||||
resource_class: medium+
|
||||
docker:
|
||||
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||
@@ -58,7 +48,7 @@ jobs:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: yarn wsrun test:circleci @0x/contracts-exchange
|
||||
test-integrations-ganache-3.0:
|
||||
test-integrations-ganache:
|
||||
resource_class: medium+
|
||||
docker:
|
||||
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||
@@ -68,7 +58,7 @@ jobs:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: yarn wsrun test:circleci @0x/contracts-integrations
|
||||
test-contracts-rest-ganache-3.0:
|
||||
test-contracts-staking-ganache:
|
||||
resource_class: medium+
|
||||
docker:
|
||||
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||
@@ -77,11 +67,17 @@ jobs:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: yarn wsrun test:circleci @0x/contracts-multisig @0x/contracts-utils @0x/contracts-exchange-libs @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-asset-proxy @0x/contracts-exchange-forwarder @0x/contracts-staking @0x/contracts-coordinator @0x/contracts-erc20-bridge-sampler
|
||||
# TODO(dorothy-zbornak): Re-enable after updating this package for
|
||||
# 3.0. At that time, also remove exclusion from monorepo
|
||||
# package.json's test script.
|
||||
# - run: yarn wsrun test:circleci @0x/contracts-extensions
|
||||
- run: yarn wsrun test:circleci @0x/contracts-staking
|
||||
test-contracts-rest-ganache:
|
||||
resource_class: medium+
|
||||
docker:
|
||||
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: yarn wsrun test:circleci @0x/contracts-multisig @0x/contracts-utils @0x/contracts-exchange-libs @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-asset-proxy @0x/contracts-exchange-forwarder @0x/contracts-coordinator @0x/contracts-erc20-bridge-sampler @0x/contracts-broker @0x/contracts-zero-ex
|
||||
test-publish:
|
||||
resource_class: medium+
|
||||
docker:
|
||||
@@ -194,6 +190,8 @@ jobs:
|
||||
docker:
|
||||
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||
- image: 0xorg/ganache-cli:6.0.0
|
||||
environment:
|
||||
VERSION: '6.0.0'
|
||||
- image: 0xorg/mesh:0xV3
|
||||
environment:
|
||||
ETHEREUM_RPC_URL: 'http://localhost:8545'
|
||||
@@ -417,13 +415,16 @@ workflows:
|
||||
main:
|
||||
jobs:
|
||||
- build
|
||||
- test-exchange-ganache-3.0:
|
||||
- test-exchange-ganache:
|
||||
requires:
|
||||
- build
|
||||
- test-integrations-ganache-3.0:
|
||||
- test-integrations-ganache:
|
||||
requires:
|
||||
- build
|
||||
- test-contracts-rest-ganache-3.0:
|
||||
- test-contracts-staking-ganache:
|
||||
requires:
|
||||
- build
|
||||
- test-contracts-rest-ganache:
|
||||
requires:
|
||||
- build
|
||||
- test-rest:
|
||||
@@ -440,8 +441,9 @@ workflows:
|
||||
- build
|
||||
- submit-coverage:
|
||||
requires:
|
||||
- test-contracts-rest-ganache-3.0
|
||||
- test-exchange-ganache-3.0
|
||||
- test-contracts-rest-ganache
|
||||
- test-contracts-staking-ganache
|
||||
- test-exchange-ganache
|
||||
- test-rest
|
||||
- static-tests
|
||||
- test-python:
|
||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@@ -111,6 +111,8 @@ contracts/exchange-forwarder/generated-artifacts/
|
||||
contracts/exchange-forwarder/test/generated-artifacts/
|
||||
contracts/dev-utils/generated-artifacts/
|
||||
contracts/dev-utils/test/generated-artifacts/
|
||||
contracts/zero-ex/generated-artifacts/
|
||||
contracts/zero-ex/test/generated-artifacts/
|
||||
packages/sol-tracing-utils/test/fixtures/artifacts/
|
||||
python-packages/contract_artifacts/src/zero_ex/contract_artifacts/artifacts/
|
||||
|
||||
@@ -165,6 +167,8 @@ contracts/exchange-forwarder/generated-wrappers/
|
||||
contracts/exchange-forwarder/test/generated-wrappers/
|
||||
contracts/dev-utils/generated-wrappers/
|
||||
contracts/dev-utils/test/generated-wrappers/
|
||||
contracts/zero-ex/generated-wrappers/
|
||||
contracts/zero-ex/test/generated-wrappers/
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/*/__init__.py
|
||||
|
||||
# solc-bin in sol-compiler
|
||||
|
@@ -64,6 +64,10 @@ lib
|
||||
/contracts/dev-utils/test/generated-wrappers
|
||||
/contracts/dev-utils/generated-artifacts
|
||||
/contracts/dev-utils/test/generated-artifacts
|
||||
/contracts/zero-ex/generated-wrappers
|
||||
/contracts/zero-ex/test/generated-wrappers
|
||||
/contracts/zero-ex/generated-artifacts
|
||||
/contracts/zero-ex/test/generated-artifacts
|
||||
/contracts/staking/build/
|
||||
/contracts/coordinator/build/
|
||||
/contracts/exchange/build/
|
||||
@@ -91,3 +95,4 @@ packages/abi-gen/test-cli/fixtures/artifacts/AbiGenDummy.json
|
||||
packages/abi-gen/test-cli/fixtures/artifacts/LibDummy.json
|
||||
packages/abi-gen/test-cli/fixtures/artifacts/TestLibDummy.json
|
||||
packages/*/docs
|
||||
*.sol
|
||||
|
@@ -1,4 +1,65 @@
|
||||
[
|
||||
{
|
||||
"version": "3.3.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Use `LibERC20Token.approveIfBelow()` in DEX bridges for for approvals.",
|
||||
"pr": 2512
|
||||
},
|
||||
{
|
||||
"note": "Emit `ERC20BridgeTransfer` events in bridges.",
|
||||
"pr": 2512
|
||||
},
|
||||
{
|
||||
"note": "Change names of `ERC20BridgeTransfer` args to be less ambiguous.",
|
||||
"pr": 2524
|
||||
},
|
||||
{
|
||||
"note": "Added `MixinGasToken` allowing Gas Tokens to be freed",
|
||||
"pr": 2523
|
||||
},
|
||||
{
|
||||
"note": "Add `DexForwaderBridge` bridge contract.",
|
||||
"pr": 2525
|
||||
},
|
||||
{
|
||||
"note": "Add `UniswapV2Bridge` bridge contract.",
|
||||
"pr": 2590
|
||||
},
|
||||
{
|
||||
"note": "Add Gas Token freeing to `DexForwarderBridge` contract.",
|
||||
"pr": 2536
|
||||
}
|
||||
],
|
||||
"timestamp": 1592969527
|
||||
},
|
||||
{
|
||||
"timestamp": 1583220306,
|
||||
"version": "3.2.5",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1582837861,
|
||||
"version": "3.2.4",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1582677073,
|
||||
"version": "3.2.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1582623685,
|
||||
"version": "3.2.2",
|
||||
@@ -42,7 +103,7 @@
|
||||
},
|
||||
{
|
||||
"note": "Add `setOperators()` to `IDydx`",
|
||||
"pr": "TODO"
|
||||
"pr": 2462
|
||||
}
|
||||
],
|
||||
"timestamp": 1581204851
|
||||
|
@@ -5,6 +5,28 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v3.3.0 - _June 24, 2020_
|
||||
|
||||
* Use `LibERC20Token.approveIfBelow()` in DEX bridges for for approvals. (#2512)
|
||||
* Emit `ERC20BridgeTransfer` events in bridges. (#2512)
|
||||
* Change names of `ERC20BridgeTransfer` args to be less ambiguous. (#2524)
|
||||
* Added `MixinGasToken` allowing Gas Tokens to be freed (#2523)
|
||||
* Add `DexForwaderBridge` bridge contract. (#2525)
|
||||
* Add `UniswapV2Bridge` bridge contract. (#2590)
|
||||
* Add Gas Token freeing to `DexForwarderBridge` contract. (#2536)
|
||||
|
||||
## v3.2.5 - _March 3, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.2.4 - _February 27, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.2.3 - _February 26, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.2.2 - _February 25, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
@@ -20,7 +42,7 @@ CHANGELOG
|
||||
* Fix broken tests. (#2462)
|
||||
* Remove dependency on `@0x/contracts-dev-utils` (#2462)
|
||||
* Add asset data decoding functions (#2462)
|
||||
* Add `setOperators()` to `IDydx` (#TODO)
|
||||
* Add `setOperators()` to `IDydx` (#2462)
|
||||
|
||||
## v3.1.3 - _February 6, 2020_
|
||||
|
||||
|
@@ -3,6 +3,7 @@
|
||||
"contractsDir": "./contracts",
|
||||
"useDockerisedSolc": false,
|
||||
"isOfflineMode": false,
|
||||
"shouldSaveStandardInput": true,
|
||||
"compilerSettings": {
|
||||
"evmVersion": "istanbul",
|
||||
"optimizer": {
|
||||
|
@@ -35,7 +35,7 @@ contract ChaiBridge is
|
||||
/// @param from Address to transfer asset from.
|
||||
/// @param to Address to transfer asset to.
|
||||
/// @param amount Amount of asset to transfer.
|
||||
/// @return success The magic bytes `0x37708e9b` if successful.
|
||||
/// @return success The magic bytes `0xdc1600f3` if successful.
|
||||
function bridgeTransferFrom(
|
||||
address /* tokenAddress */,
|
||||
address from,
|
||||
|
@@ -26,6 +26,7 @@ import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol";
|
||||
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
|
||||
import "../interfaces/IERC20Bridge.sol";
|
||||
import "../interfaces/ICurve.sol";
|
||||
import "./MixinGasToken.sol";
|
||||
|
||||
|
||||
// solhint-disable not-rely-on-time
|
||||
@@ -33,54 +34,64 @@ import "../interfaces/ICurve.sol";
|
||||
contract CurveBridge is
|
||||
IERC20Bridge,
|
||||
IWallet,
|
||||
DeploymentConstants
|
||||
DeploymentConstants,
|
||||
MixinGasToken
|
||||
{
|
||||
struct CurveBridgeData {
|
||||
address curveAddress;
|
||||
int128 fromCoinIdx;
|
||||
int128 toCoinIdx;
|
||||
int128 version;
|
||||
}
|
||||
|
||||
/// @dev Callback for `ICurve`. Tries to buy `amount` of
|
||||
/// `toTokenAddress` tokens by selling the entirety of the opposing asset
|
||||
/// (DAI, USDC) to the Curve contract, then transfers the bought
|
||||
/// tokens to `to`.
|
||||
/// @param toTokenAddress The token to give to `to` (i.e DAI, USDC, USDT).
|
||||
/// @param from The maker (this contract).
|
||||
/// @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 from,
|
||||
address to,
|
||||
uint256 amount,
|
||||
bytes calldata bridgeData
|
||||
)
|
||||
external
|
||||
freesGasTokensFromCollector
|
||||
returns (bytes4 success)
|
||||
{
|
||||
// Decode the bridge data to get the Curve metadata.
|
||||
(address curveAddress, int128 fromCoinIdx, int128 toCoinIdx, int128 version) = abi.decode(bridgeData, (address, int128, int128, int128));
|
||||
ICurve exchange = ICurve(curveAddress);
|
||||
CurveBridgeData memory data = abi.decode(bridgeData, (CurveBridgeData));
|
||||
|
||||
address fromTokenAddress = exchange.underlying_coins(fromCoinIdx);
|
||||
address fromTokenAddress = ICurve(data.curveAddress).underlying_coins(data.fromCoinIdx);
|
||||
require(toTokenAddress != fromTokenAddress, "CurveBridge/INVALID_PAIR");
|
||||
uint256 fromTokenBalance = IERC20Token(fromTokenAddress).balanceOf(address(this));
|
||||
// Grant an allowance to the exchange to spend `fromTokenAddress` token.
|
||||
LibERC20Token.approve(fromTokenAddress, address(exchange), uint256(-1));
|
||||
LibERC20Token.approveIfBelow(fromTokenAddress, data.curveAddress, fromTokenBalance);
|
||||
|
||||
// Try to sell all of this contract's `fromTokenAddress` token balance.
|
||||
if (version == 0) {
|
||||
exchange.exchange_underlying(
|
||||
fromCoinIdx,
|
||||
toCoinIdx,
|
||||
if (data.version == 0) {
|
||||
ICurve(data.curveAddress).exchange_underlying(
|
||||
data.fromCoinIdx,
|
||||
data.toCoinIdx,
|
||||
// dx
|
||||
IERC20Token(fromTokenAddress).balanceOf(address(this)),
|
||||
fromTokenBalance,
|
||||
// min dy
|
||||
amount,
|
||||
// expires
|
||||
block.timestamp + 1
|
||||
);
|
||||
} else {
|
||||
exchange.exchange_underlying(
|
||||
fromCoinIdx,
|
||||
toCoinIdx,
|
||||
ICurve(data.curveAddress).exchange_underlying(
|
||||
data.fromCoinIdx,
|
||||
data.toCoinIdx,
|
||||
// dx
|
||||
IERC20Token(fromTokenAddress).balanceOf(address(this)),
|
||||
fromTokenBalance,
|
||||
// min dy
|
||||
amount
|
||||
);
|
||||
@@ -89,6 +100,15 @@ contract CurveBridge is
|
||||
uint256 toTokenBalance = IERC20Token(toTokenAddress).balanceOf(address(this));
|
||||
// Transfer the converted `toToken`s to `to`.
|
||||
LibERC20Token.transfer(toTokenAddress, to, toTokenBalance);
|
||||
|
||||
emit ERC20BridgeTransfer(
|
||||
fromTokenAddress,
|
||||
toTokenAddress,
|
||||
fromTokenBalance,
|
||||
toTokenBalance,
|
||||
from,
|
||||
to
|
||||
);
|
||||
return BRIDGE_SUCCESS;
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
|
||||
Copyright 2020 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/LibERC20Token.sol";
|
||||
import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol";
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
|
||||
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
||||
import "../interfaces/IERC20Bridge.sol";
|
||||
import "./MixinGasToken.sol";
|
||||
|
||||
|
||||
// solhint-disable space-after-comma, indent
|
||||
contract DexForwarderBridge is
|
||||
IERC20Bridge,
|
||||
IWallet,
|
||||
DeploymentConstants,
|
||||
MixinGasToken
|
||||
{
|
||||
using LibSafeMath for uint256;
|
||||
|
||||
/// @dev Data needed to reconstruct a bridge call.
|
||||
struct BridgeCall {
|
||||
address target;
|
||||
uint256 inputTokenAmount;
|
||||
uint256 outputTokenAmount;
|
||||
bytes bridgeData;
|
||||
}
|
||||
|
||||
/// @dev Intermediate state variables used by `bridgeTransferFrom()`, in
|
||||
/// struct form to get around stack limits.
|
||||
struct TransferFromState {
|
||||
address inputToken;
|
||||
uint256 initialInputTokenBalance;
|
||||
uint256 callInputTokenAmount;
|
||||
uint256 callOutputTokenAmount;
|
||||
uint256 totalInputTokenSold;
|
||||
BridgeCall[] calls;
|
||||
}
|
||||
|
||||
/// @dev Spends this contract's entire balance of input tokens by forwarding
|
||||
/// them to other bridges. Reverts if the entire balance is not spent.
|
||||
/// @param outputToken The token being bought.
|
||||
/// @param to The recipient of the bought tokens.
|
||||
/// @param bridgeData The abi-encoded input token address.
|
||||
/// @return success The magic bytes if successful.
|
||||
function bridgeTransferFrom(
|
||||
address outputToken,
|
||||
address /* from */,
|
||||
address to,
|
||||
uint256 /* amount */,
|
||||
bytes calldata bridgeData
|
||||
)
|
||||
external
|
||||
freesGasTokensFromCollector
|
||||
returns (bytes4 success)
|
||||
{
|
||||
require(msg.sender == _getERC20BridgeProxyAddress(), "DexForwarderBridge/SENDER_NOT_AUTHORIZED");
|
||||
TransferFromState memory state;
|
||||
(
|
||||
state.inputToken,
|
||||
state.calls
|
||||
) = abi.decode(bridgeData, (address, BridgeCall[]));
|
||||
|
||||
state.initialInputTokenBalance = IERC20Token(state.inputToken).balanceOf(address(this));
|
||||
|
||||
for (uint256 i = 0; i < state.calls.length; ++i) {
|
||||
// Stop if the we've sold all our input tokens.
|
||||
if (state.totalInputTokenSold >= state.initialInputTokenBalance) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Compute token amounts.
|
||||
state.callInputTokenAmount = LibSafeMath.min256(
|
||||
state.calls[i].inputTokenAmount,
|
||||
state.initialInputTokenBalance.safeSub(state.totalInputTokenSold)
|
||||
);
|
||||
state.callOutputTokenAmount = LibMath.getPartialAmountFloor(
|
||||
state.callInputTokenAmount,
|
||||
state.calls[i].inputTokenAmount,
|
||||
state.calls[i].outputTokenAmount
|
||||
);
|
||||
|
||||
// Execute the call in a new context so we can recoup transferred
|
||||
// funds by reverting.
|
||||
(bool didSucceed, ) = address(this)
|
||||
.call(abi.encodeWithSelector(
|
||||
this.executeBridgeCall.selector,
|
||||
state.calls[i].target,
|
||||
to,
|
||||
state.inputToken,
|
||||
outputToken,
|
||||
state.callInputTokenAmount,
|
||||
state.callOutputTokenAmount,
|
||||
state.calls[i].bridgeData
|
||||
));
|
||||
|
||||
if (didSucceed) {
|
||||
// Increase the amount of tokens sold.
|
||||
state.totalInputTokenSold = state.totalInputTokenSold.safeAdd(
|
||||
state.callInputTokenAmount
|
||||
);
|
||||
}
|
||||
}
|
||||
// Revert if we were not able to sell our entire input token balance.
|
||||
require(
|
||||
state.totalInputTokenSold >= state.initialInputTokenBalance,
|
||||
"DexForwarderBridge/INCOMPLETE_FILL"
|
||||
);
|
||||
// Always succeed.
|
||||
return BRIDGE_SUCCESS;
|
||||
}
|
||||
|
||||
/// @dev Transfers `inputToken` token to a bridge contract then calls
|
||||
/// its `bridgeTransferFrom()`. This is executed in separate context
|
||||
/// so we can revert the transfer on error. This can only be called
|
||||
// by this contract itself.
|
||||
/// @param bridge The bridge contract.
|
||||
/// @param to The recipient of `outputToken` tokens.
|
||||
/// @param inputToken The input token.
|
||||
/// @param outputToken The output token.
|
||||
/// @param inputTokenAmount The amount of input tokens to transfer to `bridge`.
|
||||
/// @param outputTokenAmount The amount of expected output tokens to be sent
|
||||
/// to `to` by `bridge`.
|
||||
function executeBridgeCall(
|
||||
address bridge,
|
||||
address to,
|
||||
address inputToken,
|
||||
address outputToken,
|
||||
uint256 inputTokenAmount,
|
||||
uint256 outputTokenAmount,
|
||||
bytes calldata bridgeData
|
||||
)
|
||||
external
|
||||
{
|
||||
// Must be called through `bridgeTransferFrom()`.
|
||||
require(msg.sender == address(this), "DexForwarderBridge/ONLY_SELF");
|
||||
// `bridge` must not be this contract.
|
||||
require(bridge != address(this));
|
||||
|
||||
// Get the starting balance of output tokens for `to`.
|
||||
uint256 initialRecipientBalance = IERC20Token(outputToken).balanceOf(to);
|
||||
|
||||
// Transfer input tokens to the bridge.
|
||||
LibERC20Token.transfer(inputToken, bridge, inputTokenAmount);
|
||||
|
||||
// Call the bridge.
|
||||
(bool didSucceed, bytes memory resultData) =
|
||||
bridge.call(abi.encodeWithSelector(
|
||||
IERC20Bridge(0).bridgeTransferFrom.selector,
|
||||
outputToken,
|
||||
bridge,
|
||||
to,
|
||||
outputTokenAmount,
|
||||
bridgeData
|
||||
));
|
||||
|
||||
// Revert if the call failed or not enough tokens were bought.
|
||||
// This will also undo the token transfer.
|
||||
require(
|
||||
didSucceed
|
||||
&& resultData.length == 32
|
||||
&& LibBytes.readBytes32(resultData, 0) == bytes32(BRIDGE_SUCCESS)
|
||||
&& IERC20Token(outputToken).balanceOf(to).safeSub(initialRecipientBalance) >= outputTokenAmount
|
||||
);
|
||||
}
|
||||
|
||||
/// @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;
|
||||
}
|
||||
}
|
@@ -50,7 +50,7 @@ contract DydxBridge is
|
||||
/// @param encodedBridgeData An abi-encoded `BridgeData` struct.
|
||||
/// @return success The magic bytes if successful.
|
||||
function bridgeTransferFrom(
|
||||
address,
|
||||
address, /* toTokenAddress */
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount,
|
||||
@@ -81,6 +81,7 @@ contract DydxBridge is
|
||||
|
||||
// Run operation. This will revert on failure.
|
||||
IDydx(_getDydxAddress()).operate(accounts, actions);
|
||||
|
||||
return BRIDGE_SUCCESS;
|
||||
}
|
||||
|
||||
|
@@ -38,13 +38,14 @@ contract Eth2DaiBridge is
|
||||
/// (DAI or WETH) to the Eth2Dai contract, then transfers the bought
|
||||
/// tokens to `to`.
|
||||
/// @param toTokenAddress The token to give to `to` (either DAI or WETH).
|
||||
/// @param from The maker (this contract).
|
||||
/// @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 from,
|
||||
address to,
|
||||
uint256 amount,
|
||||
bytes calldata bridgeData
|
||||
@@ -56,18 +57,28 @@ contract Eth2DaiBridge is
|
||||
(address fromTokenAddress) = abi.decode(bridgeData, (address));
|
||||
|
||||
IEth2Dai exchange = IEth2Dai(_getEth2DaiAddress());
|
||||
uint256 fromTokenBalance = IERC20Token(fromTokenAddress).balanceOf(address(this));
|
||||
// Grant an allowance to the exchange to spend `fromTokenAddress` token.
|
||||
LibERC20Token.approve(fromTokenAddress, address(exchange), uint256(-1));
|
||||
LibERC20Token.approveIfBelow(fromTokenAddress, address(exchange), fromTokenBalance);
|
||||
|
||||
// Try to sell all of this contract's `fromTokenAddress` token balance.
|
||||
uint256 boughtAmount = exchange.sellAllAmount(
|
||||
fromTokenAddress,
|
||||
IERC20Token(fromTokenAddress).balanceOf(address(this)),
|
||||
fromTokenBalance,
|
||||
toTokenAddress,
|
||||
amount
|
||||
);
|
||||
// Transfer the converted `toToken`s to `to`.
|
||||
LibERC20Token.transfer(toTokenAddress, to, boughtAmount);
|
||||
|
||||
emit ERC20BridgeTransfer(
|
||||
fromTokenAddress,
|
||||
toTokenAddress,
|
||||
fromTokenBalance,
|
||||
boughtAmount,
|
||||
from,
|
||||
to
|
||||
);
|
||||
return BRIDGE_SUCCESS;
|
||||
}
|
||||
|
||||
|
@@ -66,13 +66,14 @@ contract KyberBridge is
|
||||
/// to the `KyberNetworkProxy` contract, then transfers the bought
|
||||
/// tokens to `to`.
|
||||
/// @param toTokenAddress The token to give to `to`.
|
||||
/// @param from The maker (this contract).
|
||||
/// @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 from,
|
||||
address to,
|
||||
uint256 amount,
|
||||
bytes calldata bridgeData
|
||||
@@ -109,7 +110,11 @@ contract KyberBridge is
|
||||
} 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));
|
||||
LibERC20Token.approveIfBelow(
|
||||
state.fromTokenAddress,
|
||||
address(state.kyber),
|
||||
state.fromTokenBalance
|
||||
);
|
||||
} else {
|
||||
// If the input token is WETH, unwrap it and attach it to the call.
|
||||
state.fromTokenAddress = KYBER_ETH_ADDRESS;
|
||||
@@ -143,6 +148,15 @@ contract KyberBridge is
|
||||
state.weth.deposit.value(boughtAmount)();
|
||||
state.weth.transfer(to, boughtAmount);
|
||||
}
|
||||
|
||||
emit ERC20BridgeTransfer(
|
||||
state.fromTokenAddress == KYBER_ETH_ADDRESS ? address(state.weth) : state.fromTokenAddress,
|
||||
toTokenAddress,
|
||||
state.fromTokenBalance,
|
||||
boughtAmount,
|
||||
from,
|
||||
to
|
||||
);
|
||||
return BRIDGE_SUCCESS;
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
|
||||
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.16;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
|
||||
import "../interfaces/IGasToken.sol";
|
||||
|
||||
|
||||
contract MixinGasToken is
|
||||
DeploymentConstants
|
||||
{
|
||||
|
||||
/// @dev Frees gas tokens based on the amount of gas consumed in the function
|
||||
modifier freesGasTokens {
|
||||
uint256 gasBefore = gasleft();
|
||||
_;
|
||||
IGasToken gst = IGasToken(_getGstAddress());
|
||||
if (address(gst) != address(0)) {
|
||||
// (gasUsed + FREE_BASE) / (2 * REIMBURSE - FREE_TOKEN)
|
||||
// 14154 24000 6870
|
||||
uint256 value = (gasBefore - gasleft() + 14154) / 41130;
|
||||
gst.freeUpTo(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Frees gas tokens using the balance of `from`. Amount freed is based
|
||||
/// on the gas consumed in the function
|
||||
modifier freesGasTokensFromCollector() {
|
||||
uint256 gasBefore = gasleft();
|
||||
_;
|
||||
IGasToken gst = IGasToken(_getGstAddress());
|
||||
if (address(gst) != address(0)) {
|
||||
// (gasUsed + FREE_BASE) / (2 * REIMBURSE - FREE_TOKEN)
|
||||
// 14154 24000 6870
|
||||
uint256 value = (gasBefore - gasleft() + 14154) / 41130;
|
||||
gst.freeFromUpTo(_getGstCollectorAddress(), value);
|
||||
}
|
||||
}
|
||||
}
|
@@ -38,10 +38,11 @@ contract UniswapBridge is
|
||||
{
|
||||
// Struct to hold `bridgeTransferFrom()` local variables in memory and to avoid
|
||||
// stack overflows.
|
||||
struct WithdrawToState {
|
||||
struct TransferState {
|
||||
IUniswapExchange exchange;
|
||||
uint256 fromTokenBalance;
|
||||
IEtherToken weth;
|
||||
uint256 boughtAmount;
|
||||
}
|
||||
|
||||
// solhint-disable no-empty-blocks
|
||||
@@ -55,13 +56,14 @@ contract UniswapBridge is
|
||||
/// `toTokenAddress` tokens by selling the entirety of the `fromTokenAddress`
|
||||
/// token encoded in the bridge data.
|
||||
/// @param toTokenAddress The token to buy and transfer to `to`.
|
||||
/// @param from The maker (this contract).
|
||||
/// @param to The recipient of the bought tokens.
|
||||
/// @param amount Minimum amount of `toTokenAddress` tokens to buy.
|
||||
/// @param bridgeData The abi-encoded "from" token address.
|
||||
/// @return success The magic bytes if successful.
|
||||
function bridgeTransferFrom(
|
||||
address toTokenAddress,
|
||||
address /* from */,
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount,
|
||||
bytes calldata bridgeData
|
||||
@@ -70,7 +72,7 @@ contract UniswapBridge is
|
||||
returns (bytes4 success)
|
||||
{
|
||||
// State memory object to avoid stack overflows.
|
||||
WithdrawToState memory state;
|
||||
TransferState memory state;
|
||||
// Decode the bridge data to get the `fromTokenAddress`.
|
||||
(address fromTokenAddress) = abi.decode(bridgeData, (address));
|
||||
|
||||
@@ -96,7 +98,7 @@ contract UniswapBridge is
|
||||
state.weth.withdraw(state.fromTokenBalance);
|
||||
// Buy as much of `toTokenAddress` token with ETH as possible and
|
||||
// transfer it to `to`.
|
||||
state.exchange.ethToTokenTransferInput.value(state.fromTokenBalance)(
|
||||
state.boughtAmount = state.exchange.ethToTokenTransferInput.value(state.fromTokenBalance)(
|
||||
// Minimum buy amount.
|
||||
amount,
|
||||
// Expires after this block.
|
||||
@@ -108,9 +110,9 @@ contract UniswapBridge is
|
||||
// Convert from a token to WETH.
|
||||
} else if (toTokenAddress == address(state.weth)) {
|
||||
// Grant the exchange an allowance.
|
||||
_grantExchangeAllowance(state.exchange, fromTokenAddress);
|
||||
_grantExchangeAllowance(state.exchange, fromTokenAddress, state.fromTokenBalance);
|
||||
// Buy as much ETH with `fromTokenAddress` token as possible.
|
||||
uint256 ethBought = state.exchange.tokenToEthSwapInput(
|
||||
state.boughtAmount = state.exchange.tokenToEthSwapInput(
|
||||
// Sell all tokens we hold.
|
||||
state.fromTokenBalance,
|
||||
// Minimum buy amount.
|
||||
@@ -119,17 +121,17 @@ contract UniswapBridge is
|
||||
block.timestamp
|
||||
);
|
||||
// Wrap the ETH.
|
||||
state.weth.deposit.value(ethBought)();
|
||||
state.weth.deposit.value(state.boughtAmount)();
|
||||
// Transfer the WETH to `to`.
|
||||
IEtherToken(toTokenAddress).transfer(to, ethBought);
|
||||
IEtherToken(toTokenAddress).transfer(to, state.boughtAmount);
|
||||
|
||||
// Convert from one token to another.
|
||||
} else {
|
||||
// Grant the exchange an allowance.
|
||||
_grantExchangeAllowance(state.exchange, fromTokenAddress);
|
||||
_grantExchangeAllowance(state.exchange, fromTokenAddress, state.fromTokenBalance);
|
||||
// Buy as much `toTokenAddress` token with `fromTokenAddress` token
|
||||
// and transfer it to `to`.
|
||||
state.exchange.tokenToTokenTransferInput(
|
||||
state.boughtAmount = state.exchange.tokenToTokenTransferInput(
|
||||
// Sell all tokens we hold.
|
||||
state.fromTokenBalance,
|
||||
// Minimum buy amount.
|
||||
@@ -144,6 +146,15 @@ contract UniswapBridge is
|
||||
toTokenAddress
|
||||
);
|
||||
}
|
||||
|
||||
emit ERC20BridgeTransfer(
|
||||
fromTokenAddress,
|
||||
toTokenAddress,
|
||||
state.fromTokenBalance,
|
||||
state.boughtAmount,
|
||||
from,
|
||||
to
|
||||
);
|
||||
return BRIDGE_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -165,10 +176,19 @@ contract UniswapBridge is
|
||||
/// on behalf of this contract.
|
||||
/// @param exchange The Uniswap token exchange.
|
||||
/// @param tokenAddress The token address for the exchange.
|
||||
function _grantExchangeAllowance(IUniswapExchange exchange, address tokenAddress)
|
||||
/// @param minimumAllowance The minimum necessary allowance.
|
||||
function _grantExchangeAllowance(
|
||||
IUniswapExchange exchange,
|
||||
address tokenAddress,
|
||||
uint256 minimumAllowance
|
||||
)
|
||||
private
|
||||
{
|
||||
LibERC20Token.approve(tokenAddress, address(exchange), uint256(-1));
|
||||
LibERC20Token.approveIfBelow(
|
||||
tokenAddress,
|
||||
address(exchange),
|
||||
minimumAllowance
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Retrieves the uniswap exchange for a given token pair.
|
||||
|
135
contracts/asset-proxy/contracts/src/bridges/UniswapV2Bridge.sol
Normal file
135
contracts/asset-proxy/contracts/src/bridges/UniswapV2Bridge.sol
Normal file
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
|
||||
Copyright 2020 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/LibAddressArray.sol";
|
||||
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
|
||||
import "../interfaces/IUniswapV2Router01.sol";
|
||||
import "../interfaces/IERC20Bridge.sol";
|
||||
|
||||
|
||||
// solhint-disable space-after-comma
|
||||
// solhint-disable not-rely-on-time
|
||||
contract UniswapV2Bridge is
|
||||
IERC20Bridge,
|
||||
IWallet,
|
||||
DeploymentConstants
|
||||
{
|
||||
struct TransferState {
|
||||
address[] path;
|
||||
uint256 fromTokenBalance;
|
||||
}
|
||||
|
||||
/// @dev Callback for `IERC20Bridge`. Tries to buy `amount` of
|
||||
/// `toTokenAddress` tokens by selling the entirety of the `fromTokenAddress`
|
||||
/// token encoded in the bridge data.
|
||||
/// @param toTokenAddress The token to buy and transfer to `to`.
|
||||
/// @param from The maker (this contract).
|
||||
/// @param to The recipient of the bought tokens.
|
||||
/// @param amount Minimum amount of `toTokenAddress` tokens to buy.
|
||||
/// @param bridgeData The abi-encoded path of token addresses. Last element must be toTokenAddress
|
||||
/// @return success The magic bytes if successful.
|
||||
function bridgeTransferFrom(
|
||||
address toTokenAddress,
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount,
|
||||
bytes calldata bridgeData
|
||||
)
|
||||
external
|
||||
returns (bytes4 success)
|
||||
{
|
||||
// hold variables to get around stack depth limitations
|
||||
TransferState memory state;
|
||||
|
||||
// Decode the bridge data to get the `fromTokenAddress`.
|
||||
// solhint-disable indent
|
||||
state.path = abi.decode(bridgeData, (address[]));
|
||||
// solhint-enable indent
|
||||
|
||||
require(state.path.length >= 2, "UniswapV2Bridge/PATH_LENGTH_MUST_BE_AT_LEAST_TWO");
|
||||
require(state.path[state.path.length - 1] == toTokenAddress, "UniswapV2Bridge/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN");
|
||||
|
||||
// Just transfer the tokens if they're the same.
|
||||
if (state.path[0] == toTokenAddress) {
|
||||
LibERC20Token.transfer(state.path[0], to, amount);
|
||||
return BRIDGE_SUCCESS;
|
||||
}
|
||||
|
||||
// Get our balance of `fromTokenAddress` token.
|
||||
state.fromTokenBalance = IERC20Token(state.path[0]).balanceOf(address(this));
|
||||
|
||||
// Grant the Uniswap router an allowance.
|
||||
LibERC20Token.approveIfBelow(
|
||||
state.path[0],
|
||||
_getUniswapV2Router01Address(),
|
||||
state.fromTokenBalance
|
||||
);
|
||||
|
||||
// Buy as much `toTokenAddress` token with `fromTokenAddress` token
|
||||
// and transfer it to `to`.
|
||||
IUniswapV2Router01 router = IUniswapV2Router01(_getUniswapV2Router01Address());
|
||||
uint[] memory amounts = router.swapExactTokensForTokens(
|
||||
// Sell all tokens we hold.
|
||||
state.fromTokenBalance,
|
||||
// Minimum buy amount.
|
||||
amount,
|
||||
// Convert `fromTokenAddress` to `toTokenAddress`.
|
||||
state.path,
|
||||
// Recipient is `to`.
|
||||
to,
|
||||
// Expires after this block.
|
||||
block.timestamp
|
||||
);
|
||||
|
||||
emit ERC20BridgeTransfer(
|
||||
// input token
|
||||
state.path[0],
|
||||
// output token
|
||||
toTokenAddress,
|
||||
// input token amount
|
||||
state.fromTokenBalance,
|
||||
// output token amount
|
||||
amounts[amounts.length - 1],
|
||||
from,
|
||||
to
|
||||
);
|
||||
|
||||
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 Success bytes, always.
|
||||
function isValidSignature(
|
||||
bytes32,
|
||||
bytes calldata
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (bytes4 magicValue)
|
||||
{
|
||||
return LEGACY_WALLET_MAGIC_VALUE;
|
||||
}
|
||||
}
|
@@ -65,7 +65,6 @@ interface ICurve {
|
||||
returns (uint256 dy);
|
||||
|
||||
/// @dev Get the amount of `fromToken` by buying `buyAmount` of `toToken`
|
||||
/// This function exists on later versions of Curve (USDC/DAI/USDT)
|
||||
/// @param i The token index being sold.
|
||||
/// @param j The token index being bought.
|
||||
/// @param buyAmount The amount of token being bought.
|
||||
|
@@ -21,16 +21,32 @@ pragma solidity ^0.5.9;
|
||||
|
||||
contract IERC20Bridge {
|
||||
|
||||
// @dev Result of a successful bridge call.
|
||||
/// @dev Result of a successful bridge call.
|
||||
bytes4 constant internal BRIDGE_SUCCESS = 0xdc1600f3;
|
||||
|
||||
/// @dev Emitted when a trade occurs.
|
||||
/// @param inputToken The token the bridge is converting from.
|
||||
/// @param outputToken The token the bridge is converting to.
|
||||
/// @param inputTokenAmount Amount of input token.
|
||||
/// @param outputTokenAmount Amount of output token.
|
||||
/// @param from The `from` address in `bridgeTransferFrom()`
|
||||
/// @param to The `to` address in `bridgeTransferFrom()`
|
||||
event ERC20BridgeTransfer(
|
||||
address inputToken,
|
||||
address outputToken,
|
||||
uint256 inputTokenAmount,
|
||||
uint256 outputTokenAmount,
|
||||
address from,
|
||||
address to
|
||||
);
|
||||
|
||||
/// @dev Transfers `amount` of the ERC20 `tokenAddress` from `from` to `to`.
|
||||
/// @param tokenAddress The address of the ERC20 token to transfer.
|
||||
/// @param from Address to transfer asset from.
|
||||
/// @param to Address to transfer asset to.
|
||||
/// @param amount Amount of asset to transfer.
|
||||
/// @param bridgeData Arbitrary asset data needed by the bridge contract.
|
||||
/// @return success The magic bytes `0x37708e9b` if successful.
|
||||
/// @return success The magic bytes `0xdc1600f3` if successful.
|
||||
function bridgeTransferFrom(
|
||||
address tokenAddress,
|
||||
address from,
|
||||
|
40
contracts/asset-proxy/contracts/src/interfaces/IGasToken.sol
Normal file
40
contracts/asset-proxy/contracts/src/interfaces/IGasToken.sol
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
|
||||
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.15;
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||
|
||||
|
||||
contract IGasToken is IERC20Token {
|
||||
|
||||
/// @dev Frees up to `value` sub-tokens
|
||||
/// @param value The amount of tokens to free
|
||||
/// @return How many tokens were freed
|
||||
function freeUpTo(uint256 value) external returns (uint256 freed);
|
||||
|
||||
/// @dev Frees up to `value` sub-tokens owned by `from`
|
||||
/// @param from The owner of tokens to spend
|
||||
/// @param value The amount of tokens to free
|
||||
/// @return How many tokens were freed
|
||||
function freeFromUpTo(address from, uint256 value) external returns (uint256 freed);
|
||||
|
||||
/// @dev Mints `value` amount of tokens
|
||||
/// @param value The amount of tokens to mint
|
||||
function mint(uint256 value) external;
|
||||
}
|
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
|
||||
Copyright 2020 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 IUniswapV2Router01 {
|
||||
|
||||
/// @dev Swaps an exact amount of input tokens for as many output tokens as possible, along the route determined by the path.
|
||||
/// The first element of path is the input token, the last is the output token, and any intermediate elements represent
|
||||
/// intermediate pairs to trade through (if, for example, a direct pair does not exist).
|
||||
/// @param amountIn The amount of input tokens to send.
|
||||
/// @param amountOutMin The minimum amount of output tokens that must be received for the transaction not to revert.
|
||||
/// @param path An array of token addresses. path.length must be >= 2. Pools for each consecutive pair of addresses must exist and have liquidity.
|
||||
/// @param to Recipient of the output tokens.
|
||||
/// @param deadline Unix timestamp after which the transaction will revert.
|
||||
/// @return amounts The input token amount and all subsequent output token amounts.
|
||||
function swapExactTokensForTokens(
|
||||
uint amountIn,
|
||||
uint amountOutMin,
|
||||
address[] calldata path,
|
||||
address to,
|
||||
uint deadline
|
||||
) external returns (uint[] memory amounts);
|
||||
}
|
244
contracts/asset-proxy/contracts/test/TestDexForwarderBridge.sol
Normal file
244
contracts/asset-proxy/contracts/test/TestDexForwarderBridge.sol
Normal file
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
|
||||
Copyright 2020 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "../src/bridges/DexForwarderBridge.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
||||
|
||||
|
||||
interface ITestDexForwarderBridge {
|
||||
event BridgeTransferFromCalled(
|
||||
address caller,
|
||||
uint256 inputTokenBalance,
|
||||
address inputToken,
|
||||
address outputToken,
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount
|
||||
);
|
||||
|
||||
event TokenTransferCalled(
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount
|
||||
);
|
||||
|
||||
function emitBridgeTransferFromCalled(
|
||||
address caller,
|
||||
uint256 inputTokenBalance,
|
||||
address inputToken,
|
||||
address outputToken,
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount
|
||||
) external;
|
||||
|
||||
function emitTokenTransferCalled(
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount
|
||||
) external;
|
||||
}
|
||||
|
||||
|
||||
interface ITestDexForwarderBridgeTestToken {
|
||||
|
||||
function transfer(address to, uint256 amount)
|
||||
external
|
||||
returns (bool);
|
||||
|
||||
function mint(address to, uint256 amount)
|
||||
external;
|
||||
|
||||
function balanceOf(address owner) external view returns (uint256);
|
||||
}
|
||||
|
||||
|
||||
contract TestDexForwarderBridgeTestBridge {
|
||||
|
||||
bytes4 private _returnCode;
|
||||
string private _revertError;
|
||||
uint256 private _transferAmount;
|
||||
ITestDexForwarderBridge private _testContract;
|
||||
|
||||
constructor(bytes4 returnCode, string memory revertError) public {
|
||||
_testContract = ITestDexForwarderBridge(msg.sender);
|
||||
_returnCode = returnCode;
|
||||
_revertError = revertError;
|
||||
}
|
||||
|
||||
function setTransferAmount(uint256 amount) external {
|
||||
_transferAmount = amount;
|
||||
}
|
||||
|
||||
function bridgeTransferFrom(
|
||||
address outputToken,
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount,
|
||||
bytes memory bridgeData
|
||||
)
|
||||
public
|
||||
returns (bytes4 success)
|
||||
{
|
||||
if (bytes(_revertError).length != 0) {
|
||||
revert(_revertError);
|
||||
}
|
||||
address inputToken = abi.decode(bridgeData, (address));
|
||||
_testContract.emitBridgeTransferFromCalled(
|
||||
msg.sender,
|
||||
ITestDexForwarderBridgeTestToken(inputToken).balanceOf(address(this)),
|
||||
inputToken,
|
||||
outputToken,
|
||||
from,
|
||||
to,
|
||||
amount
|
||||
);
|
||||
ITestDexForwarderBridgeTestToken(outputToken).mint(to, _transferAmount);
|
||||
return _returnCode;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
contract TestDexForwarderBridgeTestToken {
|
||||
|
||||
using LibSafeMath for uint256;
|
||||
|
||||
mapping(address => uint256) public balanceOf;
|
||||
ITestDexForwarderBridge private _testContract;
|
||||
|
||||
constructor() public {
|
||||
_testContract = ITestDexForwarderBridge(msg.sender);
|
||||
}
|
||||
|
||||
function transfer(address to, uint256 amount)
|
||||
external
|
||||
returns (bool)
|
||||
{
|
||||
balanceOf[msg.sender] = balanceOf[msg.sender].safeSub(amount);
|
||||
balanceOf[to] = balanceOf[to].safeAdd(amount);
|
||||
_testContract.emitTokenTransferCalled(msg.sender, to, amount);
|
||||
return true;
|
||||
}
|
||||
|
||||
function mint(address owner, uint256 amount)
|
||||
external
|
||||
{
|
||||
balanceOf[owner] = balanceOf[owner].safeAdd(amount);
|
||||
}
|
||||
|
||||
function setBalance(address owner, uint256 amount)
|
||||
external
|
||||
{
|
||||
balanceOf[owner] = amount;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
contract TestDexForwarderBridge is
|
||||
ITestDexForwarderBridge,
|
||||
DexForwarderBridge
|
||||
{
|
||||
address private AUTHORIZED_ADDRESS; // solhint-disable-line var-name-mixedcase
|
||||
|
||||
function setAuthorized(address authorized)
|
||||
public
|
||||
{
|
||||
AUTHORIZED_ADDRESS = authorized;
|
||||
}
|
||||
|
||||
function createBridge(
|
||||
bytes4 returnCode,
|
||||
string memory revertError
|
||||
)
|
||||
public
|
||||
returns (address bridge)
|
||||
{
|
||||
return address(new TestDexForwarderBridgeTestBridge(returnCode, revertError));
|
||||
}
|
||||
|
||||
function createToken() public returns (address token) {
|
||||
return address(new TestDexForwarderBridgeTestToken());
|
||||
}
|
||||
|
||||
function setTokenBalance(address token, address owner, uint256 amount) public {
|
||||
TestDexForwarderBridgeTestToken(token).setBalance(owner, amount);
|
||||
}
|
||||
|
||||
function setBridgeTransferAmount(address bridge, uint256 amount) public {
|
||||
TestDexForwarderBridgeTestBridge(bridge).setTransferAmount(amount);
|
||||
}
|
||||
|
||||
function emitBridgeTransferFromCalled(
|
||||
address caller,
|
||||
uint256 inputTokenBalance,
|
||||
address inputToken,
|
||||
address outputToken,
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount
|
||||
)
|
||||
public
|
||||
{
|
||||
emit BridgeTransferFromCalled(
|
||||
caller,
|
||||
inputTokenBalance,
|
||||
inputToken,
|
||||
outputToken,
|
||||
from,
|
||||
to,
|
||||
amount
|
||||
);
|
||||
}
|
||||
|
||||
function emitTokenTransferCalled(
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount
|
||||
)
|
||||
public
|
||||
{
|
||||
emit TokenTransferCalled(
|
||||
from,
|
||||
to,
|
||||
amount
|
||||
);
|
||||
}
|
||||
|
||||
function balanceOf(address token, address owner) public view returns (uint256) {
|
||||
return TestDexForwarderBridgeTestToken(token).balanceOf(owner);
|
||||
}
|
||||
|
||||
function _getGstAddress()
|
||||
internal
|
||||
view
|
||||
returns (address gst)
|
||||
{
|
||||
return address(0);
|
||||
}
|
||||
|
||||
function _getERC20BridgeProxyAddress()
|
||||
internal
|
||||
view
|
||||
returns (address erc20BridgeProxyAddress)
|
||||
{
|
||||
return AUTHORIZED_ADDRESS;
|
||||
}
|
||||
}
|
@@ -110,6 +110,10 @@ contract TestToken {
|
||||
return true;
|
||||
}
|
||||
|
||||
function allowance(address, address) external view returns (uint256) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// @dev Retrieve the balance for `owner`.
|
||||
function balanceOf(address owner)
|
||||
external
|
||||
|
@@ -110,6 +110,10 @@ contract TestToken {
|
||||
return _testContract.wethDeposit.value(msg.value)(msg.sender);
|
||||
}
|
||||
|
||||
function allowance(address, address) external view returns (uint256) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
function balanceOf(address owner)
|
||||
external
|
||||
view
|
||||
|
@@ -224,6 +224,10 @@ contract TestToken {
|
||||
TestEventsRaiser(msg.sender).raiseWethWithdraw(amount);
|
||||
}
|
||||
|
||||
function allowance(address, address) external view returns (uint256) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// @dev Retrieve the balance for `owner`.
|
||||
function balanceOf(address owner)
|
||||
external
|
||||
|
253
contracts/asset-proxy/contracts/test/TestUniswapV2Bridge.sol
Normal file
253
contracts/asset-proxy/contracts/test/TestUniswapV2Bridge.sol
Normal file
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
|
||||
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-utils/contracts/src/LibSafeMath.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibAddressArray.sol";
|
||||
import "../src/bridges/UniswapV2Bridge.sol";
|
||||
import "../src/interfaces/IUniswapV2Router01.sol";
|
||||
|
||||
|
||||
contract TestEventsRaiser {
|
||||
|
||||
event TokenTransfer(
|
||||
address token,
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount
|
||||
);
|
||||
|
||||
event TokenApprove(
|
||||
address spender,
|
||||
uint256 allowance
|
||||
);
|
||||
|
||||
event SwapExactTokensForTokensInput(
|
||||
uint amountIn,
|
||||
uint amountOutMin,
|
||||
address toTokenAddress,
|
||||
address to,
|
||||
uint deadline
|
||||
);
|
||||
|
||||
function raiseTokenTransfer(
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount
|
||||
)
|
||||
external
|
||||
{
|
||||
emit TokenTransfer(
|
||||
msg.sender,
|
||||
from,
|
||||
to,
|
||||
amount
|
||||
);
|
||||
}
|
||||
|
||||
function raiseTokenApprove(address spender, uint256 allowance) external {
|
||||
emit TokenApprove(spender, allowance);
|
||||
}
|
||||
|
||||
function raiseSwapExactTokensForTokensInput(
|
||||
uint amountIn,
|
||||
uint amountOutMin,
|
||||
address toTokenAddress,
|
||||
address to,
|
||||
uint deadline
|
||||
) external
|
||||
{
|
||||
emit SwapExactTokensForTokensInput(
|
||||
amountIn,
|
||||
amountOutMin,
|
||||
toTokenAddress,
|
||||
to,
|
||||
deadline
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// @dev A minimalist ERC20 token.
|
||||
contract TestToken {
|
||||
|
||||
using LibSafeMath for uint256;
|
||||
|
||||
mapping (address => uint256) public balances;
|
||||
string private _nextRevertReason;
|
||||
|
||||
/// @dev Set the balance for `owner`.
|
||||
function setBalance(address owner, uint256 balance)
|
||||
external
|
||||
payable
|
||||
{
|
||||
balances[owner] = balance;
|
||||
}
|
||||
|
||||
/// @dev Just emits a TokenTransfer event on the caller
|
||||
function transfer(address to, uint256 amount)
|
||||
external
|
||||
returns (bool)
|
||||
{
|
||||
TestEventsRaiser(msg.sender).raiseTokenTransfer(msg.sender, to, amount);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @dev Just emits a TokenApprove event on the caller
|
||||
function approve(address spender, uint256 allowance)
|
||||
external
|
||||
returns (bool)
|
||||
{
|
||||
TestEventsRaiser(msg.sender).raiseTokenApprove(spender, allowance);
|
||||
return true;
|
||||
}
|
||||
|
||||
function allowance(address, address) external view returns (uint256) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// @dev Retrieve the balance for `owner`.
|
||||
function balanceOf(address owner)
|
||||
external
|
||||
view
|
||||
returns (uint256)
|
||||
{
|
||||
return balances[owner];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// @dev Mock the UniswapV2Router01 contract
|
||||
contract TestRouter is
|
||||
IUniswapV2Router01
|
||||
{
|
||||
string private _nextRevertReason;
|
||||
|
||||
/// @dev Set the revert reason for `swapExactTokensForTokens`.
|
||||
function setRevertReason(string calldata reason)
|
||||
external
|
||||
{
|
||||
_nextRevertReason = reason;
|
||||
}
|
||||
|
||||
function swapExactTokensForTokens(
|
||||
uint amountIn,
|
||||
uint amountOutMin,
|
||||
address[] calldata path,
|
||||
address to,
|
||||
uint deadline
|
||||
) external returns (uint[] memory amounts)
|
||||
{
|
||||
_revertIfReasonExists();
|
||||
|
||||
amounts = new uint[](path.length);
|
||||
amounts[0] = amountIn;
|
||||
amounts[amounts.length - 1] = amountOutMin;
|
||||
|
||||
TestEventsRaiser(msg.sender).raiseSwapExactTokensForTokensInput(
|
||||
// tokens sold
|
||||
amountIn,
|
||||
// tokens bought
|
||||
amountOutMin,
|
||||
// output token (toTokenAddress)
|
||||
path[path.length - 1],
|
||||
// recipient
|
||||
to,
|
||||
// deadline
|
||||
deadline
|
||||
);
|
||||
}
|
||||
|
||||
function _revertIfReasonExists()
|
||||
private
|
||||
view
|
||||
{
|
||||
if (bytes(_nextRevertReason).length != 0) {
|
||||
revert(_nextRevertReason);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// @dev UniswapV2Bridge overridden to mock tokens and Uniswap router
|
||||
contract TestUniswapV2Bridge is
|
||||
UniswapV2Bridge,
|
||||
TestEventsRaiser
|
||||
{
|
||||
|
||||
// Token address to TestToken instance.
|
||||
mapping (address => TestToken) private _testTokens;
|
||||
// TestRouter instance.
|
||||
TestRouter private _testRouter;
|
||||
|
||||
constructor() public {
|
||||
_testRouter = new TestRouter();
|
||||
}
|
||||
|
||||
function setRouterRevertReason(string calldata revertReason)
|
||||
external
|
||||
{
|
||||
_testRouter.setRevertReason(revertReason);
|
||||
}
|
||||
|
||||
/// @dev Sets the balance of this contract for an existing token.
|
||||
/// The wei attached will be the balance.
|
||||
function setTokenBalance(address tokenAddress, uint256 balance)
|
||||
external
|
||||
{
|
||||
TestToken token = _testTokens[tokenAddress];
|
||||
token.setBalance(address(this), balance);
|
||||
}
|
||||
|
||||
/// @dev Create a new token
|
||||
/// @param tokenAddress The token address. If zero, one will be created.
|
||||
function createToken(
|
||||
address tokenAddress
|
||||
)
|
||||
external
|
||||
returns (TestToken token)
|
||||
{
|
||||
token = TestToken(tokenAddress);
|
||||
if (tokenAddress == address(0)) {
|
||||
token = new TestToken();
|
||||
}
|
||||
_testTokens[address(token)] = token;
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
function getRouterAddress()
|
||||
external
|
||||
view
|
||||
returns (address)
|
||||
{
|
||||
return address(_testRouter);
|
||||
}
|
||||
|
||||
function _getUniswapV2Router01Address()
|
||||
internal
|
||||
view
|
||||
returns (address)
|
||||
{
|
||||
return address(_testRouter);
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-asset-proxy",
|
||||
"version": "3.2.2",
|
||||
"version": "3.3.0",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -38,7 +38,7 @@
|
||||
"docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES"
|
||||
},
|
||||
"config": {
|
||||
"abis": "./test/generated-artifacts/@(ChaiBridge|CurveBridge|DydxBridge|ERC1155Proxy|ERC20BridgeProxy|ERC20Proxy|ERC721Proxy|Eth2DaiBridge|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|IChai|ICurve|IDydx|IDydxBridge|IERC20Bridge|IEth2Dai|IKyberNetworkProxy|IUniswapExchange|IUniswapExchangeFactory|KyberBridge|MixinAssetProxyDispatcher|MixinAuthorizable|MultiAssetProxy|Ownable|StaticCallProxy|TestChaiBridge|TestDydxBridge|TestERC20Bridge|TestEth2DaiBridge|TestKyberBridge|TestStaticCallTarget|TestUniswapBridge|UniswapBridge).json",
|
||||
"abis": "./test/generated-artifacts/@(ChaiBridge|CurveBridge|DexForwarderBridge|DydxBridge|ERC1155Proxy|ERC20BridgeProxy|ERC20Proxy|ERC721Proxy|Eth2DaiBridge|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|IChai|ICurve|IDydx|IDydxBridge|IERC20Bridge|IEth2Dai|IGasToken|IKyberNetworkProxy|IUniswapExchange|IUniswapExchangeFactory|IUniswapV2Router01|KyberBridge|MixinAssetProxyDispatcher|MixinAuthorizable|MixinGasToken|MultiAssetProxy|Ownable|StaticCallProxy|TestChaiBridge|TestDexForwarderBridge|TestDydxBridge|TestERC20Bridge|TestEth2DaiBridge|TestKyberBridge|TestStaticCallTarget|TestUniswapBridge|TestUniswapV2Bridge|UniswapBridge|UniswapV2Bridge).json",
|
||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
|
||||
},
|
||||
"repository": {
|
||||
@@ -51,12 +51,13 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/protocol/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^5.2.1",
|
||||
"@0x/contracts-gen": "^2.0.8",
|
||||
"@0x/contracts-test-utils": "^5.3.0",
|
||||
"@0x/contracts-utils": "^4.4.0",
|
||||
"@0x/dev-utils": "^3.2.1",
|
||||
"@0x/sol-compiler": "^4.0.8",
|
||||
"@0x/abi-gen": "^5.3.0",
|
||||
"@0x/contract-wrappers": "^13.7.0",
|
||||
"@0x/contracts-gen": "^2.0.9",
|
||||
"@0x/contracts-test-utils": "^5.3.3",
|
||||
"@0x/contracts-utils": "^4.5.0",
|
||||
"@0x/dev-utils": "^3.2.2",
|
||||
"@0x/sol-compiler": "^4.1.0",
|
||||
"@0x/ts-doc-gen": "^0.0.22",
|
||||
"@0x/tslint-config": "^4.0.0",
|
||||
"@types/lodash": "4.14.104",
|
||||
@@ -78,17 +79,17 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^6.2.1",
|
||||
"@0x/contracts-erc1155": "^2.1.2",
|
||||
"@0x/contracts-erc20": "^3.1.2",
|
||||
"@0x/contracts-erc721": "^3.1.2",
|
||||
"@0x/contracts-exchange-libs": "^4.3.2",
|
||||
"@0x/order-utils": "^10.2.2",
|
||||
"@0x/types": "^3.1.2",
|
||||
"@0x/typescript-typings": "^5.0.2",
|
||||
"@0x/utils": "^5.4.1",
|
||||
"@0x/web3-wrapper": "^7.0.7",
|
||||
"ethereum-types": "^3.1.0",
|
||||
"@0x/base-contract": "^6.2.2",
|
||||
"@0x/contracts-erc1155": "^2.1.6",
|
||||
"@0x/contracts-erc20": "^3.2.0",
|
||||
"@0x/contracts-erc721": "^3.1.6",
|
||||
"@0x/contracts-exchange-libs": "^4.3.6",
|
||||
"@0x/order-utils": "^10.2.5",
|
||||
"@0x/types": "^3.1.3",
|
||||
"@0x/typescript-typings": "^5.1.0",
|
||||
"@0x/utils": "^5.5.0",
|
||||
"@0x/web3-wrapper": "^7.1.0",
|
||||
"ethereum-types": "^3.1.1",
|
||||
"lodash": "^4.17.11"
|
||||
},
|
||||
"publishConfig": {
|
||||
|
@@ -7,6 +7,7 @@ import { ContractArtifact } from 'ethereum-types';
|
||||
|
||||
import * as ChaiBridge from '../generated-artifacts/ChaiBridge.json';
|
||||
import * as CurveBridge from '../generated-artifacts/CurveBridge.json';
|
||||
import * as DexForwarderBridge from '../generated-artifacts/DexForwarderBridge.json';
|
||||
import * as DydxBridge from '../generated-artifacts/DydxBridge.json';
|
||||
import * as ERC1155Proxy from '../generated-artifacts/ERC1155Proxy.json';
|
||||
import * as ERC20BridgeProxy from '../generated-artifacts/ERC20BridgeProxy.json';
|
||||
@@ -23,23 +24,29 @@ import * as IDydx from '../generated-artifacts/IDydx.json';
|
||||
import * as IDydxBridge from '../generated-artifacts/IDydxBridge.json';
|
||||
import * as IERC20Bridge from '../generated-artifacts/IERC20Bridge.json';
|
||||
import * as IEth2Dai from '../generated-artifacts/IEth2Dai.json';
|
||||
import * as IGasToken from '../generated-artifacts/IGasToken.json';
|
||||
import * as IKyberNetworkProxy from '../generated-artifacts/IKyberNetworkProxy.json';
|
||||
import * as IUniswapExchange from '../generated-artifacts/IUniswapExchange.json';
|
||||
import * as IUniswapExchangeFactory from '../generated-artifacts/IUniswapExchangeFactory.json';
|
||||
import * as IUniswapV2Router01 from '../generated-artifacts/IUniswapV2Router01.json';
|
||||
import * as KyberBridge from '../generated-artifacts/KyberBridge.json';
|
||||
import * as MixinAssetProxyDispatcher from '../generated-artifacts/MixinAssetProxyDispatcher.json';
|
||||
import * as MixinAuthorizable from '../generated-artifacts/MixinAuthorizable.json';
|
||||
import * as MixinGasToken from '../generated-artifacts/MixinGasToken.json';
|
||||
import * as MultiAssetProxy from '../generated-artifacts/MultiAssetProxy.json';
|
||||
import * as Ownable from '../generated-artifacts/Ownable.json';
|
||||
import * as StaticCallProxy from '../generated-artifacts/StaticCallProxy.json';
|
||||
import * as TestChaiBridge from '../generated-artifacts/TestChaiBridge.json';
|
||||
import * as TestDexForwarderBridge from '../generated-artifacts/TestDexForwarderBridge.json';
|
||||
import * as TestDydxBridge from '../generated-artifacts/TestDydxBridge.json';
|
||||
import * as TestERC20Bridge from '../generated-artifacts/TestERC20Bridge.json';
|
||||
import * as TestEth2DaiBridge from '../generated-artifacts/TestEth2DaiBridge.json';
|
||||
import * as TestKyberBridge from '../generated-artifacts/TestKyberBridge.json';
|
||||
import * as TestStaticCallTarget from '../generated-artifacts/TestStaticCallTarget.json';
|
||||
import * as TestUniswapBridge from '../generated-artifacts/TestUniswapBridge.json';
|
||||
import * as TestUniswapV2Bridge from '../generated-artifacts/TestUniswapV2Bridge.json';
|
||||
import * as UniswapBridge from '../generated-artifacts/UniswapBridge.json';
|
||||
import * as UniswapV2Bridge from '../generated-artifacts/UniswapV2Bridge.json';
|
||||
export const artifacts = {
|
||||
MixinAssetProxyDispatcher: MixinAssetProxyDispatcher as ContractArtifact,
|
||||
MixinAuthorizable: MixinAuthorizable as ContractArtifact,
|
||||
@@ -52,10 +59,13 @@ export const artifacts = {
|
||||
StaticCallProxy: StaticCallProxy as ContractArtifact,
|
||||
ChaiBridge: ChaiBridge as ContractArtifact,
|
||||
CurveBridge: CurveBridge as ContractArtifact,
|
||||
DexForwarderBridge: DexForwarderBridge as ContractArtifact,
|
||||
DydxBridge: DydxBridge as ContractArtifact,
|
||||
Eth2DaiBridge: Eth2DaiBridge as ContractArtifact,
|
||||
KyberBridge: KyberBridge as ContractArtifact,
|
||||
MixinGasToken: MixinGasToken as ContractArtifact,
|
||||
UniswapBridge: UniswapBridge as ContractArtifact,
|
||||
UniswapV2Bridge: UniswapV2Bridge as ContractArtifact,
|
||||
IAssetData: IAssetData as ContractArtifact,
|
||||
IAssetProxy: IAssetProxy as ContractArtifact,
|
||||
IAssetProxyDispatcher: IAssetProxyDispatcher as ContractArtifact,
|
||||
@@ -66,14 +76,18 @@ export const artifacts = {
|
||||
IDydxBridge: IDydxBridge as ContractArtifact,
|
||||
IERC20Bridge: IERC20Bridge as ContractArtifact,
|
||||
IEth2Dai: IEth2Dai as ContractArtifact,
|
||||
IGasToken: IGasToken as ContractArtifact,
|
||||
IKyberNetworkProxy: IKyberNetworkProxy as ContractArtifact,
|
||||
IUniswapExchange: IUniswapExchange as ContractArtifact,
|
||||
IUniswapExchangeFactory: IUniswapExchangeFactory as ContractArtifact,
|
||||
IUniswapV2Router01: IUniswapV2Router01 as ContractArtifact,
|
||||
TestChaiBridge: TestChaiBridge as ContractArtifact,
|
||||
TestDexForwarderBridge: TestDexForwarderBridge as ContractArtifact,
|
||||
TestDydxBridge: TestDydxBridge as ContractArtifact,
|
||||
TestERC20Bridge: TestERC20Bridge as ContractArtifact,
|
||||
TestEth2DaiBridge: TestEth2DaiBridge as ContractArtifact,
|
||||
TestKyberBridge: TestKyberBridge as ContractArtifact,
|
||||
TestStaticCallTarget: TestStaticCallTarget as ContractArtifact,
|
||||
TestUniswapBridge: TestUniswapBridge as ContractArtifact,
|
||||
TestUniswapV2Bridge: TestUniswapV2Bridge as ContractArtifact,
|
||||
};
|
||||
|
27
contracts/asset-proxy/src/dex_forwarder_bridge.ts
Normal file
27
contracts/asset-proxy/src/dex_forwarder_bridge.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { AbiEncoder, BigNumber } from '@0x/utils';
|
||||
|
||||
export interface DexForwarderBridgeCall {
|
||||
target: string;
|
||||
inputTokenAmount: BigNumber;
|
||||
outputTokenAmount: BigNumber;
|
||||
bridgeData: string;
|
||||
}
|
||||
|
||||
export interface DexForwaderBridgeData {
|
||||
inputToken: string;
|
||||
calls: DexForwarderBridgeCall[];
|
||||
}
|
||||
|
||||
export const dexForwarderBridgeDataEncoder = AbiEncoder.create([
|
||||
{ name: 'inputToken', type: 'address' },
|
||||
{
|
||||
name: 'calls',
|
||||
type: 'tuple[]',
|
||||
components: [
|
||||
{ name: 'target', type: 'address' },
|
||||
{ name: 'inputTokenAmount', type: 'uint256' },
|
||||
{ name: 'outputTokenAmount', type: 'uint256' },
|
||||
{ name: 'bridgeData', type: 'bytes' },
|
||||
],
|
||||
},
|
||||
]);
|
@@ -88,3 +88,4 @@ export {
|
||||
} from './asset_data';
|
||||
|
||||
export * from './dydx_bridge_encoder';
|
||||
export * from './dex_forwarder_bridge';
|
||||
|
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
export * from '../generated-wrappers/chai_bridge';
|
||||
export * from '../generated-wrappers/curve_bridge';
|
||||
export * from '../generated-wrappers/dex_forwarder_bridge';
|
||||
export * from '../generated-wrappers/dydx_bridge';
|
||||
export * from '../generated-wrappers/erc1155_proxy';
|
||||
export * from '../generated-wrappers/erc20_bridge_proxy';
|
||||
@@ -21,20 +22,26 @@ export * from '../generated-wrappers/i_dydx';
|
||||
export * from '../generated-wrappers/i_dydx_bridge';
|
||||
export * from '../generated-wrappers/i_erc20_bridge';
|
||||
export * from '../generated-wrappers/i_eth2_dai';
|
||||
export * from '../generated-wrappers/i_gas_token';
|
||||
export * from '../generated-wrappers/i_kyber_network_proxy';
|
||||
export * from '../generated-wrappers/i_uniswap_exchange';
|
||||
export * from '../generated-wrappers/i_uniswap_exchange_factory';
|
||||
export * from '../generated-wrappers/i_uniswap_v2_router01';
|
||||
export * from '../generated-wrappers/kyber_bridge';
|
||||
export * from '../generated-wrappers/mixin_asset_proxy_dispatcher';
|
||||
export * from '../generated-wrappers/mixin_authorizable';
|
||||
export * from '../generated-wrappers/mixin_gas_token';
|
||||
export * from '../generated-wrappers/multi_asset_proxy';
|
||||
export * from '../generated-wrappers/ownable';
|
||||
export * from '../generated-wrappers/static_call_proxy';
|
||||
export * from '../generated-wrappers/test_chai_bridge';
|
||||
export * from '../generated-wrappers/test_dex_forwarder_bridge';
|
||||
export * from '../generated-wrappers/test_dydx_bridge';
|
||||
export * from '../generated-wrappers/test_erc20_bridge';
|
||||
export * from '../generated-wrappers/test_eth2_dai_bridge';
|
||||
export * from '../generated-wrappers/test_kyber_bridge';
|
||||
export * from '../generated-wrappers/test_static_call_target';
|
||||
export * from '../generated-wrappers/test_uniswap_bridge';
|
||||
export * from '../generated-wrappers/test_uniswap_v2_bridge';
|
||||
export * from '../generated-wrappers/uniswap_bridge';
|
||||
export * from '../generated-wrappers/uniswap_v2_bridge';
|
||||
|
@@ -7,6 +7,7 @@ import { ContractArtifact } from 'ethereum-types';
|
||||
|
||||
import * as ChaiBridge from '../test/generated-artifacts/ChaiBridge.json';
|
||||
import * as CurveBridge from '../test/generated-artifacts/CurveBridge.json';
|
||||
import * as DexForwarderBridge from '../test/generated-artifacts/DexForwarderBridge.json';
|
||||
import * as DydxBridge from '../test/generated-artifacts/DydxBridge.json';
|
||||
import * as ERC1155Proxy from '../test/generated-artifacts/ERC1155Proxy.json';
|
||||
import * as ERC20BridgeProxy from '../test/generated-artifacts/ERC20BridgeProxy.json';
|
||||
@@ -23,23 +24,29 @@ import * as IDydx from '../test/generated-artifacts/IDydx.json';
|
||||
import * as IDydxBridge from '../test/generated-artifacts/IDydxBridge.json';
|
||||
import * as IERC20Bridge from '../test/generated-artifacts/IERC20Bridge.json';
|
||||
import * as IEth2Dai from '../test/generated-artifacts/IEth2Dai.json';
|
||||
import * as IGasToken from '../test/generated-artifacts/IGasToken.json';
|
||||
import * as IKyberNetworkProxy from '../test/generated-artifacts/IKyberNetworkProxy.json';
|
||||
import * as IUniswapExchange from '../test/generated-artifacts/IUniswapExchange.json';
|
||||
import * as IUniswapExchangeFactory from '../test/generated-artifacts/IUniswapExchangeFactory.json';
|
||||
import * as IUniswapV2Router01 from '../test/generated-artifacts/IUniswapV2Router01.json';
|
||||
import * as KyberBridge from '../test/generated-artifacts/KyberBridge.json';
|
||||
import * as MixinAssetProxyDispatcher from '../test/generated-artifacts/MixinAssetProxyDispatcher.json';
|
||||
import * as MixinAuthorizable from '../test/generated-artifacts/MixinAuthorizable.json';
|
||||
import * as MixinGasToken from '../test/generated-artifacts/MixinGasToken.json';
|
||||
import * as MultiAssetProxy from '../test/generated-artifacts/MultiAssetProxy.json';
|
||||
import * as Ownable from '../test/generated-artifacts/Ownable.json';
|
||||
import * as StaticCallProxy from '../test/generated-artifacts/StaticCallProxy.json';
|
||||
import * as TestChaiBridge from '../test/generated-artifacts/TestChaiBridge.json';
|
||||
import * as TestDexForwarderBridge from '../test/generated-artifacts/TestDexForwarderBridge.json';
|
||||
import * as TestDydxBridge from '../test/generated-artifacts/TestDydxBridge.json';
|
||||
import * as TestERC20Bridge from '../test/generated-artifacts/TestERC20Bridge.json';
|
||||
import * as TestEth2DaiBridge from '../test/generated-artifacts/TestEth2DaiBridge.json';
|
||||
import * as TestKyberBridge from '../test/generated-artifacts/TestKyberBridge.json';
|
||||
import * as TestStaticCallTarget from '../test/generated-artifacts/TestStaticCallTarget.json';
|
||||
import * as TestUniswapBridge from '../test/generated-artifacts/TestUniswapBridge.json';
|
||||
import * as TestUniswapV2Bridge from '../test/generated-artifacts/TestUniswapV2Bridge.json';
|
||||
import * as UniswapBridge from '../test/generated-artifacts/UniswapBridge.json';
|
||||
import * as UniswapV2Bridge from '../test/generated-artifacts/UniswapV2Bridge.json';
|
||||
export const artifacts = {
|
||||
MixinAssetProxyDispatcher: MixinAssetProxyDispatcher as ContractArtifact,
|
||||
MixinAuthorizable: MixinAuthorizable as ContractArtifact,
|
||||
@@ -52,10 +59,13 @@ export const artifacts = {
|
||||
StaticCallProxy: StaticCallProxy as ContractArtifact,
|
||||
ChaiBridge: ChaiBridge as ContractArtifact,
|
||||
CurveBridge: CurveBridge as ContractArtifact,
|
||||
DexForwarderBridge: DexForwarderBridge as ContractArtifact,
|
||||
DydxBridge: DydxBridge as ContractArtifact,
|
||||
Eth2DaiBridge: Eth2DaiBridge as ContractArtifact,
|
||||
KyberBridge: KyberBridge as ContractArtifact,
|
||||
MixinGasToken: MixinGasToken as ContractArtifact,
|
||||
UniswapBridge: UniswapBridge as ContractArtifact,
|
||||
UniswapV2Bridge: UniswapV2Bridge as ContractArtifact,
|
||||
IAssetData: IAssetData as ContractArtifact,
|
||||
IAssetProxy: IAssetProxy as ContractArtifact,
|
||||
IAssetProxyDispatcher: IAssetProxyDispatcher as ContractArtifact,
|
||||
@@ -66,14 +76,18 @@ export const artifacts = {
|
||||
IDydxBridge: IDydxBridge as ContractArtifact,
|
||||
IERC20Bridge: IERC20Bridge as ContractArtifact,
|
||||
IEth2Dai: IEth2Dai as ContractArtifact,
|
||||
IGasToken: IGasToken as ContractArtifact,
|
||||
IKyberNetworkProxy: IKyberNetworkProxy as ContractArtifact,
|
||||
IUniswapExchange: IUniswapExchange as ContractArtifact,
|
||||
IUniswapExchangeFactory: IUniswapExchangeFactory as ContractArtifact,
|
||||
IUniswapV2Router01: IUniswapV2Router01 as ContractArtifact,
|
||||
TestChaiBridge: TestChaiBridge as ContractArtifact,
|
||||
TestDexForwarderBridge: TestDexForwarderBridge as ContractArtifact,
|
||||
TestDydxBridge: TestDydxBridge as ContractArtifact,
|
||||
TestERC20Bridge: TestERC20Bridge as ContractArtifact,
|
||||
TestEth2DaiBridge: TestEth2DaiBridge as ContractArtifact,
|
||||
TestKyberBridge: TestKyberBridge as ContractArtifact,
|
||||
TestStaticCallTarget: TestStaticCallTarget as ContractArtifact,
|
||||
TestUniswapBridge: TestUniswapBridge as ContractArtifact,
|
||||
TestUniswapV2Bridge: TestUniswapV2Bridge as ContractArtifact,
|
||||
};
|
||||
|
356
contracts/asset-proxy/test/dex_forwarder_bridge.ts
Normal file
356
contracts/asset-proxy/test/dex_forwarder_bridge.ts
Normal file
@@ -0,0 +1,356 @@
|
||||
import { ContractTxFunctionObj } from '@0x/contract-wrappers';
|
||||
import {
|
||||
blockchainTests,
|
||||
constants,
|
||||
expect,
|
||||
filterLogsToArguments,
|
||||
getRandomInteger,
|
||||
randomAddress,
|
||||
shortZip,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { BigNumber, hexUtils, NULL_ADDRESS } from '@0x/utils';
|
||||
import { DecodedLogs } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { DexForwarderBridgeCall, dexForwarderBridgeDataEncoder } from '../src/dex_forwarder_bridge';
|
||||
|
||||
import { artifacts } from './artifacts';
|
||||
import {
|
||||
TestDexForwarderBridgeBridgeTransferFromCalledEventArgs as BtfCalledEventArgs,
|
||||
TestDexForwarderBridgeContract,
|
||||
TestDexForwarderBridgeEvents as TestEvents,
|
||||
} from './wrappers';
|
||||
|
||||
const { ZERO_AMOUNT } = constants;
|
||||
|
||||
blockchainTests.resets('DexForwarderBridge unit tests', env => {
|
||||
let testContract: TestDexForwarderBridgeContract;
|
||||
let inputToken: string;
|
||||
let outputToken: string;
|
||||
const BRIDGE_SUCCESS = '0xdc1600f3';
|
||||
const BRIDGE_FAILURE = '0xffffffff';
|
||||
const BRIDGE_REVERT_ERROR = 'oopsie';
|
||||
const INCOMPLETE_FILL_REVERT = 'DexForwarderBridge/INCOMPLETE_FILL';
|
||||
const NOT_AUTHORIZED_REVERT = 'DexForwarderBridge/SENDER_NOT_AUTHORIZED';
|
||||
const DEFAULTS = {
|
||||
toAddress: randomAddress(),
|
||||
};
|
||||
|
||||
before(async () => {
|
||||
testContract = await TestDexForwarderBridgeContract.deployFrom0xArtifactAsync(
|
||||
artifacts.TestDexForwarderBridge,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
// Create test tokens.
|
||||
[inputToken, outputToken] = [
|
||||
await callAndTransactAsync(testContract.createToken()),
|
||||
await callAndTransactAsync(testContract.createToken()),
|
||||
];
|
||||
await callAndTransactAsync(testContract.setAuthorized(env.txDefaults.from as string));
|
||||
});
|
||||
|
||||
async function callAndTransactAsync<TResult>(fnCall: ContractTxFunctionObj<TResult>): Promise<TResult> {
|
||||
const result = await fnCall.callAsync();
|
||||
await fnCall.awaitTransactionSuccessAsync({}, { shouldValidate: false });
|
||||
return result;
|
||||
}
|
||||
|
||||
function getRandomBridgeCall(
|
||||
bridgeAddress: string,
|
||||
fields: Partial<DexForwarderBridgeCall> = {},
|
||||
): DexForwarderBridgeCall {
|
||||
return {
|
||||
target: bridgeAddress,
|
||||
inputTokenAmount: getRandomInteger(1, '100e18'),
|
||||
outputTokenAmount: getRandomInteger(1, '100e18'),
|
||||
bridgeData: hexUtils.leftPad(inputToken),
|
||||
...fields,
|
||||
};
|
||||
}
|
||||
|
||||
describe('bridgeTransferFrom()', () => {
|
||||
let goodBridgeCalls: DexForwarderBridgeCall[];
|
||||
let revertingBridgeCall: DexForwarderBridgeCall;
|
||||
let failingBridgeCall: DexForwarderBridgeCall;
|
||||
let allBridgeCalls: DexForwarderBridgeCall[];
|
||||
let totalFillableOutputAmount: BigNumber;
|
||||
let totalFillableInputAmount: BigNumber;
|
||||
let recipientOutputBalance: BigNumber;
|
||||
|
||||
beforeEach(async () => {
|
||||
goodBridgeCalls = [];
|
||||
for (let i = 0; i < 4; ++i) {
|
||||
goodBridgeCalls.push(await createBridgeCallAsync({ returnCode: BRIDGE_SUCCESS }));
|
||||
}
|
||||
revertingBridgeCall = await createBridgeCallAsync({ revertError: BRIDGE_REVERT_ERROR });
|
||||
failingBridgeCall = await createBridgeCallAsync({ returnCode: BRIDGE_FAILURE });
|
||||
allBridgeCalls = _.shuffle([failingBridgeCall, revertingBridgeCall, ...goodBridgeCalls]);
|
||||
|
||||
totalFillableInputAmount = BigNumber.sum(...goodBridgeCalls.map(c => c.inputTokenAmount));
|
||||
totalFillableOutputAmount = BigNumber.sum(...goodBridgeCalls.map(c => c.outputTokenAmount));
|
||||
|
||||
// Grant the taker some output tokens.
|
||||
await testContract.setTokenBalance(
|
||||
outputToken,
|
||||
DEFAULTS.toAddress,
|
||||
(recipientOutputBalance = getRandomInteger(1, '100e18')),
|
||||
);
|
||||
});
|
||||
|
||||
async function setForwarderInputBalanceAsync(amount: BigNumber): Promise<void> {
|
||||
await testContract
|
||||
.setTokenBalance(inputToken, testContract.address, amount)
|
||||
.awaitTransactionSuccessAsync({}, { shouldValidate: false });
|
||||
}
|
||||
|
||||
async function createBridgeCallAsync(
|
||||
opts: Partial<{
|
||||
returnCode: string;
|
||||
revertError: string;
|
||||
callFields: Partial<DexForwarderBridgeCall>;
|
||||
outputFillAmount: BigNumber;
|
||||
}>,
|
||||
): Promise<DexForwarderBridgeCall> {
|
||||
const { returnCode, revertError, callFields, outputFillAmount } = {
|
||||
returnCode: BRIDGE_SUCCESS,
|
||||
revertError: '',
|
||||
...opts,
|
||||
};
|
||||
const bridge = await callAndTransactAsync(testContract.createBridge(returnCode, revertError));
|
||||
const call = getRandomBridgeCall(bridge, callFields);
|
||||
await testContract
|
||||
.setBridgeTransferAmount(call.target, outputFillAmount || call.outputTokenAmount)
|
||||
.awaitTransactionSuccessAsync({}, { shouldValidate: false });
|
||||
return call;
|
||||
}
|
||||
|
||||
async function callBridgeTransferFromAsync(opts: {
|
||||
bridgeData: string;
|
||||
sellAmount?: BigNumber;
|
||||
buyAmount?: BigNumber;
|
||||
}): Promise<DecodedLogs> {
|
||||
// Fund the forwarder with input tokens to sell.
|
||||
await setForwarderInputBalanceAsync(opts.sellAmount || totalFillableInputAmount);
|
||||
const call = testContract.bridgeTransferFrom(
|
||||
outputToken,
|
||||
testContract.address,
|
||||
DEFAULTS.toAddress,
|
||||
opts.buyAmount || totalFillableOutputAmount,
|
||||
opts.bridgeData,
|
||||
);
|
||||
const returnCode = await call.callAsync();
|
||||
if (returnCode !== BRIDGE_SUCCESS) {
|
||||
throw new Error('Expected BRIDGE_SUCCESS');
|
||||
}
|
||||
const receipt = await call.awaitTransactionSuccessAsync({}, { shouldValidate: false });
|
||||
// tslint:disable-next-line: no-unnecessary-type-assertion
|
||||
return receipt.logs as DecodedLogs;
|
||||
}
|
||||
|
||||
it('succeeds with no bridge calls and no input balance', async () => {
|
||||
const bridgeData = dexForwarderBridgeDataEncoder.encode({
|
||||
inputToken,
|
||||
calls: [],
|
||||
});
|
||||
await callBridgeTransferFromAsync({ bridgeData, sellAmount: ZERO_AMOUNT });
|
||||
});
|
||||
|
||||
it('succeeds with bridge calls and no input balance', async () => {
|
||||
const bridgeData = dexForwarderBridgeDataEncoder.encode({
|
||||
inputToken,
|
||||
calls: allBridgeCalls,
|
||||
});
|
||||
await callBridgeTransferFromAsync({ bridgeData, sellAmount: ZERO_AMOUNT });
|
||||
});
|
||||
|
||||
it('fails with no bridge calls and an input balance', async () => {
|
||||
const bridgeData = dexForwarderBridgeDataEncoder.encode({
|
||||
inputToken,
|
||||
calls: [],
|
||||
});
|
||||
return expect(callBridgeTransferFromAsync({ bridgeData, sellAmount: new BigNumber(1) })).to.revertWith(
|
||||
INCOMPLETE_FILL_REVERT,
|
||||
);
|
||||
});
|
||||
|
||||
it('fails if entire input token balance is not consumed', async () => {
|
||||
const bridgeData = dexForwarderBridgeDataEncoder.encode({
|
||||
inputToken,
|
||||
calls: allBridgeCalls,
|
||||
});
|
||||
return expect(
|
||||
callBridgeTransferFromAsync({
|
||||
bridgeData,
|
||||
sellAmount: totalFillableInputAmount.plus(1),
|
||||
}),
|
||||
).to.revertWith(INCOMPLETE_FILL_REVERT);
|
||||
});
|
||||
|
||||
it('fails if not authorized', async () => {
|
||||
const calls = goodBridgeCalls.slice(0, 1);
|
||||
const bridgeData = dexForwarderBridgeDataEncoder.encode({
|
||||
inputToken,
|
||||
calls,
|
||||
});
|
||||
await callAndTransactAsync(testContract.setAuthorized(NULL_ADDRESS));
|
||||
return expect(callBridgeTransferFromAsync({ bridgeData, sellAmount: new BigNumber(1) })).to.revertWith(
|
||||
NOT_AUTHORIZED_REVERT,
|
||||
);
|
||||
});
|
||||
|
||||
it('succeeds with one bridge call', async () => {
|
||||
const calls = goodBridgeCalls.slice(0, 1);
|
||||
const bridgeData = dexForwarderBridgeDataEncoder.encode({
|
||||
inputToken,
|
||||
calls,
|
||||
});
|
||||
await callBridgeTransferFromAsync({ bridgeData, sellAmount: calls[0].inputTokenAmount });
|
||||
});
|
||||
|
||||
it('succeeds with many bridge calls', async () => {
|
||||
const calls = goodBridgeCalls;
|
||||
const bridgeData = dexForwarderBridgeDataEncoder.encode({
|
||||
inputToken,
|
||||
calls,
|
||||
});
|
||||
await callBridgeTransferFromAsync({ bridgeData });
|
||||
});
|
||||
|
||||
it('swallows a failing bridge call', async () => {
|
||||
const calls = _.shuffle([...goodBridgeCalls, failingBridgeCall]);
|
||||
const bridgeData = dexForwarderBridgeDataEncoder.encode({
|
||||
inputToken,
|
||||
calls,
|
||||
});
|
||||
await callBridgeTransferFromAsync({ bridgeData });
|
||||
});
|
||||
|
||||
it('consumes input tokens for output tokens', async () => {
|
||||
const calls = allBridgeCalls;
|
||||
const bridgeData = dexForwarderBridgeDataEncoder.encode({
|
||||
inputToken,
|
||||
calls,
|
||||
});
|
||||
await callBridgeTransferFromAsync({ bridgeData });
|
||||
const currentBridgeInputBalance = await testContract
|
||||
.balanceOf(inputToken, testContract.address)
|
||||
.callAsync();
|
||||
expect(currentBridgeInputBalance).to.bignumber.eq(0);
|
||||
const currentRecipientOutputBalance = await testContract
|
||||
.balanceOf(outputToken, DEFAULTS.toAddress)
|
||||
.callAsync();
|
||||
expect(currentRecipientOutputBalance).to.bignumber.eq(totalFillableOutputAmount);
|
||||
});
|
||||
|
||||
it("transfers only up to each call's input amount to each bridge", async () => {
|
||||
const calls = goodBridgeCalls;
|
||||
const bridgeData = dexForwarderBridgeDataEncoder.encode({
|
||||
inputToken,
|
||||
calls,
|
||||
});
|
||||
const logs = await callBridgeTransferFromAsync({ bridgeData });
|
||||
const btfs = filterLogsToArguments<BtfCalledEventArgs>(logs, TestEvents.BridgeTransferFromCalled);
|
||||
for (const [call, btf] of shortZip(goodBridgeCalls, btfs)) {
|
||||
expect(btf.inputTokenBalance).to.bignumber.eq(call.inputTokenAmount);
|
||||
}
|
||||
});
|
||||
|
||||
it('transfers only up to outstanding sell amount to each bridge', async () => {
|
||||
// Prepend an extra bridge call.
|
||||
const calls = [
|
||||
await createBridgeCallAsync({
|
||||
callFields: {
|
||||
inputTokenAmount: new BigNumber(1),
|
||||
outputTokenAmount: new BigNumber(1),
|
||||
},
|
||||
}),
|
||||
...goodBridgeCalls,
|
||||
];
|
||||
const bridgeData = dexForwarderBridgeDataEncoder.encode({
|
||||
inputToken,
|
||||
calls,
|
||||
});
|
||||
const logs = await callBridgeTransferFromAsync({ bridgeData });
|
||||
const btfs = filterLogsToArguments<BtfCalledEventArgs>(logs, TestEvents.BridgeTransferFromCalled);
|
||||
expect(btfs).to.be.length(goodBridgeCalls.length + 1);
|
||||
// The last call will receive 1 less token.
|
||||
const lastCall = calls.slice(-1)[0];
|
||||
const lastBtf = btfs.slice(-1)[0];
|
||||
expect(lastBtf.inputTokenBalance).to.bignumber.eq(lastCall.inputTokenAmount.minus(1));
|
||||
});
|
||||
|
||||
it('recoups funds from a bridge that fails', async () => {
|
||||
// Prepend a call that will take the whole input amount but will
|
||||
// fail.
|
||||
const badCall = await createBridgeCallAsync({
|
||||
callFields: { inputTokenAmount: totalFillableInputAmount },
|
||||
returnCode: BRIDGE_FAILURE,
|
||||
});
|
||||
const calls = [badCall, ...goodBridgeCalls];
|
||||
const bridgeData = dexForwarderBridgeDataEncoder.encode({
|
||||
inputToken,
|
||||
calls,
|
||||
});
|
||||
const logs = await callBridgeTransferFromAsync({ bridgeData });
|
||||
const btfs = filterLogsToArguments<BtfCalledEventArgs>(logs, TestEvents.BridgeTransferFromCalled);
|
||||
expect(btfs).to.be.length(goodBridgeCalls.length);
|
||||
});
|
||||
|
||||
it('recoups funds from a bridge that reverts', async () => {
|
||||
// Prepend a call that will take the whole input amount but will
|
||||
// revert.
|
||||
const badCall = await createBridgeCallAsync({
|
||||
callFields: { inputTokenAmount: totalFillableInputAmount },
|
||||
revertError: BRIDGE_REVERT_ERROR,
|
||||
});
|
||||
const calls = [badCall, ...goodBridgeCalls];
|
||||
const bridgeData = dexForwarderBridgeDataEncoder.encode({
|
||||
inputToken,
|
||||
calls,
|
||||
});
|
||||
const logs = await callBridgeTransferFromAsync({ bridgeData });
|
||||
const btfs = filterLogsToArguments<BtfCalledEventArgs>(logs, TestEvents.BridgeTransferFromCalled);
|
||||
expect(btfs).to.be.length(goodBridgeCalls.length);
|
||||
});
|
||||
|
||||
it('recoups funds from a bridge that under-pays', async () => {
|
||||
// Prepend a call that will take the whole input amount but will
|
||||
// underpay the output amount..
|
||||
const badCall = await createBridgeCallAsync({
|
||||
callFields: {
|
||||
inputTokenAmount: totalFillableInputAmount,
|
||||
outputTokenAmount: new BigNumber(2),
|
||||
},
|
||||
outputFillAmount: new BigNumber(1),
|
||||
});
|
||||
const calls = [badCall, ...goodBridgeCalls];
|
||||
const bridgeData = dexForwarderBridgeDataEncoder.encode({
|
||||
inputToken,
|
||||
calls,
|
||||
});
|
||||
const logs = await callBridgeTransferFromAsync({ bridgeData });
|
||||
const btfs = filterLogsToArguments<BtfCalledEventArgs>(logs, TestEvents.BridgeTransferFromCalled);
|
||||
expect(btfs).to.be.length(goodBridgeCalls.length);
|
||||
});
|
||||
});
|
||||
|
||||
describe('executeBridgeCall()', () => {
|
||||
it('cannot be called externally', async () => {
|
||||
return expect(
|
||||
testContract
|
||||
.executeBridgeCall(
|
||||
randomAddress(),
|
||||
randomAddress(),
|
||||
randomAddress(),
|
||||
randomAddress(),
|
||||
new BigNumber(1),
|
||||
new BigNumber(1),
|
||||
constants.NULL_BYTES,
|
||||
)
|
||||
.callAsync(),
|
||||
).to.revertWith('DexForwarderBridge/ONLY_SELF');
|
||||
});
|
||||
});
|
||||
});
|
216
contracts/asset-proxy/test/uniswapv2_bridge.ts
Normal file
216
contracts/asset-proxy/test/uniswapv2_bridge.ts
Normal file
@@ -0,0 +1,216 @@
|
||||
import {
|
||||
blockchainTests,
|
||||
constants,
|
||||
expect,
|
||||
filterLogsToArguments,
|
||||
getRandomInteger,
|
||||
randomAddress,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { AssetProxyId } from '@0x/types';
|
||||
import { AbiEncoder, BigNumber, hexUtils } from '@0x/utils';
|
||||
import { DecodedLogs } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { artifacts } from './artifacts';
|
||||
|
||||
import {
|
||||
TestUniswapV2BridgeContract,
|
||||
TestUniswapV2BridgeEvents as ContractEvents,
|
||||
TestUniswapV2BridgeSwapExactTokensForTokensInputEventArgs as SwapExactTokensForTokensArgs,
|
||||
TestUniswapV2BridgeTokenApproveEventArgs as TokenApproveArgs,
|
||||
TestUniswapV2BridgeTokenTransferEventArgs as TokenTransferArgs,
|
||||
} from './wrappers';
|
||||
|
||||
blockchainTests.resets('UniswapV2 unit tests', env => {
|
||||
const FROM_TOKEN_DECIMALS = 6;
|
||||
const TO_TOKEN_DECIMALS = 18;
|
||||
const FROM_TOKEN_BASE = new BigNumber(10).pow(FROM_TOKEN_DECIMALS);
|
||||
const TO_TOKEN_BASE = new BigNumber(10).pow(TO_TOKEN_DECIMALS);
|
||||
let testContract: TestUniswapV2BridgeContract;
|
||||
|
||||
before(async () => {
|
||||
testContract = await TestUniswapV2BridgeContract.deployFrom0xArtifactAsync(
|
||||
artifacts.TestUniswapV2Bridge,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
});
|
||||
|
||||
describe('isValidSignature()', () => {
|
||||
it('returns success bytes', async () => {
|
||||
const LEGACY_WALLET_MAGIC_VALUE = '0xb0671381';
|
||||
const result = await testContract
|
||||
.isValidSignature(hexUtils.random(), hexUtils.random(_.random(0, 32)))
|
||||
.callAsync();
|
||||
expect(result).to.eq(LEGACY_WALLET_MAGIC_VALUE);
|
||||
});
|
||||
});
|
||||
|
||||
describe('bridgeTransferFrom()', () => {
|
||||
interface TransferFromOpts {
|
||||
tokenAddressesPath: string[];
|
||||
toAddress: string;
|
||||
// Amount to pass into `bridgeTransferFrom()`
|
||||
amount: BigNumber;
|
||||
// Token balance of the bridge.
|
||||
fromTokenBalance: BigNumber;
|
||||
// Router reverts with this reason
|
||||
routerRevertReason: string;
|
||||
}
|
||||
|
||||
interface TransferFromResult {
|
||||
opts: TransferFromOpts;
|
||||
result: string;
|
||||
logs: DecodedLogs;
|
||||
blocktime: number;
|
||||
}
|
||||
|
||||
function createTransferFromOpts(opts?: Partial<TransferFromOpts>): TransferFromOpts {
|
||||
const amount = getRandomInteger(1, TO_TOKEN_BASE.times(100));
|
||||
return {
|
||||
tokenAddressesPath: Array(2).fill(constants.NULL_ADDRESS),
|
||||
amount,
|
||||
toAddress: randomAddress(),
|
||||
fromTokenBalance: getRandomInteger(1, FROM_TOKEN_BASE.times(100)),
|
||||
routerRevertReason: '',
|
||||
...opts,
|
||||
};
|
||||
}
|
||||
|
||||
const bridgeDataEncoder = AbiEncoder.create('(address[])');
|
||||
|
||||
async function transferFromAsync(opts?: Partial<TransferFromOpts>): Promise<TransferFromResult> {
|
||||
const _opts = createTransferFromOpts(opts);
|
||||
|
||||
for (let i = 0; i < _opts.tokenAddressesPath.length; i++) {
|
||||
const createFromTokenFn = testContract.createToken(_opts.tokenAddressesPath[i]);
|
||||
_opts.tokenAddressesPath[i] = await createFromTokenFn.callAsync();
|
||||
await createFromTokenFn.awaitTransactionSuccessAsync();
|
||||
}
|
||||
|
||||
// Set the token balance for the token we're converting from.
|
||||
await testContract
|
||||
.setTokenBalance(_opts.tokenAddressesPath[0], _opts.fromTokenBalance)
|
||||
.awaitTransactionSuccessAsync();
|
||||
|
||||
// Set revert reason for the router.
|
||||
await testContract.setRouterRevertReason(_opts.routerRevertReason).awaitTransactionSuccessAsync();
|
||||
|
||||
// Call bridgeTransferFrom().
|
||||
const bridgeTransferFromFn = testContract.bridgeTransferFrom(
|
||||
// Output token
|
||||
_opts.tokenAddressesPath[_opts.tokenAddressesPath.length - 1],
|
||||
// Random maker address.
|
||||
randomAddress(),
|
||||
// Recipient address.
|
||||
_opts.toAddress,
|
||||
// Transfer amount.
|
||||
_opts.amount,
|
||||
// ABI-encode the input token address as the bridge data. // FIXME
|
||||
bridgeDataEncoder.encode([_opts.tokenAddressesPath]),
|
||||
);
|
||||
const result = await bridgeTransferFromFn.callAsync();
|
||||
const receipt = await bridgeTransferFromFn.awaitTransactionSuccessAsync();
|
||||
return {
|
||||
opts: _opts,
|
||||
result,
|
||||
logs: (receipt.logs as any) as DecodedLogs,
|
||||
blocktime: await env.web3Wrapper.getBlockTimestampAsync(receipt.blockNumber),
|
||||
};
|
||||
}
|
||||
|
||||
it('returns magic bytes on success', async () => {
|
||||
const { result } = await transferFromAsync();
|
||||
expect(result).to.eq(AssetProxyId.ERC20Bridge);
|
||||
});
|
||||
|
||||
it('performs transfer when both tokens are the same', async () => {
|
||||
const createTokenFn = testContract.createToken(constants.NULL_ADDRESS);
|
||||
const tokenAddress = await createTokenFn.callAsync();
|
||||
await createTokenFn.awaitTransactionSuccessAsync();
|
||||
|
||||
const { opts, result, logs } = await transferFromAsync({
|
||||
tokenAddressesPath: [tokenAddress, tokenAddress],
|
||||
});
|
||||
expect(result).to.eq(AssetProxyId.ERC20Bridge, 'asset proxy id');
|
||||
const transfers = filterLogsToArguments<TokenTransferArgs>(logs, ContractEvents.TokenTransfer);
|
||||
|
||||
expect(transfers.length).to.eq(1);
|
||||
expect(transfers[0].token).to.eq(tokenAddress, 'input token address');
|
||||
expect(transfers[0].from).to.eq(testContract.address);
|
||||
expect(transfers[0].to).to.eq(opts.toAddress, 'recipient address');
|
||||
expect(transfers[0].amount).to.bignumber.eq(opts.amount, 'amount');
|
||||
});
|
||||
|
||||
describe('token -> token', async () => {
|
||||
it('calls UniswapV2Router01.swapExactTokensForTokens()', async () => {
|
||||
const { opts, result, logs, blocktime } = await transferFromAsync();
|
||||
expect(result).to.eq(AssetProxyId.ERC20Bridge, 'asset proxy id');
|
||||
const transfers = filterLogsToArguments<SwapExactTokensForTokensArgs>(
|
||||
logs,
|
||||
ContractEvents.SwapExactTokensForTokensInput,
|
||||
);
|
||||
|
||||
expect(transfers.length).to.eq(1);
|
||||
expect(transfers[0].toTokenAddress).to.eq(
|
||||
opts.tokenAddressesPath[opts.tokenAddressesPath.length - 1],
|
||||
'output token address',
|
||||
);
|
||||
expect(transfers[0].to).to.eq(opts.toAddress, 'recipient address');
|
||||
expect(transfers[0].amountIn).to.bignumber.eq(opts.fromTokenBalance, 'input token amount');
|
||||
expect(transfers[0].amountOutMin).to.bignumber.eq(opts.amount, 'output token amount');
|
||||
expect(transfers[0].deadline).to.bignumber.eq(blocktime, 'deadline');
|
||||
});
|
||||
|
||||
it('sets allowance for "from" token', async () => {
|
||||
const { logs } = await transferFromAsync();
|
||||
const approvals = filterLogsToArguments<TokenApproveArgs>(logs, ContractEvents.TokenApprove);
|
||||
const routerAddress = await testContract.getRouterAddress().callAsync();
|
||||
expect(approvals.length).to.eq(1);
|
||||
expect(approvals[0].spender).to.eq(routerAddress);
|
||||
expect(approvals[0].allowance).to.bignumber.eq(constants.MAX_UINT256);
|
||||
});
|
||||
|
||||
it('sets allowance for "from" token on subsequent calls', async () => {
|
||||
const { opts } = await transferFromAsync();
|
||||
const { logs } = await transferFromAsync(opts);
|
||||
const approvals = filterLogsToArguments<TokenApproveArgs>(logs, ContractEvents.TokenApprove);
|
||||
const routerAddress = await testContract.getRouterAddress().callAsync();
|
||||
expect(approvals.length).to.eq(1);
|
||||
expect(approvals[0].spender).to.eq(routerAddress);
|
||||
expect(approvals[0].allowance).to.bignumber.eq(constants.MAX_UINT256);
|
||||
});
|
||||
|
||||
it('fails if the router fails', async () => {
|
||||
const revertReason = 'FOOBAR';
|
||||
const tx = transferFromAsync({
|
||||
routerRevertReason: revertReason,
|
||||
});
|
||||
return expect(tx).to.eventually.be.rejectedWith(revertReason);
|
||||
});
|
||||
});
|
||||
describe('token -> token -> token', async () => {
|
||||
it('calls UniswapV2Router01.swapExactTokensForTokens()', async () => {
|
||||
const { opts, result, logs, blocktime } = await transferFromAsync({
|
||||
tokenAddressesPath: Array(3).fill(constants.NULL_ADDRESS),
|
||||
});
|
||||
expect(result).to.eq(AssetProxyId.ERC20Bridge, 'asset proxy id');
|
||||
const transfers = filterLogsToArguments<SwapExactTokensForTokensArgs>(
|
||||
logs,
|
||||
ContractEvents.SwapExactTokensForTokensInput,
|
||||
);
|
||||
|
||||
expect(transfers.length).to.eq(1);
|
||||
expect(transfers[0].toTokenAddress).to.eq(
|
||||
opts.tokenAddressesPath[opts.tokenAddressesPath.length - 1],
|
||||
'output token address',
|
||||
);
|
||||
expect(transfers[0].to).to.eq(opts.toAddress, 'recipient address');
|
||||
expect(transfers[0].amountIn).to.bignumber.eq(opts.fromTokenBalance, 'input token amount');
|
||||
expect(transfers[0].amountOutMin).to.bignumber.eq(opts.amount, 'output token amount');
|
||||
expect(transfers[0].deadline).to.bignumber.eq(blocktime, 'deadline');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
export * from '../test/generated-wrappers/chai_bridge';
|
||||
export * from '../test/generated-wrappers/curve_bridge';
|
||||
export * from '../test/generated-wrappers/dex_forwarder_bridge';
|
||||
export * from '../test/generated-wrappers/dydx_bridge';
|
||||
export * from '../test/generated-wrappers/erc1155_proxy';
|
||||
export * from '../test/generated-wrappers/erc20_bridge_proxy';
|
||||
@@ -21,20 +22,26 @@ export * from '../test/generated-wrappers/i_dydx';
|
||||
export * from '../test/generated-wrappers/i_dydx_bridge';
|
||||
export * from '../test/generated-wrappers/i_erc20_bridge';
|
||||
export * from '../test/generated-wrappers/i_eth2_dai';
|
||||
export * from '../test/generated-wrappers/i_gas_token';
|
||||
export * from '../test/generated-wrappers/i_kyber_network_proxy';
|
||||
export * from '../test/generated-wrappers/i_uniswap_exchange';
|
||||
export * from '../test/generated-wrappers/i_uniswap_exchange_factory';
|
||||
export * from '../test/generated-wrappers/i_uniswap_v2_router01';
|
||||
export * from '../test/generated-wrappers/kyber_bridge';
|
||||
export * from '../test/generated-wrappers/mixin_asset_proxy_dispatcher';
|
||||
export * from '../test/generated-wrappers/mixin_authorizable';
|
||||
export * from '../test/generated-wrappers/mixin_gas_token';
|
||||
export * from '../test/generated-wrappers/multi_asset_proxy';
|
||||
export * from '../test/generated-wrappers/ownable';
|
||||
export * from '../test/generated-wrappers/static_call_proxy';
|
||||
export * from '../test/generated-wrappers/test_chai_bridge';
|
||||
export * from '../test/generated-wrappers/test_dex_forwarder_bridge';
|
||||
export * from '../test/generated-wrappers/test_dydx_bridge';
|
||||
export * from '../test/generated-wrappers/test_erc20_bridge';
|
||||
export * from '../test/generated-wrappers/test_eth2_dai_bridge';
|
||||
export * from '../test/generated-wrappers/test_kyber_bridge';
|
||||
export * from '../test/generated-wrappers/test_static_call_target';
|
||||
export * from '../test/generated-wrappers/test_uniswap_bridge';
|
||||
export * from '../test/generated-wrappers/test_uniswap_v2_bridge';
|
||||
export * from '../test/generated-wrappers/uniswap_bridge';
|
||||
export * from '../test/generated-wrappers/uniswap_v2_bridge';
|
||||
|
@@ -5,6 +5,7 @@
|
||||
"files": [
|
||||
"generated-artifacts/ChaiBridge.json",
|
||||
"generated-artifacts/CurveBridge.json",
|
||||
"generated-artifacts/DexForwarderBridge.json",
|
||||
"generated-artifacts/DydxBridge.json",
|
||||
"generated-artifacts/ERC1155Proxy.json",
|
||||
"generated-artifacts/ERC20BridgeProxy.json",
|
||||
@@ -21,25 +22,32 @@
|
||||
"generated-artifacts/IDydxBridge.json",
|
||||
"generated-artifacts/IERC20Bridge.json",
|
||||
"generated-artifacts/IEth2Dai.json",
|
||||
"generated-artifacts/IGasToken.json",
|
||||
"generated-artifacts/IKyberNetworkProxy.json",
|
||||
"generated-artifacts/IUniswapExchange.json",
|
||||
"generated-artifacts/IUniswapExchangeFactory.json",
|
||||
"generated-artifacts/IUniswapV2Router01.json",
|
||||
"generated-artifacts/KyberBridge.json",
|
||||
"generated-artifacts/MixinAssetProxyDispatcher.json",
|
||||
"generated-artifacts/MixinAuthorizable.json",
|
||||
"generated-artifacts/MixinGasToken.json",
|
||||
"generated-artifacts/MultiAssetProxy.json",
|
||||
"generated-artifacts/Ownable.json",
|
||||
"generated-artifacts/StaticCallProxy.json",
|
||||
"generated-artifacts/TestChaiBridge.json",
|
||||
"generated-artifacts/TestDexForwarderBridge.json",
|
||||
"generated-artifacts/TestDydxBridge.json",
|
||||
"generated-artifacts/TestERC20Bridge.json",
|
||||
"generated-artifacts/TestEth2DaiBridge.json",
|
||||
"generated-artifacts/TestKyberBridge.json",
|
||||
"generated-artifacts/TestStaticCallTarget.json",
|
||||
"generated-artifacts/TestUniswapBridge.json",
|
||||
"generated-artifacts/TestUniswapV2Bridge.json",
|
||||
"generated-artifacts/UniswapBridge.json",
|
||||
"generated-artifacts/UniswapV2Bridge.json",
|
||||
"test/generated-artifacts/ChaiBridge.json",
|
||||
"test/generated-artifacts/CurveBridge.json",
|
||||
"test/generated-artifacts/DexForwarderBridge.json",
|
||||
"test/generated-artifacts/DydxBridge.json",
|
||||
"test/generated-artifacts/ERC1155Proxy.json",
|
||||
"test/generated-artifacts/ERC20BridgeProxy.json",
|
||||
@@ -56,23 +64,29 @@
|
||||
"test/generated-artifacts/IDydxBridge.json",
|
||||
"test/generated-artifacts/IERC20Bridge.json",
|
||||
"test/generated-artifacts/IEth2Dai.json",
|
||||
"test/generated-artifacts/IGasToken.json",
|
||||
"test/generated-artifacts/IKyberNetworkProxy.json",
|
||||
"test/generated-artifacts/IUniswapExchange.json",
|
||||
"test/generated-artifacts/IUniswapExchangeFactory.json",
|
||||
"test/generated-artifacts/IUniswapV2Router01.json",
|
||||
"test/generated-artifacts/KyberBridge.json",
|
||||
"test/generated-artifacts/MixinAssetProxyDispatcher.json",
|
||||
"test/generated-artifacts/MixinAuthorizable.json",
|
||||
"test/generated-artifacts/MixinGasToken.json",
|
||||
"test/generated-artifacts/MultiAssetProxy.json",
|
||||
"test/generated-artifacts/Ownable.json",
|
||||
"test/generated-artifacts/StaticCallProxy.json",
|
||||
"test/generated-artifacts/TestChaiBridge.json",
|
||||
"test/generated-artifacts/TestDexForwarderBridge.json",
|
||||
"test/generated-artifacts/TestDydxBridge.json",
|
||||
"test/generated-artifacts/TestERC20Bridge.json",
|
||||
"test/generated-artifacts/TestEth2DaiBridge.json",
|
||||
"test/generated-artifacts/TestKyberBridge.json",
|
||||
"test/generated-artifacts/TestStaticCallTarget.json",
|
||||
"test/generated-artifacts/TestUniswapBridge.json",
|
||||
"test/generated-artifacts/UniswapBridge.json"
|
||||
"test/generated-artifacts/TestUniswapV2Bridge.json",
|
||||
"test/generated-artifacts/UniswapBridge.json",
|
||||
"test/generated-artifacts/UniswapV2Bridge.json"
|
||||
],
|
||||
"exclude": ["./deploy/solc/solc_bin"]
|
||||
}
|
||||
|
@@ -1,4 +1,41 @@
|
||||
[
|
||||
{
|
||||
"version": "1.1.5",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Fix broken tests.",
|
||||
"pr": 2591
|
||||
}
|
||||
],
|
||||
"timestamp": 1592969527
|
||||
},
|
||||
{
|
||||
"timestamp": 1583220306,
|
||||
"version": "1.1.4",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1582837861,
|
||||
"version": "1.1.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1582677073,
|
||||
"version": "1.1.2",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1582623685,
|
||||
"version": "1.1.1",
|
||||
|
@@ -5,6 +5,22 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v1.1.5 - _June 24, 2020_
|
||||
|
||||
* Fix broken tests. (#2591)
|
||||
|
||||
## v1.1.4 - _March 3, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.1.3 - _February 27, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.1.2 - _February 26, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.1.1 - _February 25, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -3,6 +3,7 @@
|
||||
"contractsDir": "./contracts",
|
||||
"useDockerisedSolc": false,
|
||||
"isOfflineMode": false,
|
||||
"shouldSaveStandardInput": true,
|
||||
"compilerSettings": {
|
||||
"evmVersion": "istanbul",
|
||||
"optimizer": {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-broker",
|
||||
"version": "1.1.1",
|
||||
"version": "1.1.5",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -51,20 +51,20 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^5.2.1",
|
||||
"@0x/contracts-asset-proxy": "^3.2.2",
|
||||
"@0x/contracts-erc20": "^3.1.2",
|
||||
"@0x/contracts-erc721": "^3.1.2",
|
||||
"@0x/contracts-exchange": "^3.2.2",
|
||||
"@0x/contracts-exchange-libs": "^4.3.2",
|
||||
"@0x/contracts-gen": "^2.0.8",
|
||||
"@0x/contracts-test-utils": "^5.3.0",
|
||||
"@0x/contracts-utils": "^4.4.0",
|
||||
"@0x/sol-compiler": "^4.0.8",
|
||||
"@0x/abi-gen": "^5.3.0",
|
||||
"@0x/contracts-asset-proxy": "^3.3.0",
|
||||
"@0x/contracts-erc20": "^3.2.0",
|
||||
"@0x/contracts-erc721": "^3.1.6",
|
||||
"@0x/contracts-exchange": "^3.2.6",
|
||||
"@0x/contracts-exchange-libs": "^4.3.6",
|
||||
"@0x/contracts-gen": "^2.0.9",
|
||||
"@0x/contracts-test-utils": "^5.3.3",
|
||||
"@0x/contracts-utils": "^4.5.0",
|
||||
"@0x/sol-compiler": "^4.1.0",
|
||||
"@0x/ts-doc-gen": "^0.0.22",
|
||||
"@0x/tslint-config": "^4.0.0",
|
||||
"@0x/types": "^3.1.2",
|
||||
"@0x/web3-wrapper": "^7.0.7",
|
||||
"@0x/types": "^3.1.3",
|
||||
"@0x/web3-wrapper": "^7.1.0",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/node": "*",
|
||||
@@ -84,11 +84,11 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^6.2.1",
|
||||
"@0x/order-utils": "^10.2.2",
|
||||
"@0x/typescript-typings": "^5.0.2",
|
||||
"@0x/utils": "^5.4.1",
|
||||
"ethereum-types": "^3.1.0"
|
||||
"@0x/base-contract": "^6.2.2",
|
||||
"@0x/order-utils": "^10.2.5",
|
||||
"@0x/typescript-typings": "^5.1.0",
|
||||
"@0x/utils": "^5.5.0",
|
||||
"ethereum-types": "^3.1.1"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
@@ -44,13 +44,13 @@ blockchainTests.resets('GodsUnchainedValidator unit tests', env => {
|
||||
const tokenId = getRandomInteger(0, constants.MAX_UINT256);
|
||||
await godsUnchained.setTokenProperties(tokenId, proto.plus(1), quality).awaitTransactionSuccessAsync();
|
||||
const tx = validator.checkBrokerAsset(tokenId, propertyData).callAsync();
|
||||
expect(tx).to.revertWith('PROTO_MISMATCH');
|
||||
expect(tx).to.revertWith('GodsUnchainedValidator/PROTO_MISMATCH');
|
||||
});
|
||||
it("reverts if assetData quality doesn't match proeprtyData", async () => {
|
||||
const tokenId = getRandomInteger(0, constants.MAX_UINT256);
|
||||
await godsUnchained.setTokenProperties(tokenId, proto, quality.plus(1)).awaitTransactionSuccessAsync();
|
||||
const tx = validator.checkBrokerAsset(tokenId, propertyData).callAsync();
|
||||
expect(tx).to.revertWith('QUALITY_MISMATCH');
|
||||
expect(tx).to.revertWith('GodsUnchainedValidator/QUALITY_MISMATCH');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -1,4 +1,40 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1592969527,
|
||||
"version": "3.1.6",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1583220306,
|
||||
"version": "3.1.5",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1582837861,
|
||||
"version": "3.1.4",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1582677073,
|
||||
"version": "3.1.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1582623685,
|
||||
"version": "3.1.2",
|
||||
|
@@ -5,6 +5,22 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v3.1.6 - _June 24, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.5 - _March 3, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.4 - _February 27, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.3 - _February 26, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.2 - _February 25, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -2,6 +2,7 @@
|
||||
"artifactsDir": "./test/generated-artifacts",
|
||||
"contractsDir": "./contracts",
|
||||
"useDockerisedSolc": false,
|
||||
"shouldSaveStandardInput": true,
|
||||
"compilerSettings": {
|
||||
"evmVersion": "istanbul",
|
||||
"optimizer": {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-coordinator",
|
||||
"version": "3.1.2",
|
||||
"version": "3.1.6",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -52,19 +52,19 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^5.2.1",
|
||||
"@0x/contracts-asset-proxy": "^3.2.2",
|
||||
"@0x/contracts-dev-utils": "^1.3.0",
|
||||
"@0x/contracts-erc20": "^3.1.2",
|
||||
"@0x/contracts-exchange": "^3.2.2",
|
||||
"@0x/contracts-gen": "^2.0.8",
|
||||
"@0x/contracts-test-utils": "^5.3.0",
|
||||
"@0x/dev-utils": "^3.2.1",
|
||||
"@0x/order-utils": "^10.2.2",
|
||||
"@0x/sol-compiler": "^4.0.8",
|
||||
"@0x/abi-gen": "^5.3.0",
|
||||
"@0x/contracts-asset-proxy": "^3.3.0",
|
||||
"@0x/contracts-dev-utils": "^1.3.4",
|
||||
"@0x/contracts-erc20": "^3.2.0",
|
||||
"@0x/contracts-exchange": "^3.2.6",
|
||||
"@0x/contracts-gen": "^2.0.9",
|
||||
"@0x/contracts-test-utils": "^5.3.3",
|
||||
"@0x/dev-utils": "^3.2.2",
|
||||
"@0x/order-utils": "^10.2.5",
|
||||
"@0x/sol-compiler": "^4.1.0",
|
||||
"@0x/ts-doc-gen": "^0.0.22",
|
||||
"@0x/tslint-config": "^4.0.0",
|
||||
"@0x/web3-wrapper": "^7.0.7",
|
||||
"@0x/web3-wrapper": "^7.1.0",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/node": "*",
|
||||
@@ -84,15 +84,15 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/assert": "^3.0.7",
|
||||
"@0x/base-contract": "^6.2.1",
|
||||
"@0x/contract-addresses": "^4.7.0",
|
||||
"@0x/contracts-utils": "^4.4.0",
|
||||
"@0x/json-schemas": "^5.0.7",
|
||||
"@0x/types": "^3.1.2",
|
||||
"@0x/typescript-typings": "^5.0.2",
|
||||
"@0x/utils": "^5.4.1",
|
||||
"ethereum-types": "^3.1.0",
|
||||
"@0x/assert": "^3.0.8",
|
||||
"@0x/base-contract": "^6.2.2",
|
||||
"@0x/contract-addresses": "^4.10.0",
|
||||
"@0x/contracts-utils": "^4.5.0",
|
||||
"@0x/json-schemas": "^5.0.8",
|
||||
"@0x/types": "^3.1.3",
|
||||
"@0x/typescript-typings": "^5.1.0",
|
||||
"@0x/utils": "^5.5.0",
|
||||
"ethereum-types": "^3.1.1",
|
||||
"http-status-codes": "^1.3.2"
|
||||
},
|
||||
"publishConfig": {
|
||||
|
@@ -1,4 +1,40 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1592969527,
|
||||
"version": "1.3.4",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1583220306,
|
||||
"version": "1.3.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1582837861,
|
||||
"version": "1.3.2",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1582677073,
|
||||
"version": "1.3.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "1.3.0",
|
||||
"changes": [
|
||||
|
@@ -5,6 +5,22 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v1.3.4 - _June 24, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.3.3 - _March 3, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.3.2 - _February 27, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.3.1 - _February 26, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.3.0 - _February 25, 2020_
|
||||
|
||||
* Update `DevUtils` addresses in `DeploymentConstants` (#2493)
|
||||
|
@@ -3,6 +3,7 @@
|
||||
"contractsDir": "./contracts",
|
||||
"useDockerisedSolc": false,
|
||||
"isOfflineMode": false,
|
||||
"shouldSaveStandardInput": true,
|
||||
"compilerSettings": {
|
||||
"evmVersion": "istanbul",
|
||||
"optimizer": {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-dev-utils",
|
||||
"version": "1.3.0",
|
||||
"version": "1.3.4",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -41,19 +41,19 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/dev-utils/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^5.2.1",
|
||||
"@0x/assert": "^3.0.7",
|
||||
"@0x/contracts-asset-proxy": "^3.2.2",
|
||||
"@0x/contracts-erc20": "^3.1.2",
|
||||
"@0x/contracts-gen": "^2.0.8",
|
||||
"@0x/contracts-test-utils": "^5.3.0",
|
||||
"@0x/sol-compiler": "^4.0.8",
|
||||
"@0x/abi-gen": "^5.3.0",
|
||||
"@0x/assert": "^3.0.8",
|
||||
"@0x/contracts-asset-proxy": "^3.3.0",
|
||||
"@0x/contracts-erc20": "^3.2.0",
|
||||
"@0x/contracts-gen": "^2.0.9",
|
||||
"@0x/contracts-test-utils": "^5.3.3",
|
||||
"@0x/sol-compiler": "^4.1.0",
|
||||
"@0x/ts-doc-gen": "^0.0.22",
|
||||
"@0x/tslint-config": "^4.0.0",
|
||||
"@0x/types": "^3.1.2",
|
||||
"@0x/utils": "^5.4.1",
|
||||
"@0x/types": "^3.1.3",
|
||||
"@0x/utils": "^5.5.0",
|
||||
"@types/node": "*",
|
||||
"ethereum-types": "^3.1.0",
|
||||
"ethereum-types": "^3.1.1",
|
||||
"ethers": "~4.0.4",
|
||||
"npm-run-all": "^4.1.2",
|
||||
"shx": "^0.2.2",
|
||||
@@ -64,7 +64,7 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^6.2.1"
|
||||
"@0x/base-contract": "^6.2.2"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
@@ -1,4 +1,40 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1592969527,
|
||||
"version": "2.1.6",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1583220306,
|
||||
"version": "2.1.5",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1582837861,
|
||||
"version": "2.1.4",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1582677073,
|
||||
"version": "2.1.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1582623685,
|
||||
"version": "2.1.2",
|
||||
|
@@ -5,6 +5,22 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v2.1.6 - _June 24, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.1.5 - _March 3, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.1.4 - _February 27, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.1.3 - _February 26, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.1.2 - _February 25, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -3,6 +3,7 @@
|
||||
"contractsDir": "./contracts",
|
||||
"useDockerisedSolc": false,
|
||||
"isOfflineMode": false,
|
||||
"shouldSaveStandardInput": true,
|
||||
"compilerSettings": {
|
||||
"evmVersion": "istanbul",
|
||||
"optimizer": {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-erc1155",
|
||||
"version": "2.1.2",
|
||||
"version": "2.1.6",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -52,15 +52,15 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^5.2.1",
|
||||
"@0x/contracts-gen": "^2.0.8",
|
||||
"@0x/contracts-utils": "^4.4.0",
|
||||
"@0x/dev-utils": "^3.2.1",
|
||||
"@0x/sol-compiler": "^4.0.8",
|
||||
"@0x/abi-gen": "^5.3.0",
|
||||
"@0x/contracts-gen": "^2.0.9",
|
||||
"@0x/contracts-utils": "^4.5.0",
|
||||
"@0x/dev-utils": "^3.2.2",
|
||||
"@0x/sol-compiler": "^4.1.0",
|
||||
"@0x/ts-doc-gen": "^0.0.22",
|
||||
"@0x/tslint-config": "^4.0.0",
|
||||
"@0x/types": "^3.1.2",
|
||||
"@0x/typescript-typings": "^5.0.2",
|
||||
"@0x/types": "^3.1.3",
|
||||
"@0x/typescript-typings": "^5.1.0",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/node": "*",
|
||||
@@ -68,7 +68,7 @@
|
||||
"chai-as-promised": "^7.1.0",
|
||||
"chai-bignumber": "^3.0.0",
|
||||
"dirty-chai": "^2.0.1",
|
||||
"ethereum-types": "^3.1.0",
|
||||
"ethereum-types": "^3.1.1",
|
||||
"make-promises-safe": "^1.1.0",
|
||||
"mocha": "^6.2.0",
|
||||
"npm-run-all": "^4.1.2",
|
||||
@@ -80,10 +80,10 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^6.2.1",
|
||||
"@0x/contracts-test-utils": "^5.3.0",
|
||||
"@0x/utils": "^5.4.1",
|
||||
"@0x/web3-wrapper": "^7.0.7",
|
||||
"@0x/base-contract": "^6.2.2",
|
||||
"@0x/contracts-test-utils": "^5.3.3",
|
||||
"@0x/utils": "^5.5.0",
|
||||
"@0x/web3-wrapper": "^7.1.0",
|
||||
"lodash": "^4.17.11"
|
||||
},
|
||||
"publishConfig": {
|
||||
|
@@ -1,4 +1,66 @@
|
||||
[
|
||||
{
|
||||
"version": "1.6.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Pass in `DevUtils` address as a constructor parameter",
|
||||
"pr": 2531
|
||||
},
|
||||
{
|
||||
"note": "Sample `Curve` for buy amounts",
|
||||
"pr": 2551
|
||||
},
|
||||
{
|
||||
"note": "Added `sampleBuysFromKyberNetwork`",
|
||||
"pr": 2551
|
||||
},
|
||||
{
|
||||
"note": "Use `searchBestRate` in Kyber samples. Return 0 when Uniswap/Eth2Dai reserve",
|
||||
"pr": 2575
|
||||
},
|
||||
{
|
||||
"note": "Add UniswapV2",
|
||||
"pr": 2595
|
||||
},
|
||||
{
|
||||
"note": "Sample from MultiBridge",
|
||||
"pr": 2593
|
||||
}
|
||||
],
|
||||
"timestamp": 1592969527
|
||||
},
|
||||
{
|
||||
"timestamp": 1583220306,
|
||||
"version": "1.5.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "1.5.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add generic liquidity provider sampling",
|
||||
"pr": 2487
|
||||
},
|
||||
{
|
||||
"note": "Use liquidity provider registry in sampler",
|
||||
"pr": 2499
|
||||
}
|
||||
],
|
||||
"timestamp": 1582837861
|
||||
},
|
||||
{
|
||||
"timestamp": 1582677073,
|
||||
"version": "1.4.2",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1582623685,
|
||||
"version": "1.4.1",
|
||||
|
@@ -5,6 +5,28 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v1.6.0 - _June 24, 2020_
|
||||
|
||||
* Pass in `DevUtils` address as a constructor parameter (#2531)
|
||||
* Sample `Curve` for buy amounts (#2551)
|
||||
* Added `sampleBuysFromKyberNetwork` (#2551)
|
||||
* Use `searchBestRate` in Kyber samples. Return 0 when Uniswap/Eth2Dai reserve (#2575)
|
||||
* Add UniswapV2 (#2595)
|
||||
* Sample from MultiBridge (#2593)
|
||||
|
||||
## v1.5.1 - _March 3, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.5.0 - _February 27, 2020_
|
||||
|
||||
* Add generic liquidity provider sampling (#2487)
|
||||
* Use liquidity provider registry in sampler (#2499)
|
||||
|
||||
## v1.4.2 - _February 26, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.1 - _February 25, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -3,6 +3,7 @@
|
||||
"contractsDir": "./contracts",
|
||||
"useDockerisedSolc": false,
|
||||
"isOfflineMode": false,
|
||||
"shouldSaveStandardInput": true,
|
||||
"compilerSettings": {
|
||||
"evmVersion": "istanbul",
|
||||
"optimizer": {
|
||||
|
@@ -0,0 +1,38 @@
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
|
||||
contract DummyLiquidityProvider
|
||||
{
|
||||
/// @dev Quotes the amount of `makerToken` that would be obtained by
|
||||
/// selling `sellAmount` of `takerToken`.
|
||||
/// @param sellAmount Amount of `takerToken` to sell.
|
||||
/// @return makerTokenAmount Amount of `makerToken` that would be obtained.
|
||||
function getSellQuote(
|
||||
address, /* takerToken */
|
||||
address, /* makerToken */
|
||||
uint256 sellAmount
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256 makerTokenAmount)
|
||||
{
|
||||
makerTokenAmount = sellAmount - 1;
|
||||
}
|
||||
|
||||
/// @dev Quotes the amount of `takerToken` that would need to be sold in
|
||||
/// order to obtain `buyAmount` of `makerToken`.
|
||||
/// @param buyAmount Amount of `makerToken` to buy.
|
||||
/// @return takerTokenAmount Amount of `takerToken` that would need to be sold.
|
||||
function getBuyQuote(
|
||||
address, /* takerToken */
|
||||
address, /* makerToken */
|
||||
uint256 buyAmount
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256 takerTokenAmount)
|
||||
{
|
||||
takerTokenAmount = buyAmount + 1;
|
||||
}
|
||||
}
|
@@ -0,0 +1,44 @@
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
|
||||
contract DummyLiquidityProviderRegistry
|
||||
{
|
||||
address private constant NULL_ADDRESS = address(0x0);
|
||||
|
||||
mapping (address => mapping (address => address)) internal _gAddressBook;
|
||||
|
||||
/// @dev Sets address of pool for a market given market (xAsset, yAsset).
|
||||
/// @param xToken First asset managed by pool.
|
||||
/// @param yToken Second asset managed by pool.
|
||||
/// @param poolAddress Address of pool.
|
||||
function setLiquidityProviderForMarket(
|
||||
address xToken,
|
||||
address yToken,
|
||||
address poolAddress
|
||||
)
|
||||
external
|
||||
{
|
||||
_gAddressBook[xToken][yToken] = poolAddress;
|
||||
_gAddressBook[yToken][xToken] = poolAddress;
|
||||
}
|
||||
|
||||
/// @dev Returns the address of pool for a market given market (xAsset, yAsset), or reverts if pool does not exist.
|
||||
/// @param xToken First asset managed by pool.
|
||||
/// @param yToken Second asset managed by pool.
|
||||
/// @return Address of pool.
|
||||
function getLiquidityProviderForMarket(
|
||||
address xToken,
|
||||
address yToken
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (address poolAddress)
|
||||
{
|
||||
poolAddress = _gAddressBook[xToken][yToken];
|
||||
require(
|
||||
poolAddress != NULL_ADDRESS,
|
||||
"Registry/MARKET_PAIR_NOT_SET"
|
||||
);
|
||||
}
|
||||
}
|
@@ -24,12 +24,18 @@ import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
|
||||
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||
import "./IDevUtils.sol";
|
||||
import "./IERC20BridgeSampler.sol";
|
||||
import "./IEth2Dai.sol";
|
||||
import "./IKyberNetwork.sol";
|
||||
import "./IKyberNetworkProxy.sol";
|
||||
import "./IUniswapExchangeQuotes.sol";
|
||||
import "./ICurve.sol";
|
||||
import "./ILiquidityProvider.sol";
|
||||
import "./ILiquidityProviderRegistry.sol";
|
||||
import "./IUniswapV2Router01.sol";
|
||||
import "./IMultiBridge.sol";
|
||||
|
||||
|
||||
contract ERC20BridgeSampler is
|
||||
@@ -42,11 +48,25 @@ contract ERC20BridgeSampler is
|
||||
uint256 constant internal KYBER_CALL_GAS = 1500e3; // 1.5m
|
||||
/// @dev Gas limit for Uniswap calls.
|
||||
uint256 constant internal UNISWAP_CALL_GAS = 150e3; // 150k
|
||||
/// @dev Gas limit for UniswapV2 calls.
|
||||
uint256 constant internal UNISWAPV2_CALL_GAS = 150e3; // 150k
|
||||
/// @dev Base gas limit for Eth2Dai calls.
|
||||
uint256 constant internal ETH2DAI_CALL_GAS = 1000e3; // 1m
|
||||
/// @dev Base gas limit for Curve calls. Some Curves have multiple tokens
|
||||
/// So a reasonable ceil is 150k per token. Biggest Curve has 4 tokens.
|
||||
uint256 constant internal CURVE_CALL_GAS = 600e3; // 600k
|
||||
/// @dev Default gas limit for liquidity provider calls.
|
||||
uint256 constant internal DEFAULT_CALL_GAS = 400e3; // 400k
|
||||
/// @dev The Kyber Uniswap Reserve address
|
||||
address constant internal KYBER_UNIWAP_RESERVE = 0x31E085Afd48a1d6e51Cc193153d625e8f0514C7F;
|
||||
/// @dev The Kyber Eth2Dai Reserve address
|
||||
address constant internal KYBER_ETH2DAI_RESERVE = 0x1E158c0e93c30d24e918Ef83d1e0bE23595C3c0f;
|
||||
|
||||
address private _devUtilsAddress;
|
||||
|
||||
constructor(address devUtilsAddress) public {
|
||||
_devUtilsAddress = devUtilsAddress;
|
||||
}
|
||||
|
||||
/// @dev Call multiple public functions on this contract in a single transaction.
|
||||
/// @param callDatas ABI-encoded call data for each function call.
|
||||
@@ -82,6 +102,7 @@ contract ERC20BridgeSampler is
|
||||
returns (uint256[] memory orderFillableTakerAssetAmounts)
|
||||
{
|
||||
orderFillableTakerAssetAmounts = new uint256[](orders.length);
|
||||
address devUtilsAddress = _devUtilsAddress;
|
||||
for (uint256 i = 0; i != orders.length; i++) {
|
||||
// Ignore orders with no signature or empty maker/taker amounts.
|
||||
if (orderSignatures[i].length == 0 ||
|
||||
@@ -92,11 +113,11 @@ contract ERC20BridgeSampler is
|
||||
}
|
||||
// solhint-disable indent
|
||||
(bool didSucceed, bytes memory resultData) =
|
||||
_getDevUtilsAddress()
|
||||
devUtilsAddress
|
||||
.staticcall
|
||||
.gas(DEV_UTILS_CALL_GAS)
|
||||
(abi.encodeWithSelector(
|
||||
IDevUtils(_getDevUtilsAddress()).getOrderRelevantState.selector,
|
||||
IDevUtils(devUtilsAddress).getOrderRelevantState.selector,
|
||||
orders[i],
|
||||
orderSignatures[i]
|
||||
));
|
||||
@@ -171,36 +192,63 @@ contract ERC20BridgeSampler is
|
||||
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);
|
||||
address wethAddress = _getWethAddress();
|
||||
uint256 value;
|
||||
address reserve;
|
||||
for (uint256 i = 0; i < numSamples; i++) {
|
||||
(bool didSucceed, bytes memory resultData) =
|
||||
_getKyberNetworkProxyAddress().staticcall.gas(KYBER_CALL_GAS)(
|
||||
abi.encodeWithSelector(
|
||||
IKyberNetwork(0).getExpectedRate.selector,
|
||||
_takerToken,
|
||||
_makerToken,
|
||||
takerTokenAmounts[i]
|
||||
));
|
||||
uint256 rate = 0;
|
||||
if (didSucceed) {
|
||||
rate = abi.decode(resultData, (uint256));
|
||||
if (takerToken == wethAddress || makerToken == wethAddress) {
|
||||
// Direct ETH based trade
|
||||
(value, reserve) = _sampleSellFromKyberNetwork(takerToken, makerToken, takerTokenAmounts[i]);
|
||||
// If this fills on an on-chain reserve we remove it as that can introduce collisions
|
||||
if (reserve == KYBER_UNIWAP_RESERVE || reserve == KYBER_ETH2DAI_RESERVE) {
|
||||
value = 0;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
// Hop to ETH
|
||||
(value, reserve) = _sampleSellFromKyberNetwork(takerToken, wethAddress, takerTokenAmounts[i]);
|
||||
if (value != 0) {
|
||||
address otherReserve;
|
||||
(value, otherReserve) = _sampleSellFromKyberNetwork(wethAddress, makerToken, value);
|
||||
// If this fills on Eth2Dai it is ok as we queried a different market
|
||||
// If this fills on Uniswap on both legs then this is a hard collision
|
||||
if (reserve == KYBER_UNIWAP_RESERVE && reserve == otherReserve) {
|
||||
value = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
makerTokenAmounts[i] =
|
||||
rate *
|
||||
takerTokenAmounts[i] *
|
||||
10 ** makerTokenDecimals /
|
||||
10 ** takerTokenDecimals /
|
||||
10 ** 18;
|
||||
makerTokenAmounts[i] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Sample buy quotes from Kyber.
|
||||
/// @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.
|
||||
/// @param opts `FakeBuyOptions` specifying target slippage and max iterations.
|
||||
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||
/// amount.
|
||||
function sampleBuysFromKyberNetwork(
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256[] memory makerTokenAmounts,
|
||||
FakeBuyOptions memory opts
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (uint256[] memory takerTokenAmounts)
|
||||
{
|
||||
return _sampleApproximateBuysFromSource(
|
||||
takerToken,
|
||||
makerToken,
|
||||
makerTokenAmounts,
|
||||
opts,
|
||||
this.sampleSellsFromKyberNetwork.selector,
|
||||
address(0) // PLP registry address
|
||||
);
|
||||
}
|
||||
|
||||
/// @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).
|
||||
@@ -238,6 +286,31 @@ contract ERC20BridgeSampler is
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Sample sell quotes from Eth2Dai/Oasis using a hop to an intermediate token.
|
||||
/// I.e WBTC/DAI via ETH or WBTC/ETH via DAI
|
||||
/// @param takerToken Address of the taker token (what to sell).
|
||||
/// @param makerToken Address of the maker token (what to buy).
|
||||
/// @param intermediateToken Address of the token to hop to.
|
||||
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
||||
/// amount.
|
||||
function sampleSellsFromEth2DaiHop(
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
address intermediateToken,
|
||||
uint256[] memory takerTokenAmounts
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (uint256[] memory makerTokenAmounts)
|
||||
{
|
||||
if (makerToken == intermediateToken || takerToken == intermediateToken) {
|
||||
return makerTokenAmounts;
|
||||
}
|
||||
uint256[] memory intermediateAmounts = sampleSellsFromEth2Dai(takerToken, intermediateToken, takerTokenAmounts);
|
||||
makerTokenAmounts = sampleSellsFromEth2Dai(intermediateToken, makerToken, intermediateAmounts);
|
||||
}
|
||||
|
||||
/// @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).
|
||||
@@ -431,6 +504,269 @@ contract ERC20BridgeSampler is
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Sample buy quotes from Curve.
|
||||
/// @param curveAddress Address of the Curve contract.
|
||||
/// @param fromTokenIdx Index of the taker token (what to sell).
|
||||
/// @param toTokenIdx Index of the maker token (what to buy).
|
||||
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
||||
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||
/// amount.
|
||||
function sampleBuysFromCurve(
|
||||
address curveAddress,
|
||||
int128 fromTokenIdx,
|
||||
int128 toTokenIdx,
|
||||
uint256[] memory makerTokenAmounts
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (uint256[] memory takerTokenAmounts)
|
||||
{
|
||||
uint256 numSamples = makerTokenAmounts.length;
|
||||
takerTokenAmounts = new uint256[](numSamples);
|
||||
for (uint256 i = 0; i < numSamples; i++) {
|
||||
(bool didSucceed, bytes memory resultData) =
|
||||
curveAddress.staticcall.gas(CURVE_CALL_GAS)(
|
||||
abi.encodeWithSelector(
|
||||
ICurve(0).get_dx_underlying.selector,
|
||||
fromTokenIdx,
|
||||
toTokenIdx,
|
||||
makerTokenAmounts[i]
|
||||
));
|
||||
uint256 sellAmount = 0;
|
||||
if (didSucceed) {
|
||||
sellAmount = abi.decode(resultData, (uint256));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
takerTokenAmounts[i] = sellAmount;
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Sample sell quotes from an arbitrary on-chain liquidity provider.
|
||||
/// @param registryAddress Address of the liquidity provider registry contract.
|
||||
/// @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 sampleSellsFromLiquidityProviderRegistry(
|
||||
address registryAddress,
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256[] memory takerTokenAmounts
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (uint256[] memory makerTokenAmounts)
|
||||
{
|
||||
// Initialize array of maker token amounts.
|
||||
uint256 numSamples = takerTokenAmounts.length;
|
||||
makerTokenAmounts = new uint256[](numSamples);
|
||||
|
||||
// Query registry for provider address.
|
||||
address providerAddress = getLiquidityProviderFromRegistry(
|
||||
registryAddress,
|
||||
takerToken,
|
||||
makerToken
|
||||
);
|
||||
// If provider doesn't exist, return all zeros.
|
||||
if (providerAddress == address(0)) {
|
||||
return makerTokenAmounts;
|
||||
}
|
||||
|
||||
for (uint256 i = 0; i < numSamples; i++) {
|
||||
(bool didSucceed, bytes memory resultData) =
|
||||
providerAddress.staticcall.gas(DEFAULT_CALL_GAS)(
|
||||
abi.encodeWithSelector(
|
||||
ILiquidityProvider(0).getSellQuote.selector,
|
||||
takerToken,
|
||||
makerToken,
|
||||
takerTokenAmounts[i]
|
||||
));
|
||||
uint256 buyAmount = 0;
|
||||
if (didSucceed) {
|
||||
buyAmount = abi.decode(resultData, (uint256));
|
||||
} else {
|
||||
// Exit early if the amount is too high for the liquidity provider to serve
|
||||
break;
|
||||
}
|
||||
makerTokenAmounts[i] = buyAmount;
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Sample sell quotes from MultiBridge.
|
||||
/// @param multibridge Address of the MultiBridge contract.
|
||||
/// @param takerToken Address of the taker token (what to sell).
|
||||
/// @param intermediateToken The address of the intermediate token to
|
||||
/// use in an indirect route.
|
||||
/// @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 sampleSellsFromMultiBridge(
|
||||
address multibridge,
|
||||
address takerToken,
|
||||
address intermediateToken,
|
||||
address makerToken,
|
||||
uint256[] memory takerTokenAmounts
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (uint256[] memory makerTokenAmounts)
|
||||
{
|
||||
// Initialize array of maker token amounts.
|
||||
uint256 numSamples = takerTokenAmounts.length;
|
||||
makerTokenAmounts = new uint256[](numSamples);
|
||||
|
||||
// If no address provided, return all zeros.
|
||||
if (multibridge == address(0)) {
|
||||
return makerTokenAmounts;
|
||||
}
|
||||
|
||||
for (uint256 i = 0; i < numSamples; i++) {
|
||||
(bool didSucceed, bytes memory resultData) =
|
||||
multibridge.staticcall.gas(DEFAULT_CALL_GAS)(
|
||||
abi.encodeWithSelector(
|
||||
IMultiBridge(0).getSellQuote.selector,
|
||||
takerToken,
|
||||
intermediateToken,
|
||||
makerToken,
|
||||
takerTokenAmounts[i]
|
||||
));
|
||||
uint256 buyAmount = 0;
|
||||
if (didSucceed) {
|
||||
buyAmount = abi.decode(resultData, (uint256));
|
||||
} else {
|
||||
// Exit early if the amount is too high for the liquidity provider to serve
|
||||
break;
|
||||
}
|
||||
makerTokenAmounts[i] = buyAmount;
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Sample buy quotes from an arbitrary on-chain liquidity provider.
|
||||
/// @param registryAddress Address of the liquidity provider registry contract.
|
||||
/// @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.
|
||||
/// @param opts `FakeBuyOptions` specifying target slippage and max iterations.
|
||||
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||
/// amount.
|
||||
function sampleBuysFromLiquidityProviderRegistry(
|
||||
address registryAddress,
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256[] memory makerTokenAmounts,
|
||||
FakeBuyOptions memory opts
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (uint256[] memory takerTokenAmounts)
|
||||
{
|
||||
return _sampleApproximateBuysFromSource(
|
||||
takerToken,
|
||||
makerToken,
|
||||
makerTokenAmounts,
|
||||
opts,
|
||||
this.sampleSellsFromLiquidityProviderRegistry.selector,
|
||||
registryAddress
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Returns the address of a liquidity provider for the given market
|
||||
/// (takerToken, makerToken), from a registry of liquidity providers.
|
||||
/// Returns address(0) if no such provider exists in the registry.
|
||||
/// @param takerToken Taker asset managed by liquidity provider.
|
||||
/// @param makerToken Maker asset managed by liquidity provider.
|
||||
/// @return providerAddress Address of the liquidity provider.
|
||||
function getLiquidityProviderFromRegistry(
|
||||
address registryAddress,
|
||||
address takerToken,
|
||||
address makerToken
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (address providerAddress)
|
||||
{
|
||||
bytes memory callData = abi.encodeWithSelector(
|
||||
ILiquidityProviderRegistry(0).getLiquidityProviderForMarket.selector,
|
||||
takerToken,
|
||||
makerToken
|
||||
);
|
||||
(bool didSucceed, bytes memory returnData) = registryAddress.staticcall(callData);
|
||||
if (didSucceed && returnData.length == 32) {
|
||||
return LibBytes.readAddress(returnData, 12);
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Sample sell quotes from UniswapV2.
|
||||
/// @param path Token route. Should be takerToken -> makerToken
|
||||
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
||||
/// amount.
|
||||
function sampleSellsFromUniswapV2(
|
||||
address[] memory path,
|
||||
uint256[] memory takerTokenAmounts
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (uint256[] memory makerTokenAmounts)
|
||||
{
|
||||
uint256 numSamples = takerTokenAmounts.length;
|
||||
makerTokenAmounts = new uint256[](numSamples);
|
||||
for (uint256 i = 0; i < numSamples; i++) {
|
||||
(bool didSucceed, bytes memory resultData) =
|
||||
_getUniswapV2Router01Address().staticcall.gas(UNISWAPV2_CALL_GAS)(
|
||||
abi.encodeWithSelector(
|
||||
IUniswapV2Router01(0).getAmountsOut.selector,
|
||||
takerTokenAmounts[i],
|
||||
path
|
||||
));
|
||||
uint256 buyAmount = 0;
|
||||
if (didSucceed) {
|
||||
// solhint-disable-next-line indent
|
||||
buyAmount = abi.decode(resultData, (uint256[]))[path.length - 1];
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
makerTokenAmounts[i] = buyAmount;
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Sample buy quotes from UniswapV2.
|
||||
/// @param path Token route. Should be takerToken -> makerToken.
|
||||
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
||||
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||
/// amount.
|
||||
function sampleBuysFromUniswapV2(
|
||||
address[] memory path,
|
||||
uint256[] memory makerTokenAmounts
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (uint256[] memory takerTokenAmounts)
|
||||
{
|
||||
uint256 numSamples = makerTokenAmounts.length;
|
||||
takerTokenAmounts = new uint256[](numSamples);
|
||||
for (uint256 i = 0; i < numSamples; i++) {
|
||||
(bool didSucceed, bytes memory resultData) =
|
||||
_getUniswapV2Router01Address().staticcall.gas(UNISWAPV2_CALL_GAS)(
|
||||
abi.encodeWithSelector(
|
||||
IUniswapV2Router01(0).getAmountsIn.selector,
|
||||
makerTokenAmounts[i],
|
||||
path
|
||||
));
|
||||
uint256 sellAmount = 0;
|
||||
if (didSucceed) {
|
||||
// solhint-disable-next-line indent
|
||||
sellAmount = abi.decode(resultData, (uint256[]))[0];
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
takerTokenAmounts[i] = sellAmount;
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Overridable way to get token decimals.
|
||||
/// @param tokenAddress Address of the token.
|
||||
/// @return decimals The decimal places for the token.
|
||||
@@ -496,4 +832,178 @@ contract ERC20BridgeSampler is
|
||||
{
|
||||
require(makerToken != takerToken, "ERC20BridgeSampler/INVALID_TOKEN_PAIR");
|
||||
}
|
||||
|
||||
function _sampleSellForApproximateBuy(
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256 takerTokenAmount,
|
||||
bytes4 selector,
|
||||
address plpRegistryAddress
|
||||
)
|
||||
private
|
||||
view
|
||||
returns (uint256 makerTokenAmount)
|
||||
{
|
||||
bytes memory callData;
|
||||
uint256[] memory tmpTakerAmounts = new uint256[](1);
|
||||
tmpTakerAmounts[0] = takerTokenAmount;
|
||||
if (selector == this.sampleSellsFromKyberNetwork.selector) {
|
||||
callData = abi.encodeWithSelector(
|
||||
this.sampleSellsFromKyberNetwork.selector,
|
||||
takerToken,
|
||||
makerToken,
|
||||
tmpTakerAmounts
|
||||
);
|
||||
} else {
|
||||
callData = abi.encodeWithSelector(
|
||||
this.sampleSellsFromLiquidityProviderRegistry.selector,
|
||||
plpRegistryAddress,
|
||||
takerToken,
|
||||
makerToken,
|
||||
tmpTakerAmounts
|
||||
);
|
||||
}
|
||||
(bool success, bytes memory resultData) = address(this).staticcall(callData);
|
||||
if (!success) {
|
||||
return 0;
|
||||
}
|
||||
// solhint-disable indent
|
||||
makerTokenAmount = abi.decode(resultData, (uint256[]))[0];
|
||||
}
|
||||
|
||||
function _sampleApproximateBuysFromSource(
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256[] memory makerTokenAmounts,
|
||||
FakeBuyOptions memory opts,
|
||||
bytes4 selector,
|
||||
address plpRegistryAddress
|
||||
)
|
||||
private
|
||||
view
|
||||
returns (uint256[] memory takerTokenAmounts)
|
||||
{
|
||||
_assertValidPair(makerToken, takerToken);
|
||||
if (makerTokenAmounts.length == 0) {
|
||||
return takerTokenAmounts;
|
||||
}
|
||||
uint256 sellAmount;
|
||||
uint256 buyAmount;
|
||||
uint256 slippageFromTarget;
|
||||
takerTokenAmounts = new uint256[](makerTokenAmounts.length);
|
||||
sellAmount = _sampleSellForApproximateBuy(
|
||||
makerToken,
|
||||
takerToken,
|
||||
makerTokenAmounts[0],
|
||||
selector,
|
||||
plpRegistryAddress
|
||||
);
|
||||
|
||||
if (sellAmount == 0) {
|
||||
return takerTokenAmounts;
|
||||
}
|
||||
|
||||
buyAmount = _sampleSellForApproximateBuy(
|
||||
takerToken,
|
||||
makerToken,
|
||||
sellAmount,
|
||||
selector,
|
||||
plpRegistryAddress
|
||||
);
|
||||
if (buyAmount == 0) {
|
||||
return takerTokenAmounts;
|
||||
}
|
||||
|
||||
for (uint256 i = 0; i < makerTokenAmounts.length; i++) {
|
||||
for (uint256 iter = 0; iter < opts.maxIterations; iter++) {
|
||||
// adjustedSellAmount = previousSellAmount * (target/actual) * JUMP_MULTIPLIER
|
||||
sellAmount = LibMath.getPartialAmountCeil(
|
||||
makerTokenAmounts[i],
|
||||
buyAmount,
|
||||
sellAmount
|
||||
);
|
||||
sellAmount = LibMath.getPartialAmountCeil(
|
||||
(10000 + opts.targetSlippageBps),
|
||||
10000,
|
||||
sellAmount
|
||||
);
|
||||
uint256 _buyAmount = _sampleSellForApproximateBuy(
|
||||
takerToken,
|
||||
makerToken,
|
||||
sellAmount,
|
||||
selector,
|
||||
plpRegistryAddress
|
||||
);
|
||||
if (_buyAmount == 0) {
|
||||
break;
|
||||
}
|
||||
// We re-use buyAmount next iteration, only assign if it is
|
||||
// non zero
|
||||
buyAmount = _buyAmount;
|
||||
// If we've reached our goal, exit early
|
||||
if (buyAmount >= makerTokenAmounts[i]) {
|
||||
uint256 slippageFromTarget = (buyAmount - makerTokenAmounts[i]) * 10000 /
|
||||
makerTokenAmounts[i];
|
||||
if (slippageFromTarget <= opts.targetSlippageBps) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// We do our best to close in on the requested amount, but we can either over buy or under buy and exit
|
||||
// if we hit a max iteration limit
|
||||
// We scale the sell amount to get the approximate target
|
||||
takerTokenAmounts[i] = LibMath.getPartialAmountCeil(
|
||||
makerTokenAmounts[i],
|
||||
buyAmount,
|
||||
sellAmount
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function _sampleSellFromKyberNetwork(
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256 takerTokenAmount
|
||||
)
|
||||
private
|
||||
view
|
||||
returns (uint256 makerTokenAmount, address reserve)
|
||||
{
|
||||
address _takerToken = takerToken == _getWethAddress() ? KYBER_ETH_ADDRESS : takerToken;
|
||||
address _makerToken = makerToken == _getWethAddress() ? KYBER_ETH_ADDRESS : makerToken;
|
||||
uint256 takerTokenDecimals = _getTokenDecimals(takerToken);
|
||||
uint256 makerTokenDecimals = _getTokenDecimals(makerToken);
|
||||
(bool didSucceed, bytes memory resultData) = _getKyberNetworkProxyAddress().staticcall.gas(DEFAULT_CALL_GAS)(
|
||||
abi.encodeWithSelector(
|
||||
IKyberNetworkProxy(0).kyberNetworkContract.selector
|
||||
));
|
||||
if (!didSucceed) {
|
||||
return (0, address(0));
|
||||
}
|
||||
address kyberNetworkContract = abi.decode(resultData, (address));
|
||||
(didSucceed, resultData) =
|
||||
kyberNetworkContract.staticcall.gas(KYBER_CALL_GAS)(
|
||||
abi.encodeWithSelector(
|
||||
IKyberNetwork(0).searchBestRate.selector,
|
||||
_takerToken,
|
||||
_makerToken,
|
||||
takerTokenAmount,
|
||||
false // usePermissionless
|
||||
));
|
||||
uint256 rate = 0;
|
||||
address reserve;
|
||||
if (didSucceed) {
|
||||
(reserve, rate) = abi.decode(resultData, (address, uint256));
|
||||
} else {
|
||||
return (0, address(0));
|
||||
}
|
||||
makerTokenAmount =
|
||||
rate *
|
||||
takerTokenAmount *
|
||||
10 ** makerTokenDecimals /
|
||||
10 ** takerTokenDecimals /
|
||||
10 ** 18;
|
||||
|
||||
return (makerTokenAmount, reserve);
|
||||
}
|
||||
}
|
||||
|
@@ -24,6 +24,11 @@ import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||
|
||||
interface IERC20BridgeSampler {
|
||||
|
||||
struct FakeBuyOptions {
|
||||
uint256 targetSlippageBps;
|
||||
uint256 maxIterations;
|
||||
}
|
||||
|
||||
/// @dev Call multiple public functions on this contract in a single transaction.
|
||||
/// @param callDatas ABI-encoded call data for each function call.
|
||||
/// @return callResults ABI-encoded results data for each call.
|
||||
@@ -73,6 +78,23 @@ interface IERC20BridgeSampler {
|
||||
view
|
||||
returns (uint256[] memory makerTokenAmounts);
|
||||
|
||||
/// @dev Sample buy quotes from Kyber.
|
||||
/// @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.
|
||||
/// @param opts `FakeBuyOptions` specifying target slippage and max iterations.
|
||||
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||
/// amount.
|
||||
function sampleBuysFromKyberNetwork(
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256[] calldata makerTokenAmounts,
|
||||
FakeBuyOptions calldata opts
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256[] memory takerTokenAmounts);
|
||||
|
||||
/// @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).
|
||||
@@ -106,7 +128,7 @@ interface IERC20BridgeSampler {
|
||||
/// @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.
|
||||
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
||||
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||
/// amount.
|
||||
function sampleBuysFromUniswap(
|
||||
@@ -121,7 +143,7 @@ interface IERC20BridgeSampler {
|
||||
/// @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.
|
||||
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
||||
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||
/// amount.
|
||||
function sampleBuysFromEth2Dai(
|
||||
@@ -149,4 +171,119 @@ interface IERC20BridgeSampler {
|
||||
external
|
||||
view
|
||||
returns (uint256[] memory makerTokenAmounts);
|
||||
|
||||
/// @dev Sample buy quotes from Curve.
|
||||
/// @param curveAddress Address of the Curve contract.
|
||||
/// @param fromTokenIdx Index of the taker token (what to sell).
|
||||
/// @param toTokenIdx Index of the maker token (what to buy).
|
||||
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
||||
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||
/// amount.
|
||||
function sampleBuysFromCurve(
|
||||
address curveAddress,
|
||||
int128 fromTokenIdx,
|
||||
int128 toTokenIdx,
|
||||
uint256[] calldata makerTokenAmounts
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256[] memory takerTokenAmounts);
|
||||
|
||||
/// @dev Sample sell quotes from an arbitrary on-chain liquidity provider.
|
||||
/// @param registryAddress Address of the liquidity provider registry contract.
|
||||
/// @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 sampleSellsFromLiquidityProviderRegistry(
|
||||
address registryAddress,
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256[] calldata takerTokenAmounts
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256[] memory makerTokenAmounts);
|
||||
|
||||
/// @dev Sample sell quotes from MultiBridge.
|
||||
/// @param multibridge Address of the MultiBridge contract.
|
||||
/// @param takerToken Address of the taker token (what to sell).
|
||||
/// @param intermediateToken The address of the intermediate token to
|
||||
/// use in an indirect route.
|
||||
/// @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 sampleSellsFromMultiBridge(
|
||||
address multibridge,
|
||||
address takerToken,
|
||||
address intermediateToken,
|
||||
address makerToken,
|
||||
uint256[] calldata takerTokenAmounts
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256[] memory makerTokenAmounts);
|
||||
|
||||
/// @dev Sample buy quotes from an arbitrary on-chain liquidity provider.
|
||||
/// @param registryAddress Address of the liquidity provider registry contract.
|
||||
/// @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.
|
||||
/// @param opts `FakeBuyOptions` specifying target slippage and max iterations.
|
||||
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||
/// amount.
|
||||
function sampleBuysFromLiquidityProviderRegistry(
|
||||
address registryAddress,
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256[] calldata makerTokenAmounts,
|
||||
FakeBuyOptions calldata opts
|
||||
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256[] memory takerTokenAmounts);
|
||||
|
||||
/// @dev Returns the address of a liquidity provider for the given market
|
||||
/// (takerToken, makerToken), from a registry of liquidity providers.
|
||||
/// Returns address(0) if no such provider exists in the registry.
|
||||
/// @param takerToken Taker asset managed by liquidity provider.
|
||||
/// @param makerToken Maker asset managed by liquidity provider.
|
||||
/// @return providerAddress Address of the liquidity provider.
|
||||
function getLiquidityProviderFromRegistry(
|
||||
address registryAddress,
|
||||
address takerToken,
|
||||
address makerToken
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (address providerAddress);
|
||||
|
||||
/// @dev Sample sell quotes from UniswapV2.
|
||||
/// @param path Token route.
|
||||
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
||||
/// amount.
|
||||
function sampleSellsFromUniswapV2(
|
||||
address[] calldata path,
|
||||
uint256[] calldata takerTokenAmounts
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256[] memory makerTokenAmounts);
|
||||
|
||||
/// @dev Sample buy quotes from UniswapV2.
|
||||
/// @param path Token route.
|
||||
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
||||
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||
/// amount.
|
||||
function sampleBuysFromUniswapV2(
|
||||
address[] calldata path,
|
||||
uint256[] calldata makerTokenAmounts
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256[] memory takerTokenAmounts);
|
||||
}
|
||||
|
@@ -21,12 +21,13 @@ pragma solidity ^0.5.9;
|
||||
|
||||
interface IKyberNetwork {
|
||||
|
||||
function getExpectedRate(
|
||||
function searchBestRate(
|
||||
address fromToken,
|
||||
address toToken,
|
||||
uint256 fromAmount
|
||||
uint256 fromAmount,
|
||||
bool usePermissionless
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256 expectedRate, uint256 slippageRate);
|
||||
returns (address reserve, uint256 expectedRate);
|
||||
}
|
||||
|
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
|
||||
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 {
|
||||
|
||||
function kyberNetworkContract() external view returns (address);
|
||||
|
||||
function getExpectedRate(
|
||||
address fromToken,
|
||||
address toToken,
|
||||
uint256 fromAmount
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256 expectedRate, uint256 slippageRate);
|
||||
}
|
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
|
||||
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 ILiquidityProvider {
|
||||
|
||||
/// @dev Transfers `amount` of the ERC20 `tokenAddress` from `from` to `to`.
|
||||
/// @param tokenAddress The address of the ERC20 token to transfer.
|
||||
/// @param from Address to transfer asset from.
|
||||
/// @param to Address to transfer asset to.
|
||||
/// @param amount Amount of asset to transfer.
|
||||
/// @param bridgeData Arbitrary asset data needed by the bridge contract.
|
||||
/// @return success The magic bytes `0xdc1600f3` if successful.
|
||||
function bridgeTransferFrom(
|
||||
address tokenAddress,
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount,
|
||||
bytes calldata bridgeData
|
||||
)
|
||||
external
|
||||
returns (bytes4 success);
|
||||
|
||||
/// @dev Quotes the amount of `makerToken` that would be obtained by
|
||||
/// selling `sellAmount` of `takerToken`.
|
||||
/// @param takerToken Address of the taker token (what to sell).
|
||||
/// @param makerToken Address of the maker token (what to buy).
|
||||
/// @param sellAmount Amount of `takerToken` to sell.
|
||||
/// @return makerTokenAmount Amount of `makerToken` that would be obtained.
|
||||
function getSellQuote(
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256 sellAmount
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256 makerTokenAmount);
|
||||
|
||||
/// @dev Quotes the amount of `takerToken` that would need to be sold in
|
||||
/// order to obtain `buyAmount` of `makerToken`.
|
||||
/// @param takerToken Address of the taker token (what to sell).
|
||||
/// @param makerToken Address of the maker token (what to buy).
|
||||
/// @param buyAmount Amount of `makerToken` to buy.
|
||||
/// @return takerTokenAmount Amount of `takerToken` that would need to be sold.
|
||||
function getBuyQuote(
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256 buyAmount
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256 takerTokenAmount);
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
|
||||
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 ILiquidityProviderRegistry {
|
||||
|
||||
/// @dev Returns the address of a liquidity provider for the given market
|
||||
/// (takerToken, makerToken), reverting if the pool does not exist.
|
||||
/// @param takerToken Taker asset managed by liquidity provider.
|
||||
/// @param makerToken Maker asset managed by liquidity provider.
|
||||
/// @return Address of the liquidity provider.
|
||||
function getLiquidityProviderForMarket(
|
||||
address takerToken,
|
||||
address makerToken
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (address providerAddress);
|
||||
}
|
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
|
||||
Copyright 2020 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 IMultiBridge {
|
||||
|
||||
/// @dev Transfers `amount` of the ERC20 `tokenAddress` from `from` to `to`.
|
||||
/// @param tokenAddress The address of the ERC20 token to transfer.
|
||||
/// @param from Address to transfer asset from.
|
||||
/// @param to Address to transfer asset to.
|
||||
/// @param amount Amount of asset to transfer.
|
||||
/// @param bridgeData Arbitrary asset data needed by the bridge contract.
|
||||
/// @return success The magic bytes `0xdc1600f3` if successful.
|
||||
function bridgeTransferFrom(
|
||||
address tokenAddress,
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount,
|
||||
bytes calldata bridgeData
|
||||
)
|
||||
external
|
||||
returns (bytes4 success);
|
||||
|
||||
/// @dev Quotes the amount of `makerToken` that would be obtained by
|
||||
/// selling `sellAmount` of `takerToken`.
|
||||
/// @param takerToken Address of the taker token (what to sell).
|
||||
/// @param intermediateToken The address of the intermediate token to
|
||||
/// use in an indirect route.
|
||||
/// @param makerToken Address of the maker token (what to buy).
|
||||
/// @param sellAmount Amount of `takerToken` to sell.
|
||||
/// @return makerTokenAmount Amount of `makerToken` that would be obtained.
|
||||
function getSellQuote(
|
||||
address takerToken,
|
||||
address intermediateToken,
|
||||
address makerToken,
|
||||
uint256 sellAmount
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256 makerTokenAmount);
|
||||
}
|
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
|
||||
|
||||
interface IUniswapV2Router01 {
|
||||
|
||||
function getAmountsOut(uint256 amountIn, address[] calldata path)
|
||||
external
|
||||
view
|
||||
returns (uint256[] memory amounts);
|
||||
|
||||
function getAmountsIn(uint256 amountOut, address[] calldata path)
|
||||
external
|
||||
view
|
||||
returns (uint256[] memory amounts);
|
||||
}
|
@@ -24,7 +24,8 @@ import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||
import "../src/ERC20BridgeSampler.sol";
|
||||
import "../src/IEth2Dai.sol";
|
||||
import "../src/IDevUtils.sol";
|
||||
import "../src/IKyberNetwork.sol";
|
||||
import "../src/IKyberNetworkProxy.sol";
|
||||
import "../src/IUniswapV2Router01.sol";
|
||||
|
||||
|
||||
library LibDeterministicQuotes {
|
||||
@@ -194,6 +195,55 @@ contract TestERC20BridgeSamplerUniswapExchange is
|
||||
}
|
||||
|
||||
|
||||
contract TestERC20BridgeSamplerUniswapV2Router01 is
|
||||
IUniswapV2Router01,
|
||||
DeploymentConstants,
|
||||
FailTrigger
|
||||
{
|
||||
bytes32 constant private SALT = 0xadc7fcb33c735913b8635927e66896b356a53a912ab2ceff929e60a04b53b3c1;
|
||||
|
||||
// Deterministic `IUniswapV2Router01.getAmountsOut()`.
|
||||
function getAmountsOut(uint256 amountIn, address[] calldata path)
|
||||
external
|
||||
view
|
||||
returns (uint256[] memory amounts)
|
||||
{
|
||||
require(path.length >= 2, "PATH_TOO_SHORT");
|
||||
_revertIfShouldFail();
|
||||
amounts = new uint256[](path.length);
|
||||
amounts[0] = amountIn;
|
||||
for (uint256 i = 0; i < path.length - 1; ++i) {
|
||||
amounts[i + 1] = LibDeterministicQuotes.getDeterministicSellQuote(
|
||||
SALT,
|
||||
path[i],
|
||||
path[i + 1],
|
||||
amounts[i]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Deterministic `IUniswapV2Router01.getAmountsInt()`.
|
||||
function getAmountsIn(uint256 amountOut, address[] calldata path)
|
||||
external
|
||||
view
|
||||
returns (uint256[] memory amounts)
|
||||
{
|
||||
require(path.length >= 2, "PATH_TOO_SHORT");
|
||||
_revertIfShouldFail();
|
||||
amounts = new uint256[](path.length);
|
||||
amounts[path.length - 1] = amountOut;
|
||||
for (uint256 i = path.length - 1; i > 0; --i) {
|
||||
amounts[i - 1] = LibDeterministicQuotes.getDeterministicBuyQuote(
|
||||
SALT,
|
||||
path[i - 1],
|
||||
path[i],
|
||||
amounts[i]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
contract TestERC20BridgeSamplerKyberNetwork is
|
||||
IKyberNetwork,
|
||||
DeploymentConstants,
|
||||
@@ -202,7 +252,30 @@ contract TestERC20BridgeSamplerKyberNetwork is
|
||||
bytes32 constant private SALT = 0x0ff3ca9d46195c39f9a12afb74207b4970349fb3cfb1e459bbf170298d326bc7;
|
||||
address constant public ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
|
||||
|
||||
// Deterministic `IKyberNetwork.getExpectedRate()`.
|
||||
function kyberNetworkContract()
|
||||
external
|
||||
view
|
||||
returns (address)
|
||||
{
|
||||
return address(this);
|
||||
}
|
||||
|
||||
// IKyberNetwork not exposed via IKyberNetworkProxy
|
||||
function searchBestRate(
|
||||
address fromToken,
|
||||
address toToken,
|
||||
uint256 fromAmount,
|
||||
bool // usePermissionless
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (address reserve, uint256 expectedRate)
|
||||
{
|
||||
(expectedRate, ) = this.getExpectedRate(fromToken, toToken, fromAmount);
|
||||
return (address(this), expectedRate);
|
||||
}
|
||||
|
||||
// Deterministic `IKyberNetworkProxy.getExpectedRate()`.
|
||||
function getExpectedRate(
|
||||
address fromToken,
|
||||
address toToken,
|
||||
@@ -302,13 +375,15 @@ contract TestERC20BridgeSampler is
|
||||
FailTrigger
|
||||
{
|
||||
TestERC20BridgeSamplerUniswapExchangeFactory public uniswap;
|
||||
TestERC20BridgeSamplerUniswapV2Router01 public uniswapV2Router;
|
||||
TestERC20BridgeSamplerEth2Dai public eth2Dai;
|
||||
TestERC20BridgeSamplerKyberNetwork public kyber;
|
||||
|
||||
uint8 private constant MAX_ORDER_STATUS = uint8(LibOrder.OrderStatus.CANCELLED) + 1;
|
||||
|
||||
constructor() public {
|
||||
constructor() public ERC20BridgeSampler(address(this)) {
|
||||
uniswap = new TestERC20BridgeSamplerUniswapExchangeFactory();
|
||||
uniswapV2Router = new TestERC20BridgeSamplerUniswapV2Router01();
|
||||
eth2Dai = new TestERC20BridgeSamplerEth2Dai();
|
||||
kyber = new TestERC20BridgeSamplerKyberNetwork();
|
||||
}
|
||||
@@ -327,6 +402,7 @@ contract TestERC20BridgeSampler is
|
||||
bytes memory
|
||||
)
|
||||
public
|
||||
pure
|
||||
returns (
|
||||
LibOrder.OrderInfo memory orderInfo,
|
||||
uint256 fillableTakerAssetAmount,
|
||||
@@ -357,15 +433,6 @@ contract TestERC20BridgeSampler is
|
||||
return LibDeterministicQuotes.getDeterministicTokenDecimals(tokenAddress);
|
||||
}
|
||||
|
||||
// Overriden to point to a this contract.
|
||||
function _getDevUtilsAddress()
|
||||
internal
|
||||
view
|
||||
returns (address devUtilAddress)
|
||||
{
|
||||
return address(this);
|
||||
}
|
||||
|
||||
// Overriden to point to a custom contract.
|
||||
function _getEth2DaiAddress()
|
||||
internal
|
||||
@@ -384,6 +451,15 @@ contract TestERC20BridgeSampler is
|
||||
return address(uniswap);
|
||||
}
|
||||
|
||||
// Overriden to point to a custom contract.
|
||||
function _getUniswapV2Router01Address()
|
||||
internal
|
||||
view
|
||||
returns (address uniswapV2RouterAddress)
|
||||
{
|
||||
return address(uniswapV2Router);
|
||||
}
|
||||
|
||||
// Overriden to point to a custom contract.
|
||||
function _getKyberNetworkProxyAddress()
|
||||
internal
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-erc20-bridge-sampler",
|
||||
"version": "1.4.1",
|
||||
"version": "1.6.0",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -36,9 +36,9 @@
|
||||
"compile:truffle": "truffle compile"
|
||||
},
|
||||
"config": {
|
||||
"publicInterfaceContracts": "ERC20BridgeSampler,IERC20BridgeSampler",
|
||||
"publicInterfaceContracts": "ERC20BridgeSampler,IERC20BridgeSampler,ILiquidityProvider,ILiquidityProviderRegistry,DummyLiquidityProviderRegistry,DummyLiquidityProvider",
|
||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
|
||||
"abis": "./test/generated-artifacts/@(ERC20BridgeSampler|ICurve|IDevUtils|IERC20BridgeSampler|IEth2Dai|IKyberNetwork|IUniswapExchangeQuotes|TestERC20BridgeSampler).json"
|
||||
"abis": "./test/generated-artifacts/@(DummyLiquidityProvider|DummyLiquidityProviderRegistry|ERC20BridgeSampler|ICurve|IDevUtils|IERC20BridgeSampler|IEth2Dai|IKyberNetwork|IKyberNetworkProxy|ILiquidityProvider|ILiquidityProviderRegistry|IMultiBridge|IUniswapExchangeQuotes|IUniswapV2Router01|TestERC20BridgeSampler).json"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -50,18 +50,18 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/protocol/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^5.2.1",
|
||||
"@0x/contracts-asset-proxy": "^3.2.2",
|
||||
"@0x/contracts-erc20": "^3.1.2",
|
||||
"@0x/contracts-exchange": "^3.2.2",
|
||||
"@0x/contracts-exchange-libs": "^4.3.2",
|
||||
"@0x/contracts-gen": "^2.0.8",
|
||||
"@0x/contracts-test-utils": "^5.3.0",
|
||||
"@0x/contracts-utils": "^4.4.0",
|
||||
"@0x/dev-utils": "^3.2.1",
|
||||
"@0x/sol-compiler": "^4.0.8",
|
||||
"@0x/abi-gen": "^5.3.0",
|
||||
"@0x/contracts-asset-proxy": "^3.3.0",
|
||||
"@0x/contracts-erc20": "^3.2.0",
|
||||
"@0x/contracts-exchange": "^3.2.6",
|
||||
"@0x/contracts-exchange-libs": "^4.3.6",
|
||||
"@0x/contracts-gen": "^2.0.9",
|
||||
"@0x/contracts-test-utils": "^5.3.3",
|
||||
"@0x/contracts-utils": "^4.5.0",
|
||||
"@0x/dev-utils": "^3.2.2",
|
||||
"@0x/sol-compiler": "^4.1.0",
|
||||
"@0x/tslint-config": "^4.0.0",
|
||||
"@0x/web3-wrapper": "^7.0.7",
|
||||
"@0x/web3-wrapper": "^7.1.0",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/node": "*",
|
||||
@@ -79,11 +79,11 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^6.2.1",
|
||||
"@0x/types": "^3.1.2",
|
||||
"@0x/typescript-typings": "^5.0.2",
|
||||
"@0x/utils": "^5.4.1",
|
||||
"ethereum-types": "^3.1.0",
|
||||
"@0x/base-contract": "^6.2.2",
|
||||
"@0x/types": "^3.1.3",
|
||||
"@0x/typescript-typings": "^5.1.0",
|
||||
"@0x/utils": "^5.5.0",
|
||||
"ethereum-types": "^3.1.1",
|
||||
"lodash": "^4.17.11"
|
||||
},
|
||||
"publishConfig": {
|
||||
|
@@ -5,9 +5,17 @@
|
||||
*/
|
||||
import { ContractArtifact } from 'ethereum-types';
|
||||
|
||||
import * as DummyLiquidityProvider from '../generated-artifacts/DummyLiquidityProvider.json';
|
||||
import * as DummyLiquidityProviderRegistry from '../generated-artifacts/DummyLiquidityProviderRegistry.json';
|
||||
import * as ERC20BridgeSampler from '../generated-artifacts/ERC20BridgeSampler.json';
|
||||
import * as IERC20BridgeSampler from '../generated-artifacts/IERC20BridgeSampler.json';
|
||||
import * as ILiquidityProvider from '../generated-artifacts/ILiquidityProvider.json';
|
||||
import * as ILiquidityProviderRegistry from '../generated-artifacts/ILiquidityProviderRegistry.json';
|
||||
export const artifacts = {
|
||||
ERC20BridgeSampler: ERC20BridgeSampler as ContractArtifact,
|
||||
IERC20BridgeSampler: IERC20BridgeSampler as ContractArtifact,
|
||||
ILiquidityProvider: ILiquidityProvider as ContractArtifact,
|
||||
ILiquidityProviderRegistry: ILiquidityProviderRegistry as ContractArtifact,
|
||||
DummyLiquidityProviderRegistry: DummyLiquidityProviderRegistry as ContractArtifact,
|
||||
DummyLiquidityProvider: DummyLiquidityProvider as ContractArtifact,
|
||||
};
|
||||
|
@@ -3,5 +3,9 @@
|
||||
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
export * from '../generated-wrappers/dummy_liquidity_provider';
|
||||
export * from '../generated-wrappers/dummy_liquidity_provider_registry';
|
||||
export * from '../generated-wrappers/erc20_bridge_sampler';
|
||||
export * from '../generated-wrappers/i_erc20_bridge_sampler';
|
||||
export * from '../generated-wrappers/i_liquidity_provider';
|
||||
export * from '../generated-wrappers/i_liquidity_provider_registry';
|
||||
|
@@ -5,21 +5,35 @@
|
||||
*/
|
||||
import { ContractArtifact } from 'ethereum-types';
|
||||
|
||||
import * as DummyLiquidityProvider from '../test/generated-artifacts/DummyLiquidityProvider.json';
|
||||
import * as DummyLiquidityProviderRegistry from '../test/generated-artifacts/DummyLiquidityProviderRegistry.json';
|
||||
import * as ERC20BridgeSampler from '../test/generated-artifacts/ERC20BridgeSampler.json';
|
||||
import * as ICurve from '../test/generated-artifacts/ICurve.json';
|
||||
import * as IDevUtils from '../test/generated-artifacts/IDevUtils.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 IKyberNetworkProxy from '../test/generated-artifacts/IKyberNetworkProxy.json';
|
||||
import * as ILiquidityProvider from '../test/generated-artifacts/ILiquidityProvider.json';
|
||||
import * as ILiquidityProviderRegistry from '../test/generated-artifacts/ILiquidityProviderRegistry.json';
|
||||
import * as IMultiBridge from '../test/generated-artifacts/IMultiBridge.json';
|
||||
import * as IUniswapExchangeQuotes from '../test/generated-artifacts/IUniswapExchangeQuotes.json';
|
||||
import * as IUniswapV2Router01 from '../test/generated-artifacts/IUniswapV2Router01.json';
|
||||
import * as TestERC20BridgeSampler from '../test/generated-artifacts/TestERC20BridgeSampler.json';
|
||||
export const artifacts = {
|
||||
DummyLiquidityProvider: DummyLiquidityProvider as ContractArtifact,
|
||||
DummyLiquidityProviderRegistry: DummyLiquidityProviderRegistry as ContractArtifact,
|
||||
ERC20BridgeSampler: ERC20BridgeSampler as ContractArtifact,
|
||||
ICurve: ICurve as ContractArtifact,
|
||||
IDevUtils: IDevUtils as ContractArtifact,
|
||||
IERC20BridgeSampler: IERC20BridgeSampler as ContractArtifact,
|
||||
IEth2Dai: IEth2Dai as ContractArtifact,
|
||||
IKyberNetwork: IKyberNetwork as ContractArtifact,
|
||||
IKyberNetworkProxy: IKyberNetworkProxy as ContractArtifact,
|
||||
ILiquidityProvider: ILiquidityProvider as ContractArtifact,
|
||||
ILiquidityProviderRegistry: ILiquidityProviderRegistry as ContractArtifact,
|
||||
IMultiBridge: IMultiBridge as ContractArtifact,
|
||||
IUniswapExchangeQuotes: IUniswapExchangeQuotes as ContractArtifact,
|
||||
IUniswapV2Router01: IUniswapV2Router01 as ContractArtifact,
|
||||
TestERC20BridgeSampler: TestERC20BridgeSampler as ContractArtifact,
|
||||
};
|
||||
|
@@ -11,7 +11,11 @@ import { BigNumber, hexUtils } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { artifacts } from './artifacts';
|
||||
import { TestERC20BridgeSamplerContract } from './wrappers';
|
||||
import {
|
||||
DummyLiquidityProviderContract,
|
||||
DummyLiquidityProviderRegistryContract,
|
||||
TestERC20BridgeSamplerContract,
|
||||
} from './wrappers';
|
||||
|
||||
blockchainTests('erc20-bridge-sampler', env => {
|
||||
let testContract: TestERC20BridgeSamplerContract;
|
||||
@@ -24,10 +28,15 @@ blockchainTests('erc20-bridge-sampler', env => {
|
||||
const KYBER_SALT = '0x0ff3ca9d46195c39f9a12afb74207b4970349fb3cfb1e459bbf170298d326bc7';
|
||||
const ETH2DAI_SALT = '0xb713b61bb9bb2958a0f5d1534b21e94fc68c4c0c034b0902ed844f2f6cd1b4f7';
|
||||
const UNISWAP_BASE_SALT = '0x1d6a6a0506b0b4a554b907a4c29d9f4674e461989d9c1921feb17b26716385ab';
|
||||
const UNISWAP_V2_SALT = '0xadc7fcb33c735913b8635927e66896b356a53a912ab2ceff929e60a04b53b3c1';
|
||||
const ERC20_PROXY_ID = '0xf47261b0';
|
||||
const INVALID_TOKEN_PAIR_ERROR = 'ERC20BridgeSampler/INVALID_TOKEN_PAIR';
|
||||
const MAKER_TOKEN = randomAddress();
|
||||
const TAKER_TOKEN = randomAddress();
|
||||
const FAKE_BUY_OPTS = {
|
||||
targetSlippageBps: new BigNumber(5),
|
||||
maxIterations: new BigNumber(5),
|
||||
};
|
||||
|
||||
before(async () => {
|
||||
testContract = await TestERC20BridgeSamplerContract.deployFrom0xArtifactAsync(
|
||||
@@ -165,8 +174,15 @@ blockchainTests('erc20-bridge-sampler', env => {
|
||||
for (const source of sources) {
|
||||
const sampleOutputs = [];
|
||||
for (const amount of sampleAmounts) {
|
||||
if (source === 'Eth2Dai') {
|
||||
sampleOutputs.push(getDeterministicBuyQuote(ETH2DAI_SALT, sellToken, buyToken, amount));
|
||||
if (source === 'Kyber' || source === 'Eth2Dai') {
|
||||
sampleOutputs.push(
|
||||
getDeterministicBuyQuote(
|
||||
source === 'Kyber' ? KYBER_SALT : ETH2DAI_SALT,
|
||||
sellToken,
|
||||
buyToken,
|
||||
amount,
|
||||
),
|
||||
);
|
||||
} else if (source === 'Uniswap') {
|
||||
sampleOutputs.push(getDeterministicUniswapBuyQuote(sellToken, buyToken, amount));
|
||||
}
|
||||
@@ -176,6 +192,22 @@ blockchainTests('erc20-bridge-sampler', env => {
|
||||
return quotes;
|
||||
}
|
||||
|
||||
function getDeterministicUniswapV2SellQuote(path: string[], sellAmount: BigNumber): BigNumber {
|
||||
let bought = sellAmount;
|
||||
for (let i = 0; i < path.length - 1; ++i) {
|
||||
bought = getDeterministicSellQuote(UNISWAP_V2_SALT, path[i], path[i + 1], bought);
|
||||
}
|
||||
return bought;
|
||||
}
|
||||
|
||||
function getDeterministicUniswapV2BuyQuote(path: string[], buyAmount: BigNumber): BigNumber {
|
||||
let sold = buyAmount;
|
||||
for (let i = path.length - 1; i > 0; --i) {
|
||||
sold = getDeterministicBuyQuote(UNISWAP_V2_SALT, path[i - 1], path[i], sold);
|
||||
}
|
||||
return sold;
|
||||
}
|
||||
|
||||
function getDeterministicFillableTakerAssetAmount(order: Order): BigNumber {
|
||||
const hash = getPackedHash(hexUtils.leftPad(order.salt));
|
||||
const orderStatus = new BigNumber(hash).mod(100).toNumber() > 90 ? 5 : 3;
|
||||
@@ -200,7 +232,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
||||
|
||||
function getSampleAmounts(tokenAddress: string, count?: number): BigNumber[] {
|
||||
const tokenDecimals = getDeterministicTokenDecimals(tokenAddress);
|
||||
const _upperLimit = getRandomPortion(getRandomInteger(1, 1000).times(10 ** tokenDecimals));
|
||||
const _upperLimit = getRandomPortion(getRandomInteger(1000, 50000).times(10 ** tokenDecimals));
|
||||
const _count = count || _.random(1, 16);
|
||||
const d = _upperLimit.div(_count);
|
||||
return _.times(_count, i => d.times((i + 1) / _count).integerValue());
|
||||
@@ -326,9 +358,10 @@ blockchainTests('erc20-bridge-sampler', env => {
|
||||
expect(quotes).to.deep.eq([]);
|
||||
});
|
||||
|
||||
it('can quote token - token', async () => {
|
||||
it('can quote token -> token', async () => {
|
||||
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Kyber'], sampleAmounts);
|
||||
const [takerToEthQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Kyber'], sampleAmounts);
|
||||
const [expectedQuotes] = getDeterministicSellQuotes(WETH_ADDRESS, MAKER_TOKEN, ['Kyber'], takerToEthQuotes);
|
||||
const quotes = await testContract
|
||||
.sampleSellsFromKyberNetwork(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
|
||||
.callAsync();
|
||||
@@ -384,6 +417,109 @@ blockchainTests('erc20-bridge-sampler', env => {
|
||||
});
|
||||
});
|
||||
|
||||
blockchainTests.resets('sampleBuysFromKyberNetwork()', () => {
|
||||
const ACCEPTABLE_SLIPPAGE = 0.0005;
|
||||
before(async () => {
|
||||
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
||||
});
|
||||
|
||||
it('throws if tokens are the same', async () => {
|
||||
const tx = testContract.sampleBuysFromKyberNetwork(MAKER_TOKEN, MAKER_TOKEN, [], FAKE_BUY_OPTS).callAsync();
|
||||
return expect(tx).to.revertWith(INVALID_TOKEN_PAIR_ERROR);
|
||||
});
|
||||
|
||||
it('can return no quotes', async () => {
|
||||
const quotes = await testContract
|
||||
.sampleBuysFromKyberNetwork(TAKER_TOKEN, MAKER_TOKEN, [], FAKE_BUY_OPTS)
|
||||
.callAsync();
|
||||
expect(quotes).to.deep.eq([]);
|
||||
});
|
||||
const expectQuotesWithinRange = (
|
||||
quotes: BigNumber[],
|
||||
expectedQuotes: BigNumber[],
|
||||
maxSlippage: BigNumber | number,
|
||||
) => {
|
||||
quotes.forEach((_q, i) => {
|
||||
// If we're within 1 base unit of a low decimal token
|
||||
// then that's as good as we're going to get (and slippage is "high")
|
||||
if (
|
||||
expectedQuotes[i].isZero() ||
|
||||
BigNumber.max(expectedQuotes[i], quotes[i])
|
||||
.minus(BigNumber.min(expectedQuotes[i], quotes[i]))
|
||||
.eq(1)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const slippage = quotes[i]
|
||||
.dividedBy(expectedQuotes[i])
|
||||
.minus(1)
|
||||
.decimalPlaces(4);
|
||||
expect(slippage, `quote[${i}]: ${slippage} ${quotes[i]} ${expectedQuotes[i]}`).to.be.bignumber.gte(0);
|
||||
expect(slippage, `quote[${i}] ${slippage} ${quotes[i]} ${expectedQuotes[i]}`).to.be.bignumber.lte(
|
||||
new BigNumber(maxSlippage),
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
it('can quote token -> token', async () => {
|
||||
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||
const [ethToMakerQuotes] = getDeterministicBuyQuotes(WETH_ADDRESS, MAKER_TOKEN, ['Kyber'], sampleAmounts);
|
||||
const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Kyber'], ethToMakerQuotes);
|
||||
const quotes = await testContract
|
||||
.sampleBuysFromKyberNetwork(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts, FAKE_BUY_OPTS)
|
||||
.callAsync();
|
||||
expectQuotesWithinRange(quotes, expectedQuotes, ACCEPTABLE_SLIPPAGE);
|
||||
});
|
||||
|
||||
it('returns zero if token -> token fails', async () => {
|
||||
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
|
||||
await enableFailTriggerAsync();
|
||||
const quotes = await testContract
|
||||
.sampleBuysFromKyberNetwork(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts, FAKE_BUY_OPTS)
|
||||
.callAsync();
|
||||
expect(quotes).to.deep.eq(expectedQuotes);
|
||||
});
|
||||
|
||||
it('can quote token -> ETH', async () => {
|
||||
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||
const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Kyber'], sampleAmounts);
|
||||
const quotes = await testContract
|
||||
.sampleBuysFromKyberNetwork(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts, FAKE_BUY_OPTS)
|
||||
.callAsync();
|
||||
expectQuotesWithinRange(quotes, expectedQuotes, ACCEPTABLE_SLIPPAGE);
|
||||
});
|
||||
|
||||
it('returns zero if token -> ETH fails', async () => {
|
||||
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
|
||||
await enableFailTriggerAsync();
|
||||
const quotes = await testContract
|
||||
.sampleBuysFromKyberNetwork(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts, FAKE_BUY_OPTS)
|
||||
.callAsync();
|
||||
expect(quotes).to.deep.eq(expectedQuotes);
|
||||
});
|
||||
|
||||
it('can quote ETH -> token', async () => {
|
||||
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||
const [expectedQuotes] = getDeterministicBuyQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Kyber'], sampleAmounts);
|
||||
const quotes = await testContract
|
||||
.sampleBuysFromKyberNetwork(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts, FAKE_BUY_OPTS)
|
||||
.callAsync();
|
||||
expectQuotesWithinRange(quotes, expectedQuotes, ACCEPTABLE_SLIPPAGE);
|
||||
});
|
||||
|
||||
it('returns zero if ETH -> token fails', async () => {
|
||||
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
|
||||
await enableFailTriggerAsync();
|
||||
const quotes = await testContract
|
||||
.sampleBuysFromKyberNetwork(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts, FAKE_BUY_OPTS)
|
||||
.callAsync();
|
||||
expect(quotes).to.deep.eq(expectedQuotes);
|
||||
});
|
||||
});
|
||||
|
||||
blockchainTests.resets('sampleSellsFromEth2Dai()', () => {
|
||||
before(async () => {
|
||||
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
||||
@@ -716,6 +852,177 @@ blockchainTests('erc20-bridge-sampler', env => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('getLiquidityProviderFromRegistry', () => {
|
||||
const xAsset = randomAddress();
|
||||
const yAsset = randomAddress();
|
||||
const sampleAmounts = getSampleAmounts(yAsset);
|
||||
let liquidityProvider: DummyLiquidityProviderContract;
|
||||
let registryContract: DummyLiquidityProviderRegistryContract;
|
||||
|
||||
before(async () => {
|
||||
liquidityProvider = await DummyLiquidityProviderContract.deployFrom0xArtifactAsync(
|
||||
artifacts.DummyLiquidityProvider,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
{},
|
||||
);
|
||||
|
||||
registryContract = await DummyLiquidityProviderRegistryContract.deployFrom0xArtifactAsync(
|
||||
artifacts.DummyLiquidityProviderRegistry,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
{},
|
||||
);
|
||||
await registryContract
|
||||
.setLiquidityProviderForMarket(xAsset, yAsset, liquidityProvider.address)
|
||||
.awaitTransactionSuccessAsync();
|
||||
});
|
||||
|
||||
it('should be able to get the liquidity provider', async () => {
|
||||
const xyLiquidityProvider = await testContract
|
||||
.getLiquidityProviderFromRegistry(registryContract.address, xAsset, yAsset)
|
||||
.callAsync();
|
||||
const yxLiquidityProvider = await testContract
|
||||
.getLiquidityProviderFromRegistry(registryContract.address, yAsset, xAsset)
|
||||
.callAsync();
|
||||
const unknownLiquidityProvider = await testContract
|
||||
.getLiquidityProviderFromRegistry(registryContract.address, yAsset, randomAddress())
|
||||
.callAsync();
|
||||
|
||||
expect(xyLiquidityProvider).to.eq(liquidityProvider.address);
|
||||
expect(yxLiquidityProvider).to.eq(liquidityProvider.address);
|
||||
expect(unknownLiquidityProvider).to.eq(constants.NULL_ADDRESS);
|
||||
});
|
||||
|
||||
it('should be able to query sells from the liquidity provider', async () => {
|
||||
const result = await testContract
|
||||
.sampleSellsFromLiquidityProviderRegistry(registryContract.address, yAsset, xAsset, sampleAmounts)
|
||||
.callAsync();
|
||||
result.forEach((value, idx) => {
|
||||
expect(value).is.bignumber.eql(sampleAmounts[idx].minus(1));
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to query buys from the liquidity provider', async () => {
|
||||
const result = await testContract
|
||||
.sampleBuysFromLiquidityProviderRegistry(
|
||||
registryContract.address,
|
||||
yAsset,
|
||||
xAsset,
|
||||
sampleAmounts,
|
||||
FAKE_BUY_OPTS,
|
||||
)
|
||||
.callAsync();
|
||||
result.forEach((value, idx) => {
|
||||
expect(value).is.bignumber.eql(sampleAmounts[idx].plus(1));
|
||||
});
|
||||
});
|
||||
|
||||
it('should just return zeros if the liquidity provider cannot be found', async () => {
|
||||
const result = await testContract
|
||||
.sampleBuysFromLiquidityProviderRegistry(
|
||||
registryContract.address,
|
||||
yAsset,
|
||||
randomAddress(),
|
||||
sampleAmounts,
|
||||
FAKE_BUY_OPTS,
|
||||
)
|
||||
.callAsync();
|
||||
result.forEach(value => {
|
||||
expect(value).is.bignumber.eql(constants.ZERO_AMOUNT);
|
||||
});
|
||||
});
|
||||
|
||||
it('should just return zeros if the registry does not exist', async () => {
|
||||
const result = await testContract
|
||||
.sampleBuysFromLiquidityProviderRegistry(randomAddress(), yAsset, xAsset, sampleAmounts, FAKE_BUY_OPTS)
|
||||
.callAsync();
|
||||
result.forEach(value => {
|
||||
expect(value).is.bignumber.eql(constants.ZERO_AMOUNT);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
blockchainTests.resets('sampleSellsFromUniswapV2()', () => {
|
||||
function predictSellQuotes(path: string[], sellAmounts: BigNumber[]): BigNumber[] {
|
||||
return sellAmounts.map(a => getDeterministicUniswapV2SellQuote(path, a));
|
||||
}
|
||||
|
||||
it('can return no quotes', async () => {
|
||||
const quotes = await testContract.sampleSellsFromUniswapV2([TAKER_TOKEN, MAKER_TOKEN], []).callAsync();
|
||||
expect(quotes).to.deep.eq([]);
|
||||
});
|
||||
|
||||
it('can quote token -> token', async () => {
|
||||
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||
const expectedQuotes = predictSellQuotes([TAKER_TOKEN, MAKER_TOKEN], sampleAmounts);
|
||||
const quotes = await testContract
|
||||
.sampleSellsFromUniswapV2([TAKER_TOKEN, MAKER_TOKEN], sampleAmounts)
|
||||
.callAsync();
|
||||
expect(quotes).to.deep.eq(expectedQuotes);
|
||||
});
|
||||
|
||||
it('returns zero if token -> token fails', async () => {
|
||||
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
|
||||
await enableFailTriggerAsync();
|
||||
const quotes = await testContract
|
||||
.sampleSellsFromUniswapV2([TAKER_TOKEN, MAKER_TOKEN], sampleAmounts)
|
||||
.callAsync();
|
||||
expect(quotes).to.deep.eq(expectedQuotes);
|
||||
});
|
||||
|
||||
it('can quote token -> token -> token', async () => {
|
||||
const intermediateToken = randomAddress();
|
||||
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||
const expectedQuotes = predictSellQuotes([TAKER_TOKEN, intermediateToken, MAKER_TOKEN], sampleAmounts);
|
||||
const quotes = await testContract
|
||||
.sampleSellsFromUniswapV2([TAKER_TOKEN, intermediateToken, MAKER_TOKEN], sampleAmounts)
|
||||
.callAsync();
|
||||
expect(quotes).to.deep.eq(expectedQuotes);
|
||||
});
|
||||
});
|
||||
|
||||
blockchainTests.resets('sampleBuysFromUniswapV2()', () => {
|
||||
function predictBuyQuotes(path: string[], buyAmounts: BigNumber[]): BigNumber[] {
|
||||
return buyAmounts.map(a => getDeterministicUniswapV2BuyQuote(path, a));
|
||||
}
|
||||
|
||||
it('can return no quotes', async () => {
|
||||
const quotes = await testContract.sampleBuysFromUniswapV2([TAKER_TOKEN, MAKER_TOKEN], []).callAsync();
|
||||
expect(quotes).to.deep.eq([]);
|
||||
});
|
||||
|
||||
it('can quote token -> token', async () => {
|
||||
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
|
||||
const expectedQuotes = predictBuyQuotes([TAKER_TOKEN, MAKER_TOKEN], sampleAmounts);
|
||||
const quotes = await testContract
|
||||
.sampleBuysFromUniswapV2([TAKER_TOKEN, MAKER_TOKEN], sampleAmounts)
|
||||
.callAsync();
|
||||
expect(quotes).to.deep.eq(expectedQuotes);
|
||||
});
|
||||
|
||||
it('returns zero if token -> token fails', async () => {
|
||||
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
|
||||
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
|
||||
await enableFailTriggerAsync();
|
||||
const quotes = await testContract
|
||||
.sampleBuysFromUniswapV2([TAKER_TOKEN, MAKER_TOKEN], sampleAmounts)
|
||||
.callAsync();
|
||||
expect(quotes).to.deep.eq(expectedQuotes);
|
||||
});
|
||||
|
||||
it('can quote token -> token -> token', async () => {
|
||||
const intermediateToken = randomAddress();
|
||||
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
|
||||
const expectedQuotes = predictBuyQuotes([TAKER_TOKEN, intermediateToken, MAKER_TOKEN], sampleAmounts);
|
||||
const quotes = await testContract
|
||||
.sampleBuysFromUniswapV2([TAKER_TOKEN, intermediateToken, MAKER_TOKEN], sampleAmounts)
|
||||
.callAsync();
|
||||
expect(quotes).to.deep.eq(expectedQuotes);
|
||||
});
|
||||
});
|
||||
|
||||
describe('batchCall()', () => {
|
||||
it('can call one function', async () => {
|
||||
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN);
|
||||
|
@@ -3,11 +3,18 @@
|
||||
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
export * from '../test/generated-wrappers/dummy_liquidity_provider';
|
||||
export * from '../test/generated-wrappers/dummy_liquidity_provider_registry';
|
||||
export * from '../test/generated-wrappers/erc20_bridge_sampler';
|
||||
export * from '../test/generated-wrappers/i_curve';
|
||||
export * from '../test/generated-wrappers/i_dev_utils';
|
||||
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_kyber_network_proxy';
|
||||
export * from '../test/generated-wrappers/i_liquidity_provider';
|
||||
export * from '../test/generated-wrappers/i_liquidity_provider_registry';
|
||||
export * from '../test/generated-wrappers/i_multi_bridge';
|
||||
export * from '../test/generated-wrappers/i_uniswap_exchange_quotes';
|
||||
export * from '../test/generated-wrappers/i_uniswap_v2_router01';
|
||||
export * from '../test/generated-wrappers/test_erc20_bridge_sampler';
|
||||
|
@@ -3,15 +3,26 @@
|
||||
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
|
||||
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
|
||||
"files": [
|
||||
"generated-artifacts/DummyLiquidityProvider.json",
|
||||
"generated-artifacts/DummyLiquidityProviderRegistry.json",
|
||||
"generated-artifacts/ERC20BridgeSampler.json",
|
||||
"generated-artifacts/IERC20BridgeSampler.json",
|
||||
"generated-artifacts/ILiquidityProvider.json",
|
||||
"generated-artifacts/ILiquidityProviderRegistry.json",
|
||||
"test/generated-artifacts/DummyLiquidityProvider.json",
|
||||
"test/generated-artifacts/DummyLiquidityProviderRegistry.json",
|
||||
"test/generated-artifacts/ERC20BridgeSampler.json",
|
||||
"test/generated-artifacts/ICurve.json",
|
||||
"test/generated-artifacts/IDevUtils.json",
|
||||
"test/generated-artifacts/IERC20BridgeSampler.json",
|
||||
"test/generated-artifacts/IEth2Dai.json",
|
||||
"test/generated-artifacts/IKyberNetwork.json",
|
||||
"test/generated-artifacts/IKyberNetworkProxy.json",
|
||||
"test/generated-artifacts/ILiquidityProvider.json",
|
||||
"test/generated-artifacts/ILiquidityProviderRegistry.json",
|
||||
"test/generated-artifacts/IMultiBridge.json",
|
||||
"test/generated-artifacts/IUniswapExchangeQuotes.json",
|
||||
"test/generated-artifacts/IUniswapV2Router01.json",
|
||||
"test/generated-artifacts/TestERC20BridgeSampler.json"
|
||||
],
|
||||
"exclude": ["./deploy/solc/solc_bin"]
|
||||
|
@@ -1,4 +1,49 @@
|
||||
[
|
||||
{
|
||||
"version": "3.2.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add `LibERC20Token.approveIfBelow()`",
|
||||
"pr": 2512
|
||||
},
|
||||
{
|
||||
"note": "Add solidity 0.6 contracts",
|
||||
"pr": 2545
|
||||
},
|
||||
{
|
||||
"note": "Update `LibERC20TokenV06` comments.",
|
||||
"pr": 2597
|
||||
}
|
||||
],
|
||||
"timestamp": 1592969527
|
||||
},
|
||||
{
|
||||
"timestamp": 1583220306,
|
||||
"version": "3.1.5",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1582837861,
|
||||
"version": "3.1.4",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1582677073,
|
||||
"version": "3.1.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1582623685,
|
||||
"version": "3.1.2",
|
||||
|
@@ -5,6 +5,24 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v3.2.0 - _June 24, 2020_
|
||||
|
||||
* Add `LibERC20Token.approveIfBelow()` (#2512)
|
||||
* Add solidity 0.6 contracts (#2545)
|
||||
* Update `LibERC20TokenV06` comments. (#2597)
|
||||
|
||||
## v3.1.5 - _March 3, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.4 - _February 27, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.3 - _February 26, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.2 - _February 25, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -3,6 +3,7 @@
|
||||
"contractsDir": "./contracts",
|
||||
"useDockerisedSolc": false,
|
||||
"isOfflineMode": false,
|
||||
"shouldSaveStandardInput": true,
|
||||
"compilerSettings": {
|
||||
"evmVersion": "istanbul",
|
||||
"optimizer": {
|
||||
|
@@ -47,6 +47,25 @@ library LibERC20Token {
|
||||
_callWithOptionalBooleanResult(token, callData);
|
||||
}
|
||||
|
||||
/// @dev Calls `IERC20Token(token).approve()` and sets the allowance to the
|
||||
/// maximum if the current approval is not already >= an amount.
|
||||
/// Reverts if `false` is returned or if the return
|
||||
/// data length is nonzero and not 32 bytes.
|
||||
/// @param token The address of the token contract.
|
||||
/// @param spender The address that receives an allowance.
|
||||
/// @param amount The minimum allowance needed.
|
||||
function approveIfBelow(
|
||||
address token,
|
||||
address spender,
|
||||
uint256 amount
|
||||
)
|
||||
internal
|
||||
{
|
||||
if (IERC20Token(token).allowance(address(this), spender) < amount) {
|
||||
approve(token, spender, uint256(-1));
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Calls `IERC20Token(token).transfer()`.
|
||||
/// Reverts if `false` is returned or if the return
|
||||
/// data length is nonzero and not 32 bytes.
|
||||
|
95
contracts/erc20/contracts/src/v06/IERC20TokenV06.sol
Normal file
95
contracts/erc20/contracts/src/v06/IERC20TokenV06.sol
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
|
||||
Copyright 2020 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.6.5;
|
||||
|
||||
|
||||
interface IERC20TokenV06 {
|
||||
|
||||
// solhint-disable no-simple-event-func-name
|
||||
event Transfer(
|
||||
address indexed from,
|
||||
address indexed to,
|
||||
uint256 value
|
||||
);
|
||||
|
||||
event Approval(
|
||||
address indexed owner,
|
||||
address indexed spender,
|
||||
uint256 value
|
||||
);
|
||||
|
||||
/// @dev send `value` token to `to` from `msg.sender`
|
||||
/// @param to The address of the recipient
|
||||
/// @param value The amount of token to be transferred
|
||||
/// @return True if transfer was successful
|
||||
function transfer(address to, uint256 value)
|
||||
external
|
||||
returns (bool);
|
||||
|
||||
/// @dev send `value` token to `to` from `from` on the condition it is approved by `from`
|
||||
/// @param from The address of the sender
|
||||
/// @param to The address of the recipient
|
||||
/// @param value The amount of token to be transferred
|
||||
/// @return True if transfer was successful
|
||||
function transferFrom(
|
||||
address from,
|
||||
address to,
|
||||
uint256 value
|
||||
)
|
||||
external
|
||||
returns (bool);
|
||||
|
||||
/// @dev `msg.sender` approves `spender` to spend `value` tokens
|
||||
/// @param spender The address of the account able to transfer the tokens
|
||||
/// @param value The amount of wei to be approved for transfer
|
||||
/// @return Always true if the call has enough gas to complete execution
|
||||
function approve(address spender, uint256 value)
|
||||
external
|
||||
returns (bool);
|
||||
|
||||
/// @dev Query total supply of token
|
||||
/// @return Total supply of token
|
||||
function totalSupply()
|
||||
external
|
||||
view
|
||||
returns (uint256);
|
||||
|
||||
/// @dev Get the balance of `owner`.
|
||||
/// @param owner The address from which the balance will be retrieved
|
||||
/// @return Balance of owner
|
||||
function balanceOf(address owner)
|
||||
external
|
||||
view
|
||||
returns (uint256);
|
||||
|
||||
/// @dev Get the allowance for `spender` to spend from `owner`.
|
||||
/// @param owner The address of the account owning tokens
|
||||
/// @param spender The address of the account able to transfer the tokens
|
||||
/// @return Amount of remaining tokens allowed to spent
|
||||
function allowance(address owner, address spender)
|
||||
external
|
||||
view
|
||||
returns (uint256);
|
||||
|
||||
/// @dev Get the number of decimals this token has.
|
||||
function decimals()
|
||||
external
|
||||
view
|
||||
returns (uint8);
|
||||
}
|
32
contracts/erc20/contracts/src/v06/IEtherTokenV06.sol
Normal file
32
contracts/erc20/contracts/src/v06/IEtherTokenV06.sol
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
|
||||
Copyright 2020 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.6.5;
|
||||
|
||||
import "./IERC20TokenV06.sol";
|
||||
|
||||
|
||||
interface IEtherTokenV06 is
|
||||
IERC20TokenV06
|
||||
{
|
||||
/// @dev Wrap ether.
|
||||
function deposit() external payable;
|
||||
|
||||
/// @dev Unwrap ether.
|
||||
function withdraw(uint256 amount) external;
|
||||
}
|
208
contracts/erc20/contracts/src/v06/LibERC20TokenV06.sol
Normal file
208
contracts/erc20/contracts/src/v06/LibERC20TokenV06.sol
Normal file
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
|
||||
Copyright 2020 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.6.5;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
|
||||
import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol";
|
||||
import "./IERC20TokenV06.sol";
|
||||
|
||||
|
||||
library LibERC20TokenV06 {
|
||||
bytes constant private DECIMALS_CALL_DATA = hex"313ce567";
|
||||
|
||||
/// @dev Calls `IERC20TokenV06(token).approve()`.
|
||||
/// Reverts if the result fails `isSuccessfulResult()` or the call reverts.
|
||||
/// @param token The address of the token contract.
|
||||
/// @param spender The address that receives an allowance.
|
||||
/// @param allowance The allowance to set.
|
||||
function compatApprove(
|
||||
IERC20TokenV06 token,
|
||||
address spender,
|
||||
uint256 allowance
|
||||
)
|
||||
internal
|
||||
{
|
||||
bytes memory callData = abi.encodeWithSelector(
|
||||
token.approve.selector,
|
||||
spender,
|
||||
allowance
|
||||
);
|
||||
_callWithOptionalBooleanResult(address(token), callData);
|
||||
}
|
||||
|
||||
/// @dev Calls `IERC20TokenV06(token).approve()` and sets the allowance to the
|
||||
/// maximum if the current approval is not already >= an amount.
|
||||
/// Reverts if the result fails `isSuccessfulResult()` or the call reverts.
|
||||
/// @param token The address of the token contract.
|
||||
/// @param spender The address that receives an allowance.
|
||||
/// @param amount The minimum allowance needed.
|
||||
function approveIfBelow(
|
||||
IERC20TokenV06 token,
|
||||
address spender,
|
||||
uint256 amount
|
||||
)
|
||||
internal
|
||||
{
|
||||
if (token.allowance(address(this), spender) < amount) {
|
||||
compatApprove(token, spender, uint256(-1));
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Calls `IERC20TokenV06(token).transfer()`.
|
||||
/// Reverts if the result fails `isSuccessfulResult()` or the call reverts.
|
||||
/// @param token The address of the token contract.
|
||||
/// @param to The address that receives the tokens
|
||||
/// @param amount Number of tokens to transfer.
|
||||
function compatTransfer(
|
||||
IERC20TokenV06 token,
|
||||
address to,
|
||||
uint256 amount
|
||||
)
|
||||
internal
|
||||
{
|
||||
bytes memory callData = abi.encodeWithSelector(
|
||||
token.transfer.selector,
|
||||
to,
|
||||
amount
|
||||
);
|
||||
_callWithOptionalBooleanResult(address(token), callData);
|
||||
}
|
||||
|
||||
/// @dev Calls `IERC20TokenV06(token).transferFrom()`.
|
||||
/// Reverts if the result fails `isSuccessfulResult()` or the call reverts.
|
||||
/// @param token The address of the token contract.
|
||||
/// @param from The owner of the tokens.
|
||||
/// @param to The address that receives the tokens
|
||||
/// @param amount Number of tokens to transfer.
|
||||
function compatTransferFrom(
|
||||
IERC20TokenV06 token,
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount
|
||||
)
|
||||
internal
|
||||
{
|
||||
bytes memory callData = abi.encodeWithSelector(
|
||||
token.transferFrom.selector,
|
||||
from,
|
||||
to,
|
||||
amount
|
||||
);
|
||||
_callWithOptionalBooleanResult(address(token), callData);
|
||||
}
|
||||
|
||||
/// @dev Retrieves the number of decimals for a token.
|
||||
/// Returns `18` if the call reverts.
|
||||
/// @param token The address of the token contract.
|
||||
/// @return tokenDecimals The number of decimals places for the token.
|
||||
function compatDecimals(IERC20TokenV06 token)
|
||||
internal
|
||||
view
|
||||
returns (uint8 tokenDecimals)
|
||||
{
|
||||
tokenDecimals = 18;
|
||||
(bool didSucceed, bytes memory resultData) = address(token).staticcall(DECIMALS_CALL_DATA);
|
||||
if (didSucceed && resultData.length == 32) {
|
||||
tokenDecimals = uint8(LibBytesV06.readUint256(resultData, 0));
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Retrieves the allowance for a token, owner, and spender.
|
||||
/// Returns `0` if the call reverts.
|
||||
/// @param token The address of the token contract.
|
||||
/// @param owner The owner of the tokens.
|
||||
/// @param spender The address the spender.
|
||||
/// @return allowance_ The allowance for a token, owner, and spender.
|
||||
function compatAllowance(IERC20TokenV06 token, address owner, address spender)
|
||||
internal
|
||||
view
|
||||
returns (uint256 allowance_)
|
||||
{
|
||||
(bool didSucceed, bytes memory resultData) = address(token).staticcall(
|
||||
abi.encodeWithSelector(
|
||||
token.allowance.selector,
|
||||
owner,
|
||||
spender
|
||||
)
|
||||
);
|
||||
if (didSucceed && resultData.length == 32) {
|
||||
allowance_ = LibBytesV06.readUint256(resultData, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Retrieves the balance for a token owner.
|
||||
/// Returns `0` if the call reverts.
|
||||
/// @param token The address of the token contract.
|
||||
/// @param owner The owner of the tokens.
|
||||
/// @return balance The token balance of an owner.
|
||||
function compatBalanceOf(IERC20TokenV06 token, address owner)
|
||||
internal
|
||||
view
|
||||
returns (uint256 balance)
|
||||
{
|
||||
(bool didSucceed, bytes memory resultData) = address(token).staticcall(
|
||||
abi.encodeWithSelector(
|
||||
token.balanceOf.selector,
|
||||
owner
|
||||
)
|
||||
);
|
||||
if (didSucceed && resultData.length == 32) {
|
||||
balance = LibBytesV06.readUint256(resultData, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Check if the data returned by a non-static call to an ERC20 token
|
||||
/// is a successful result. Supported functions are `transfer()`,
|
||||
/// `transferFrom()`, and `approve()`.
|
||||
/// @param resultData The raw data returned by a non-static call to the ERC20 token.
|
||||
/// @return isSuccessful Whether the result data indicates success.
|
||||
function isSuccessfulResult(bytes memory resultData)
|
||||
internal
|
||||
pure
|
||||
returns (bool isSuccessful)
|
||||
{
|
||||
if (resultData.length == 0) {
|
||||
return true;
|
||||
}
|
||||
if (resultData.length == 32) {
|
||||
uint256 result = LibBytesV06.readUint256(resultData, 0);
|
||||
if (result == 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Executes a call on address `target` with calldata `callData`
|
||||
/// and asserts that either nothing was returned or a single boolean
|
||||
/// was returned equal to `true`.
|
||||
/// @param target The call target.
|
||||
/// @param callData The abi-encoded call data.
|
||||
function _callWithOptionalBooleanResult(
|
||||
address target,
|
||||
bytes memory callData
|
||||
)
|
||||
private
|
||||
{
|
||||
(bool didSucceed, bytes memory resultData) = target.call(callData);
|
||||
if (didSucceed && isSuccessfulResult(resultData)) {
|
||||
return;
|
||||
}
|
||||
LibRichErrorsV06.rrevert(resultData);
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-erc20",
|
||||
"version": "3.1.2",
|
||||
"version": "3.2.0",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -38,7 +38,7 @@
|
||||
},
|
||||
"config": {
|
||||
"publicInterfaceContracts": "DummyERC20Token,ERC20Token,WETH9,ZRXToken,DummyNoReturnERC20Token,DummyMultipleReturnERC20Token",
|
||||
"abis": "./test/generated-artifacts/@(DummyERC20Token|DummyMultipleReturnERC20Token|DummyNoReturnERC20Token|ERC20Token|IERC20Token|IEtherToken|LibERC20Token|MintableERC20Token|TestLibERC20Token|TestLibERC20TokenTarget|UnlimitedAllowanceERC20Token|UntransferrableDummyERC20Token|WETH9|ZRXToken).json",
|
||||
"abis": "./test/generated-artifacts/@(DummyERC20Token|DummyMultipleReturnERC20Token|DummyNoReturnERC20Token|ERC20Token|IERC20Token|IERC20TokenV06|IEtherToken|IEtherTokenV06|LibERC20Token|LibERC20TokenV06|MintableERC20Token|TestLibERC20Token|TestLibERC20TokenTarget|UnlimitedAllowanceERC20Token|UntransferrableDummyERC20Token|WETH9|ZRXToken).json",
|
||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
|
||||
},
|
||||
"repository": {
|
||||
@@ -51,18 +51,18 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^5.2.1",
|
||||
"@0x/contracts-gen": "^2.0.8",
|
||||
"@0x/contracts-test-utils": "^5.3.0",
|
||||
"@0x/contracts-utils": "^4.4.0",
|
||||
"@0x/dev-utils": "^3.2.1",
|
||||
"@0x/sol-compiler": "^4.0.8",
|
||||
"@0x/abi-gen": "^5.3.0",
|
||||
"@0x/contracts-gen": "^2.0.9",
|
||||
"@0x/contracts-test-utils": "^5.3.3",
|
||||
"@0x/contracts-utils": "^4.5.0",
|
||||
"@0x/dev-utils": "^3.2.2",
|
||||
"@0x/sol-compiler": "^4.1.0",
|
||||
"@0x/ts-doc-gen": "^0.0.22",
|
||||
"@0x/tslint-config": "^4.0.0",
|
||||
"@0x/types": "^3.1.2",
|
||||
"@0x/typescript-typings": "^5.0.2",
|
||||
"@0x/utils": "^5.4.1",
|
||||
"@0x/web3-wrapper": "^7.0.7",
|
||||
"@0x/types": "^3.1.3",
|
||||
"@0x/typescript-typings": "^5.1.0",
|
||||
"@0x/utils": "^5.5.0",
|
||||
"@0x/web3-wrapper": "^7.1.0",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/node": "*",
|
||||
@@ -70,7 +70,7 @@
|
||||
"chai-as-promised": "^7.1.0",
|
||||
"chai-bignumber": "^3.0.0",
|
||||
"dirty-chai": "^2.0.1",
|
||||
"ethereum-types": "^3.1.0",
|
||||
"ethereum-types": "^3.1.1",
|
||||
"lodash": "^4.17.11",
|
||||
"make-promises-safe": "^1.1.0",
|
||||
"mocha": "^6.2.0",
|
||||
@@ -82,7 +82,7 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^6.2.1"
|
||||
"@0x/base-contract": "^6.2.2"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
@@ -10,8 +10,11 @@ import * as DummyMultipleReturnERC20Token from '../test/generated-artifacts/Dumm
|
||||
import * as DummyNoReturnERC20Token from '../test/generated-artifacts/DummyNoReturnERC20Token.json';
|
||||
import * as ERC20Token from '../test/generated-artifacts/ERC20Token.json';
|
||||
import * as IERC20Token from '../test/generated-artifacts/IERC20Token.json';
|
||||
import * as IERC20TokenV06 from '../test/generated-artifacts/IERC20TokenV06.json';
|
||||
import * as IEtherToken from '../test/generated-artifacts/IEtherToken.json';
|
||||
import * as IEtherTokenV06 from '../test/generated-artifacts/IEtherTokenV06.json';
|
||||
import * as LibERC20Token from '../test/generated-artifacts/LibERC20Token.json';
|
||||
import * as LibERC20TokenV06 from '../test/generated-artifacts/LibERC20TokenV06.json';
|
||||
import * as MintableERC20Token from '../test/generated-artifacts/MintableERC20Token.json';
|
||||
import * as TestLibERC20Token from '../test/generated-artifacts/TestLibERC20Token.json';
|
||||
import * as TestLibERC20TokenTarget from '../test/generated-artifacts/TestLibERC20TokenTarget.json';
|
||||
@@ -28,6 +31,9 @@ export const artifacts = {
|
||||
ZRXToken: (ZRXToken as any) as ContractArtifact,
|
||||
IERC20Token: IERC20Token as ContractArtifact,
|
||||
IEtherToken: IEtherToken as ContractArtifact,
|
||||
IERC20TokenV06: IERC20TokenV06 as ContractArtifact,
|
||||
IEtherTokenV06: IEtherTokenV06 as ContractArtifact,
|
||||
LibERC20TokenV06: LibERC20TokenV06 as ContractArtifact,
|
||||
DummyERC20Token: DummyERC20Token as ContractArtifact,
|
||||
DummyMultipleReturnERC20Token: DummyMultipleReturnERC20Token as ContractArtifact,
|
||||
DummyNoReturnERC20Token: DummyNoReturnERC20Token as ContractArtifact,
|
||||
|
@@ -8,8 +8,11 @@ export * from '../test/generated-wrappers/dummy_multiple_return_erc20_token';
|
||||
export * from '../test/generated-wrappers/dummy_no_return_erc20_token';
|
||||
export * from '../test/generated-wrappers/erc20_token';
|
||||
export * from '../test/generated-wrappers/i_erc20_token';
|
||||
export * from '../test/generated-wrappers/i_erc20_token_v06';
|
||||
export * from '../test/generated-wrappers/i_ether_token';
|
||||
export * from '../test/generated-wrappers/i_ether_token_v06';
|
||||
export * from '../test/generated-wrappers/lib_erc20_token';
|
||||
export * from '../test/generated-wrappers/lib_erc20_token_v06';
|
||||
export * from '../test/generated-wrappers/mintable_erc20_token';
|
||||
export * from '../test/generated-wrappers/test_lib_erc20_token';
|
||||
export * from '../test/generated-wrappers/test_lib_erc20_token_target';
|
||||
|
@@ -14,8 +14,11 @@
|
||||
"test/generated-artifacts/DummyNoReturnERC20Token.json",
|
||||
"test/generated-artifacts/ERC20Token.json",
|
||||
"test/generated-artifacts/IERC20Token.json",
|
||||
"test/generated-artifacts/IERC20TokenV06.json",
|
||||
"test/generated-artifacts/IEtherToken.json",
|
||||
"test/generated-artifacts/IEtherTokenV06.json",
|
||||
"test/generated-artifacts/LibERC20Token.json",
|
||||
"test/generated-artifacts/LibERC20TokenV06.json",
|
||||
"test/generated-artifacts/MintableERC20Token.json",
|
||||
"test/generated-artifacts/TestLibERC20Token.json",
|
||||
"test/generated-artifacts/TestLibERC20TokenTarget.json",
|
||||
|
@@ -1,4 +1,40 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1592969527,
|
||||
"version": "3.1.6",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1583220306,
|
||||
"version": "3.1.5",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1582837861,
|
||||
"version": "3.1.4",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1582677073,
|
||||
"version": "3.1.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1582623685,
|
||||
"version": "3.1.2",
|
||||
|
@@ -5,6 +5,22 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v3.1.6 - _June 24, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.5 - _March 3, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.4 - _February 27, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.3 - _February 26, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.2 - _February 25, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -3,6 +3,7 @@
|
||||
"contractsDir": "./contracts",
|
||||
"useDockerisedSolc": false,
|
||||
"isOfflineMode": false,
|
||||
"shouldSaveStandardInput": true,
|
||||
"compilerSettings": {
|
||||
"evmVersion": "istanbul",
|
||||
"optimizer": {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-erc721",
|
||||
"version": "3.1.2",
|
||||
"version": "3.1.6",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -52,18 +52,18 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^5.2.1",
|
||||
"@0x/contracts-gen": "^2.0.8",
|
||||
"@0x/contracts-test-utils": "^5.3.0",
|
||||
"@0x/contracts-utils": "^4.4.0",
|
||||
"@0x/dev-utils": "^3.2.1",
|
||||
"@0x/sol-compiler": "^4.0.8",
|
||||
"@0x/abi-gen": "^5.3.0",
|
||||
"@0x/contracts-gen": "^2.0.9",
|
||||
"@0x/contracts-test-utils": "^5.3.3",
|
||||
"@0x/contracts-utils": "^4.5.0",
|
||||
"@0x/dev-utils": "^3.2.2",
|
||||
"@0x/sol-compiler": "^4.1.0",
|
||||
"@0x/ts-doc-gen": "^0.0.22",
|
||||
"@0x/tslint-config": "^4.0.0",
|
||||
"@0x/types": "^3.1.2",
|
||||
"@0x/typescript-typings": "^5.0.2",
|
||||
"@0x/utils": "^5.4.1",
|
||||
"@0x/web3-wrapper": "^7.0.7",
|
||||
"@0x/types": "^3.1.3",
|
||||
"@0x/typescript-typings": "^5.1.0",
|
||||
"@0x/utils": "^5.5.0",
|
||||
"@0x/web3-wrapper": "^7.1.0",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/node": "*",
|
||||
@@ -71,7 +71,7 @@
|
||||
"chai-as-promised": "^7.1.0",
|
||||
"chai-bignumber": "^3.0.0",
|
||||
"dirty-chai": "^2.0.1",
|
||||
"ethereum-types": "^3.1.0",
|
||||
"ethereum-types": "^3.1.1",
|
||||
"lodash": "^4.17.11",
|
||||
"make-promises-safe": "^1.1.0",
|
||||
"mocha": "^6.2.0",
|
||||
@@ -84,7 +84,7 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^6.2.1"
|
||||
"@0x/base-contract": "^6.2.2"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
@@ -1,4 +1,40 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1592969527,
|
||||
"version": "4.2.6",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1583220306,
|
||||
"version": "4.2.5",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1582837861,
|
||||
"version": "4.2.4",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1582677073,
|
||||
"version": "4.2.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1582623685,
|
||||
"version": "4.2.2",
|
||||
|
@@ -5,6 +5,22 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v4.2.6 - _June 24, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.2.5 - _March 3, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.2.4 - _February 27, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.2.3 - _February 26, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.2.2 - _February 25, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -3,6 +3,7 @@
|
||||
"contractsDir": "./contracts",
|
||||
"useDockerisedSolc": false,
|
||||
"isOfflineMode": false,
|
||||
"shouldSaveStandardInput": true,
|
||||
"compilerSettings": {
|
||||
"evmVersion": "istanbul",
|
||||
"optimizer": {
|
||||
|
@@ -151,6 +151,72 @@ contract Forwarder is
|
||||
_unwrapAndTransferEth(wethRemaining);
|
||||
}
|
||||
|
||||
/// @dev Purchases as much of orders' makerAssets as possible by selling the specified amount of ETH
|
||||
/// accounting for order and forwarder fees. This functions throws if ethSellAmount was not reached.
|
||||
/// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset.
|
||||
/// @param ethSellAmount Desired amount of ETH to sell.
|
||||
/// @param signatures Proofs that orders have been created by makers.
|
||||
/// @param ethFeeAmounts Amounts of ETH, denominated in Wei, that are paid to corresponding feeRecipients.
|
||||
/// @param feeRecipients Addresses that will receive ETH when orders are filled.
|
||||
/// @return wethSpentAmount Amount of WETH spent on the given set of orders.
|
||||
/// @return makerAssetAcquiredAmount Amount of maker asset acquired from the given set of orders.
|
||||
function marketSellAmountWithEth(
|
||||
LibOrder.Order[] memory orders,
|
||||
uint256 ethSellAmount,
|
||||
bytes[] memory signatures,
|
||||
uint256[] memory ethFeeAmounts,
|
||||
address payable[] memory feeRecipients
|
||||
)
|
||||
public
|
||||
payable
|
||||
returns (
|
||||
uint256 wethSpentAmount,
|
||||
uint256 makerAssetAcquiredAmount
|
||||
)
|
||||
{
|
||||
if (ethSellAmount > msg.value) {
|
||||
LibRichErrors.rrevert(LibForwarderRichErrors.CompleteSellFailedError(
|
||||
ethSellAmount,
|
||||
msg.value
|
||||
));
|
||||
}
|
||||
// Pay ETH affiliate fees to all feeRecipient addresses
|
||||
uint256 wethRemaining = _transferEthFeesAndWrapRemaining(
|
||||
ethFeeAmounts,
|
||||
feeRecipients
|
||||
);
|
||||
// Need enough remaining to ensure we can sell ethSellAmount
|
||||
if (wethRemaining < ethSellAmount) {
|
||||
LibRichErrors.rrevert(LibForwarderRichErrors.OverspentWethError(
|
||||
wethRemaining,
|
||||
ethSellAmount
|
||||
));
|
||||
}
|
||||
// Spends up to ethSellAmount to fill orders, transfers purchased assets to msg.sender,
|
||||
// and pays WETH order fees.
|
||||
(
|
||||
wethSpentAmount,
|
||||
makerAssetAcquiredAmount
|
||||
) = _marketSellExactAmountNoThrow(
|
||||
orders,
|
||||
ethSellAmount,
|
||||
signatures
|
||||
);
|
||||
// Ensure we sold the specified amount (note: wethSpentAmount includes fees)
|
||||
if (wethSpentAmount < ethSellAmount) {
|
||||
LibRichErrors.rrevert(LibForwarderRichErrors.CompleteSellFailedError(
|
||||
ethSellAmount,
|
||||
wethSpentAmount
|
||||
));
|
||||
}
|
||||
|
||||
// Calculate amount of WETH that hasn't been spent.
|
||||
wethRemaining = wethRemaining.safeSub(wethSpentAmount);
|
||||
|
||||
// Refund remaining ETH to msg.sender.
|
||||
_unwrapAndTransferEth(wethRemaining);
|
||||
}
|
||||
|
||||
/// @dev Attempt to buy makerAssetBuyAmount of makerAsset by selling ETH provided with transaction.
|
||||
/// The Forwarder may *fill* more than makerAssetBuyAmount of the makerAsset so that it can
|
||||
/// pay takerFees where takerFeeAssetData == makerAssetData (i.e. percentage fees).
|
||||
|
@@ -53,6 +53,7 @@ contract MixinExchangeWrapper {
|
||||
// ")"
|
||||
// )));
|
||||
bytes4 constant public EXCHANGE_V2_ORDER_ID = 0x770501f8;
|
||||
bytes4 constant internal ERC20_BRIDGE_PROXY_ID = 0xdc1600f3;
|
||||
|
||||
// solhint-disable var-name-mixedcase
|
||||
IExchange internal EXCHANGE;
|
||||
@@ -73,6 +74,12 @@ contract MixinExchangeWrapper {
|
||||
EXCHANGE_V2 = IExchangeV2(_exchangeV2);
|
||||
}
|
||||
|
||||
struct SellFillResults {
|
||||
uint256 wethSpentAmount;
|
||||
uint256 makerAssetAcquiredAmount;
|
||||
uint256 protocolFeePaid;
|
||||
}
|
||||
|
||||
/// @dev Fills the input order.
|
||||
/// Returns false if the transaction would otherwise revert.
|
||||
/// @param order Order struct containing order specifications.
|
||||
@@ -115,11 +122,16 @@ contract MixinExchangeWrapper {
|
||||
uint256 remainingTakerAssetFillAmount
|
||||
)
|
||||
internal
|
||||
returns (
|
||||
uint256 wethSpentAmount,
|
||||
uint256 makerAssetAcquiredAmount
|
||||
)
|
||||
returns (SellFillResults memory sellFillResults)
|
||||
{
|
||||
// If the maker asset is ERC20Bridge, take a snapshot of the Forwarder contract's balance.
|
||||
bytes4 makerAssetProxyId = order.makerAssetData.readBytes4(0);
|
||||
address tokenAddress;
|
||||
uint256 balanceBefore;
|
||||
if (makerAssetProxyId == ERC20_BRIDGE_PROXY_ID) {
|
||||
tokenAddress = order.makerAssetData.readAddress(16);
|
||||
balanceBefore = IERC20Token(tokenAddress).balanceOf(address(this));
|
||||
}
|
||||
// No taker fee or percentage fee
|
||||
if (
|
||||
order.takerFee == 0 ||
|
||||
@@ -132,11 +144,11 @@ contract MixinExchangeWrapper {
|
||||
signature
|
||||
);
|
||||
|
||||
wethSpentAmount = singleFillResults.takerAssetFilledAmount
|
||||
.safeAdd(singleFillResults.protocolFeePaid);
|
||||
sellFillResults.wethSpentAmount = singleFillResults.takerAssetFilledAmount;
|
||||
sellFillResults.protocolFeePaid = singleFillResults.protocolFeePaid;
|
||||
|
||||
// Subtract fee from makerAssetFilledAmount for the net amount acquired.
|
||||
makerAssetAcquiredAmount = singleFillResults.makerAssetFilledAmount
|
||||
sellFillResults.makerAssetAcquiredAmount = singleFillResults.makerAssetFilledAmount
|
||||
.safeSub(singleFillResults.takerFeePaid);
|
||||
|
||||
// WETH fee
|
||||
@@ -157,18 +169,27 @@ contract MixinExchangeWrapper {
|
||||
);
|
||||
|
||||
// WETH is also spent on the taker fee, so we add it here.
|
||||
wethSpentAmount = singleFillResults.takerAssetFilledAmount
|
||||
.safeAdd(singleFillResults.takerFeePaid)
|
||||
.safeAdd(singleFillResults.protocolFeePaid);
|
||||
|
||||
makerAssetAcquiredAmount = singleFillResults.makerAssetFilledAmount;
|
||||
sellFillResults.wethSpentAmount = singleFillResults.takerAssetFilledAmount
|
||||
.safeAdd(singleFillResults.takerFeePaid);
|
||||
sellFillResults.makerAssetAcquiredAmount = singleFillResults.makerAssetFilledAmount;
|
||||
sellFillResults.protocolFeePaid = singleFillResults.protocolFeePaid;
|
||||
|
||||
// Unsupported fee
|
||||
} else {
|
||||
LibRichErrors.rrevert(LibForwarderRichErrors.UnsupportedFeeError(order.takerFeeAssetData));
|
||||
}
|
||||
|
||||
return (wethSpentAmount, makerAssetAcquiredAmount);
|
||||
// Account for the ERC20Bridge transfering more of the maker asset than expected.
|
||||
if (makerAssetProxyId == ERC20_BRIDGE_PROXY_ID) {
|
||||
uint256 balanceAfter = IERC20Token(tokenAddress).balanceOf(address(this));
|
||||
sellFillResults.makerAssetAcquiredAmount = LibSafeMath.max256(
|
||||
balanceAfter.safeSub(balanceBefore),
|
||||
sellFillResults.makerAssetAcquiredAmount
|
||||
);
|
||||
}
|
||||
|
||||
order.makerAssetData.transferOut(sellFillResults.makerAssetAcquiredAmount);
|
||||
return sellFillResults;
|
||||
}
|
||||
|
||||
/// @dev Synchronously executes multiple calls of fillOrder until total amount of WETH has been sold by taker.
|
||||
@@ -189,7 +210,6 @@ contract MixinExchangeWrapper {
|
||||
)
|
||||
{
|
||||
uint256 protocolFee = tx.gasprice.safeMul(EXCHANGE.protocolFeeMultiplier());
|
||||
bytes4 erc20BridgeProxyId = IAssetData(address(0)).ERC20Bridge.selector;
|
||||
|
||||
for (uint256 i = 0; i != orders.length; i++) {
|
||||
// Preemptively skip to avoid division by zero in _marketSellSingleOrder
|
||||
@@ -199,42 +219,27 @@ contract MixinExchangeWrapper {
|
||||
|
||||
// The remaining amount of WETH to sell
|
||||
uint256 remainingTakerAssetFillAmount = wethSellAmount
|
||||
.safeSub(totalWethSpentAmount)
|
||||
.safeSub(_isV2Order(orders[i]) ? 0 : 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));
|
||||
.safeSub(totalWethSpentAmount);
|
||||
uint256 currentProtocolFee = _isV2Order(orders[i]) ? 0 : protocolFee;
|
||||
if (remainingTakerAssetFillAmount > currentProtocolFee) {
|
||||
// Do not count the protocol fee as part of the fill amount.
|
||||
remainingTakerAssetFillAmount = remainingTakerAssetFillAmount.safeSub(currentProtocolFee);
|
||||
} else {
|
||||
// Stop if we don't have at least enough ETH to pay another protocol fee.
|
||||
break;
|
||||
}
|
||||
|
||||
(
|
||||
uint256 wethSpentAmount,
|
||||
uint256 makerAssetAcquiredAmount
|
||||
) = _marketSellSingleOrder(
|
||||
SellFillResults memory sellFillResults = _marketSellSingleOrder(
|
||||
orders[i],
|
||||
signatures[i],
|
||||
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
|
||||
);
|
||||
}
|
||||
|
||||
orders[i].makerAssetData.transferOut(makerAssetAcquiredAmount);
|
||||
|
||||
totalWethSpentAmount = totalWethSpentAmount
|
||||
.safeAdd(wethSpentAmount);
|
||||
.safeAdd(sellFillResults.wethSpentAmount)
|
||||
.safeAdd(sellFillResults.protocolFeePaid);
|
||||
totalMakerAssetAcquiredAmount = totalMakerAssetAcquiredAmount
|
||||
.safeAdd(makerAssetAcquiredAmount);
|
||||
.safeAdd(sellFillResults.makerAssetAcquiredAmount);
|
||||
|
||||
// Stop execution if the entire amount of WETH has been sold
|
||||
if (totalWethSpentAmount >= wethSellAmount) {
|
||||
@@ -243,6 +248,56 @@ contract MixinExchangeWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Synchronously executes multiple calls of fillOrder until total amount of WETH (exclusive of protocol fee)
|
||||
/// has been sold by taker.
|
||||
/// @param orders Array of order specifications.
|
||||
/// @param wethSellAmount Desired amount of WETH to sell.
|
||||
/// @param signatures Proofs that orders have been signed by makers.
|
||||
/// @return totalWethSpentAmount Total amount of WETH spent on the given orders.
|
||||
/// @return totalMakerAssetAcquiredAmount Total amount of maker asset acquired from the given orders.
|
||||
function _marketSellExactAmountNoThrow(
|
||||
LibOrder.Order[] memory orders,
|
||||
uint256 wethSellAmount,
|
||||
bytes[] memory signatures
|
||||
)
|
||||
internal
|
||||
returns (
|
||||
uint256 totalWethSpentAmount,
|
||||
uint256 totalMakerAssetAcquiredAmount
|
||||
)
|
||||
{
|
||||
uint256 totalProtocolFeePaid;
|
||||
|
||||
for (uint256 i = 0; i != orders.length; i++) {
|
||||
// Preemptively skip to avoid division by zero in _marketSellSingleOrder
|
||||
if (orders[i].makerAssetAmount == 0 || orders[i].takerAssetAmount == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// The remaining amount of WETH to sell
|
||||
uint256 remainingTakerAssetFillAmount = wethSellAmount
|
||||
.safeSub(totalWethSpentAmount);
|
||||
|
||||
SellFillResults memory sellFillResults = _marketSellSingleOrder(
|
||||
orders[i],
|
||||
signatures[i],
|
||||
remainingTakerAssetFillAmount
|
||||
);
|
||||
|
||||
totalWethSpentAmount = totalWethSpentAmount
|
||||
.safeAdd(sellFillResults.wethSpentAmount);
|
||||
totalMakerAssetAcquiredAmount = totalMakerAssetAcquiredAmount
|
||||
.safeAdd(sellFillResults.makerAssetAcquiredAmount);
|
||||
totalProtocolFeePaid = totalProtocolFeePaid.safeAdd(sellFillResults.protocolFeePaid);
|
||||
|
||||
// Stop execution if the entire amount of WETH has been sold
|
||||
if (totalWethSpentAmount >= wethSellAmount) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
totalWethSpentAmount = totalWethSpentAmount.safeAdd(totalProtocolFeePaid);
|
||||
}
|
||||
|
||||
/// @dev Executes a single call of fillOrder according to the makerAssetBuyAmount and
|
||||
/// the amount already bought.
|
||||
/// @param order A single order specification.
|
||||
@@ -338,8 +393,6 @@ contract MixinExchangeWrapper {
|
||||
uint256 totalMakerAssetAcquiredAmount
|
||||
)
|
||||
{
|
||||
bytes4 erc20BridgeProxyId = IAssetData(address(0)).ERC20Bridge.selector;
|
||||
|
||||
uint256 ordersLength = orders.length;
|
||||
for (uint256 i = 0; i != ordersLength; i++) {
|
||||
// Preemptively skip to avoid division by zero in _marketBuySingleOrder
|
||||
@@ -354,7 +407,7 @@ contract MixinExchangeWrapper {
|
||||
bytes4 makerAssetProxyId = orders[i].makerAssetData.readBytes4(0);
|
||||
address tokenAddress;
|
||||
uint256 balanceBefore;
|
||||
if (makerAssetProxyId == erc20BridgeProxyId) {
|
||||
if (makerAssetProxyId == ERC20_BRIDGE_PROXY_ID) {
|
||||
tokenAddress = orders[i].makerAssetData.readAddress(16);
|
||||
balanceBefore = IERC20Token(tokenAddress).balanceOf(address(this));
|
||||
}
|
||||
@@ -369,7 +422,7 @@ contract MixinExchangeWrapper {
|
||||
);
|
||||
|
||||
// Account for the ERC20Bridge transfering more of the maker asset than expected.
|
||||
if (makerAssetProxyId == erc20BridgeProxyId) {
|
||||
if (makerAssetProxyId == ERC20_BRIDGE_PROXY_ID) {
|
||||
uint256 balanceAfter = IERC20Token(tokenAddress).balanceOf(address(this));
|
||||
makerAssetAcquiredAmount = LibSafeMath.max256(
|
||||
balanceAfter.safeSub(balanceBefore),
|
||||
|
@@ -29,6 +29,10 @@ library LibForwarderRichErrors {
|
||||
bytes4 internal constant COMPLETE_BUY_FAILED_ERROR_SELECTOR =
|
||||
0x91353a0c;
|
||||
|
||||
// bytes4(keccak256("CompleteSellFailedError(uint256,uint256)"))
|
||||
bytes4 internal constant COMPLETE_SELL_FAILED_ERROR_SELECTOR =
|
||||
0x450a0219;
|
||||
|
||||
// bytes4(keccak256("UnsupportedFeeError(bytes)"))
|
||||
bytes4 internal constant UNSUPPORTED_FEE_ERROR_SELECTOR =
|
||||
0x31360af1;
|
||||
@@ -61,6 +65,21 @@ library LibForwarderRichErrors {
|
||||
);
|
||||
}
|
||||
|
||||
function CompleteSellFailedError(
|
||||
uint256 expectedAssetSellAmount,
|
||||
uint256 actualAssetSellAmount
|
||||
)
|
||||
internal
|
||||
pure
|
||||
returns (bytes memory)
|
||||
{
|
||||
return abi.encodeWithSelector(
|
||||
COMPLETE_SELL_FAILED_ERROR_SELECTOR,
|
||||
expectedAssetSellAmount,
|
||||
actualAssetSellAmount
|
||||
);
|
||||
}
|
||||
|
||||
function UnsupportedFeeError(
|
||||
bytes memory takerFeeAssetData
|
||||
)
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-exchange-forwarder",
|
||||
"version": "4.2.2",
|
||||
"version": "4.2.6",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -52,25 +52,25 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^5.2.1",
|
||||
"@0x/contracts-asset-proxy": "^3.2.2",
|
||||
"@0x/contracts-dev-utils": "^1.3.0",
|
||||
"@0x/contracts-erc1155": "^2.1.2",
|
||||
"@0x/contracts-erc20": "^3.1.2",
|
||||
"@0x/contracts-erc721": "^3.1.2",
|
||||
"@0x/contracts-exchange": "^3.2.2",
|
||||
"@0x/contracts-exchange-libs": "^4.3.2",
|
||||
"@0x/contracts-gen": "^2.0.8",
|
||||
"@0x/contracts-test-utils": "^5.3.0",
|
||||
"@0x/contracts-utils": "^4.4.0",
|
||||
"@0x/dev-utils": "^3.2.1",
|
||||
"@0x/order-utils": "^10.2.2",
|
||||
"@0x/sol-compiler": "^4.0.8",
|
||||
"@0x/abi-gen": "^5.3.0",
|
||||
"@0x/contracts-asset-proxy": "^3.3.0",
|
||||
"@0x/contracts-dev-utils": "^1.3.4",
|
||||
"@0x/contracts-erc1155": "^2.1.6",
|
||||
"@0x/contracts-erc20": "^3.2.0",
|
||||
"@0x/contracts-erc721": "^3.1.6",
|
||||
"@0x/contracts-exchange": "^3.2.6",
|
||||
"@0x/contracts-exchange-libs": "^4.3.6",
|
||||
"@0x/contracts-gen": "^2.0.9",
|
||||
"@0x/contracts-test-utils": "^5.3.3",
|
||||
"@0x/contracts-utils": "^4.5.0",
|
||||
"@0x/dev-utils": "^3.2.2",
|
||||
"@0x/order-utils": "^10.2.5",
|
||||
"@0x/sol-compiler": "^4.1.0",
|
||||
"@0x/ts-doc-gen": "^0.0.22",
|
||||
"@0x/tslint-config": "^4.0.0",
|
||||
"@0x/types": "^3.1.2",
|
||||
"@0x/utils": "^5.4.1",
|
||||
"@0x/web3-wrapper": "^7.0.7",
|
||||
"@0x/types": "^3.1.3",
|
||||
"@0x/utils": "^5.5.0",
|
||||
"@0x/web3-wrapper": "^7.1.0",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/node": "*",
|
||||
@@ -90,9 +90,9 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^6.2.1",
|
||||
"@0x/typescript-typings": "^5.0.2",
|
||||
"ethereum-types": "^3.1.0"
|
||||
"@0x/base-contract": "^6.2.2",
|
||||
"@0x/typescript-typings": "^5.1.0",
|
||||
"ethereum-types": "^3.1.1"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
@@ -1,4 +1,40 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1592969527,
|
||||
"version": "4.3.6",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1583220306,
|
||||
"version": "4.3.5",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1582837861,
|
||||
"version": "4.3.4",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1582677073,
|
||||
"version": "4.3.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1582623685,
|
||||
"version": "4.3.2",
|
||||
|
@@ -5,6 +5,22 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v4.3.6 - _June 24, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.3.5 - _March 3, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.3.4 - _February 27, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.3.3 - _February 26, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.3.2 - _February 25, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -3,6 +3,7 @@
|
||||
"contractsDir": "./contracts",
|
||||
"useDockerisedSolc": false,
|
||||
"isOfflineMode": false,
|
||||
"shouldSaveStandardInput": true,
|
||||
"compilerSettings": {
|
||||
"evmVersion": "istanbul",
|
||||
"optimizer": {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-exchange-libs",
|
||||
"version": "4.3.2",
|
||||
"version": "4.3.6",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -52,14 +52,14 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/libs/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^5.2.1",
|
||||
"@0x/contracts-gen": "^2.0.8",
|
||||
"@0x/dev-utils": "^3.2.1",
|
||||
"@0x/sol-compiler": "^4.0.8",
|
||||
"@0x/subproviders": "^6.0.8",
|
||||
"@0x/abi-gen": "^5.3.0",
|
||||
"@0x/contracts-gen": "^2.0.9",
|
||||
"@0x/dev-utils": "^3.2.2",
|
||||
"@0x/sol-compiler": "^4.1.0",
|
||||
"@0x/subproviders": "^6.1.0",
|
||||
"@0x/ts-doc-gen": "^0.0.22",
|
||||
"@0x/tslint-config": "^4.0.0",
|
||||
"@0x/web3-wrapper": "^7.0.7",
|
||||
"@0x/web3-wrapper": "^7.1.0",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/node": "*",
|
||||
@@ -80,14 +80,14 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^6.2.1",
|
||||
"@0x/contracts-test-utils": "^5.3.0",
|
||||
"@0x/contracts-utils": "^4.4.0",
|
||||
"@0x/order-utils": "^10.2.2",
|
||||
"@0x/types": "^3.1.2",
|
||||
"@0x/typescript-typings": "^5.0.2",
|
||||
"@0x/utils": "^5.4.1",
|
||||
"ethereum-types": "^3.1.0"
|
||||
"@0x/base-contract": "^6.2.2",
|
||||
"@0x/contracts-test-utils": "^5.3.3",
|
||||
"@0x/contracts-utils": "^4.5.0",
|
||||
"@0x/order-utils": "^10.2.5",
|
||||
"@0x/types": "^3.1.3",
|
||||
"@0x/typescript-typings": "^5.1.0",
|
||||
"@0x/utils": "^5.5.0",
|
||||
"ethereum-types": "^3.1.1"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
@@ -1,4 +1,40 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1592969527,
|
||||
"version": "3.2.6",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1583220306,
|
||||
"version": "3.2.5",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1582837861,
|
||||
"version": "3.2.4",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1582677073,
|
||||
"version": "3.2.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1582623685,
|
||||
"version": "3.2.2",
|
||||
|
@@ -5,6 +5,22 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v3.2.6 - _June 24, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.2.5 - _March 3, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.2.4 - _February 27, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.2.3 - _February 26, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.2.2 - _February 25, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user