Compare commits
484 Commits
@0x/types@
...
@0x/sol-co
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 | ||
|
2dd47a2103 | ||
|
f31e530b6e | ||
|
eb50f3fa9c | ||
|
8417fe4fdb | ||
|
0f1c15a6ca | ||
|
9345c4fb7f | ||
|
5e0758917b | ||
|
f56839ec6b | ||
|
cbb23a42e2 | ||
|
5a59c286d0 | ||
|
2e8c600d7a | ||
|
e851cb1cbc | ||
|
3da03da32a | ||
|
3ec8924e7f | ||
|
a04722b612 | ||
|
f9a7857a90 | ||
|
58f772c74e | ||
|
a425c7e260 | ||
|
809885afd0 | ||
|
bf9b4b993f | ||
|
d94a26f0f4 | ||
|
bd9c9cedca | ||
|
651e94bd94 | ||
|
162b6f1a74 | ||
|
865a253eb5 | ||
|
9b3781abf1 | ||
|
d89243a0d3 | ||
|
b4cefc64b4 | ||
|
2fd26587e5 | ||
|
282a351859 | ||
|
a84b848ea9 | ||
|
d329320fc2 | ||
|
bc096554b5 | ||
|
5902d878d8 | ||
|
4a133ca36f | ||
|
f7252f919a | ||
|
e05a03a842 | ||
|
dcce8276b8 | ||
|
fd47947e55 | ||
|
ae151df2eb | ||
|
79de188683 | ||
|
6e5c788e13 | ||
|
f53606007d | ||
|
a4ac418bc9 | ||
|
a8c09d0bdb | ||
|
871105a48a | ||
|
3b61129ade | ||
|
f471c79b59 | ||
|
dfd9443f74 | ||
|
a36ff9e365 | ||
|
12e65bbf26 | ||
|
ab9841e60b | ||
|
7a52f12e57 | ||
|
11fd4506ac | ||
|
0c9c68030e | ||
|
55d6eddbb2 | ||
|
8341e60edb | ||
|
6273a1ca73 | ||
|
1b83ebdf89 | ||
|
fef7f0506f | ||
|
f44eb4e383 | ||
|
05df485c4a | ||
|
44857c526b | ||
|
b8ad5d5d32 | ||
|
e3e0d00e21 | ||
|
a9b1ea9690 | ||
|
8e5dd0f8d9 | ||
|
3b0c8f6d92 | ||
|
21058c2227 | ||
|
f3b8ae0781 | ||
|
d590b004c1 | ||
|
02e21141c6 | ||
|
f839a3087d | ||
|
f458815541 | ||
|
7ba754d2a4 | ||
|
09706e4ae2 | ||
|
d18de4c541 | ||
|
001c7bfdbc | ||
|
d4e04dc712 | ||
|
8650cb5217 | ||
|
0eaaddeb95 | ||
|
1cf8663f20 | ||
|
5130259552 | ||
|
ba5e19a015 | ||
|
c0400fa986 | ||
|
51179d10ce | ||
|
78752f9178 | ||
|
762db417d7 | ||
|
85bdccbc06 | ||
|
9f8cb99340 | ||
|
87b90bb04b | ||
|
e961d88277 |
@@ -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,7 +1,94 @@
|
||||
[
|
||||
{
|
||||
"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",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1581748629,
|
||||
"version": "3.2.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "3.2.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add more types and functions to `IDydx`",
|
||||
"pr": 2466
|
||||
},
|
||||
{
|
||||
"note": "Rename `DydxBrigeAction.accountId` to `DydxBridgeAction.accountIdx`",
|
||||
"pr": 2466
|
||||
},
|
||||
{
|
||||
"note": "Fix broken tests.",
|
||||
"pr": 2462
|
||||
@@ -13,6 +100,10 @@
|
||||
{
|
||||
"note": "Add asset data decoding functions",
|
||||
"pr": 2462
|
||||
},
|
||||
{
|
||||
"note": "Add `setOperators()` to `IDydx`",
|
||||
"pr": 2462
|
||||
}
|
||||
],
|
||||
"timestamp": 1581204851
|
||||
|
@@ -5,11 +5,44 @@ 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
|
||||
|
||||
## v3.2.1 - _February 15, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.2.0 - _February 8, 2020_
|
||||
|
||||
* Add more types and functions to `IDydx` (#2466)
|
||||
* Rename `DydxBrigeAction.accountId` to `DydxBridgeAction.accountIdx` (#2466)
|
||||
* Fix broken tests. (#2462)
|
||||
* Remove dependency on `@0x/contracts-dev-utils` (#2462)
|
||||
* Add asset data decoding functions (#2462)
|
||||
* 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,
|
||||
|
128
contracts/asset-proxy/contracts/src/bridges/CurveBridge.sol
Normal file
128
contracts/asset-proxy/contracts/src/bridges/CurveBridge.sol
Normal file
@@ -0,0 +1,128 @@
|
||||
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
|
||||
import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol";
|
||||
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
|
||||
import "../interfaces/IERC20Bridge.sol";
|
||||
import "../interfaces/ICurve.sol";
|
||||
import "./MixinGasToken.sol";
|
||||
|
||||
|
||||
// solhint-disable not-rely-on-time
|
||||
// solhint-disable space-after-comma
|
||||
contract CurveBridge is
|
||||
IERC20Bridge,
|
||||
IWallet,
|
||||
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 to,
|
||||
uint256 amount,
|
||||
bytes calldata bridgeData
|
||||
)
|
||||
external
|
||||
freesGasTokensFromCollector
|
||||
returns (bytes4 success)
|
||||
{
|
||||
// Decode the bridge data to get the Curve metadata.
|
||||
CurveBridgeData memory data = abi.decode(bridgeData, (CurveBridgeData));
|
||||
|
||||
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.approveIfBelow(fromTokenAddress, data.curveAddress, fromTokenBalance);
|
||||
|
||||
// Try to sell all of this contract's `fromTokenAddress` token balance.
|
||||
if (data.version == 0) {
|
||||
ICurve(data.curveAddress).exchange_underlying(
|
||||
data.fromCoinIdx,
|
||||
data.toCoinIdx,
|
||||
// dx
|
||||
fromTokenBalance,
|
||||
// min dy
|
||||
amount,
|
||||
// expires
|
||||
block.timestamp + 1
|
||||
);
|
||||
} else {
|
||||
ICurve(data.curveAddress).exchange_underlying(
|
||||
data.fromCoinIdx,
|
||||
data.toCoinIdx,
|
||||
// dx
|
||||
fromTokenBalance,
|
||||
// min dy
|
||||
amount
|
||||
);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/// @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;
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
|
||||
@@ -191,12 +192,12 @@ contract DydxBridge is
|
||||
depositAction = IDydx.ActionArgs({
|
||||
actionType: IDydx.ActionType.Deposit, // deposit tokens.
|
||||
amount: dydxAmount, // amount to deposit.
|
||||
accountId: bridgeAction.accountId, // index in the `accounts` when calling `operate`.
|
||||
accountIdx: bridgeAction.accountIdx, // index in the `accounts` when calling `operate`.
|
||||
primaryMarketId: bridgeAction.marketId, // indicates which token to deposit.
|
||||
otherAddress: depositFrom, // deposit from the account owner.
|
||||
// unused parameters
|
||||
secondaryMarketId: 0,
|
||||
otherAccountId: 0,
|
||||
otherAccountIdx: 0,
|
||||
data: hex''
|
||||
});
|
||||
}
|
||||
@@ -229,12 +230,12 @@ contract DydxBridge is
|
||||
withdrawAction = IDydx.ActionArgs({
|
||||
actionType: IDydx.ActionType.Withdraw, // withdraw tokens.
|
||||
amount: amountToWithdraw, // amount to withdraw.
|
||||
accountId: bridgeAction.accountId, // index in the `accounts` when calling `operate`.
|
||||
accountIdx: bridgeAction.accountIdx, // index in the `accounts` when calling `operate`.
|
||||
primaryMarketId: bridgeAction.marketId, // indicates which token to withdraw.
|
||||
otherAddress: withdrawTo, // withdraw tokens to this address.
|
||||
// unused parameters
|
||||
secondaryMarketId: 0,
|
||||
otherAccountId: 0,
|
||||
otherAccountIdx: 0,
|
||||
data: hex''
|
||||
});
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
86
contracts/asset-proxy/contracts/src/interfaces/ICurve.sol
Normal file
86
contracts/asset-proxy/contracts/src/interfaces/ICurve.sol
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
|
||||
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;
|
||||
|
||||
|
||||
// solhint-disable func-name-mixedcase
|
||||
interface ICurve {
|
||||
|
||||
/// @dev Sell `sellAmount` of `fromToken` token and receive `toToken` token.
|
||||
/// This function exists on early versions of Curve (USDC/DAI)
|
||||
/// @param i The token index being sold.
|
||||
/// @param j The token index being bought.
|
||||
/// @param sellAmount The amount of token being bought.
|
||||
/// @param minBuyAmount The minimum buy amount of the token being bought.
|
||||
/// @param deadline The time in seconds when this operation should expire.
|
||||
function exchange_underlying(
|
||||
int128 i,
|
||||
int128 j,
|
||||
uint256 sellAmount,
|
||||
uint256 minBuyAmount,
|
||||
uint256 deadline
|
||||
)
|
||||
external;
|
||||
|
||||
/// @dev Sell `sellAmount` of `fromToken` token and receive `toToken` token.
|
||||
/// 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 sellAmount The amount of token being bought.
|
||||
/// @param minBuyAmount The minimum buy amount of the token being bought.
|
||||
function exchange_underlying(
|
||||
int128 i,
|
||||
int128 j,
|
||||
uint256 sellAmount,
|
||||
uint256 minBuyAmount
|
||||
)
|
||||
external;
|
||||
|
||||
/// @dev Get the amount of `toToken` by selling `sellAmount` of `fromToken`
|
||||
/// @param i The token index being sold.
|
||||
/// @param j The token index being bought.
|
||||
/// @param sellAmount The amount of token being bought.
|
||||
function get_dy_underlying(
|
||||
int128 i,
|
||||
int128 j,
|
||||
uint256 sellAmount
|
||||
)
|
||||
external
|
||||
returns (uint256 dy);
|
||||
|
||||
/// @dev Get the amount of `fromToken` by buying `buyAmount` of `toToken`
|
||||
/// @param i The token index being sold.
|
||||
/// @param j The token index being bought.
|
||||
/// @param buyAmount The amount of token being bought.
|
||||
function get_dx_underlying(
|
||||
int128 i,
|
||||
int128 j,
|
||||
uint256 buyAmount
|
||||
)
|
||||
external
|
||||
returns (uint256 dx);
|
||||
|
||||
/// @dev Get the underlying token address from the token index
|
||||
/// @param i The token index.
|
||||
function underlying_coins(
|
||||
int128 i
|
||||
)
|
||||
external
|
||||
returns (address tokenAddress);
|
||||
}
|
@@ -45,12 +45,12 @@ interface IDydx {
|
||||
/// parsed into before being processed.
|
||||
struct ActionArgs {
|
||||
ActionType actionType;
|
||||
uint256 accountId;
|
||||
uint256 accountIdx;
|
||||
AssetAmount amount;
|
||||
uint256 primaryMarketId;
|
||||
uint256 secondaryMarketId;
|
||||
address otherAddress;
|
||||
uint256 otherAccountId;
|
||||
uint256 otherAccountIdx;
|
||||
bytes data;
|
||||
}
|
||||
|
||||
@@ -71,6 +71,36 @@ interface IDydx {
|
||||
uint256 value;
|
||||
}
|
||||
|
||||
struct D256 {
|
||||
uint256 value;
|
||||
}
|
||||
|
||||
struct Value {
|
||||
uint256 value;
|
||||
}
|
||||
|
||||
struct Price {
|
||||
uint256 value;
|
||||
}
|
||||
|
||||
struct OperatorArg {
|
||||
address operator;
|
||||
bool trusted;
|
||||
}
|
||||
|
||||
/// @dev The global risk parameters that govern the health and security of the system
|
||||
struct RiskParams {
|
||||
// Required ratio of over-collateralization
|
||||
D256 marginRatio;
|
||||
// Percentage penalty incurred by liquidated accounts
|
||||
D256 liquidationSpread;
|
||||
// Percentage of the borrower's interest fee that gets passed to the suppliers
|
||||
D256 earningsRate;
|
||||
// The minimum absolute borrow value of an account
|
||||
// There must be sufficient incentivize to liquidate undercollateralized accounts
|
||||
Value minBorrowedValue;
|
||||
}
|
||||
|
||||
/// @dev The main entry-point to Solo that allows users and contracts to manage accounts.
|
||||
/// Take one or more actions on one or more accounts. The msg.sender must be the owner or
|
||||
/// operator of all accounts except for those being liquidated, vaporized, or traded with.
|
||||
@@ -86,4 +116,77 @@ interface IDydx {
|
||||
ActionArgs[] calldata actions
|
||||
)
|
||||
external;
|
||||
|
||||
// @dev Approves/disapproves any number of operators. An operator is an external address that has the
|
||||
// same permissions to manipulate an account as the owner of the account. Operators are simply
|
||||
// addresses and therefore may either be externally-owned Ethereum accounts OR smart contracts.
|
||||
// Operators are also able to act as AutoTrader contracts on behalf of the account owner if the
|
||||
// operator is a smart contract and implements the IAutoTrader interface.
|
||||
// @param args A list of OperatorArgs which have an address and a boolean. The boolean value
|
||||
// denotes whether to approve (true) or revoke approval (false) for that address.
|
||||
function setOperators(OperatorArg[] calldata args) external;
|
||||
|
||||
/// @dev Return true if a particular address is approved as an operator for an owner's accounts.
|
||||
/// Approved operators can act on the accounts of the owner as if it were the operator's own.
|
||||
/// @param owner The owner of the accounts
|
||||
/// @param operator The possible operator
|
||||
/// @return isLocalOperator True if operator is approved for owner's accounts
|
||||
function getIsLocalOperator(
|
||||
address owner,
|
||||
address operator
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (bool isLocalOperator);
|
||||
|
||||
/// @dev Get the ERC20 token address for a market.
|
||||
/// @param marketId The market to query
|
||||
/// @return tokenAddress The token address
|
||||
function getMarketTokenAddress(
|
||||
uint256 marketId
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (address tokenAddress);
|
||||
|
||||
/// @dev Get all risk parameters in a single struct.
|
||||
/// @return riskParams All global risk parameters
|
||||
function getRiskParams()
|
||||
external
|
||||
view
|
||||
returns (RiskParams memory riskParams);
|
||||
|
||||
/// @dev Get the price of the token for a market.
|
||||
/// @param marketId The market to query
|
||||
/// @return price The price of each atomic unit of the token
|
||||
function getMarketPrice(
|
||||
uint256 marketId
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (Price memory price);
|
||||
|
||||
/// @dev Get the margin premium for a market. A margin premium makes it so that any positions that
|
||||
/// include the market require a higher collateralization to avoid being liquidated.
|
||||
/// @param marketId The market to query
|
||||
/// @return premium The market's margin premium
|
||||
function getMarketMarginPremium(uint256 marketId)
|
||||
external
|
||||
view
|
||||
returns (D256 memory premium);
|
||||
|
||||
/// @dev Get the total supplied and total borrowed values of an account adjusted by the marginPremium
|
||||
/// of each market. Supplied values are divided by (1 + marginPremium) for each market and
|
||||
/// borrowed values are multiplied by (1 + marginPremium) for each market. Comparing these
|
||||
/// adjusted values gives the margin-ratio of the account which will be compared to the global
|
||||
/// margin-ratio when determining if the account can be liquidated.
|
||||
/// @param account The account to query
|
||||
/// @return supplyValue The supplied value of the account (adjusted for marginPremium)
|
||||
/// @return borrowValue The borrowed value of the account (adjusted for marginPremium)
|
||||
function getAdjustedAccountValues(
|
||||
AccountInfo calldata account
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (Value memory supplyValue, Value memory borrowValue);
|
||||
}
|
||||
|
@@ -29,7 +29,7 @@ interface IDydxBridge {
|
||||
|
||||
struct BridgeAction {
|
||||
BridgeActionType actionType; // Action to run on dydx account.
|
||||
uint256 accountId; // Index in `BridgeData.accountNumbers` for this action.
|
||||
uint256 accountIdx; // Index in `BridgeData.accountNumbers` for this action.
|
||||
uint256 marketId; // Market to operate on.
|
||||
uint256 conversionRateNumerator; // Optional. If set, transfer amount is scaled by (conversionRateNumerator/conversionRateDenominator).
|
||||
uint256 conversionRateDenominator; // Optional. If set, transfer amount is scaled by (conversionRateNumerator/conversionRateDenominator).
|
||||
@@ -39,4 +39,4 @@ interface IDydxBridge {
|
||||
uint256[] accountNumbers; // Account number used to identify the owner's specific account.
|
||||
BridgeAction[] actions; // Actions to carry out on the owner's accounts.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -23,6 +23,7 @@ import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||
import "../src/bridges/DydxBridge.sol";
|
||||
|
||||
|
||||
// solhint-disable no-empty-blocks
|
||||
contract TestDydxBridgeToken {
|
||||
|
||||
uint256 private constant INIT_HOLDER_BALANCE = 10 * 10**18; // 10 tokens
|
||||
@@ -79,7 +80,7 @@ contract TestDydxBridge is
|
||||
|
||||
event OperateAction(
|
||||
ActionType actionType,
|
||||
uint256 accountId,
|
||||
uint256 accountIdx,
|
||||
bool amountSign,
|
||||
AssetDenomination amountDenomination,
|
||||
AssetReference amountRef,
|
||||
@@ -120,7 +121,7 @@ contract TestDydxBridge is
|
||||
for (uint i = 0; i < actions.length; ++i) {
|
||||
emit OperateAction(
|
||||
actions[i].actionType,
|
||||
actions[i].accountId,
|
||||
actions[i].accountIdx,
|
||||
actions[i].amount.sign,
|
||||
actions[i].amount.denomination,
|
||||
actions[i].amount.ref,
|
||||
@@ -128,7 +129,7 @@ contract TestDydxBridge is
|
||||
actions[i].primaryMarketId,
|
||||
actions[i].secondaryMarketId,
|
||||
actions[i].otherAddress,
|
||||
actions[i].otherAccountId,
|
||||
actions[i].otherAccountIdx,
|
||||
actions[i].data
|
||||
);
|
||||
|
||||
@@ -171,6 +172,60 @@ contract TestDydxBridge is
|
||||
return _testTokenAddress;
|
||||
}
|
||||
|
||||
/// @dev Unused.
|
||||
function setOperators(OperatorArg[] calldata args) external {}
|
||||
|
||||
/// @dev Unused.
|
||||
function getIsLocalOperator(
|
||||
address owner,
|
||||
address operator
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (bool isLocalOperator)
|
||||
{}
|
||||
|
||||
/// @dev Unused.
|
||||
function getMarketTokenAddress(
|
||||
uint256 marketId
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (address tokenAddress)
|
||||
{}
|
||||
|
||||
/// @dev Unused.
|
||||
function getRiskParams()
|
||||
external
|
||||
view
|
||||
returns (RiskParams memory riskParams)
|
||||
{}
|
||||
|
||||
/// @dev Unsused.
|
||||
function getMarketPrice(
|
||||
uint256 marketId
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (Price memory price)
|
||||
{}
|
||||
|
||||
/// @dev Unsused
|
||||
function getMarketMarginPremium(uint256 marketId)
|
||||
external
|
||||
view
|
||||
returns (IDydx.D256 memory premium)
|
||||
{}
|
||||
|
||||
/// @dev Unused.
|
||||
function getAdjustedAccountValues(
|
||||
AccountInfo calldata account
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (Value memory supplyValue, Value memory borrowValue)
|
||||
{}
|
||||
|
||||
/// @dev overrides `_getDydxAddress()` from `DeploymentConstants` to return this address.
|
||||
function _getDydxAddress()
|
||||
internal
|
||||
@@ -188,4 +243,4 @@ contract TestDydxBridge is
|
||||
{
|
||||
return msg.sender == ALWAYS_REVERT_ADDRESS ? address(0) : msg.sender;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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.0",
|
||||
"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|DydxBridge|ERC1155Proxy|ERC20BridgeProxy|ERC20Proxy|ERC721Proxy|Eth2DaiBridge|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|IChai|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.0",
|
||||
"@0x/contracts-gen": "^2.0.7",
|
||||
"@0x/contracts-test-utils": "^5.1.4",
|
||||
"@0x/contracts-utils": "^4.3.0",
|
||||
"@0x/dev-utils": "^3.2.0",
|
||||
"@0x/sol-compiler": "^4.0.7",
|
||||
"@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.0",
|
||||
"@0x/contracts-erc1155": "^2.1.0",
|
||||
"@0x/contracts-erc20": "^3.1.0",
|
||||
"@0x/contracts-erc721": "^3.1.0",
|
||||
"@0x/contracts-exchange-libs": "^4.3.0",
|
||||
"@0x/order-utils": "^10.2.0",
|
||||
"@0x/types": "^3.1.2",
|
||||
"@0x/typescript-typings": "^5.0.2",
|
||||
"@0x/utils": "^5.4.0",
|
||||
"@0x/web3-wrapper": "^7.0.6",
|
||||
"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": {
|
||||
|
@@ -6,6 +6,8 @@
|
||||
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';
|
||||
@@ -17,27 +19,34 @@ import * as IAssetProxy from '../generated-artifacts/IAssetProxy.json';
|
||||
import * as IAssetProxyDispatcher from '../generated-artifacts/IAssetProxyDispatcher.json';
|
||||
import * as IAuthorizable from '../generated-artifacts/IAuthorizable.json';
|
||||
import * as IChai from '../generated-artifacts/IChai.json';
|
||||
import * as ICurve from '../generated-artifacts/ICurve.json';
|
||||
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,
|
||||
@@ -49,27 +58,36 @@ export const artifacts = {
|
||||
MultiAssetProxy: MultiAssetProxy as ContractArtifact,
|
||||
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,
|
||||
IAuthorizable: IAuthorizable as ContractArtifact,
|
||||
IChai: IChai as ContractArtifact,
|
||||
ICurve: ICurve as ContractArtifact,
|
||||
IDydx: IDydx as ContractArtifact,
|
||||
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' },
|
||||
],
|
||||
},
|
||||
]);
|
@@ -7,7 +7,7 @@ export enum DydxBridgeActionType {
|
||||
|
||||
export interface DydxBridgeAction {
|
||||
actionType: DydxBridgeActionType;
|
||||
accountId: BigNumber;
|
||||
accountIdx: BigNumber;
|
||||
marketId: BigNumber;
|
||||
conversionRateNumerator: BigNumber;
|
||||
conversionRateDenominator: BigNumber;
|
||||
@@ -29,7 +29,7 @@ export const dydxBridgeDataEncoder = AbiEncoder.create([
|
||||
type: 'tuple[]',
|
||||
components: [
|
||||
{ name: 'actionType', type: 'uint8' },
|
||||
{ name: 'accountId', type: 'uint256' },
|
||||
{ name: 'accountIdx', type: 'uint256' },
|
||||
{ name: 'marketId', type: 'uint256' },
|
||||
{ name: 'conversionRateNumerator', type: 'uint256' },
|
||||
{ name: 'conversionRateDenominator', type: 'uint256' },
|
||||
|
@@ -1,21 +1,22 @@
|
||||
export { artifacts } from './artifacts';
|
||||
export {
|
||||
ChaiBridgeContract,
|
||||
ERC1155ProxyContract,
|
||||
ERC20BridgeProxyContract,
|
||||
ERC20ProxyContract,
|
||||
ERC721ProxyContract,
|
||||
Eth2DaiBridgeContract,
|
||||
DydxBridgeContract,
|
||||
TestDydxBridgeContract,
|
||||
IAssetDataContract,
|
||||
IAssetProxyContract,
|
||||
IChaiContract,
|
||||
IDydxContract,
|
||||
KyberBridgeContract,
|
||||
MultiAssetProxyContract,
|
||||
StaticCallProxyContract,
|
||||
TestDydxBridgeContract,
|
||||
TestStaticCallTargetContract,
|
||||
UniswapBridgeContract,
|
||||
KyberBridgeContract,
|
||||
ChaiBridgeContract,
|
||||
IChaiContract,
|
||||
} from './wrappers';
|
||||
|
||||
export { ERC20Wrapper } from './erc20_wrapper';
|
||||
@@ -87,3 +88,4 @@ export {
|
||||
} from './asset_data';
|
||||
|
||||
export * from './dydx_bridge_encoder';
|
||||
export * from './dex_forwarder_bridge';
|
||||
|
@@ -4,6 +4,8 @@
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
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';
|
||||
@@ -15,24 +17,31 @@ export * from '../generated-wrappers/i_asset_proxy';
|
||||
export * from '../generated-wrappers/i_asset_proxy_dispatcher';
|
||||
export * from '../generated-wrappers/i_authorizable';
|
||||
export * from '../generated-wrappers/i_chai';
|
||||
export * from '../generated-wrappers/i_curve';
|
||||
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';
|
||||
|
@@ -6,6 +6,8 @@
|
||||
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';
|
||||
@@ -17,27 +19,34 @@ import * as IAssetProxy from '../test/generated-artifacts/IAssetProxy.json';
|
||||
import * as IAssetProxyDispatcher from '../test/generated-artifacts/IAssetProxyDispatcher.json';
|
||||
import * as IAuthorizable from '../test/generated-artifacts/IAuthorizable.json';
|
||||
import * as IChai from '../test/generated-artifacts/IChai.json';
|
||||
import * as ICurve from '../test/generated-artifacts/ICurve.json';
|
||||
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,
|
||||
@@ -49,27 +58,36 @@ export const artifacts = {
|
||||
MultiAssetProxy: MultiAssetProxy as ContractArtifact,
|
||||
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,
|
||||
IAuthorizable: IAuthorizable as ContractArtifact,
|
||||
IChai: IChai as ContractArtifact,
|
||||
ICurve: ICurve as ContractArtifact,
|
||||
IDydx: IDydx as ContractArtifact,
|
||||
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');
|
||||
});
|
||||
});
|
||||
});
|
@@ -17,14 +17,14 @@ blockchainTests.resets('DydxBridge unit tests', env => {
|
||||
const notAuthorized = '0x0000000000000000000000000000000000000001';
|
||||
const defaultDepositAction = {
|
||||
actionType: DydxBridgeActionType.Deposit,
|
||||
accountId: constants.ZERO_AMOUNT,
|
||||
accountIdx: constants.ZERO_AMOUNT,
|
||||
marketId,
|
||||
conversionRateNumerator: constants.ZERO_AMOUNT,
|
||||
conversionRateDenominator: constants.ZERO_AMOUNT,
|
||||
};
|
||||
const defaultWithdrawAction = {
|
||||
actionType: DydxBridgeActionType.Withdraw,
|
||||
accountId: constants.ZERO_AMOUNT,
|
||||
accountIdx: constants.ZERO_AMOUNT,
|
||||
marketId,
|
||||
conversionRateNumerator: constants.ZERO_AMOUNT,
|
||||
conversionRateDenominator: constants.ZERO_AMOUNT,
|
||||
@@ -118,7 +118,7 @@ blockchainTests.resets('DydxBridge unit tests', env => {
|
||||
for (const action of bridgeData.actions) {
|
||||
expectedOperateActionEvents.push({
|
||||
actionType: action.actionType as number,
|
||||
accountId: action.accountId,
|
||||
accountIdx: action.accountIdx,
|
||||
amountSign: action.actionType === DydxBridgeActionType.Deposit ? true : false,
|
||||
amountDenomination: weiDenomination,
|
||||
amountRef: deltaAmountRef,
|
||||
|
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');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@@ -4,6 +4,8 @@
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
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';
|
||||
@@ -15,24 +17,31 @@ export * from '../test/generated-wrappers/i_asset_proxy';
|
||||
export * from '../test/generated-wrappers/i_asset_proxy_dispatcher';
|
||||
export * from '../test/generated-wrappers/i_authorizable';
|
||||
export * from '../test/generated-wrappers/i_chai';
|
||||
export * from '../test/generated-wrappers/i_curve';
|
||||
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';
|
||||
|
@@ -4,6 +4,8 @@
|
||||
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
|
||||
"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",
|
||||
@@ -15,28 +17,37 @@
|
||||
"generated-artifacts/IAssetProxyDispatcher.json",
|
||||
"generated-artifacts/IAuthorizable.json",
|
||||
"generated-artifacts/IChai.json",
|
||||
"generated-artifacts/ICurve.json",
|
||||
"generated-artifacts/IDydx.json",
|
||||
"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",
|
||||
@@ -48,27 +59,34 @@
|
||||
"test/generated-artifacts/IAssetProxyDispatcher.json",
|
||||
"test/generated-artifacts/IAuthorizable.json",
|
||||
"test/generated-artifacts/IChai.json",
|
||||
"test/generated-artifacts/ICurve.json",
|
||||
"test/generated-artifacts/IDydx.json",
|
||||
"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,60 @@
|
||||
[
|
||||
{
|
||||
"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",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "1.1.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Added decoders for broker data",
|
||||
"pr": 2484
|
||||
}
|
||||
],
|
||||
"timestamp": 1581748629
|
||||
},
|
||||
{
|
||||
"timestamp": 1581204851,
|
||||
"version": "1.0.2",
|
||||
|
@@ -5,6 +5,30 @@ 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
|
||||
|
||||
## v1.1.0 - _February 15, 2020_
|
||||
|
||||
* Added decoders for broker data (#2484)
|
||||
|
||||
## v1.0.2 - _February 8, 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.0.2",
|
||||
"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.0",
|
||||
"@0x/contracts-asset-proxy": "^3.2.0",
|
||||
"@0x/contracts-erc20": "^3.1.0",
|
||||
"@0x/contracts-erc721": "^3.1.0",
|
||||
"@0x/contracts-exchange": "^3.2.0",
|
||||
"@0x/contracts-exchange-libs": "^4.3.0",
|
||||
"@0x/contracts-gen": "^2.0.7",
|
||||
"@0x/contracts-test-utils": "^5.1.4",
|
||||
"@0x/contracts-utils": "^4.3.0",
|
||||
"@0x/sol-compiler": "^4.0.7",
|
||||
"@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.6",
|
||||
"@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.0",
|
||||
"@0x/order-utils": "^10.2.0",
|
||||
"@0x/typescript-typings": "^5.0.2",
|
||||
"@0x/utils": "^5.4.0",
|
||||
"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"
|
||||
|
@@ -1,42 +1,59 @@
|
||||
import { assetDataUtils } from '@0x/order-utils';
|
||||
import { ERC1155AssetData } from '@0x/types';
|
||||
import { AbiEncoder, BigNumber } from '@0x/utils';
|
||||
|
||||
export const godsUnchainedUtils = {
|
||||
/**
|
||||
* Encodes the given proto and quality into the bytes format expected by the GodsUnchainedValidator.
|
||||
*/
|
||||
encodePropertyData(proto: BigNumber, quality: BigNumber): string {
|
||||
return AbiEncoder.create([{ name: 'proto', type: 'uint16' }, { name: 'quality', type: 'uint8' }]).encode({
|
||||
proto,
|
||||
quality,
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Encodes the given proto and quality into ERC1155 asset data to be used as the takerAssetData
|
||||
* of a property-based GodsUnchained order. Must also provide the addresses of the Broker,
|
||||
* GodsUnchained, and GodsUnchainedValidator contracts. The optional bundleSize parameter specifies
|
||||
* how many cards are expected for each "unit" of the takerAssetAmount. For example, If the
|
||||
* takerAssetAmount is 3 and the bundleSize is 2, the taker must provide 2, 4, or 6 cards
|
||||
* with the given proto and quality to fill the order. If an odd number is provided, the fill fails.
|
||||
*/
|
||||
encodeBrokerAssetData(
|
||||
brokerAddress: string,
|
||||
godsUnchainedAddress: string,
|
||||
validatorAddress: string,
|
||||
proto: BigNumber,
|
||||
quality: BigNumber,
|
||||
bundleSize: number = 1,
|
||||
): string {
|
||||
const dataEncoder = AbiEncoder.create([
|
||||
{ name: 'godsUnchainedAddress', type: 'address' },
|
||||
{ name: 'validatorAddress', type: 'address' },
|
||||
{ name: 'propertyData', type: 'bytes' },
|
||||
]);
|
||||
const propertyData = AbiEncoder.create([
|
||||
{ name: 'proto', type: 'uint16' },
|
||||
{ name: 'quality', type: 'uint8' },
|
||||
]).encode({ proto, quality });
|
||||
const data = dataEncoder.encode({ godsUnchainedAddress, validatorAddress, propertyData });
|
||||
return assetDataUtils.encodeERC1155AssetData(brokerAddress, [], [new BigNumber(bundleSize)], data);
|
||||
},
|
||||
};
|
||||
export interface GodsUnchainedProperties {
|
||||
proto: BigNumber | number;
|
||||
quality: BigNumber | number;
|
||||
}
|
||||
|
||||
const propertyDataEncoder = AbiEncoder.create([{ name: 'proto', type: 'uint16' }, { name: 'quality', type: 'uint8' }]);
|
||||
const brokerDataEncoder = AbiEncoder.create([
|
||||
{ name: 'godsUnchainedAddress', type: 'address' },
|
||||
{ name: 'validatorAddress', type: 'address' },
|
||||
{ name: 'propertyData', type: 'bytes' },
|
||||
]);
|
||||
|
||||
/**
|
||||
* Encodes the given proto and quality into the bytes format expected by the GodsUnchainedValidator.
|
||||
*/
|
||||
export function encodePropertyData(properties: GodsUnchainedProperties): string {
|
||||
return propertyDataEncoder.encode(properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the given proto and quality into ERC1155 asset data to be used as the takerAssetData
|
||||
* of a property-based GodsUnchained order. Must also provide the addresses of the Broker,
|
||||
* GodsUnchained, and GodsUnchainedValidator contracts. The optional bundleSize parameter specifies
|
||||
* how many cards are expected for each "unit" of the takerAssetAmount. For example, If the
|
||||
* takerAssetAmount is 3 and the bundleSize is 2, the taker must provide 2, 4, or 6 cards
|
||||
* with the given proto and quality to fill the order. If an odd number is provided, the fill fails.
|
||||
*/
|
||||
export function encodeBrokerAssetData(
|
||||
brokerAddress: string,
|
||||
godsUnchainedAddress: string,
|
||||
validatorAddress: string,
|
||||
properties: GodsUnchainedProperties,
|
||||
bundleSize: number = 1,
|
||||
): string {
|
||||
const propertyData = propertyDataEncoder.encode(properties);
|
||||
const brokerData = brokerDataEncoder.encode({ godsUnchainedAddress, validatorAddress, propertyData });
|
||||
return assetDataUtils.encodeERC1155AssetData(brokerAddress, [], [new BigNumber(bundleSize)], brokerData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes proto and quality from the bytes format expected by the GodsUnchainedValidator.
|
||||
*/
|
||||
export function decodePropertyData(propertyData: string): GodsUnchainedProperties {
|
||||
return propertyDataEncoder.decode(propertyData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes proto and quality from the ERC1155 takerAssetData of a property-based GodsUnchained order.
|
||||
*/
|
||||
export function decodeBrokerAssetData(brokerAssetData: string): GodsUnchainedProperties {
|
||||
// tslint:disable-next-line:no-unnecessary-type-assertion
|
||||
const { callbackData: brokerData } = assetDataUtils.decodeAssetDataOrThrow(brokerAssetData) as ERC1155AssetData;
|
||||
const { propertyData } = brokerDataEncoder.decode(brokerData);
|
||||
return decodePropertyData(propertyData);
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
export { artifacts } from './artifacts';
|
||||
export { BrokerContract, GodsUnchainedValidatorContract, TestGodsUnchainedContract } from './wrappers';
|
||||
export { godsUnchainedUtils } from './gods_unchained_utils';
|
||||
export * from './gods_unchained_utils';
|
||||
export { BrokerRevertErrors } from '@0x/utils';
|
||||
export {
|
||||
ContractArtifact,
|
||||
|
@@ -2,7 +2,7 @@ import { blockchainTests, constants, expect, getRandomInteger } from '@0x/contra
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { godsUnchainedUtils } from '../src/gods_unchained_utils';
|
||||
import { encodePropertyData } from '../src/gods_unchained_utils';
|
||||
|
||||
import { artifacts } from './artifacts';
|
||||
import { GodsUnchainedValidatorContract, TestGodsUnchainedContract } from './wrappers';
|
||||
@@ -33,7 +33,7 @@ blockchainTests.resets('GodsUnchainedValidator unit tests', env => {
|
||||
describe('checkBrokerAsset', () => {
|
||||
const proto = new BigNumber(42);
|
||||
const quality = new BigNumber(7);
|
||||
const propertyData = godsUnchainedUtils.encodePropertyData(proto, quality);
|
||||
const propertyData = encodePropertyData({ proto, quality });
|
||||
|
||||
it('succeeds if assetData proto and quality match propertyData', async () => {
|
||||
const tokenId = getRandomInteger(0, constants.MAX_UINT256);
|
||||
@@ -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,58 @@
|
||||
[
|
||||
{
|
||||
"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",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1581748629,
|
||||
"version": "3.1.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "3.1.0",
|
||||
"changes": [
|
||||
|
@@ -5,6 +5,30 @@ 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
|
||||
|
||||
## v3.1.1 - _February 15, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.0 - _February 8, 2020_
|
||||
|
||||
* Update tests. (#2462)
|
||||
|
@@ -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.0",
|
||||
"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.0",
|
||||
"@0x/contracts-asset-proxy": "^3.2.0",
|
||||
"@0x/contracts-dev-utils": "^1.1.0",
|
||||
"@0x/contracts-erc20": "^3.1.0",
|
||||
"@0x/contracts-exchange": "^3.2.0",
|
||||
"@0x/contracts-gen": "^2.0.7",
|
||||
"@0x/contracts-test-utils": "^5.1.4",
|
||||
"@0x/dev-utils": "^3.2.0",
|
||||
"@0x/order-utils": "^10.2.0",
|
||||
"@0x/sol-compiler": "^4.0.7",
|
||||
"@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.6",
|
||||
"@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.6",
|
||||
"@0x/base-contract": "^6.2.0",
|
||||
"@0x/contract-addresses": "^4.5.0",
|
||||
"@0x/contracts-utils": "^4.3.0",
|
||||
"@0x/json-schemas": "^5.0.6",
|
||||
"@0x/types": "^3.1.2",
|
||||
"@0x/typescript-typings": "^5.0.2",
|
||||
"@0x/utils": "^5.4.0",
|
||||
"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,68 @@
|
||||
[
|
||||
{
|
||||
"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": [
|
||||
{
|
||||
"note": "Update `DevUtils` addresses in `DeploymentConstants`",
|
||||
"pr": 2493
|
||||
}
|
||||
],
|
||||
"timestamp": 1582623685
|
||||
},
|
||||
{
|
||||
"version": "1.2.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add `DydxBridge` order validation",
|
||||
"pr": 2466
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1581748629,
|
||||
"version": "1.1.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "1.1.0",
|
||||
"changes": [
|
||||
|
@@ -5,6 +5,34 @@ 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)
|
||||
|
||||
## v1.2.0 - _Invalid date_
|
||||
|
||||
* Add `DydxBridge` order validation (#2466)
|
||||
|
||||
## v1.1.1 - _February 15, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.1.0 - _February 8, 2020_
|
||||
|
||||
* Refactor mixins into public libraries. (#2464)
|
||||
|
@@ -3,6 +3,7 @@
|
||||
"contractsDir": "./contracts",
|
||||
"useDockerisedSolc": false,
|
||||
"isOfflineMode": false,
|
||||
"shouldSaveStandardInput": true,
|
||||
"compilerSettings": {
|
||||
"evmVersion": "istanbul",
|
||||
"optimizer": {
|
||||
|
@@ -34,15 +34,18 @@ contract Addresses is
|
||||
address public erc1155ProxyAddress;
|
||||
address public staticCallProxyAddress;
|
||||
address public chaiBridgeAddress;
|
||||
address public dydxBridgeAddress;
|
||||
|
||||
constructor (
|
||||
address exchange_,
|
||||
address chaiBridge_
|
||||
address chaiBridge_,
|
||||
address dydxBridge_
|
||||
)
|
||||
public
|
||||
{
|
||||
exchangeAddress = exchange_;
|
||||
chaiBridgeAddress = chaiBridge_;
|
||||
dydxBridgeAddress = dydxBridge_;
|
||||
erc20ProxyAddress = IExchange(exchange_).getAssetProxy(IAssetData(address(0)).ERC20Token.selector);
|
||||
erc721ProxyAddress = IExchange(exchange_).getAssetProxy(IAssetData(address(0)).ERC721Token.selector);
|
||||
erc1155ProxyAddress = IExchange(exchange_).getAssetProxy(IAssetData(address(0)).ERC1155Assets.selector);
|
||||
|
@@ -28,7 +28,7 @@ import "@0x/contracts-erc1155/contracts/src/interfaces/IERC1155.sol";
|
||||
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IChai.sol";
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
|
||||
import "./Addresses.sol";
|
||||
import "./LibAssetData.sol";
|
||||
import "./LibDydxBalance.sol";
|
||||
|
||||
|
||||
contract AssetBalance is
|
||||
@@ -269,11 +269,14 @@ contract AssetBalance is
|
||||
|
||||
} else if (assetProxyId == IAssetData(address(0)).ERC20Bridge.selector) {
|
||||
// Get address of ERC20 token and bridge contract
|
||||
(, address tokenAddress, address bridgeAddress,) = LibAssetData.decodeERC20BridgeAssetData(assetData);
|
||||
(, address tokenAddress, address bridgeAddress,) =
|
||||
LibAssetData.decodeERC20BridgeAssetData(assetData);
|
||||
if (tokenAddress == _getDaiAddress() && bridgeAddress == chaiBridgeAddress) {
|
||||
uint256 chaiAllowance = LibERC20Token.allowance(_getChaiAddress(), ownerAddress, chaiBridgeAddress);
|
||||
// Dai allowance is unlimited if Chai allowance is unlimited
|
||||
allowance = chaiAllowance == _MAX_UINT256 ? _MAX_UINT256 : _convertChaiToDaiAmount(chaiAllowance);
|
||||
} else if (bridgeAddress == dydxBridgeAddress) {
|
||||
allowance = LibDydxBalance.getDydxMakerAllowance(ownerAddress, bridgeAddress, _getDydxAddress());
|
||||
}
|
||||
// Allowance will be 0 if bridge is not supported
|
||||
}
|
||||
@@ -366,6 +369,17 @@ contract AssetBalance is
|
||||
if (order.makerAssetData.length < 4) {
|
||||
return (0, 0);
|
||||
}
|
||||
bytes4 assetProxyId = order.makerAssetData.readBytes4(0);
|
||||
// Handle dydx bridge assets.
|
||||
if (assetProxyId == IAssetData(address(0)).ERC20Bridge.selector) {
|
||||
(, , address bridgeAddress, ) = LibAssetData.decodeERC20BridgeAssetData(order.makerAssetData);
|
||||
if (bridgeAddress == dydxBridgeAddress) {
|
||||
return (
|
||||
LibDydxBalance.getDydxMakerBalance(order, _getDydxAddress()),
|
||||
getAssetProxyAllowance(order.makerAddress, order.makerAssetData)
|
||||
);
|
||||
}
|
||||
}
|
||||
return (
|
||||
getBalance(order.makerAddress, order.makerAssetData),
|
||||
getAssetProxyAllowance(order.makerAddress, order.makerAssetData)
|
||||
|
@@ -40,12 +40,14 @@ contract DevUtils is
|
||||
{
|
||||
constructor (
|
||||
address exchange_,
|
||||
address chaiBridge_
|
||||
address chaiBridge_,
|
||||
address dydxBridge_
|
||||
)
|
||||
public
|
||||
Addresses(
|
||||
exchange_,
|
||||
chaiBridge_
|
||||
chaiBridge_,
|
||||
dydxBridge_
|
||||
)
|
||||
LibEIP712ExchangeDomain(uint256(0), address(0)) // null args because because we only use constants
|
||||
{}
|
||||
|
436
contracts/dev-utils/contracts/src/LibDydxBalance.sol
Normal file
436
contracts/dev-utils/contracts/src/LibDydxBalance.sol
Normal file
@@ -0,0 +1,436 @@
|
||||
/*
|
||||
|
||||
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;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetData.sol";
|
||||
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IDydxBridge.sol";
|
||||
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IDydx.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
||||
import "@0x/contracts-utils/contracts/src/D18.sol";
|
||||
import "./LibAssetData.sol";
|
||||
|
||||
|
||||
library LibDydxBalance {
|
||||
|
||||
using LibBytes for bytes;
|
||||
using LibSafeMath for uint256;
|
||||
|
||||
/// @dev Padding % added to the minimum collateralization ratio to
|
||||
/// prevent withdrawing exactly the amount that would make an account
|
||||
/// insolvent. 1 bps.
|
||||
int256 private constant MARGIN_RATIO_PADDING = 0.0001e18;
|
||||
|
||||
/// @dev Structure that holds all pertinent info needed to perform a balance
|
||||
/// check.
|
||||
struct BalanceCheckInfo {
|
||||
IDydx dydx;
|
||||
address bridgeAddress;
|
||||
address makerAddress;
|
||||
address makerTokenAddress;
|
||||
address takerTokenAddress;
|
||||
int256 orderMakerToTakerRate;
|
||||
uint256[] accounts;
|
||||
IDydxBridge.BridgeAction[] actions;
|
||||
}
|
||||
|
||||
/// @dev Gets the maker asset allowance for a Dydx bridge order.
|
||||
/// @param makerAddress The maker of the order.
|
||||
/// @param bridgeAddress The address of the Dydx bridge.
|
||||
/// @param dydx The Dydx contract address.
|
||||
/// @return allowance The maker asset allowance.
|
||||
function getDydxMakerAllowance(address makerAddress, address bridgeAddress, address dydx)
|
||||
public
|
||||
view
|
||||
returns (uint256 allowance)
|
||||
{
|
||||
// Allowance is infinite if the dydx bridge is an operator for the maker.
|
||||
return IDydx(dydx).getIsLocalOperator(makerAddress, bridgeAddress)
|
||||
? uint256(-1) : 0;
|
||||
}
|
||||
|
||||
/// @dev Gets the maker allowance for a
|
||||
/// @dev Get the maker asset balance of an order with a `DydxBridge` maker asset.
|
||||
/// @param order An order with a dydx maker asset.
|
||||
/// @param dydx The address of the dydx contract.
|
||||
/// @return balance The maker asset balance.
|
||||
function getDydxMakerBalance(LibOrder.Order memory order, address dydx)
|
||||
public
|
||||
view
|
||||
returns (uint256 balance)
|
||||
{
|
||||
BalanceCheckInfo memory info = _getBalanceCheckInfo(order, dydx);
|
||||
// Actions must be well-formed.
|
||||
if (!_areActionsWellFormed(info)) {
|
||||
return 0;
|
||||
}
|
||||
// If the rate we withdraw maker tokens is less than one, the asset
|
||||
// proxy will throw because we will always transfer less maker tokens
|
||||
// than asked.
|
||||
if (_getMakerTokenWithdrawRate(info) < D18.one()) {
|
||||
return 0;
|
||||
}
|
||||
// The maker balance is the smaller of:
|
||||
return LibSafeMath.min256(
|
||||
// How many times we can execute all the deposit actions.
|
||||
_getDepositableMakerAmount(info),
|
||||
// How many times we can execute all the actions before the an
|
||||
// account becomes undercollateralized.
|
||||
_getSolventMakerAmount(info)
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Checks that:
|
||||
/// 1. Actions are arranged as [...deposits, withdraw].
|
||||
/// 2. There is only one deposit for each market ID.
|
||||
/// 3. Every action has a valid account index.
|
||||
/// 4. There is exactly one withdraw at the end and it is for the
|
||||
/// maker token.
|
||||
/// @param info State from `_getBalanceCheckInfo()`.
|
||||
/// @return areWellFormed Whether the actions are well-formed.
|
||||
function _areActionsWellFormed(BalanceCheckInfo memory info)
|
||||
internal
|
||||
view
|
||||
returns (bool areWellFormed)
|
||||
{
|
||||
if (info.actions.length == 0) {
|
||||
return false;
|
||||
}
|
||||
uint256 depositCount = 0;
|
||||
// Count the number of deposits.
|
||||
for (; depositCount < info.actions.length; ++depositCount) {
|
||||
IDydxBridge.BridgeAction memory action = info.actions[depositCount];
|
||||
if (action.actionType != IDydxBridge.BridgeActionType.Deposit) {
|
||||
break;
|
||||
}
|
||||
// Search all prior actions for the same market ID.
|
||||
uint256 marketId = action.marketId;
|
||||
for (uint256 j = 0; j < depositCount; ++j) {
|
||||
if (info.actions[j].marketId == marketId) {
|
||||
// Market ID is not unique.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Check that the account index is within the valid range.
|
||||
if (action.accountIdx >= info.accounts.length) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// There must be exactly one withdraw action at the end.
|
||||
if (depositCount + 1 != info.actions.length) {
|
||||
return false;
|
||||
}
|
||||
IDydxBridge.BridgeAction memory withdraw = info.actions[depositCount];
|
||||
if (withdraw.actionType != IDydxBridge.BridgeActionType.Withdraw) {
|
||||
return false;
|
||||
}
|
||||
// And it must be for the maker token.
|
||||
if (info.dydx.getMarketTokenAddress(withdraw.marketId) != info.makerTokenAddress) {
|
||||
return false;
|
||||
}
|
||||
// Check the account index.
|
||||
return withdraw.accountIdx < info.accounts.length;
|
||||
}
|
||||
|
||||
/// @dev Returns the rate at which we withdraw maker tokens.
|
||||
/// @param info State from `_getBalanceCheckInfo()`.
|
||||
/// @return makerTokenWithdrawRate Maker token withdraw rate.
|
||||
function _getMakerTokenWithdrawRate(BalanceCheckInfo memory info)
|
||||
internal
|
||||
pure
|
||||
returns (int256 makerTokenWithdrawRate)
|
||||
{
|
||||
// The last action is always a withdraw for the maker token.
|
||||
IDydxBridge.BridgeAction memory withdraw = info.actions[info.actions.length - 1];
|
||||
return _getActionRate(withdraw);
|
||||
}
|
||||
|
||||
/// @dev Get how much maker asset we can transfer before a deposit fails.
|
||||
/// @param info State from `_getBalanceCheckInfo()`.
|
||||
function _getDepositableMakerAmount(BalanceCheckInfo memory info)
|
||||
internal
|
||||
view
|
||||
returns (uint256 depositableMakerAmount)
|
||||
{
|
||||
depositableMakerAmount = uint256(-1);
|
||||
// Take the minimum maker amount from all deposits.
|
||||
for (uint256 i = 0; i < info.actions.length; ++i) {
|
||||
IDydxBridge.BridgeAction memory action = info.actions[i];
|
||||
// Only looking at deposit actions.
|
||||
if (action.actionType != IDydxBridge.BridgeActionType.Deposit) {
|
||||
continue;
|
||||
}
|
||||
// `depositRate` is the rate at which we convert a maker token into
|
||||
// a taker token for deposit.
|
||||
int256 depositRate = _getActionRate(action);
|
||||
// Taker tokens will be transferred to the maker for every fill, so
|
||||
// we reduce the effective deposit rate if we're depositing the taker
|
||||
// token.
|
||||
address depositToken = info.dydx.getMarketTokenAddress(action.marketId);
|
||||
if (info.takerTokenAddress != address(0) && depositToken == info.takerTokenAddress) {
|
||||
depositRate = D18.sub(depositRate, info.orderMakerToTakerRate);
|
||||
}
|
||||
// If the deposit rate is > 0, we are limited by the transferrable
|
||||
// token balance of the maker.
|
||||
if (depositRate > 0) {
|
||||
uint256 supply = _getTransferabeTokenAmount(
|
||||
depositToken,
|
||||
info.makerAddress,
|
||||
address(info.dydx)
|
||||
);
|
||||
depositableMakerAmount = LibSafeMath.min256(
|
||||
depositableMakerAmount,
|
||||
uint256(D18.div(supply, depositRate))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Get how much maker asset we can transfer before an account
|
||||
/// becomes insolvent.
|
||||
/// @param info State from `_getBalanceCheckInfo()`.
|
||||
function _getSolventMakerAmount(BalanceCheckInfo memory info)
|
||||
internal
|
||||
view
|
||||
returns (uint256 solventMakerAmount)
|
||||
{
|
||||
solventMakerAmount = uint256(-1);
|
||||
assert(info.actions.length >= 1);
|
||||
IDydxBridge.BridgeAction memory withdraw = info.actions[info.actions.length - 1];
|
||||
assert(withdraw.actionType == IDydxBridge.BridgeActionType.Withdraw);
|
||||
int256 minCr = D18.add(_getMinimumCollateralizationRatio(info.dydx), MARGIN_RATIO_PADDING);
|
||||
// Loop through the accounts.
|
||||
for (uint256 accountIdx = 0; accountIdx < info.accounts.length; ++accountIdx) {
|
||||
(uint256 supplyValue, uint256 borrowValue) =
|
||||
_getAccountMarketValues(info, info.accounts[accountIdx]);
|
||||
// All accounts must currently be solvent.
|
||||
if (borrowValue != 0 && D18.div(supplyValue, borrowValue) < minCr) {
|
||||
return 0;
|
||||
}
|
||||
// If this is the same account used to in the withdraw/borrow action,
|
||||
// compute the maker amount at which it will become insolvent.
|
||||
if (accountIdx != withdraw.accountIdx) {
|
||||
continue;
|
||||
}
|
||||
// Compute the deposit/collateralization rate, which is the rate at
|
||||
// which (USD) value is added to the account across all markets.
|
||||
int256 dd = 0;
|
||||
for (uint256 i = 0; i < info.actions.length - 1; ++i) {
|
||||
IDydxBridge.BridgeAction memory deposit = info.actions[i];
|
||||
assert(deposit.actionType == IDydxBridge.BridgeActionType.Deposit);
|
||||
if (deposit.accountIdx == accountIdx) {
|
||||
dd = D18.add(
|
||||
dd,
|
||||
_getActionRateValue(
|
||||
info,
|
||||
deposit
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
// Compute the borrow/withdraw rate, which is the rate at which
|
||||
// (USD) value is deducted from the account.
|
||||
int256 db = _getActionRateValue(
|
||||
info,
|
||||
withdraw
|
||||
);
|
||||
// If the deposit to withdraw ratio is >= the minimum collateralization
|
||||
// ratio, then we will never become insolvent at these prices.
|
||||
if (D18.div(dd, db) >= minCr) {
|
||||
continue;
|
||||
}
|
||||
// If the adjusted deposit rates are equal, the account will remain
|
||||
// at the same level of collateralization.
|
||||
if (D18.mul(minCr, db) == dd) {
|
||||
continue;
|
||||
}
|
||||
// The collateralization ratio for this account, parameterized by
|
||||
// `t` (maker amount), is given by:
|
||||
// `cr = (supplyValue + t * dd) / (borrowValue + t * db)`
|
||||
// Solving for `t` gives us:
|
||||
// `t = (supplyValue - cr * borrowValue) / (cr * db - dd)`
|
||||
int256 t = D18.div(
|
||||
D18.sub(supplyValue, D18.mul(minCr, borrowValue)),
|
||||
D18.sub(D18.mul(minCr, db), dd)
|
||||
);
|
||||
solventMakerAmount = LibSafeMath.min256(
|
||||
solventMakerAmount,
|
||||
// `t` is in maker token units, so convert it to maker wei.
|
||||
_toWei(info.makerTokenAddress, uint256(D18.clip(t)))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Create a `BalanceCheckInfo` struct.
|
||||
/// @param order An order with a `DydxBridge` maker asset.
|
||||
/// @param dydx The address of the Dydx contract.
|
||||
/// @return info The `BalanceCheckInfo` struct.
|
||||
function _getBalanceCheckInfo(LibOrder.Order memory order, address dydx)
|
||||
private
|
||||
pure
|
||||
returns (BalanceCheckInfo memory info)
|
||||
{
|
||||
bytes memory rawBridgeData;
|
||||
(, info.makerTokenAddress, info.bridgeAddress, rawBridgeData) =
|
||||
LibAssetData.decodeERC20BridgeAssetData(order.makerAssetData);
|
||||
info.dydx = IDydx(dydx);
|
||||
info.makerAddress = order.makerAddress;
|
||||
if (order.takerAssetData.length == 36) {
|
||||
if (order.takerAssetData.readBytes4(0) == IAssetData(0).ERC20Token.selector) {
|
||||
(, info.takerTokenAddress) =
|
||||
LibAssetData.decodeERC20AssetData(order.takerAssetData);
|
||||
}
|
||||
}
|
||||
info.orderMakerToTakerRate = D18.div(order.takerAssetAmount, order.makerAssetAmount);
|
||||
(IDydxBridge.BridgeData memory bridgeData) =
|
||||
abi.decode(rawBridgeData, (IDydxBridge.BridgeData));
|
||||
info.accounts = bridgeData.accountNumbers;
|
||||
info.actions = bridgeData.actions;
|
||||
}
|
||||
|
||||
/// @dev Returns the conversion rate for an action.
|
||||
/// @param action A `BridgeAction`.
|
||||
function _getActionRate(IDydxBridge.BridgeAction memory action)
|
||||
private
|
||||
pure
|
||||
returns (int256 rate)
|
||||
{
|
||||
rate = action.conversionRateDenominator == 0
|
||||
? D18.one()
|
||||
: D18.div(
|
||||
action.conversionRateNumerator,
|
||||
action.conversionRateDenominator
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Returns the USD value of an action based on its conversion rate
|
||||
/// and market prices.
|
||||
/// @param info State from `_getBalanceCheckInfo()`.
|
||||
/// @param action A `BridgeAction`.
|
||||
function _getActionRateValue(
|
||||
BalanceCheckInfo memory info,
|
||||
IDydxBridge.BridgeAction memory action
|
||||
)
|
||||
private
|
||||
view
|
||||
returns (int256 value)
|
||||
{
|
||||
address toToken = info.dydx.getMarketTokenAddress(action.marketId);
|
||||
uint256 fromTokenDecimals = LibERC20Token.decimals(info.makerTokenAddress);
|
||||
uint256 toTokenDecimals = LibERC20Token.decimals(toToken);
|
||||
// First express the rate as 18-decimal units.
|
||||
value = toTokenDecimals > fromTokenDecimals
|
||||
? int256(
|
||||
uint256(_getActionRate(action))
|
||||
.safeDiv(10 ** (toTokenDecimals - fromTokenDecimals))
|
||||
)
|
||||
: int256(
|
||||
uint256(_getActionRate(action))
|
||||
.safeMul(10 ** (fromTokenDecimals - toTokenDecimals))
|
||||
);
|
||||
// Prices have 18 + (18 - TOKEN_DECIMALS) decimal places because
|
||||
// consistency is stupid.
|
||||
uint256 price = info.dydx.getMarketPrice(action.marketId).value;
|
||||
// Make prices have 18 decimals.
|
||||
if (toTokenDecimals > 18) {
|
||||
price = price.safeMul(10 ** (toTokenDecimals - 18));
|
||||
} else {
|
||||
price = price.safeDiv(10 ** (18 - toTokenDecimals));
|
||||
}
|
||||
// The action value is the action rate times the price.
|
||||
value = D18.mul(price, value);
|
||||
// Scale by the market premium.
|
||||
int256 marketPremium = D18.add(
|
||||
D18.one(),
|
||||
info.dydx.getMarketMarginPremium(action.marketId).value
|
||||
);
|
||||
if (action.actionType == IDydxBridge.BridgeActionType.Deposit) {
|
||||
value = D18.div(value, marketPremium);
|
||||
} else {
|
||||
value = D18.mul(value, marketPremium);
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Convert a `D18` fraction of 1 token to the equivalent integer wei.
|
||||
/// @param token Address the of the token.
|
||||
/// @param units Token units expressed with 18 digit precision.
|
||||
function _toWei(address token, uint256 units)
|
||||
private
|
||||
view
|
||||
returns (uint256 rate)
|
||||
{
|
||||
uint256 decimals = LibERC20Token.decimals(token);
|
||||
rate = decimals > 18
|
||||
? units.safeMul(10 ** (decimals - 18))
|
||||
: units.safeDiv(10 ** (18 - decimals));
|
||||
}
|
||||
|
||||
/// @dev Get the global minimum collateralization ratio required for
|
||||
/// an account to be considered solvent.
|
||||
/// @param dydx The Dydx interface.
|
||||
function _getMinimumCollateralizationRatio(IDydx dydx)
|
||||
private
|
||||
view
|
||||
returns (int256 ratio)
|
||||
{
|
||||
IDydx.RiskParams memory riskParams = dydx.getRiskParams();
|
||||
return D18.add(D18.one(), D18.toSigned(riskParams.marginRatio.value));
|
||||
}
|
||||
|
||||
/// @dev Get the total supply and borrow values for an account across all markets.
|
||||
/// @param info State from `_getBalanceCheckInfo()`.
|
||||
/// @param account The Dydx account identifier.
|
||||
function _getAccountMarketValues(BalanceCheckInfo memory info, uint256 account)
|
||||
private
|
||||
view
|
||||
returns (uint256 supplyValue, uint256 borrowValue)
|
||||
{
|
||||
(IDydx.Value memory supplyValue_, IDydx.Value memory borrowValue_) =
|
||||
info.dydx.getAdjustedAccountValues(IDydx.AccountInfo(
|
||||
info.makerAddress,
|
||||
account
|
||||
));
|
||||
// Account values have 36 decimal places because dydx likes to make sure
|
||||
// you're paying attention.
|
||||
return (supplyValue_.value / 1e18, borrowValue_.value / 1e18);
|
||||
}
|
||||
|
||||
/// @dev Get the amount of an ERC20 token held by `owner` that can be transferred
|
||||
/// by `spender`.
|
||||
/// @param tokenAddress The address of the ERC20 token.
|
||||
/// @param owner The address of the token holder.
|
||||
/// @param spender The address of the token spender.
|
||||
function _getTransferabeTokenAmount(
|
||||
address tokenAddress,
|
||||
address owner,
|
||||
address spender
|
||||
)
|
||||
private
|
||||
view
|
||||
returns (uint256 transferableAmount)
|
||||
{
|
||||
return LibSafeMath.min256(
|
||||
LibERC20Token.allowance(tokenAddress, owner, spender),
|
||||
LibERC20Token.balanceOf(tokenAddress, owner)
|
||||
);
|
||||
}
|
||||
}
|
@@ -118,22 +118,14 @@ contract OrderValidationUtils is
|
||||
transferableTakerAssetAmount
|
||||
);
|
||||
|
||||
// Execute the maker transfers.
|
||||
fillableTakerAssetAmount = LibOrderTransferSimulation.getSimulatedOrderMakerTransferResults(
|
||||
exchangeAddress,
|
||||
order,
|
||||
order.takerAddress,
|
||||
fillableTakerAssetAmount
|
||||
) == LibOrderTransferSimulation.OrderTransferResults.TransfersSuccessful ? fillableTakerAssetAmount : 0;
|
||||
|
||||
if (!_isAssetDataValid(order.takerAssetData)) {
|
||||
fillableTakerAssetAmount = 0;
|
||||
}
|
||||
|
||||
if (order.takerFee != 0 && !_isAssetDataValid(order.takerFeeAssetData)) {
|
||||
// Ensure that all of the asset data is valid. Fee asset data only needs
|
||||
// to be valid if the fees are nonzero.
|
||||
if (!_areOrderAssetDatasValid(order)) {
|
||||
fillableTakerAssetAmount = 0;
|
||||
}
|
||||
|
||||
// If the order is not fillable, then the fillable taker asset amount is
|
||||
// zero by definition.
|
||||
if (orderInfo.orderStatus != LibOrder.OrderStatus.FILLABLE) {
|
||||
fillableTakerAssetAmount = 0;
|
||||
}
|
||||
@@ -207,7 +199,22 @@ contract OrderValidationUtils is
|
||||
{
|
||||
(uint256 balance, uint256 allowance) = _getConvertibleMakerBalanceAndAssetProxyAllowance(order);
|
||||
transferableAssetAmount = LibSafeMath.min256(balance, allowance);
|
||||
return transferableAssetAmount;
|
||||
return LibSafeMath.min256(transferableAssetAmount, order.makerAssetAmount);
|
||||
}
|
||||
|
||||
/// @dev Checks that the asset data contained in a ZeroEx is valid and returns
|
||||
/// a boolean that indicates whether or not the asset data was found to be valid.
|
||||
/// @param order A ZeroEx order to validate.
|
||||
/// @return The validatity of the asset data.
|
||||
function _areOrderAssetDatasValid(LibOrder.Order memory order)
|
||||
internal
|
||||
pure
|
||||
returns (bool)
|
||||
{
|
||||
return _isAssetDataValid(order.makerAssetData) &&
|
||||
(order.makerFee == 0 || _isAssetDataValid(order.makerFeeAssetData)) &&
|
||||
_isAssetDataValid(order.takerAssetData) &&
|
||||
(order.takerFee == 0 || _isAssetDataValid(order.takerFeeAssetData));
|
||||
}
|
||||
|
||||
/// @dev This function handles the edge cases around taker validation. This function
|
||||
|
181
contracts/dev-utils/contracts/test/TestDydx.sol
Normal file
181
contracts/dev-utils/contracts/test/TestDydx.sol
Normal file
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
|
||||
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;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IDydx.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
|
||||
|
||||
|
||||
// solhint-disable separate-by-one-line-in-contract
|
||||
contract TestDydx {
|
||||
|
||||
struct OperatorConfig {
|
||||
address owner;
|
||||
address operator;
|
||||
}
|
||||
|
||||
struct AccountConfig {
|
||||
address owner;
|
||||
uint256 accountId;
|
||||
int256[] balances;
|
||||
}
|
||||
|
||||
struct MarketInfo {
|
||||
address token;
|
||||
uint256 price;
|
||||
}
|
||||
|
||||
struct TestConfig {
|
||||
uint256 marginRatio;
|
||||
OperatorConfig[] operators;
|
||||
AccountConfig[] accounts;
|
||||
MarketInfo[] markets;
|
||||
}
|
||||
|
||||
mapping (bytes32 => bool) private _operators;
|
||||
mapping (bytes32 => int256) private _balance;
|
||||
MarketInfo[] private _markets;
|
||||
uint256 private _marginRatio;
|
||||
|
||||
constructor(TestConfig memory config) public {
|
||||
_marginRatio = config.marginRatio;
|
||||
for (uint256 marketId = 0; marketId < config.markets.length; ++marketId) {
|
||||
_markets.push(config.markets[marketId]);
|
||||
}
|
||||
for (uint256 i = 0; i < config.operators.length; ++i) {
|
||||
OperatorConfig memory op = config.operators[i];
|
||||
_operators[_getOperatorHash(op.owner, op.operator)] = true;
|
||||
}
|
||||
for (uint256 i = 0; i < config.accounts.length; ++i) {
|
||||
AccountConfig memory acct = config.accounts[i];
|
||||
for (uint256 marketId = 0; marketId < acct.balances.length; ++marketId) {
|
||||
_balance[_getBalanceHash(acct.owner, acct.accountId, marketId)] =
|
||||
acct.balances[marketId];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getIsLocalOperator(
|
||||
address owner,
|
||||
address operator
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (bool isLocalOperator)
|
||||
{
|
||||
return _operators[_getOperatorHash(owner, operator)];
|
||||
}
|
||||
|
||||
function getMarketTokenAddress(
|
||||
uint256 marketId
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (address tokenAddress)
|
||||
{
|
||||
return _markets[marketId].token;
|
||||
}
|
||||
|
||||
function getRiskParams()
|
||||
external
|
||||
view
|
||||
returns (IDydx.RiskParams memory riskParams)
|
||||
{
|
||||
return IDydx.RiskParams({
|
||||
marginRatio: IDydx.D256(_marginRatio),
|
||||
liquidationSpread: IDydx.D256(0),
|
||||
earningsRate: IDydx.D256(0),
|
||||
minBorrowedValue: IDydx.Value(0)
|
||||
});
|
||||
}
|
||||
|
||||
function getAdjustedAccountValues(
|
||||
IDydx.AccountInfo calldata account
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (IDydx.Value memory supplyValue, IDydx.Value memory borrowValue)
|
||||
{
|
||||
for (uint256 marketId = 0; marketId < _markets.length; ++marketId) {
|
||||
int256 balance =
|
||||
_balance[_getBalanceHash(account.owner, account.number, marketId)];
|
||||
// Account values have 36 decimal places.
|
||||
// `getMarketPrice()` returns a unit with
|
||||
// 18 + (18 - TOKEN_DECIMALS) decimal places so multiplying the price
|
||||
// with the wei balance will result in a 36 decimal value.
|
||||
balance = balance * int256(getMarketPrice(marketId).value);
|
||||
if (balance >= 0) {
|
||||
supplyValue.value += uint256(balance);
|
||||
} else {
|
||||
borrowValue.value += uint256(-balance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getMarketMarginPremium(uint256)
|
||||
external
|
||||
view
|
||||
returns (IDydx.D256 memory premium)
|
||||
{
|
||||
// Return 0.
|
||||
return premium;
|
||||
}
|
||||
|
||||
function getMarketPrice(
|
||||
uint256 marketId
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (IDydx.Price memory price)
|
||||
{
|
||||
MarketInfo memory market = _markets[marketId];
|
||||
uint256 decimals = LibERC20Token.decimals(market.token);
|
||||
price.value = _markets[marketId].price;
|
||||
// Market prices have 18 + (18 - TOKEN_DECIMALS)
|
||||
if (decimals > 18) {
|
||||
price.value /= 10 ** (decimals - 18);
|
||||
} else {
|
||||
price.value *= 10 ** (18 - decimals);
|
||||
}
|
||||
}
|
||||
|
||||
function _getOperatorHash(address owner, address operator)
|
||||
private
|
||||
pure
|
||||
returns (bytes32 operatorHash)
|
||||
{
|
||||
return keccak256(abi.encode(
|
||||
owner,
|
||||
operator
|
||||
));
|
||||
}
|
||||
|
||||
function _getBalanceHash(address owner, uint256 accountId, uint256 marketId)
|
||||
private
|
||||
pure
|
||||
returns (bytes32 balanceHash)
|
||||
{
|
||||
return keccak256(abi.encode(
|
||||
owner,
|
||||
accountId,
|
||||
marketId
|
||||
));
|
||||
}
|
||||
}
|
116
contracts/dev-utils/contracts/test/TestLibDydxBalance.sol
Normal file
116
contracts/dev-utils/contracts/test/TestLibDydxBalance.sol
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
|
||||
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;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "../src/LibDydxBalance.sol";
|
||||
|
||||
|
||||
contract TestLibDydxBalanceToken {
|
||||
|
||||
uint8 public decimals;
|
||||
mapping (address => uint256) public balanceOf;
|
||||
mapping (address => mapping (address => uint256)) public allowance;
|
||||
|
||||
constructor(uint8 decimals_) public {
|
||||
decimals = decimals_;
|
||||
}
|
||||
|
||||
function setBalance(address owner, uint256 balance) external {
|
||||
balanceOf[owner] = balance;
|
||||
}
|
||||
|
||||
function setApproval(
|
||||
address owner,
|
||||
address spender,
|
||||
uint256 allowance_
|
||||
)
|
||||
external
|
||||
{
|
||||
allowance[owner][spender] = allowance_;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
contract TestLibDydxBalance {
|
||||
|
||||
mapping (address => TestLibDydxBalanceToken) private tokens;
|
||||
|
||||
function createToken(uint8 decimals) external returns (address) {
|
||||
TestLibDydxBalanceToken token = new TestLibDydxBalanceToken(decimals);
|
||||
return address(tokens[address(token)] = token);
|
||||
}
|
||||
|
||||
function setTokenBalance(
|
||||
address tokenAddress,
|
||||
address owner,
|
||||
uint256 balance
|
||||
)
|
||||
external
|
||||
{
|
||||
tokens[tokenAddress].setBalance(owner, balance);
|
||||
}
|
||||
|
||||
function setTokenApproval(
|
||||
address tokenAddress,
|
||||
address owner,
|
||||
address spender,
|
||||
uint256 allowance
|
||||
)
|
||||
external
|
||||
{
|
||||
tokens[tokenAddress].setApproval(owner, spender, allowance);
|
||||
}
|
||||
|
||||
function getDydxMakerBalance(LibOrder.Order memory order, address dydx)
|
||||
public
|
||||
view
|
||||
returns (uint256 balance)
|
||||
{
|
||||
return LibDydxBalance.getDydxMakerBalance(order, dydx);
|
||||
}
|
||||
|
||||
function getSolventMakerAmount(
|
||||
LibDydxBalance.BalanceCheckInfo memory info
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (uint256 solventMakerAmount)
|
||||
{
|
||||
return LibDydxBalance._getSolventMakerAmount(info);
|
||||
}
|
||||
|
||||
function getDepositableMakerAmount(
|
||||
LibDydxBalance.BalanceCheckInfo memory info
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (uint256 depositableMakerAmount)
|
||||
{
|
||||
return LibDydxBalance._getDepositableMakerAmount(info);
|
||||
}
|
||||
|
||||
function areActionsWellFormed(LibDydxBalance.BalanceCheckInfo memory info)
|
||||
public
|
||||
view
|
||||
returns (bool areWellFormed)
|
||||
{
|
||||
return LibDydxBalance._areActionsWellFormed(info);
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-dev-utils",
|
||||
"version": "1.1.0",
|
||||
"version": "1.3.4",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -8,7 +8,7 @@
|
||||
"main": "lib/src/index.js",
|
||||
"scripts": {
|
||||
"build": "yarn pre_build && tsc -b",
|
||||
"test": "yarn assert_deployable",
|
||||
"test": "yarn assert_deployable && yarn mocha -t 10000 -b ./lib/test/**_test.js",
|
||||
"assert_deployable": "node -e \"const bytecodeLen = (require('./generated-artifacts/DevUtils.json').compilerOutput.evm.bytecode.object.length-2)/2; assert(bytecodeLen<=0x6000,'DevUtils contract is too big to deploy, per EIP-170. '+bytecodeLen+'>'+0x6000)\"",
|
||||
"build:ci": "yarn build",
|
||||
"pre_build": "run-s compile quantify_bytecode contracts:gen generate_contract_wrappers contracts:copy",
|
||||
@@ -27,8 +27,8 @@
|
||||
"docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES"
|
||||
},
|
||||
"config": {
|
||||
"publicInterfaceContracts": "DevUtils,LibAssetData,LibOrderTransferSimulation,LibTransactionDecoder",
|
||||
"abis": "./test/generated-artifacts/@(Addresses|AssetBalance|DevUtils|EthBalanceChecker|ExternalFunctions|LibAssetData|LibOrderTransferSimulation|LibTransactionDecoder|OrderTransferSimulationUtils|OrderValidationUtils).json",
|
||||
"publicInterfaceContracts": "DevUtils,LibAssetData,LibDydxBalance,LibOrderTransferSimulation,LibTransactionDecoder",
|
||||
"abis": "./test/generated-artifacts/@(Addresses|AssetBalance|DevUtils|EthBalanceChecker|ExternalFunctions|LibAssetData|LibDydxBalance|LibOrderTransferSimulation|LibTransactionDecoder|OrderTransferSimulationUtils|OrderValidationUtils|TestDydx|TestLibDydxBalance).json",
|
||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
|
||||
},
|
||||
"repository": {
|
||||
@@ -41,19 +41,19 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/dev-utils/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^5.2.0",
|
||||
"@0x/assert": "^3.0.6",
|
||||
"@0x/contracts-asset-proxy": "^3.2.0",
|
||||
"@0x/contracts-erc20": "^3.1.0",
|
||||
"@0x/contracts-gen": "^2.0.7",
|
||||
"@0x/contracts-test-utils": "^5.1.4",
|
||||
"@0x/sol-compiler": "^4.0.7",
|
||||
"@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.0",
|
||||
"@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.0"
|
||||
"@0x/base-contract": "^6.2.2"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
@@ -7,11 +7,13 @@ import { ContractArtifact } from 'ethereum-types';
|
||||
|
||||
import * as DevUtils from '../generated-artifacts/DevUtils.json';
|
||||
import * as LibAssetData from '../generated-artifacts/LibAssetData.json';
|
||||
import * as LibDydxBalance from '../generated-artifacts/LibDydxBalance.json';
|
||||
import * as LibOrderTransferSimulation from '../generated-artifacts/LibOrderTransferSimulation.json';
|
||||
import * as LibTransactionDecoder from '../generated-artifacts/LibTransactionDecoder.json';
|
||||
export const artifacts = {
|
||||
DevUtils: DevUtils as ContractArtifact,
|
||||
LibAssetData: LibAssetData as ContractArtifact,
|
||||
LibDydxBalance: LibDydxBalance as ContractArtifact,
|
||||
LibOrderTransferSimulation: LibOrderTransferSimulation as ContractArtifact,
|
||||
LibTransactionDecoder: LibTransactionDecoder as ContractArtifact,
|
||||
};
|
||||
|
@@ -5,5 +5,6 @@
|
||||
*/
|
||||
export * from '../generated-wrappers/dev_utils';
|
||||
export * from '../generated-wrappers/lib_asset_data';
|
||||
export * from '../generated-wrappers/lib_dydx_balance';
|
||||
export * from '../generated-wrappers/lib_order_transfer_simulation';
|
||||
export * from '../generated-wrappers/lib_transaction_decoder';
|
||||
|
@@ -11,10 +11,13 @@ import * as DevUtils from '../test/generated-artifacts/DevUtils.json';
|
||||
import * as EthBalanceChecker from '../test/generated-artifacts/EthBalanceChecker.json';
|
||||
import * as ExternalFunctions from '../test/generated-artifacts/ExternalFunctions.json';
|
||||
import * as LibAssetData from '../test/generated-artifacts/LibAssetData.json';
|
||||
import * as LibDydxBalance from '../test/generated-artifacts/LibDydxBalance.json';
|
||||
import * as LibOrderTransferSimulation from '../test/generated-artifacts/LibOrderTransferSimulation.json';
|
||||
import * as LibTransactionDecoder from '../test/generated-artifacts/LibTransactionDecoder.json';
|
||||
import * as OrderTransferSimulationUtils from '../test/generated-artifacts/OrderTransferSimulationUtils.json';
|
||||
import * as OrderValidationUtils from '../test/generated-artifacts/OrderValidationUtils.json';
|
||||
import * as TestDydx from '../test/generated-artifacts/TestDydx.json';
|
||||
import * as TestLibDydxBalance from '../test/generated-artifacts/TestLibDydxBalance.json';
|
||||
export const artifacts = {
|
||||
Addresses: Addresses as ContractArtifact,
|
||||
AssetBalance: AssetBalance as ContractArtifact,
|
||||
@@ -22,8 +25,11 @@ export const artifacts = {
|
||||
EthBalanceChecker: EthBalanceChecker as ContractArtifact,
|
||||
ExternalFunctions: ExternalFunctions as ContractArtifact,
|
||||
LibAssetData: LibAssetData as ContractArtifact,
|
||||
LibDydxBalance: LibDydxBalance as ContractArtifact,
|
||||
LibOrderTransferSimulation: LibOrderTransferSimulation as ContractArtifact,
|
||||
LibTransactionDecoder: LibTransactionDecoder as ContractArtifact,
|
||||
OrderTransferSimulationUtils: OrderTransferSimulationUtils as ContractArtifact,
|
||||
OrderValidationUtils: OrderValidationUtils as ContractArtifact,
|
||||
TestDydx: TestDydx as ContractArtifact,
|
||||
TestLibDydxBalance: TestLibDydxBalance as ContractArtifact,
|
||||
};
|
||||
|
1170
contracts/dev-utils/test/lib_dydx_balance_test.ts
Normal file
1170
contracts/dev-utils/test/lib_dydx_balance_test.ts
Normal file
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,10 @@ export * from '../test/generated-wrappers/dev_utils';
|
||||
export * from '../test/generated-wrappers/eth_balance_checker';
|
||||
export * from '../test/generated-wrappers/external_functions';
|
||||
export * from '../test/generated-wrappers/lib_asset_data';
|
||||
export * from '../test/generated-wrappers/lib_dydx_balance';
|
||||
export * from '../test/generated-wrappers/lib_order_transfer_simulation';
|
||||
export * from '../test/generated-wrappers/lib_transaction_decoder';
|
||||
export * from '../test/generated-wrappers/order_transfer_simulation_utils';
|
||||
export * from '../test/generated-wrappers/order_validation_utils';
|
||||
export * from '../test/generated-wrappers/test_dydx';
|
||||
export * from '../test/generated-wrappers/test_lib_dydx_balance';
|
||||
|
@@ -5,6 +5,7 @@
|
||||
"files": [
|
||||
"generated-artifacts/DevUtils.json",
|
||||
"generated-artifacts/LibAssetData.json",
|
||||
"generated-artifacts/LibDydxBalance.json",
|
||||
"generated-artifacts/LibOrderTransferSimulation.json",
|
||||
"generated-artifacts/LibTransactionDecoder.json",
|
||||
"test/generated-artifacts/Addresses.json",
|
||||
@@ -13,10 +14,13 @@
|
||||
"test/generated-artifacts/EthBalanceChecker.json",
|
||||
"test/generated-artifacts/ExternalFunctions.json",
|
||||
"test/generated-artifacts/LibAssetData.json",
|
||||
"test/generated-artifacts/LibDydxBalance.json",
|
||||
"test/generated-artifacts/LibOrderTransferSimulation.json",
|
||||
"test/generated-artifacts/LibTransactionDecoder.json",
|
||||
"test/generated-artifacts/OrderTransferSimulationUtils.json",
|
||||
"test/generated-artifacts/OrderValidationUtils.json"
|
||||
"test/generated-artifacts/OrderValidationUtils.json",
|
||||
"test/generated-artifacts/TestDydx.json",
|
||||
"test/generated-artifacts/TestLibDydxBalance.json"
|
||||
],
|
||||
"exclude": ["./deploy/solc/solc_bin"]
|
||||
}
|
||||
|
@@ -1,4 +1,58 @@
|
||||
[
|
||||
{
|
||||
"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",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1581748629,
|
||||
"version": "2.1.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "2.1.0",
|
||||
"changes": [
|
||||
|
@@ -5,6 +5,30 @@ 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
|
||||
|
||||
## v2.1.1 - _February 15, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.1.0 - _February 8, 2020_
|
||||
|
||||
* Fix broken tests (#2462)
|
||||
|
@@ -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.0",
|
||||
"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.0",
|
||||
"@0x/contracts-gen": "^2.0.7",
|
||||
"@0x/contracts-utils": "^4.3.0",
|
||||
"@0x/dev-utils": "^3.2.0",
|
||||
"@0x/sol-compiler": "^4.0.7",
|
||||
"@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.0",
|
||||
"@0x/contracts-test-utils": "^5.1.4",
|
||||
"@0x/utils": "^5.4.0",
|
||||
"@0x/web3-wrapper": "^7.0.6",
|
||||
"@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,10 +1,95 @@
|
||||
[
|
||||
{
|
||||
"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",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "1.4.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Added Curve contract sampling",
|
||||
"pr": 2483
|
||||
}
|
||||
],
|
||||
"timestamp": 1581748629
|
||||
},
|
||||
{
|
||||
"version": "1.3.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Catch reverts to `DevUtils` calls",
|
||||
"pr": 2476
|
||||
},
|
||||
{
|
||||
"note": "Remove wrapper functions and introduce `batchCall()`",
|
||||
"pr": 2477
|
||||
}
|
||||
],
|
||||
"timestamp": 1581204851
|
||||
|
@@ -5,9 +5,40 @@ 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
|
||||
|
||||
## v1.4.0 - _February 15, 2020_
|
||||
|
||||
* Added Curve contract sampling (#2483)
|
||||
|
||||
## v1.3.0 - _February 8, 2020_
|
||||
|
||||
* Catch reverts to `DevUtils` calls (#2476)
|
||||
* Remove wrapper functions and introduce `batchCall()` (#2477)
|
||||
|
||||
## v1.2.1 - _February 6, 2020_
|
||||
|
||||
|
@@ -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"
|
||||
);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
87
contracts/erc20-bridge-sampler/contracts/src/ICurve.sol
Normal file
87
contracts/erc20-bridge-sampler/contracts/src/ICurve.sol
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
|
||||
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;
|
||||
|
||||
|
||||
// solhint-disable func-name-mixedcase
|
||||
interface ICurve {
|
||||
|
||||
/// @dev Sell `sellAmount` of `fromToken` token and receive `toToken` token.
|
||||
/// This function exists on early versions of Curve (USDC/DAI)
|
||||
/// @param i The token index being sold.
|
||||
/// @param j The token index being bought.
|
||||
/// @param sellAmount The amount of token being bought.
|
||||
/// @param minBuyAmount The minimum buy amount of the token being bought.
|
||||
/// @param deadline The time in seconds when this operation should expire.
|
||||
function exchange_underlying(
|
||||
int128 i,
|
||||
int128 j,
|
||||
uint256 sellAmount,
|
||||
uint256 minBuyAmount,
|
||||
uint256 deadline
|
||||
)
|
||||
external;
|
||||
|
||||
/// @dev Sell `sellAmount` of `fromToken` token and receive `toToken` token.
|
||||
/// 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 sellAmount The amount of token being bought.
|
||||
/// @param minBuyAmount The minimum buy amount of the token being bought.
|
||||
function exchange_underlying(
|
||||
int128 i,
|
||||
int128 j,
|
||||
uint256 sellAmount,
|
||||
uint256 minBuyAmount
|
||||
)
|
||||
external;
|
||||
|
||||
/// @dev Get the amount of `toToken` by selling `sellAmount` of `fromToken`
|
||||
/// @param i The token index being sold.
|
||||
/// @param j The token index being bought.
|
||||
/// @param sellAmount The amount of token being bought.
|
||||
function get_dy_underlying(
|
||||
int128 i,
|
||||
int128 j,
|
||||
uint256 sellAmount
|
||||
)
|
||||
external
|
||||
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.
|
||||
function get_dx_underlying(
|
||||
int128 i,
|
||||
int128 j,
|
||||
uint256 buyAmount
|
||||
)
|
||||
external
|
||||
returns (uint256 dx);
|
||||
|
||||
/// @dev Get the underlying token address from the token index
|
||||
/// @param i The token index.
|
||||
function underlying_coins(
|
||||
int128 i
|
||||
)
|
||||
external
|
||||
returns (address tokenAddress);
|
||||
}
|
@@ -23,98 +23,19 @@ import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||
|
||||
|
||||
interface IERC20BridgeSampler {
|
||||
struct OrdersAndSample {
|
||||
uint256[] orderFillableAssetAmounts;
|
||||
uint256[][] tokenAmountsBySource;
|
||||
|
||||
struct FakeBuyOptions {
|
||||
uint256 targetSlippageBps;
|
||||
uint256 maxIterations;
|
||||
}
|
||||
|
||||
/// @dev Query batches of native orders and sample sell quotes on multiple DEXes at once.
|
||||
/// @param orders Batches of Native orders to query.
|
||||
/// @param orderSignatures Batches of Signatures for each respective order in `orders`.
|
||||
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
|
||||
/// @param takerTokenAmounts Batches of Taker token sell amount for each sample.
|
||||
/// @return ordersAndSamples How much taker asset can be filled
|
||||
/// by each order in `orders`. Maker amounts bought for each source at
|
||||
/// each taker token amount. First indexed by source index, then sample
|
||||
/// index.
|
||||
function queryBatchOrdersAndSampleSells(
|
||||
LibOrder.Order[][] calldata orders,
|
||||
bytes[][] calldata orderSignatures,
|
||||
address[] calldata sources,
|
||||
uint256[][] calldata takerTokenAmounts
|
||||
)
|
||||
/// @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.
|
||||
function batchCall(bytes[] calldata callDatas)
|
||||
external
|
||||
view
|
||||
returns (
|
||||
OrdersAndSample[] memory ordersAndSamples
|
||||
);
|
||||
|
||||
/// @dev Query batches of native orders and sample buy quotes on multiple DEXes at once.
|
||||
/// @param orders Batches of Native orders to query.
|
||||
/// @param orderSignatures Batches of Signatures for each respective order in `orders`.
|
||||
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
|
||||
/// @param makerTokenAmounts Batches of Maker token sell amount for each sample.
|
||||
/// @return ordersAndSamples How much taker asset can be filled
|
||||
/// by each order in `orders`. Taker amounts sold for each source at
|
||||
/// each maker token amount. First indexed by source index, then sample
|
||||
/// index
|
||||
function queryBatchOrdersAndSampleBuys(
|
||||
LibOrder.Order[][] calldata orders,
|
||||
bytes[][] calldata orderSignatures,
|
||||
address[] calldata sources,
|
||||
uint256[][] calldata makerTokenAmounts
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (
|
||||
OrdersAndSample[] memory ordersAndSamples
|
||||
);
|
||||
|
||||
/// @dev Query native orders and sample sell quotes on multiple DEXes at once.
|
||||
/// @param orders Native orders to query.
|
||||
/// @param orderSignatures Signatures for each respective order in `orders`.
|
||||
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
|
||||
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||
/// @return orderFillableTakerAssetAmounts How much taker asset can be filled
|
||||
/// by each order in `orders`.
|
||||
/// @return makerTokenAmountsBySource Maker amounts bought for each source at
|
||||
/// each taker token amount. First indexed by source index, then sample
|
||||
/// index.
|
||||
function queryOrdersAndSampleSells(
|
||||
LibOrder.Order[] calldata orders,
|
||||
bytes[] calldata orderSignatures,
|
||||
address[] calldata sources,
|
||||
uint256[] calldata takerTokenAmounts
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (
|
||||
uint256[] memory orderFillableTakerAssetAmounts,
|
||||
uint256[][] memory makerTokenAmountsBySource
|
||||
);
|
||||
|
||||
/// @dev Query native orders and sample buy quotes on multiple DEXes at once.
|
||||
/// @param orders Native orders to query.
|
||||
/// @param orderSignatures Signatures for each respective order in `orders`.
|
||||
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
|
||||
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
||||
/// @return orderFillableMakerAssetAmounts How much maker asset can be filled
|
||||
/// by each order in `orders`.
|
||||
/// @return takerTokenAmountsBySource Taker amounts sold for each source at
|
||||
/// each maker token amount. First indexed by source index, then sample
|
||||
/// index.
|
||||
function queryOrdersAndSampleBuys(
|
||||
LibOrder.Order[] calldata orders,
|
||||
bytes[] calldata orderSignatures,
|
||||
address[] calldata sources,
|
||||
uint256[] calldata makerTokenAmounts
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (
|
||||
uint256[] memory orderFillableMakerAssetAmounts,
|
||||
uint256[][] memory makerTokenAmountsBySource
|
||||
);
|
||||
returns (bytes[] memory callResults);
|
||||
|
||||
/// @dev Queries the fillable taker asset amounts of native orders.
|
||||
/// @param orders Native orders to query.
|
||||
@@ -142,39 +63,227 @@ interface IERC20BridgeSampler {
|
||||
view
|
||||
returns (uint256[] memory orderFillableMakerAssetAmounts);
|
||||
|
||||
/// @dev Sample sell quotes on multiple DEXes at once.
|
||||
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
|
||||
/// @dev Sample sell quotes from Kyber.
|
||||
/// @param takerToken Address of the taker token (what to sell).
|
||||
/// @param makerToken Address of the maker token (what to buy).
|
||||
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||
/// @return makerTokenAmountsBySource Maker amounts bought for each source at
|
||||
/// each taker token amount. First indexed by source index, then sample
|
||||
/// index.
|
||||
function sampleSells(
|
||||
address[] calldata sources,
|
||||
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
||||
/// amount.
|
||||
function sampleSellsFromKyberNetwork(
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256[] calldata takerTokenAmounts
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256[][] memory makerTokenAmountsBySource);
|
||||
returns (uint256[] memory makerTokenAmounts);
|
||||
|
||||
/// @dev Query native orders and sample buy quotes on multiple DEXes at once.
|
||||
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
|
||||
/// @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.
|
||||
/// @return takerTokenAmountsBySource Taker amounts sold for each source at
|
||||
/// each maker token amount. First indexed by source index, then sample
|
||||
/// index.
|
||||
function sampleBuys(
|
||||
address[] calldata sources,
|
||||
/// @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).
|
||||
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
||||
/// amount.
|
||||
function sampleSellsFromEth2Dai(
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256[] calldata takerTokenAmounts
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256[] memory makerTokenAmounts);
|
||||
|
||||
/// @dev Sample sell quotes from Uniswap.
|
||||
/// @param takerToken Address of the taker token (what to sell).
|
||||
/// @param makerToken Address of the maker token (what to buy).
|
||||
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
||||
/// amount.
|
||||
function sampleSellsFromUniswap(
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256[] calldata takerTokenAmounts
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256[] memory makerTokenAmounts);
|
||||
|
||||
/// @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 buy amount for each sample.
|
||||
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||
/// amount.
|
||||
function sampleBuysFromUniswap(
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256[] calldata makerTokenAmounts
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256[][] memory takerTokenAmountsBySource);
|
||||
returns (uint256[] memory takerTokenAmounts);
|
||||
|
||||
/// @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 makerTokenAmounts Maker token buy amount for each sample.
|
||||
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||
/// amount.
|
||||
function sampleBuysFromEth2Dai(
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256[] calldata makerTokenAmounts
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256[] memory takerTokenAmounts);
|
||||
|
||||
/// @dev Sample sell 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 takerTokenAmounts Taker token sell amount for each sample.
|
||||
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
||||
/// amount.
|
||||
function sampleSellsFromCurve(
|
||||
address curveAddress,
|
||||
int128 fromTokenIdx,
|
||||
int128 toTokenIdx,
|
||||
uint256[] calldata takerTokenAmounts
|
||||
)
|
||||
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,7 +402,7 @@ contract TestERC20BridgeSampler is
|
||||
bytes memory
|
||||
)
|
||||
public
|
||||
view
|
||||
pure
|
||||
returns (
|
||||
LibOrder.OrderInfo memory orderInfo,
|
||||
uint256 fillableTakerAssetAmount,
|
||||
@@ -358,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
|
||||
@@ -385,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.3.0",
|
||||
"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|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.0",
|
||||
"@0x/contracts-asset-proxy": "^3.2.0",
|
||||
"@0x/contracts-erc20": "^3.1.0",
|
||||
"@0x/contracts-exchange": "^3.2.0",
|
||||
"@0x/contracts-exchange-libs": "^4.3.0",
|
||||
"@0x/contracts-gen": "^2.0.7",
|
||||
"@0x/contracts-test-utils": "^5.1.4",
|
||||
"@0x/contracts-utils": "^4.3.0",
|
||||
"@0x/dev-utils": "^3.2.0",
|
||||
"@0x/sol-compiler": "^4.0.7",
|
||||
"@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.6",
|
||||
"@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.0",
|
||||
"@0x/types": "^3.1.2",
|
||||
"@0x/typescript-typings": "^5.0.2",
|
||||
"@0x/utils": "^5.4.0",
|
||||
"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,19 +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,23 +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_ASSET_PROXY_ASSET_DATA = hexUtils.concat('0xf47261b1', hexUtils.leftPad(randomAddress()));
|
||||
const INVALID_ASSET_DATA = hexUtils.random(37);
|
||||
const SELL_SOURCES = ['Eth2Dai', 'Kyber', 'Uniswap'];
|
||||
const BUY_SOURCES = ['Eth2Dai', 'Uniswap'];
|
||||
const SOURCE_IDS: { [source: string]: string } = {
|
||||
Uniswap: '0xc0a47dfe034b400b47bdad5fecda2621de6c4d95',
|
||||
Eth2Dai: '0x39755357759ce0d7f32dc8dc45414cca409ae24e',
|
||||
Kyber: '0x818e6fecd516ecc3849daf6845e3ec868087b755',
|
||||
};
|
||||
const EMPTY_ORDERS_ERROR = 'ERC20BridgeSampler/EMPTY_ORDERS';
|
||||
const UNSUPPORTED_ASSET_PROXY_ERROR = 'ERC20BridgeSampler/UNSUPPORTED_ASSET_PROXY';
|
||||
const INVALID_ASSET_DATA_ERROR = 'ERC20BridgeSampler/INVALID_ASSET_DATA';
|
||||
const UNSUPPORTED_SOURCE_ERROR = 'ERC20BridgeSampler/UNSUPPORTED_SOURCE';
|
||||
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(
|
||||
@@ -178,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));
|
||||
}
|
||||
@@ -189,8 +192,24 @@ 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.toHex(order.salt, 32));
|
||||
const hash = getPackedHash(hexUtils.leftPad(order.salt));
|
||||
const orderStatus = new BigNumber(hash).mod(100).toNumber() > 90 ? 5 : 3;
|
||||
const isValidSignature = !!new BigNumber(hash).mod(2).toNumber();
|
||||
if (orderStatus !== 3 || !isValidSignature) {
|
||||
@@ -213,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());
|
||||
@@ -324,329 +343,6 @@ blockchainTests('erc20-bridge-sampler', env => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('queryOrdersAndSampleSells()', () => {
|
||||
const ORDERS = createOrders(MAKER_TOKEN, TAKER_TOKEN);
|
||||
const SIGNATURES: string[] = _.times(ORDERS.length, i => hexUtils.random());
|
||||
|
||||
before(async () => {
|
||||
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
||||
});
|
||||
|
||||
it('returns expected fillable amounts for each order', async () => {
|
||||
const takerTokenAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||
const expectedFillableAmounts = ORDERS.map(getDeterministicFillableTakerAssetAmount);
|
||||
const [orderInfos] = await testContract
|
||||
.queryOrdersAndSampleSells(ORDERS, SIGNATURES, SELL_SOURCES.map(n => SOURCE_IDS[n]), takerTokenAmounts)
|
||||
.callAsync();
|
||||
expect(orderInfos).to.deep.eq(expectedFillableAmounts);
|
||||
});
|
||||
|
||||
it('can return quotes for all sources', async () => {
|
||||
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||
const expectedQuotes = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, SELL_SOURCES, sampleAmounts);
|
||||
const [, quotes] = await testContract
|
||||
.queryOrdersAndSampleSells(ORDERS, SIGNATURES, SELL_SOURCES.map(n => SOURCE_IDS[n]), sampleAmounts)
|
||||
.callAsync();
|
||||
expect(quotes).to.deep.eq(expectedQuotes);
|
||||
});
|
||||
|
||||
it('throws if no orders are passed in', async () => {
|
||||
const tx = testContract
|
||||
.queryOrdersAndSampleSells([], [], SELL_SOURCES.map(n => SOURCE_IDS[n]), getSampleAmounts(TAKER_TOKEN))
|
||||
.callAsync();
|
||||
return expect(tx).to.revertWith(EMPTY_ORDERS_ERROR);
|
||||
});
|
||||
|
||||
it('throws with an unsupported source', async () => {
|
||||
const tx = testContract
|
||||
.queryOrdersAndSampleSells(
|
||||
ORDERS,
|
||||
SIGNATURES,
|
||||
[...SELL_SOURCES.map(n => SOURCE_IDS[n]), randomAddress()],
|
||||
getSampleAmounts(TAKER_TOKEN),
|
||||
)
|
||||
.callAsync();
|
||||
return expect(tx).to.revertWith(UNSUPPORTED_SOURCE_ERROR);
|
||||
});
|
||||
|
||||
it('throws with non-ERC20 maker asset data', async () => {
|
||||
const tx = testContract
|
||||
.queryOrdersAndSampleSells(
|
||||
ORDERS.map(o => ({
|
||||
...o,
|
||||
makerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
|
||||
})),
|
||||
SIGNATURES,
|
||||
SELL_SOURCES.map(n => SOURCE_IDS[n]),
|
||||
getSampleAmounts(TAKER_TOKEN),
|
||||
)
|
||||
.callAsync();
|
||||
return expect(tx).to.revertWith(UNSUPPORTED_ASSET_PROXY_ERROR);
|
||||
});
|
||||
|
||||
it('throws with non-ERC20 taker asset data', async () => {
|
||||
const tx = testContract
|
||||
.queryOrdersAndSampleSells(
|
||||
ORDERS.map(o => ({
|
||||
...o,
|
||||
takerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
|
||||
})),
|
||||
SIGNATURES,
|
||||
SELL_SOURCES.map(n => SOURCE_IDS[n]),
|
||||
getSampleAmounts(TAKER_TOKEN),
|
||||
)
|
||||
.callAsync();
|
||||
return expect(tx).to.revertWith(UNSUPPORTED_ASSET_PROXY_ERROR);
|
||||
});
|
||||
|
||||
it('throws with invalid maker asset data', async () => {
|
||||
const tx = testContract
|
||||
.queryOrdersAndSampleSells(
|
||||
ORDERS.map(o => ({
|
||||
...o,
|
||||
makerAssetData: INVALID_ASSET_DATA,
|
||||
})),
|
||||
SIGNATURES,
|
||||
SELL_SOURCES.map(n => SOURCE_IDS[n]),
|
||||
getSampleAmounts(TAKER_TOKEN),
|
||||
)
|
||||
.callAsync();
|
||||
return expect(tx).to.revertWith(INVALID_ASSET_DATA_ERROR);
|
||||
});
|
||||
|
||||
it('throws with invalid taker asset data', async () => {
|
||||
const tx = testContract
|
||||
.queryOrdersAndSampleSells(
|
||||
ORDERS.map(o => ({
|
||||
...o,
|
||||
takerAssetData: INVALID_ASSET_DATA,
|
||||
})),
|
||||
SIGNATURES,
|
||||
SELL_SOURCES.map(n => SOURCE_IDS[n]),
|
||||
getSampleAmounts(TAKER_TOKEN),
|
||||
)
|
||||
.callAsync();
|
||||
return expect(tx).to.revertWith(INVALID_ASSET_DATA_ERROR);
|
||||
});
|
||||
});
|
||||
|
||||
describe('queryOrdersAndSampleBuys()', () => {
|
||||
const ORDERS = createOrders(MAKER_TOKEN, TAKER_TOKEN);
|
||||
const SIGNATURES: string[] = _.times(ORDERS.length, i => hexUtils.random());
|
||||
|
||||
before(async () => {
|
||||
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
||||
});
|
||||
|
||||
it('returns expected fillable amounts for each order', async () => {
|
||||
const takerTokenAmounts = getSampleAmounts(MAKER_TOKEN);
|
||||
const expectedFillableAmounts = ORDERS.map(getDeterministicFillableMakerAssetAmount);
|
||||
const [orderInfos] = await testContract
|
||||
.queryOrdersAndSampleBuys(ORDERS, SIGNATURES, BUY_SOURCES.map(n => SOURCE_IDS[n]), takerTokenAmounts)
|
||||
.callAsync();
|
||||
expect(orderInfos).to.deep.eq(expectedFillableAmounts);
|
||||
});
|
||||
|
||||
it('can return quotes for all sources', async () => {
|
||||
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
|
||||
const expectedQuotes = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, BUY_SOURCES, sampleAmounts);
|
||||
const [, quotes] = await testContract
|
||||
.queryOrdersAndSampleBuys(ORDERS, SIGNATURES, BUY_SOURCES.map(n => SOURCE_IDS[n]), sampleAmounts)
|
||||
.callAsync();
|
||||
expect(quotes).to.deep.eq(expectedQuotes);
|
||||
});
|
||||
|
||||
it('throws if no orders are passed in', async () => {
|
||||
const tx = testContract
|
||||
.queryOrdersAndSampleBuys([], [], BUY_SOURCES.map(n => SOURCE_IDS[n]), getSampleAmounts(MAKER_TOKEN))
|
||||
.callAsync();
|
||||
return expect(tx).to.revertWith(EMPTY_ORDERS_ERROR);
|
||||
});
|
||||
|
||||
it('throws with an unsupported source', async () => {
|
||||
const tx = testContract
|
||||
.queryOrdersAndSampleBuys(
|
||||
ORDERS,
|
||||
SIGNATURES,
|
||||
[...BUY_SOURCES.map(n => SOURCE_IDS[n]), randomAddress()],
|
||||
getSampleAmounts(MAKER_TOKEN),
|
||||
)
|
||||
.callAsync();
|
||||
return expect(tx).to.revertWith(UNSUPPORTED_SOURCE_ERROR);
|
||||
});
|
||||
|
||||
it('throws if kyber is passed in as a source', async () => {
|
||||
const sources = [...BUY_SOURCES, 'Kyber'];
|
||||
const tx = testContract
|
||||
.queryOrdersAndSampleBuys(
|
||||
ORDERS,
|
||||
SIGNATURES,
|
||||
sources.map(n => SOURCE_IDS[n]),
|
||||
getSampleAmounts(MAKER_TOKEN),
|
||||
)
|
||||
.callAsync();
|
||||
return expect(tx).to.revertWith(UNSUPPORTED_SOURCE_ERROR);
|
||||
});
|
||||
|
||||
it('throws with non-ERC20 maker asset data', async () => {
|
||||
const tx = testContract
|
||||
.queryOrdersAndSampleBuys(
|
||||
ORDERS.map(o => ({
|
||||
...o,
|
||||
makerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
|
||||
})),
|
||||
SIGNATURES,
|
||||
BUY_SOURCES.map(n => SOURCE_IDS[n]),
|
||||
getSampleAmounts(MAKER_TOKEN),
|
||||
)
|
||||
.callAsync();
|
||||
return expect(tx).to.revertWith(UNSUPPORTED_ASSET_PROXY_ERROR);
|
||||
});
|
||||
|
||||
it('throws with non-ERC20 taker asset data', async () => {
|
||||
const tx = testContract
|
||||
.queryOrdersAndSampleBuys(
|
||||
ORDERS.map(o => ({
|
||||
...o,
|
||||
takerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
|
||||
})),
|
||||
SIGNATURES,
|
||||
BUY_SOURCES.map(n => SOURCE_IDS[n]),
|
||||
getSampleAmounts(MAKER_TOKEN),
|
||||
)
|
||||
.callAsync();
|
||||
return expect(tx).to.revertWith(UNSUPPORTED_ASSET_PROXY_ERROR);
|
||||
});
|
||||
|
||||
it('throws with invalid maker asset data', async () => {
|
||||
const tx = testContract
|
||||
.queryOrdersAndSampleBuys(
|
||||
ORDERS.map(o => ({
|
||||
...o,
|
||||
makerAssetData: INVALID_ASSET_DATA,
|
||||
})),
|
||||
SIGNATURES,
|
||||
BUY_SOURCES.map(n => SOURCE_IDS[n]),
|
||||
getSampleAmounts(MAKER_TOKEN),
|
||||
)
|
||||
.callAsync();
|
||||
return expect(tx).to.revertWith(INVALID_ASSET_DATA_ERROR);
|
||||
});
|
||||
|
||||
it('throws with invalid taker asset data', async () => {
|
||||
const tx = testContract
|
||||
.queryOrdersAndSampleBuys(
|
||||
ORDERS.map(o => ({
|
||||
...o,
|
||||
takerAssetData: INVALID_ASSET_DATA,
|
||||
})),
|
||||
SIGNATURES,
|
||||
BUY_SOURCES.map(n => SOURCE_IDS[n]),
|
||||
getSampleAmounts(MAKER_TOKEN),
|
||||
)
|
||||
.callAsync();
|
||||
return expect(tx).to.revertWith(INVALID_ASSET_DATA_ERROR);
|
||||
});
|
||||
});
|
||||
|
||||
describe('sampleSells()', () => {
|
||||
before(async () => {
|
||||
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
||||
});
|
||||
|
||||
it('returns empty quotes with no sample amounts', async () => {
|
||||
const emptyQuotes = _.times(SELL_SOURCES.length, () => []);
|
||||
const quotes = await testContract
|
||||
.sampleSells(SELL_SOURCES.map(n => SOURCE_IDS[n]), TAKER_TOKEN, MAKER_TOKEN, [])
|
||||
.callAsync();
|
||||
expect(quotes).to.deep.eq(emptyQuotes);
|
||||
});
|
||||
|
||||
it('can return quotes for all sources', async () => {
|
||||
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||
const expectedQuotes = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, SELL_SOURCES, sampleAmounts);
|
||||
const quotes = await testContract
|
||||
.sampleSells(SELL_SOURCES.map(n => SOURCE_IDS[n]), TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
|
||||
.callAsync();
|
||||
expect(quotes).to.deep.eq(expectedQuotes);
|
||||
});
|
||||
|
||||
it('can return quotes for some sources', async () => {
|
||||
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||
const sources = _.sampleSize(SELL_SOURCES, 1);
|
||||
const expectedQuotes = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, sources, sampleAmounts);
|
||||
const quotes = await testContract
|
||||
.sampleSells(sources.map(n => SOURCE_IDS[n]), TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
|
||||
.callAsync();
|
||||
expect(quotes).to.deep.eq(expectedQuotes);
|
||||
});
|
||||
|
||||
it('throws with an unsupported source', async () => {
|
||||
const tx = testContract
|
||||
.sampleSells(
|
||||
[...SELL_SOURCES.map(n => SOURCE_IDS[n]), randomAddress()],
|
||||
TAKER_TOKEN,
|
||||
MAKER_TOKEN,
|
||||
getSampleAmounts(TAKER_TOKEN),
|
||||
)
|
||||
.callAsync();
|
||||
return expect(tx).to.revertWith(UNSUPPORTED_SOURCE_ERROR);
|
||||
});
|
||||
});
|
||||
|
||||
describe('sampleBuys()', () => {
|
||||
before(async () => {
|
||||
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
||||
});
|
||||
|
||||
it('returns empty quotes with no sample amounts', async () => {
|
||||
const emptyQuotes = _.times(BUY_SOURCES.length, () => []);
|
||||
const quotes = await testContract
|
||||
.sampleBuys(BUY_SOURCES.map(n => SOURCE_IDS[n]), TAKER_TOKEN, MAKER_TOKEN, [])
|
||||
.callAsync();
|
||||
expect(quotes).to.deep.eq(emptyQuotes);
|
||||
});
|
||||
|
||||
it('can return quotes for all sources', async () => {
|
||||
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
|
||||
const expectedQuotes = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, BUY_SOURCES, sampleAmounts);
|
||||
const quotes = await testContract
|
||||
.sampleBuys(BUY_SOURCES.map(n => SOURCE_IDS[n]), TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
|
||||
.callAsync();
|
||||
expect(quotes).to.deep.eq(expectedQuotes);
|
||||
});
|
||||
|
||||
it('can return quotes for some sources', async () => {
|
||||
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
|
||||
const sources = _.sampleSize(BUY_SOURCES, 1);
|
||||
const expectedQuotes = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, sources, sampleAmounts);
|
||||
const quotes = await testContract
|
||||
.sampleBuys(sources.map(n => SOURCE_IDS[n]), TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
|
||||
.callAsync();
|
||||
expect(quotes).to.deep.eq(expectedQuotes);
|
||||
});
|
||||
|
||||
it('throws with an unsupported source', async () => {
|
||||
const tx = testContract
|
||||
.sampleBuys(
|
||||
[...BUY_SOURCES.map(n => SOURCE_IDS[n]), randomAddress()],
|
||||
TAKER_TOKEN,
|
||||
MAKER_TOKEN,
|
||||
getSampleAmounts(MAKER_TOKEN),
|
||||
)
|
||||
.callAsync();
|
||||
return expect(tx).to.revertWith(UNSUPPORTED_SOURCE_ERROR);
|
||||
});
|
||||
|
||||
it('throws if kyber is passed in as a source', async () => {
|
||||
const sources = [...BUY_SOURCES, 'Kyber'];
|
||||
const tx = testContract
|
||||
.sampleBuys(sources.map(n => SOURCE_IDS[n]), TAKER_TOKEN, MAKER_TOKEN, getSampleAmounts(MAKER_TOKEN))
|
||||
.callAsync();
|
||||
return expect(tx).to.revertWith(UNSUPPORTED_SOURCE_ERROR);
|
||||
});
|
||||
});
|
||||
|
||||
blockchainTests.resets('sampleSellsFromKyberNetwork()', () => {
|
||||
before(async () => {
|
||||
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
||||
@@ -662,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();
|
||||
@@ -720,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();
|
||||
@@ -1051,4 +851,236 @@ blockchainTests('erc20-bridge-sampler', env => {
|
||||
expect(quotes).to.deep.eq(expectedQuotes);
|
||||
});
|
||||
});
|
||||
|
||||
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);
|
||||
const signatures: string[] = _.times(orders.length, i => hexUtils.random());
|
||||
const expected = orders.map(getDeterministicFillableTakerAssetAmount);
|
||||
const calls = [
|
||||
testContract.getOrderFillableTakerAssetAmounts(orders, signatures).getABIEncodedTransactionData(),
|
||||
];
|
||||
const r = await testContract.batchCall(calls).callAsync();
|
||||
expect(r).to.be.length(1);
|
||||
const actual = testContract.getABIDecodedReturnData<BigNumber[]>('getOrderFillableTakerAssetAmounts', r[0]);
|
||||
expect(actual).to.deep.eq(expected);
|
||||
});
|
||||
|
||||
it('can call two functions', async () => {
|
||||
const numOrders = _.random(1, 10);
|
||||
const orders = _.times(2, () => createOrders(MAKER_TOKEN, TAKER_TOKEN, numOrders));
|
||||
const signatures: string[] = _.times(numOrders, i => hexUtils.random());
|
||||
const expecteds = [
|
||||
orders[0].map(getDeterministicFillableTakerAssetAmount),
|
||||
orders[1].map(getDeterministicFillableMakerAssetAmount),
|
||||
];
|
||||
const calls = [
|
||||
testContract.getOrderFillableTakerAssetAmounts(orders[0], signatures).getABIEncodedTransactionData(),
|
||||
testContract.getOrderFillableMakerAssetAmounts(orders[1], signatures).getABIEncodedTransactionData(),
|
||||
];
|
||||
const r = await testContract.batchCall(calls).callAsync();
|
||||
expect(r).to.be.length(2);
|
||||
expect(testContract.getABIDecodedReturnData('getOrderFillableTakerAssetAmounts', r[0])).to.deep.eq(
|
||||
expecteds[0],
|
||||
);
|
||||
expect(testContract.getABIDecodedReturnData('getOrderFillableMakerAssetAmounts', r[1])).to.deep.eq(
|
||||
expecteds[1],
|
||||
);
|
||||
});
|
||||
|
||||
it('can make recursive calls', async () => {
|
||||
const numOrders = _.random(1, 10);
|
||||
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, numOrders);
|
||||
const signatures: string[] = _.times(numOrders, i => hexUtils.random());
|
||||
const expected = orders.map(getDeterministicFillableTakerAssetAmount);
|
||||
let r = await testContract
|
||||
.batchCall([
|
||||
testContract
|
||||
.batchCall([
|
||||
testContract
|
||||
.getOrderFillableTakerAssetAmounts(orders, signatures)
|
||||
.getABIEncodedTransactionData(),
|
||||
])
|
||||
.getABIEncodedTransactionData(),
|
||||
])
|
||||
.callAsync();
|
||||
expect(r).to.be.length(1);
|
||||
r = testContract.getABIDecodedReturnData<string[]>('batchCall', r[0]);
|
||||
expect(r).to.be.length(1);
|
||||
expect(testContract.getABIDecodedReturnData('getOrderFillableTakerAssetAmounts', r[0])).to.deep.eq(
|
||||
expected,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -3,10 +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,14 +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,67 @@
|
||||
[
|
||||
{
|
||||
"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",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1581748629,
|
||||
"version": "3.1.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "3.1.0",
|
||||
"changes": [
|
||||
|
@@ -5,6 +5,32 @@ 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
|
||||
|
||||
## v3.1.1 - _February 15, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.0 - _February 8, 2020_
|
||||
|
||||
* Add `allowance()` and `balanceOf()` to `LibERC20Token` (#2464)
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user