Compare commits
526 Commits
@0x/contra
...
protocol@4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4057bdab91 | ||
|
|
1cd10f0ac9 | ||
|
|
68f87b2432 | ||
|
|
69bafc3bcd | ||
|
|
2c44b06b7b | ||
|
|
0233f00b4e | ||
|
|
fedb53187d | ||
|
|
6774d2f588 | ||
|
|
cf740b74f5 | ||
|
|
177c00463a | ||
|
|
49b0e32129 | ||
|
|
938fc94756 | ||
|
|
1561d91c2b | ||
|
|
9a28e51f51 | ||
|
|
f55eaa867b | ||
|
|
6b2856424a | ||
|
|
da757c4700 | ||
|
|
75e6654884 | ||
|
|
87308e7693 | ||
|
|
d5eef93a76 | ||
|
|
a7f23a982e | ||
|
|
9eadc5fc28 | ||
|
|
92ad1a612e | ||
|
|
09413c0e12 | ||
|
|
23788b41d5 | ||
|
|
ccf999a495 | ||
|
|
aa1016ee5f | ||
|
|
423ef57344 | ||
|
|
c18149e82f | ||
|
|
d14aebf724 | ||
|
|
ba719a9631 | ||
|
|
d36034d958 | ||
|
|
7750c57620 | ||
|
|
4d027e11d1 | ||
|
|
470e9a4697 | ||
|
|
7c51412e2f | ||
|
|
b3d1f3cd10 | ||
|
|
389bb77439 | ||
|
|
4327885a00 | ||
|
|
0aef0afbbb | ||
|
|
fa4c3a4f5f | ||
|
|
1d7c527c5c | ||
|
|
cbe3135e4b | ||
|
|
955ad49711 | ||
|
|
8d6f6e76e0 | ||
|
|
9337115650 | ||
|
|
fa45a44fe4 | ||
|
|
c9c7ac8559 | ||
|
|
c881723578 | ||
|
|
c9c30d3a76 | ||
|
|
73dfdb5b69 | ||
|
|
e638268f94 | ||
|
|
0bfd765481 | ||
|
|
1f12893735 | ||
|
|
dd3d9337c4 | ||
|
|
904214f4a8 | ||
|
|
64c090c4b4 | ||
|
|
e24474f152 | ||
|
|
29fa408256 | ||
|
|
1b94cc68af | ||
|
|
f5b4bb3035 | ||
|
|
afd880f28c | ||
|
|
cd14cdd168 | ||
|
|
c8ff53a75f | ||
|
|
6d08add20b | ||
|
|
29f9c725e3 | ||
|
|
a83453f07f | ||
|
|
ae365ce92c | ||
|
|
77a592e891 | ||
|
|
9a1df67d6b | ||
|
|
4b91411faf | ||
|
|
622a542d57 | ||
|
|
cba53a9a50 | ||
|
|
e186f27f63 | ||
|
|
4cd767ecb8 | ||
|
|
f6e85aedf1 | ||
|
|
b3ee294ba5 | ||
|
|
1c242def93 | ||
|
|
f0fe6f2f69 | ||
|
|
f86d555e49 | ||
|
|
b0f2c40463 | ||
|
|
87be6fbb8a | ||
|
|
9141a9d2c8 | ||
|
|
7f75de347e | ||
|
|
329f7761c3 | ||
|
|
0d8e83cd75 | ||
|
|
e5d60b8077 | ||
|
|
ae2fe55efa | ||
|
|
6073607d3e | ||
|
|
389ebb5df8 | ||
|
|
fd9655e9d4 | ||
|
|
6480aaa189 | ||
|
|
38969bb0a8 | ||
|
|
1847ab93af | ||
|
|
c1177416f5 | ||
|
|
4fdd203211 | ||
|
|
4bc11776d7 | ||
|
|
eb12eac5f3 | ||
|
|
5f5b951998 | ||
|
|
8e90547604 | ||
|
|
fbea74d7ff | ||
|
|
dcea16bc13 | ||
|
|
d59a074bd7 | ||
|
|
39cc4f4dbf | ||
|
|
85bcf87af8 | ||
|
|
a0fe1c610f | ||
|
|
5d21af1a0a | ||
|
|
f6edbd210c | ||
|
|
e084807a8f | ||
|
|
1c7d512829 | ||
|
|
df0e0866e4 | ||
|
|
9e6efc3676 | ||
|
|
3aef29dace | ||
|
|
9a16e00577 | ||
|
|
84e4819e6e | ||
|
|
25dd6bc79a | ||
|
|
5d2cdb00c2 | ||
|
|
0f701f42d3 | ||
|
|
3e3e82d3f7 | ||
|
|
c57bf86273 | ||
|
|
f470d282ee | ||
|
|
b8a2526da5 | ||
|
|
97575bbde9 | ||
|
|
e5ed8b2c81 | ||
|
|
61fbae3ae2 | ||
|
|
5c2255c841 | ||
|
|
e036dee6c5 | ||
|
|
8583aab241 | ||
|
|
5d05b62821 | ||
|
|
0063e8178f | ||
|
|
ec6e5dd517 | ||
|
|
9d08fefa1c | ||
|
|
2fdca24d4e | ||
|
|
42ec0b144e | ||
|
|
3f6ce78b46 | ||
|
|
c1300c1068 | ||
|
|
9a641cfab6 | ||
|
|
60345d4465 | ||
|
|
11dfea47a6 | ||
|
|
55e9dd39a2 | ||
|
|
1993929bed | ||
|
|
e1d81de517 | ||
|
|
a6b3a21635 | ||
|
|
fd59cdc2db | ||
|
|
98e11b5189 | ||
|
|
3bebc7cd62 | ||
|
|
56dab6ae8c | ||
|
|
285f98e9e9 | ||
|
|
8ae9f59f20 | ||
|
|
4c341c5ca3 | ||
|
|
a3c912c2af | ||
|
|
5e72eb9af9 | ||
|
|
9b08b73c06 | ||
|
|
76c7eb7c3e | ||
|
|
9b2e5a3adb | ||
|
|
813d703d12 | ||
|
|
83005d0f3d | ||
|
|
d07ffd2688 | ||
|
|
4170f970d0 | ||
|
|
dcde12dd70 | ||
|
|
84a60ec982 | ||
|
|
9615570dc6 | ||
|
|
880a9c3da0 | ||
|
|
c44bd9d42d | ||
|
|
90b441330b | ||
|
|
e12ed1eddf | ||
|
|
ac94023ab3 | ||
|
|
281e6acca5 | ||
|
|
2f1b520409 | ||
|
|
21b4eb3d26 | ||
|
|
644f6c7d28 | ||
|
|
0647b9e4f8 | ||
|
|
82d42eeede | ||
|
|
6ef4d95043 | ||
|
|
6044140f86 | ||
|
|
4da1ef0f56 | ||
|
|
8c668a3918 | ||
|
|
f1ff1cde39 | ||
|
|
1668a24e31 | ||
|
|
4b7c376d96 | ||
|
|
bb4a9c656c | ||
|
|
6ab07b6304 | ||
|
|
8903f1ab01 | ||
|
|
415535612a | ||
|
|
5248a135c3 | ||
|
|
602290925c | ||
|
|
01813746e8 | ||
|
|
7d668d8801 | ||
|
|
797a00a33a | ||
|
|
4b3d98f43c | ||
|
|
9ff77c1cd5 | ||
|
|
18a8351671 | ||
|
|
9a994dfcd3 | ||
|
|
b7adc5a889 | ||
|
|
10b0d7f363 | ||
|
|
e2c905a15f | ||
|
|
8589ba728c | ||
|
|
43512fd07a | ||
|
|
c090608d99 | ||
|
|
89817428ed | ||
|
|
fce3664258 | ||
|
|
aa522fe49b | ||
|
|
c03653ebd7 | ||
|
|
ae08f77381 | ||
|
|
73a07e512d | ||
|
|
7cff09f40a | ||
|
|
a6d690f10a | ||
|
|
d7cff52e75 | ||
|
|
cf1f29a37d | ||
|
|
9af22110b4 | ||
|
|
2c187c7e85 | ||
|
|
d46756ae2e | ||
|
|
d9a16ed1f9 | ||
|
|
57a1120997 | ||
|
|
0945d4cef2 | ||
|
|
8f6f7ad453 | ||
|
|
bb4fad37fa | ||
|
|
d06daf2957 | ||
|
|
34314960ef | ||
|
|
832ba737ec | ||
|
|
6950fdbebb | ||
|
|
1849b1bb9a | ||
|
|
a883139220 | ||
|
|
bb2c26cb94 | ||
|
|
ba09a0b2bf | ||
|
|
b2d54f0238 | ||
|
|
4a3096495b | ||
|
|
23f6e9e53c | ||
|
|
d7dbc0576d | ||
|
|
15fb00e958 | ||
|
|
1d9295cc94 | ||
|
|
79f36cf6fb | ||
|
|
6ce4458a5d | ||
|
|
fad6e65c07 | ||
|
|
840c85373e | ||
|
|
0479bb5fe1 | ||
|
|
9ecc31ed54 | ||
|
|
eb29abd36c | ||
|
|
d202e01522 | ||
|
|
e838a6801b | ||
|
|
7439871aa0 | ||
|
|
317f2138c5 | ||
|
|
e5834f1901 | ||
|
|
5063446f93 | ||
|
|
0caf495a1a | ||
|
|
a20de0fc69 | ||
|
|
9aa0065d2d | ||
|
|
c24855e627 | ||
|
|
6bb72dd775 | ||
|
|
77d1ed257c | ||
|
|
5d265360c4 | ||
|
|
c9097f6e8b | ||
|
|
d3df985a42 | ||
|
|
7267420874 | ||
|
|
17e81432f1 | ||
|
|
57c767c3b1 | ||
|
|
dbb1c88ad9 | ||
|
|
254b850a8b | ||
|
|
e2242e5955 | ||
|
|
7a0255fee7 | ||
|
|
ec6522cfbf | ||
|
|
1bf96e91bb | ||
|
|
eb733cc58f | ||
|
|
d9fdcf813b | ||
|
|
88bd1c9cea | ||
|
|
9e759e82b4 | ||
|
|
da433854ac | ||
|
|
ed3524e0d7 | ||
|
|
20e23bf3f6 | ||
|
|
be4f85690e | ||
|
|
e061dfa59b | ||
|
|
cb4f42dfea | ||
|
|
9727f0cebe | ||
|
|
68656b4a4d | ||
|
|
aae46bef84 | ||
|
|
a0bb36954e | ||
|
|
4f32f3174f | ||
|
|
b84107d142 | ||
|
|
b46eeadc64 | ||
|
|
692231c2ea | ||
|
|
3a1becc3e4 | ||
|
|
3653e2d7f9 | ||
|
|
65def38d98 | ||
|
|
859c36cb10 | ||
|
|
3e34386812 | ||
|
|
a35af11981 | ||
|
|
59eabec71e | ||
|
|
bf4c7e7d50 | ||
|
|
fd098ca4df | ||
|
|
c360f8d8fd | ||
|
|
b358559421 | ||
|
|
df5aad8e8e | ||
|
|
8dbf79db59 | ||
|
|
1839608e84 | ||
|
|
6e3e795b8b | ||
|
|
d9c410a7e3 | ||
|
|
609727afe8 | ||
|
|
8a5c74c0b4 | ||
|
|
cd93f3b07e | ||
|
|
8397b12de6 | ||
|
|
3f85acec3a | ||
|
|
d6a9e3d600 | ||
|
|
361569ac2f | ||
|
|
719664c145 | ||
|
|
f800d6c24c | ||
|
|
a9a81bcafb | ||
|
|
4280307a15 | ||
|
|
7b57d3ae51 | ||
|
|
8a8a5bbda0 | ||
|
|
76987c8db1 | ||
|
|
6f8971cc42 | ||
|
|
71ab882143 | ||
|
|
5a4961c8d9 | ||
|
|
4c4fb99d87 | ||
|
|
872abf09e9 | ||
|
|
f10bfe7d04 | ||
|
|
a74d8deff3 | ||
|
|
835ee4e8de | ||
|
|
63ec42303f | ||
|
|
f789aebddc | ||
|
|
efd83be779 | ||
|
|
603bc1d51c | ||
|
|
32a930a7fc | ||
|
|
f464bf68d7 | ||
|
|
ebdc4fb509 | ||
|
|
7580719586 | ||
|
|
aba9db2be7 | ||
|
|
a6680411c8 | ||
|
|
0d0e87de94 | ||
|
|
ccf2000c09 | ||
|
|
3eb2e0f56a | ||
|
|
d07c7d5b69 | ||
|
|
adf6684c29 | ||
|
|
9bf889aa30 | ||
|
|
e81c88564e | ||
|
|
901d400d62 | ||
|
|
289474e2ce | ||
|
|
407ca21168 | ||
|
|
5c68fc24d2 | ||
|
|
548800e0a9 | ||
|
|
bde3d6dc6a | ||
|
|
56550a6acc | ||
|
|
e51b83accc | ||
|
|
d5ae971f1c | ||
|
|
5a2f5f9a42 | ||
|
|
75a3b70cef | ||
|
|
803cf65ba1 | ||
|
|
5d3947b838 | ||
|
|
4397a59008 | ||
|
|
966d54c935 | ||
|
|
234ddb495d | ||
|
|
a744acc7bc | ||
|
|
27c624633c | ||
|
|
7ef75101b4 | ||
|
|
6f8aace00d | ||
|
|
6c264b2f18 | ||
|
|
df055e1958 | ||
|
|
70d2117470 | ||
|
|
2c173ccaf3 | ||
|
|
d2f4a0c5f3 | ||
|
|
0d6021e5e3 | ||
|
|
bb04726e7f | ||
|
|
220ca370c2 | ||
|
|
63af4e3e98 | ||
|
|
9754e12d82 | ||
|
|
d72ebed246 | ||
|
|
587fc71058 | ||
|
|
7d34e09a12 | ||
|
|
7d15baad0f | ||
|
|
1e6476ada7 | ||
|
|
1d6ca5f6b5 | ||
|
|
fb249f02fc | ||
|
|
fdf04ef275 | ||
|
|
b0f5f634f2 | ||
|
|
6ee0108565 | ||
|
|
c73097e688 | ||
|
|
a2d42b07b5 | ||
|
|
f9a794af93 | ||
|
|
a2643674ca | ||
|
|
c00ce9daac | ||
|
|
c68b5d7844 | ||
|
|
09ed106d4c | ||
|
|
a6b92fc658 | ||
|
|
4be4a1a30b | ||
|
|
9bede5d331 | ||
|
|
b50d4aee6d | ||
|
|
55bc367bd6 | ||
|
|
7a59b7eafe | ||
|
|
9e59d41e44 | ||
|
|
475e6c7bca | ||
|
|
dbc5a5293e | ||
|
|
f4bd2bd0d8 | ||
|
|
f1782a83ba | ||
|
|
cbade0d558 | ||
|
|
fe0c26387c | ||
|
|
c03a014740 | ||
|
|
84e6d788aa | ||
|
|
cd296b8767 | ||
|
|
5946d32a7d | ||
|
|
842dd8572b | ||
|
|
33e260f9db | ||
|
|
c44f8d0060 | ||
|
|
411548a33e | ||
|
|
9a17ce1383 | ||
|
|
2b120d0669 | ||
|
|
6d877d5242 | ||
|
|
e4abd690e7 | ||
|
|
2a194384b6 | ||
|
|
1e069e6f8a | ||
|
|
a019bb913d | ||
|
|
9ce73931f7 | ||
|
|
97020df178 | ||
|
|
dfb7b3de8f | ||
|
|
9e152912fe | ||
|
|
b2c2f1e1aa | ||
|
|
629c7d8e92 | ||
|
|
62f24d4356 | ||
|
|
ae281c33ca | ||
|
|
5d034dd106 | ||
|
|
c1f8df0eca | ||
|
|
76dda9eeda | ||
|
|
a9b84a92ac | ||
|
|
0f7e881899 | ||
|
|
6045f777ab | ||
|
|
5a15044ead | ||
|
|
a69d76e487 | ||
|
|
3adfcdffa8 | ||
|
|
164a5d44d9 | ||
|
|
70ddab0231 | ||
|
|
7bf009fbf6 | ||
|
|
525bc8197b | ||
|
|
24397c51a8 | ||
|
|
06b3464756 | ||
|
|
bbaa90bd9a | ||
|
|
5c683cbc0f | ||
|
|
95345f18bc | ||
|
|
4c3fbe83ac | ||
|
|
7caa43d02c | ||
|
|
3d4c03c9df | ||
|
|
22e1ed35d3 | ||
|
|
dabe6fd793 | ||
|
|
3cc639c8d0 | ||
|
|
22c8e0b6db | ||
|
|
f3ca4293bc | ||
|
|
db3e076d03 | ||
|
|
1a6759820a | ||
|
|
61c5e7b948 | ||
|
|
5fd78ef32f | ||
|
|
14ff9b827c | ||
|
|
598dc2cd71 | ||
|
|
08e1c5109f | ||
|
|
6f7a843742 | ||
|
|
c9c9615bb5 | ||
|
|
f98609686d | ||
|
|
5b8bbc34e8 | ||
|
|
49cb00a9ab | ||
|
|
74b240fb88 | ||
|
|
514f9d2621 | ||
|
|
fa78d1092a | ||
|
|
297342092b | ||
|
|
076f263a86 | ||
|
|
0c56207abc | ||
|
|
23953d8a5a | ||
|
|
c6919eb25a | ||
|
|
d509604b52 | ||
|
|
a74a3450eb | ||
|
|
72c5399b9d | ||
|
|
b29196b983 | ||
|
|
ce76a7033d | ||
|
|
3f4bb933d1 | ||
|
|
501b7b9b65 | ||
|
|
c0ea88e864 | ||
|
|
48f8bea460 | ||
|
|
f4e5b6e38d | ||
|
|
477cb0a48d | ||
|
|
d6bc0a3368 | ||
|
|
e544a804c2 | ||
|
|
96e0ad7899 | ||
|
|
5a1fee5d31 | ||
|
|
3557a5e5a9 | ||
|
|
0bd43d5265 | ||
|
|
8fd7c1b386 | ||
|
|
50068750f5 | ||
|
|
a7a905de4c | ||
|
|
f283108586 | ||
|
|
9afe05095a | ||
|
|
3dab892978 | ||
|
|
e40ddb800e | ||
|
|
72a74e7c66 | ||
|
|
e8ae64673f | ||
|
|
2451f419c8 | ||
|
|
2394eb62aa | ||
|
|
f9078bb1c3 | ||
|
|
bf25c81f1c | ||
|
|
03ecc530c3 | ||
|
|
d52b1d24d0 | ||
|
|
5083fab06a | ||
|
|
1249bf9ccc | ||
|
|
681f6b3f07 | ||
|
|
d3ca1fe96b | ||
|
|
9e4f5815e4 | ||
|
|
c3c27eaedc | ||
|
|
4bf6a23d23 | ||
|
|
aaaf0d02de | ||
|
|
825cc4d035 | ||
|
|
c2d44e5c10 | ||
|
|
0efd0860c8 | ||
|
|
a890a06664 | ||
|
|
5befb87071 | ||
|
|
12ba4c373a | ||
|
|
7fd25be02e | ||
|
|
aa688c4a92 | ||
|
|
fb437551c9 | ||
|
|
6fa1de7889 | ||
|
|
7a42df9a65 | ||
|
|
15a508f3ea | ||
|
|
b3c20ff909 | ||
|
|
682c07cb73 | ||
|
|
602605ab4b | ||
|
|
0eff2548d5 | ||
|
|
93ee681204 | ||
|
|
d7bea98075 | ||
|
|
437a3b048d | ||
|
|
f55a9454b5 | ||
|
|
3b03ad0db4 | ||
|
|
27d679e1f1 |
@@ -1,25 +1,24 @@
|
|||||||
version: 2
|
version: 2.1
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
resource_class: medium+
|
resource_class: xlarge
|
||||||
docker:
|
docker:
|
||||||
- image: nikolaik/python-nodejs:python3.7-nodejs10
|
- image: node:16
|
||||||
environment:
|
environment:
|
||||||
CONTRACTS_COMMIT_HASH: '9ed05f5'
|
NODE_OPTIONS: '--max-old-space-size=16384'
|
||||||
working_directory: ~/repo
|
working_directory: ~/repo
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- run: echo 'export PATH=$HOME/CIRCLE_PROJECT_REPONAME/node_modules/.bin:$PATH' >> $BASH_ENV
|
- run: echo 'export PATH=$HOME/CIRCLE_PROJECT_REPONAME/node_modules/.bin:$PATH' >> $BASH_ENV
|
||||||
- run:
|
- run:
|
||||||
name: install-yarn
|
name: install-yarn
|
||||||
command: npm install --force --global yarn@1.17.0
|
command: npm install --force --global yarn@1.22.0
|
||||||
- run:
|
- run:
|
||||||
name: yarn
|
name: yarn
|
||||||
command: yarn --frozen-lockfile --ignore-engines install || yarn --frozen-lockfile --ignore-engines install
|
command: yarn --frozen-lockfile --ignore-engines install || yarn --frozen-lockfile --ignore-engines install
|
||||||
- setup_remote_docker
|
- setup_remote_docker
|
||||||
- run: yarn build:ci
|
- run: yarn build:ci || yarn build:ci || yarn build:ci || yarn build:ci || yarn build:ci || yarn build:ci
|
||||||
- run: yarn build:ts
|
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: repo-{{ .Environment.CIRCLE_SHA1 }}
|
key: repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
paths:
|
paths:
|
||||||
@@ -31,57 +30,59 @@ jobs:
|
|||||||
test-exchange-ganache:
|
test-exchange-ganache:
|
||||||
resource_class: medium+
|
resource_class: medium+
|
||||||
docker:
|
docker:
|
||||||
- image: nikolaik/python-nodejs:python3.7-nodejs10
|
- image: node:16
|
||||||
working_directory: ~/repo
|
working_directory: ~/repo
|
||||||
steps:
|
steps:
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
- run: yarn wsrun test:circleci @0x/contracts-exchange
|
- run: yarn wsrun -p @0x/contracts-exchange -m --serial -c test:circleci
|
||||||
test-integrations-ganache:
|
test-integrations-ganache:
|
||||||
resource_class: medium+
|
resource_class: medium+
|
||||||
docker:
|
docker:
|
||||||
- image: nikolaik/python-nodejs:python3.7-nodejs10
|
- image: node:16
|
||||||
working_directory: ~/repo
|
working_directory: ~/repo
|
||||||
steps:
|
steps:
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
- run: yarn wsrun test:circleci @0x/contracts-integrations
|
- run: yarn wsrun -p @0x/contracts-integrations -m --serial -c test:circleci
|
||||||
test-contracts-staking-ganache:
|
test-contracts-staking-ganache:
|
||||||
resource_class: medium+
|
resource_class: medium+
|
||||||
docker:
|
docker:
|
||||||
- image: nikolaik/python-nodejs:python3.7-nodejs10
|
- image: node:16
|
||||||
working_directory: ~/repo
|
working_directory: ~/repo
|
||||||
steps:
|
steps:
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
- run: yarn wsrun test:circleci @0x/contracts-staking
|
- run: yarn wsrun -p @0x/contracts-staking -m --serial -c test:circleci
|
||||||
test-contracts-extra-ganache:
|
test-contracts-extra-ganache:
|
||||||
resource_class: medium+
|
resource_class: medium+
|
||||||
docker:
|
docker:
|
||||||
- image: nikolaik/python-nodejs:python3.7-nodejs10
|
- image: node:16
|
||||||
working_directory: ~/repo
|
working_directory: ~/repo
|
||||||
steps:
|
steps:
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
- run: yarn wsrun test:circleci @0x/contracts-exchange-forwarder @0x/contracts-coordinator
|
- run: yarn wsrun -p @0x/contracts-exchange-forwarder -p @0x/contracts-coordinator -m --serial -c test:circleci
|
||||||
test-contracts-rest-ganache:
|
test-contracts-rest-ganache:
|
||||||
resource_class: medium+
|
resource_class: medium+
|
||||||
docker:
|
docker:
|
||||||
- image: nikolaik/python-nodejs:python3.7-nodejs10
|
- image: node:16
|
||||||
working_directory: ~/repo
|
working_directory: ~/repo
|
||||||
steps:
|
steps:
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
- run: yarn wsrun test:circleci @0x/contracts-multisig @0x/contracts-utils @0x/contracts-exchange-libs @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-asset-proxy @0x/contracts-broker @0x/contracts-zero-ex
|
- run: yarn wsrun -p @0x/contracts-multisig -p @0x/contracts-utils -p @0x/contracts-exchange-libs -p @0x/contracts-erc20 -p @0x/contracts-erc721 -p @0x/contracts-erc1155 -p @0x/contracts-asset-proxy -p @0x/contracts-broker -p @0x/contracts-zero-ex -m --serial -c test:circleci
|
||||||
test-publish:
|
test-publish:
|
||||||
resource_class: medium+
|
resource_class: large
|
||||||
|
environment:
|
||||||
|
NODE_OPTIONS: '--max-old-space-size=6442'
|
||||||
docker:
|
docker:
|
||||||
- image: nikolaik/python-nodejs:python3.7-nodejs10
|
- image: node:16
|
||||||
- image: 0xorg/verdaccio
|
- image: 0xorg/verdaccio
|
||||||
working_directory: ~/repo
|
working_directory: ~/repo
|
||||||
steps:
|
steps:
|
||||||
@@ -95,7 +96,7 @@ jobs:
|
|||||||
path: ~/.npm/_logs
|
path: ~/.npm/_logs
|
||||||
test-doc-generation:
|
test-doc-generation:
|
||||||
docker:
|
docker:
|
||||||
- image: nikolaik/python-nodejs:python3.7-nodejs10
|
- image: node:16
|
||||||
working_directory: ~/repo
|
working_directory: ~/repo
|
||||||
steps:
|
steps:
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
@@ -106,18 +107,20 @@ jobs:
|
|||||||
no_output_timeout: 1200
|
no_output_timeout: 1200
|
||||||
test-rest:
|
test-rest:
|
||||||
docker:
|
docker:
|
||||||
- image: nikolaik/python-nodejs:python3.7-nodejs10
|
- image: node:16
|
||||||
working_directory: ~/repo
|
working_directory: ~/repo
|
||||||
|
environment:
|
||||||
|
RUST_ROUTER: "true"
|
||||||
steps:
|
steps:
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
- run: yarn wsrun test:circleci @0x/contracts-test-utils
|
- run: yarn wsrun -p @0x/contracts-test-utils -m --serial -c test:circleci
|
||||||
- run: yarn wsrun test:circleci @0x/contract-artifacts
|
- run: yarn wsrun -p @0x/contract-artifacts -m --serial -c test:circleci
|
||||||
- run: yarn wsrun test:circleci @0x/contract-wrappers-test
|
- run: yarn wsrun -p @0x/contract-wrappers-test -m --serial -c test:circleci
|
||||||
- run: yarn wsrun test:circleci @0x/migrations
|
- run: yarn wsrun -p @0x/migrations -m --serial -c test:circleci
|
||||||
- run: yarn wsrun test:circleci @0x/order-utils
|
- run: yarn wsrun -p @0x/order-utils -m --serial -c test:circleci
|
||||||
- run: yarn wsrun test:circleci @0x/asset-swapper
|
- run: yarn wsrun -p @0x/asset-swapper -m --serial -c test:circleci
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: coverage-contract-wrappers-test-{{ .Environment.CIRCLE_SHA1 }}
|
key: coverage-contract-wrappers-test-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
paths:
|
paths:
|
||||||
@@ -134,7 +137,7 @@ jobs:
|
|||||||
resource_class: large
|
resource_class: large
|
||||||
working_directory: ~/repo
|
working_directory: ~/repo
|
||||||
docker:
|
docker:
|
||||||
- image: nikolaik/python-nodejs:python3.7-nodejs10
|
- image: node:16
|
||||||
steps:
|
steps:
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
@@ -145,7 +148,7 @@ jobs:
|
|||||||
- run: yarn diff_md_docs:ci
|
- run: yarn diff_md_docs:ci
|
||||||
submit-coverage:
|
submit-coverage:
|
||||||
docker:
|
docker:
|
||||||
- image: nikolaik/python-nodejs:python3.7-nodejs10
|
- image: node:16
|
||||||
working_directory: ~/repo
|
working_directory: ~/repo
|
||||||
steps:
|
steps:
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
|
|||||||
9
.github/workflows/publish.yml
vendored
9
.github/workflows/publish.yml
vendored
@@ -7,6 +7,9 @@ on:
|
|||||||
description: 'required CI status'
|
description: 'required CI status'
|
||||||
default: 'success'
|
default: 'success'
|
||||||
required: true
|
required: true
|
||||||
|
prerelease:
|
||||||
|
description: 'prerelease name'
|
||||||
|
required: false
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
publish:
|
publish:
|
||||||
@@ -21,11 +24,11 @@ jobs:
|
|||||||
(echo "::error ::${{ github.ref }} does not have a successful CI status" && false)
|
(echo "::error ::${{ github.ref }} does not have a successful CI status" && false)
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
ref: 'development'
|
ref: ${{ github.ref }}
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- uses: actions/setup-node@v1
|
- uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: 10
|
node-version: 16
|
||||||
- uses: actions/setup-python@v2
|
- uses: actions/setup-python@v2
|
||||||
- name: 'configure git'
|
- name: 'configure git'
|
||||||
run: |
|
run: |
|
||||||
@@ -41,7 +44,9 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
NPM_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
|
NPM_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
|
||||||
GITHUB_TOKEN: ${{ github.token }}
|
GITHUB_TOKEN: ${{ github.token }}
|
||||||
|
PUBLISH_PRERELEASE: ${{ github.event.inputs.prerelease }}
|
||||||
- name: 'merge into main branch'
|
- name: 'merge into main branch'
|
||||||
|
if: github.event.inputs.prerelease == '' # unless it's a prerelease
|
||||||
run: |
|
run: |
|
||||||
git checkout main && \
|
git checkout main && \
|
||||||
git merge ${{ github.ref }} && \
|
git merge ${{ github.ref }} && \
|
||||||
|
|||||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -75,8 +75,9 @@ generated_docs/
|
|||||||
|
|
||||||
TODO.md
|
TODO.md
|
||||||
|
|
||||||
# VSCode file
|
# IDE file
|
||||||
.vscode
|
.vscode
|
||||||
|
.idea
|
||||||
|
|
||||||
# generated contract artifacts/
|
# generated contract artifacts/
|
||||||
contracts/broker/generated-artifacts/
|
contracts/broker/generated-artifacts/
|
||||||
@@ -113,6 +114,8 @@ contracts/dev-utils/generated-artifacts/
|
|||||||
contracts/dev-utils/test/generated-artifacts/
|
contracts/dev-utils/test/generated-artifacts/
|
||||||
contracts/zero-ex/generated-artifacts/
|
contracts/zero-ex/generated-artifacts/
|
||||||
contracts/zero-ex/test/generated-artifacts/
|
contracts/zero-ex/test/generated-artifacts/
|
||||||
|
contracts/treasury/generated-artifacts/
|
||||||
|
contracts/treasury/test/generated-artifacts/
|
||||||
|
|
||||||
# generated truffle contract artifacts/
|
# generated truffle contract artifacts/
|
||||||
contracts/broker/build/
|
contracts/broker/build/
|
||||||
@@ -167,6 +170,8 @@ contracts/dev-utils/generated-wrappers/
|
|||||||
contracts/dev-utils/test/generated-wrappers/
|
contracts/dev-utils/test/generated-wrappers/
|
||||||
contracts/zero-ex/generated-wrappers/
|
contracts/zero-ex/generated-wrappers/
|
||||||
contracts/zero-ex/test/generated-wrappers/
|
contracts/zero-ex/test/generated-wrappers/
|
||||||
|
contracts/treasury/generated-wrappers/
|
||||||
|
contracts/treasury/test/generated-wrappers/
|
||||||
|
|
||||||
# Doc README copy
|
# Doc README copy
|
||||||
packages/*/docs/README.md
|
packages/*/docs/README.md
|
||||||
|
|||||||
@@ -64,6 +64,10 @@ lib
|
|||||||
/contracts/zero-ex/test/generated-wrappers
|
/contracts/zero-ex/test/generated-wrappers
|
||||||
/contracts/zero-ex/generated-artifacts
|
/contracts/zero-ex/generated-artifacts
|
||||||
/contracts/zero-ex/test/generated-artifacts
|
/contracts/zero-ex/test/generated-artifacts
|
||||||
|
/contracts/treasury/generated-wrappers
|
||||||
|
/contracts/treasury/test/generated-wrappers
|
||||||
|
/contracts/treasury/generated-artifacts
|
||||||
|
/contracts/treasury/test/generated-artifacts
|
||||||
/contracts/staking/build/
|
/contracts/staking/build/
|
||||||
/contracts/coordinator/build/
|
/contracts/coordinator/build/
|
||||||
/contracts/exchange/build/
|
/contracts/exchange/build/
|
||||||
|
|||||||
@@ -3,5 +3,6 @@
|
|||||||
"tabWidth": 4,
|
"tabWidth": 4,
|
||||||
"singleQuote": true,
|
"singleQuote": true,
|
||||||
"trailingComma": "all",
|
"trailingComma": "all",
|
||||||
"bracketSpacing": true
|
"bracketSpacing": true,
|
||||||
|
"arrowParens": "avoid"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,6 @@
|
|||||||
| Package | Version |
|
| Package | Version |
|
||||||
| ------: | :------ |
|
| ------: | :------ |
|
||||||
|
|
||||||
|
|
||||||
<!-- For example:
|
<!-- For example:
|
||||||
| `0x.js` | 2.0.4 |
|
| `0x.js` | 2.0.4 |
|
||||||
| `Exchange Contract` | v2 |
|
| `Exchange Contract` | v2 |
|
||||||
|
|||||||
41
README.md
41
README.md
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
[0x][website-url] is an open protocol that facilitates trustless, low friction exchange of Ethereum-based assets. For more information on how it works, check out the [0x protocol specification](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md).
|
[0x][website-url] is an open protocol that facilitates trustless, low friction exchange of Ethereum-based assets. For more information on how it works, check out the [0x protocol specification](https://protocol.0x.org/).
|
||||||
|
|
||||||
This repository is a monorepo including the 0x protocol smart contracts and numerous developer tools. Each public sub-package is independently published to NPM.
|
This repository is a monorepo including the 0x protocol smart contracts and numerous developer tools. Each public sub-package is independently published to NPM.
|
||||||
|
|
||||||
@@ -15,28 +15,18 @@ This repository is a monorepo including the 0x protocol smart contracts and nume
|
|||||||
|
|
||||||
## Packages
|
## Packages
|
||||||
|
|
||||||
Visit our [developer portal](https://0x.org/docs/tools/order-utils) for a comprehensive list of core & community maintained packages. All packages maintained with this monorepo are listed below.
|
Visit our [developer portal](https://0x.org/docs/) for a comprehensive list of core & community maintained packages. All packages maintained with this monorepo are listed below.
|
||||||
|
|
||||||
### Solidity Packages
|
### Solidity Packages
|
||||||
|
|
||||||
These packages are all under development. See [/contracts/README.md](/contracts/README.md) for a list of deployed packages.
|
These packages are all under development. See [/contracts/README.md](/contracts/README.md) for a list of deployed packages.
|
||||||
|
|
||||||
| Package | Version | Description |
|
| Package | Version | Description |
|
||||||
| ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| --------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- |
|
||||||
| [`@0x/contracts-asset-proxy`](/contracts/asset-proxy) | [](https://www.npmjs.com/package/@0x/contracts-asset-proxy) | [`AssetProxy`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#assetproxy) contracts used within the protocol |
|
| [`@0x/contracts-zero-ex`](/contracts/zero-ex) | [](https://www.npmjs.com/package/@0x/contracts-zero-ex) | The contracts used for settling trades within the protocol |
|
||||||
| [`@0x/contracts-erc20`](/contracts/erc20) | [](https://www.npmjs.com/package/@0x/contracts-erc20) | Implementations of various ERC20 tokens |
|
| [`@0x/contracts-erc20`](/contracts/erc20) | [](https://www.npmjs.com/package/@0x/contracts-erc20) | Implementations of various ERC20 tokens |
|
||||||
| [`@0x/contracts-erc721`](/contracts/erc721) | [](https://www.npmjs.com/package/@0x/contracts-erc721) | Implementations of various ERC721 tokens |
|
| [`@0x/contracts-test-utils`](/contracts/test-utils) | [](https://www.npmjs.com/package/@0x/contracts-test-utils) | TypeScript/Javascript shared utilities used for testing contracts |
|
||||||
| [`@0x/contracts-erc1155`](/contracts/erc1155) | [](https://www.npmjs.com/package/@0x/contracts-erc1155) | Implementations of various ERC1155 tokens |
|
| [`@0x/contracts-utils`](/contracts/utils) | [](https://www.npmjs.com/package/@0x/contracts-utils) | Generic libraries and utilities used throughout all of the contracts |
|
||||||
| [`@0x/contracts-exchange`](/contracts/exchange) | [](https://www.npmjs.com/package/@0x/contracts-exchange) | The [`Exchange`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#exchange) contract used for settling trades within the protocol |
|
|
||||||
| [`@0x/contracts-exchange-forwarder`](/contracts/exchange-forwarder) | [](https://www.npmjs.com/package/@0x/contracts-exchange-forwarder) | A [`Forwarder`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/forwarder-specification.md) contract used to simplify UX for interacting with the protocol |
|
|
||||||
| [`@0x/contracts-exchange-libs`](/contracts/exchange-libs) | [](https://www.npmjs.com/package/@0x/contracts-exchange-libs) | Protocol specific libraries used within the [`Exchange`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#exchange) contract |
|
|
||||||
| [`@0x/contracts-extensions`](/contracts/extensions) | [](https://www.npmjs.com/package/@0x/contracts-extensions) | Contracts that interact with and extend the functionality of the core protocol |
|
|
||||||
| [`@0x/contracts-multisig`](/contracts/multisig) | [](https://www.npmjs.com/package/@0x/contracts-multisig) | Various implementations of multisignature wallets, including the [`AssetProxyOwner`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#assetproxyowner) contract that has permissions to upgrade the protocol |
|
|
||||||
| [`@0x/contracts-test-utils`](/contracts/test-utils) | [](https://www.npmjs.com/package/@0x/contracts-test-utils) | TypeScript/Javascript shared utilities used for testing contracts |
|
|
||||||
| [`@0x/contracts-utils`](/contracts/utils) | [](https://www.npmjs.com/package/@0x/contracts-utils) | Generic libraries and utilities used throughout all of the contracts |
|
|
||||||
| [`@0x/contracts-coordinator`](/contracts/coordinator) | [](https://www.npmjs.com/package/@0x/contracts-coordinator) | A contract that allows users to execute 0x transactions with permission from a Coordinator |
|
|
||||||
| [`@0x/contracts-dev-utils`](/contracts/dev-utils) | [](https://www.npmjs.com/package/@0x/contracts-dev-utils) | A contract contains utility functions for developers (such as validating many orders using a single eth_call) |
|
|
||||||
| [`@0x/contracts-staking`](/contracts/staking) | [](https://www.npmjs.com/package/@0x/contracts-staking) | Implements the stake-based liquidity incentives defined by [`ZEIP-31`](https://github.com/0xProject/ZEIPs/issues/31) |
|
|
||||||
|
|
||||||
### TypeScript/Javascript Packages
|
### TypeScript/Javascript Packages
|
||||||
|
|
||||||
@@ -44,9 +34,10 @@ These packages are all under development. See [/contracts/README.md](/contracts/
|
|||||||
|
|
||||||
| Package | Version | Description |
|
| Package | Version | Description |
|
||||||
| -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
|
| -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
|
||||||
|
| [`@0x/asset-swapper`](/packages/asset-swapper) | [](https://www.npmjs.com/package/@0x/asset-swapper) | Package used to find and create aggregated swaps |
|
||||||
|
| [`@0x/protocol-utils`](/packages/protocol-utils) | [](https://www.npmjs.com/package/@0x/protocol-utils) | A set of utilities for generating, parsing, signing and validating 0x orders |
|
||||||
| [`@0x/contract-addresses`](/packages/contract-addresses) | [](https://www.npmjs.com/package/@0x/contract-addresses) | A tiny utility library for getting known deployed contract addresses for a particular network. |
|
| [`@0x/contract-addresses`](/packages/contract-addresses) | [](https://www.npmjs.com/package/@0x/contract-addresses) | A tiny utility library for getting known deployed contract addresses for a particular network. |
|
||||||
| [`@0x/contract-wrappers`](/packages/contract-wrappers) | [](https://www.npmjs.com/package/@0x/contract-wrappers) | JS/TS wrappers for interacting with the 0x smart contracts |
|
| [`@0x/contract-wrappers`](/packages/contract-wrappers) | [](https://www.npmjs.com/package/@0x/contract-wrappers) | JS/TS wrappers for interacting with the 0x smart contracts |
|
||||||
| [`@0x/order-utils`](/packages/order-utils) | [](https://www.npmjs.com/package/@0x/order-utils) | A set of utilities for generating, parsing, signing and validating 0x orders |
|
|
||||||
| [`@0x/migrations`](/packages/migrations) | [](https://www.npmjs.com/package/@0x/migrations) | Migration tool for deploying 0x smart contracts on private testnets |
|
| [`@0x/migrations`](/packages/migrations) | [](https://www.npmjs.com/package/@0x/migrations) | Migration tool for deploying 0x smart contracts on private testnets |
|
||||||
| [`@0x/contract-artifacts`](/packages/contract-artifacts) | [](https://www.npmjs.com/package/@0x/contract-artifacts) | 0x smart contract compilation artifacts | |
|
| [`@0x/contract-artifacts`](/packages/contract-artifacts) | [](https://www.npmjs.com/package/@0x/contract-artifacts) | 0x smart contract compilation artifacts | |
|
||||||
|
|
||||||
@@ -92,7 +83,7 @@ yarn build
|
|||||||
To build a specific package:
|
To build a specific package:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
PKG=@0x/contract-wrappers yarn build
|
PKG=@0x/asset-swapper yarn build
|
||||||
```
|
```
|
||||||
|
|
||||||
To build all contracts packages:
|
To build all contracts packages:
|
||||||
@@ -115,7 +106,7 @@ To watch a specific package and all it's dependent packages:
|
|||||||
PKG=[NPM_PACKAGE_NAME] yarn watch
|
PKG=[NPM_PACKAGE_NAME] yarn watch
|
||||||
|
|
||||||
e.g
|
e.g
|
||||||
PKG=@0x/contract-wrappers yarn watch
|
PKG=@0x/asset-swapper yarn watch
|
||||||
```
|
```
|
||||||
|
|
||||||
### Clean
|
### Clean
|
||||||
@@ -129,7 +120,7 @@ yarn clean
|
|||||||
Clean a specific package
|
Clean a specific package
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
PKG=0x.js yarn clean
|
PKG=@0x/asset-swapper yarn clean
|
||||||
```
|
```
|
||||||
|
|
||||||
### Rebuild
|
### Rebuild
|
||||||
@@ -143,7 +134,7 @@ yarn rebuild
|
|||||||
To re-build (clean & build) a specific package & it's deps:
|
To re-build (clean & build) a specific package & it's deps:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
PKG=@0x/contract-wrappers yarn rebuild
|
PKG=@0x/asset-swapper yarn rebuild
|
||||||
```
|
```
|
||||||
|
|
||||||
### Lint
|
### Lint
|
||||||
@@ -157,7 +148,7 @@ yarn lint
|
|||||||
Lint a specific package:
|
Lint a specific package:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
PKG=@0x/contract-wrappers yarn lint
|
PKG=@0x/asset-swapper yarn lint
|
||||||
```
|
```
|
||||||
|
|
||||||
### Run Tests
|
### Run Tests
|
||||||
@@ -171,7 +162,7 @@ yarn test
|
|||||||
Run a specific package's test:
|
Run a specific package's test:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
PKG=@0x/contract-wrappers yarn test
|
PKG=@0x/asset-swapper yarn test
|
||||||
```
|
```
|
||||||
|
|
||||||
Run all contracts packages tests:
|
Run all contracts packages tests:
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
"indent": ["error", 4],
|
"indent": ["error", 4],
|
||||||
"max-line-length": ["warn", 160],
|
"max-line-length": ["warn", 160],
|
||||||
"no-inline-assembly": false,
|
"no-inline-assembly": false,
|
||||||
"no-empty-blocks": false,
|
|
||||||
"quotes": ["error", "double"],
|
"quotes": ["error", "double"],
|
||||||
"separate-by-one-line-in-contract": "error",
|
"separate-by-one-line-in-contract": "error",
|
||||||
"space-after-comma": "error",
|
"space-after-comma": "error",
|
||||||
|
|||||||
@@ -1,16 +1,3 @@
|
|||||||
#### Deployed Contract Packages
|
|
||||||
|
|
||||||
| Contract | Package | Version | Git Tag |
|
|
||||||
| --------------- | ------------------------------------------------------------------- | -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
|
|
||||||
| AssetProxyOwner | [`@0x/contracts-multisig`](/contracts/multisig) | [v1.0.2](https://www.npmjs.com/package/@0x/contracts-multisig/v/1.0.2) | [@0x/contracts-multisig@1.0.2](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-multisig@1.0.2) |
|
|
||||||
| ERC20Proxy | [`@0x/contracts-asset-proxy`](/contracts/asset-proxy) | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-asset-proxy/v/1.0.1) | [@0x/contracts-asset-proxy@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-asset-proxy@1.0.1) |
|
|
||||||
| ERC721Proxy | [`@0x/contracts-asset-proxy`](/contracts/asset-proxy) | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-asset-proxy/v/1.0.1) | [@0x/contracts-asset-proxy@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-asset-proxy@1.0.1) |
|
|
||||||
| Exchange | [`@0x/contracts-exchange`](/contracts/exchange) | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-exchange/v/1.0.1) | [@0x/contracts-exchange@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-exchange@1.0.1) |
|
|
||||||
| DutchAuction | [`@0x/contracts-extensions`](/contracts/extensions) | [v1.0.2](https://www.npmjs.com/package/@0x/contracts-extensions/v/1.0.2) | [@0x/contracts-extensions@1.0.2](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-extensions@1.0.2) |
|
|
||||||
| Forwarder | [`@0x/contracts-exchange-forwarder`](/contracts/exchange-forwarder) | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-exchange-forwarder/v/1.0.1) | [@0x/contracts-exchange-forwarder@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-exchange-forwarder@1.0.1) |
|
|
||||||
| MultiAssetProxy | [`@0x/contracts-asset-proxy`](/contracts/asset-proxy) | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-asset-proxy/v/1.0.1) | [@0x/contracts-asset-proxy@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-asset-proxy@1.0.1) |
|
|
||||||
| ZRXToken | [`@0x/contracts-erc20`](/contracts/erc20) | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-erc20/v/1.0.1) | [@0x/contracts-erc20@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-erc20@1.0.1) |
|
|
||||||
|
|
||||||
#### Development
|
#### Development
|
||||||
|
|
||||||
Building solidity files will update the contract artifact in `{package-name}/generated-artifacts/{contract}.json`, but does not automatically update the `contract-artifacts` or `contract-wrappers` packages, which are generated from the artifact JSON. See `contract-artifacts/README.md` for instructions on updating these packages.
|
Building solidity files will update the contract artifact in `{package-name}/generated-artifacts/{contract}.json`, but does not automatically update the `contract-artifacts` or `contract-wrappers` packages, which are generated from the artifact JSON. See `contract-artifacts/README.md` for instructions on updating these packages.
|
||||||
|
|||||||
@@ -1,48 +0,0 @@
|
|||||||
# Contracts testing options
|
|
||||||
|
|
||||||
## Revert stack traces
|
|
||||||
|
|
||||||
If you want to see helpful stack traces (incl. line number, code snippet) for smart contract reverts, run the tests with:
|
|
||||||
|
|
||||||
```
|
|
||||||
yarn test:trace
|
|
||||||
```
|
|
||||||
|
|
||||||
**Note:** This currently slows down the test runs and is therefore not enabled by default.
|
|
||||||
|
|
||||||
## Backing Ethereum node
|
|
||||||
|
|
||||||
By default, our tests run against an in-process [Ganache](https://github.com/trufflesuite/ganache-core) instance. In order to run the tests against [Geth](https://github.com/ethereum/go-ethereum), first follow the instructions in the README for the devnet package to start the devnet Geth node. Then run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
TEST_PROVIDER=geth yarn test
|
|
||||||
```
|
|
||||||
|
|
||||||
## Code coverage
|
|
||||||
|
|
||||||
In order to see the Solidity code coverage output generated by `@0x/sol-coverage`, run:
|
|
||||||
|
|
||||||
```
|
|
||||||
yarn test:coverage
|
|
||||||
```
|
|
||||||
|
|
||||||
## Gas profiler
|
|
||||||
|
|
||||||
In order to profile the gas costs for a specific smart contract call/transaction, you can run the tests in `profiler` mode.
|
|
||||||
|
|
||||||
**Note:** Traces emitted by ganache have incorrect gas costs so we recommend using Geth for profiling.
|
|
||||||
|
|
||||||
```
|
|
||||||
TEST_PROVIDER=geth yarn test:profiler
|
|
||||||
```
|
|
||||||
|
|
||||||
You'll see a warning that you need to explicitly enable and disable the profiler before and after the block of code you want to profile.
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { profiler } from './utils/profiler';
|
|
||||||
profiler.start();
|
|
||||||
// Some call to a smart contract
|
|
||||||
profiler.stop();
|
|
||||||
```
|
|
||||||
|
|
||||||
Without explicitly starting and stopping the profiler, the profiler output will be too busy, and therefore unusable.
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
# solhint can't parse `abi.decode` syntax.
|
|
||||||
contracts/src/ERC1155Proxy.sol
|
|
||||||
@@ -1,723 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"timestamp": 1607485227,
|
|
||||||
"version": "3.6.9",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Dependencies updated"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"timestamp": 1607381756,
|
|
||||||
"version": "3.6.8",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Dependencies updated"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"timestamp": 1606961263,
|
|
||||||
"version": "3.6.7",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Dependencies updated"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"timestamp": 1605763885,
|
|
||||||
"version": "3.6.6",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Dependencies updated"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"timestamp": 1605302002,
|
|
||||||
"version": "3.6.5",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Dependencies updated"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"timestamp": 1604385937,
|
|
||||||
"version": "3.6.4",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Dependencies updated"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"timestamp": 1604376968,
|
|
||||||
"version": "3.6.3",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Dependencies updated"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"timestamp": 1604355662,
|
|
||||||
"version": "3.6.2",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Dependencies updated"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"timestamp": 1603851023,
|
|
||||||
"version": "3.6.1",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Dependencies updated"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"version": "3.6.0",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Add `SwerveBridge` and `SnowSwapBridge` (duplicate of `CurveBridge`)",
|
|
||||||
"pr": 2707
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"timestamp": 1603833198
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"version": "3.5.0",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Update `CurveBridge` to support more varied curves",
|
|
||||||
"pr": 2633
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"note": "Export DexForwarderBridgeContract",
|
|
||||||
"pr": 2656
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"note": "Add BancorBridge and IBancorNetwork, ",
|
|
||||||
"pr": 2650
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"note": "Added `MStableBridge`",
|
|
||||||
"pr": 2662
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"note": "Added `MooniswapBridge`",
|
|
||||||
"pr": 2675
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"note": "Reworked `KyberBridge`",
|
|
||||||
"pr": 2683
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"note": "Added `CreamBridge`",
|
|
||||||
"pr": 2715
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"note": "Added `ShellBridge`",
|
|
||||||
"pr": 2722
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"note": "Added `DODOBridge`",
|
|
||||||
"pr": 2701
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"timestamp": 1603265572
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"version": "3.4.0",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Fix instability with DFB.",
|
|
||||||
"pr": 2616
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"note": "Add `BalancerBridge`",
|
|
||||||
"pr": 2613
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"timestamp": 1594788383
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"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
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"note": "Remove dependency on `@0x/contracts-dev-utils`",
|
|
||||||
"pr": 2462
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"note": "Add asset data decoding functions",
|
|
||||||
"pr": 2462
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"note": "Add `setOperators()` to `IDydx`",
|
|
||||||
"pr": 2462
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"timestamp": 1581204851
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"timestamp": 1580988106,
|
|
||||||
"version": "3.1.3",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Dependencies updated"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"timestamp": 1580811564,
|
|
||||||
"version": "3.1.2",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Dependencies updated"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"timestamp": 1579682890,
|
|
||||||
"version": "3.1.1",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Dependencies updated"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"version": "3.1.0",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Integration tests for DydxBridge with ERC20BridgeProxy.",
|
|
||||||
"pr": 2401
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"note": "Fix `UniswapBridge` token -> token transfer call.",
|
|
||||||
"pr": 2412
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"note": "Fix `KyberBridge` incorrect `minConversionRate` calculation.",
|
|
||||||
"pr": 2412
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"timestamp": 1578272714
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"timestamp": 1576540892,
|
|
||||||
"version": "3.0.2",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Dependencies updated"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"timestamp": 1575931811,
|
|
||||||
"version": "3.0.1",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Dependencies updated"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"version": "3.0.0",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Implement `KyberBridge`.",
|
|
||||||
"pr": 2352
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"note": "Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils",
|
|
||||||
"pr": 2330
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"note": "ERC20Wrapper and ERC1155ProxyWrapper constructors now require an instance of DevUtilsContract",
|
|
||||||
"pr": 2034
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"note": "Disallow the zero address from being made an authorized address in MixinAuthorizable, and created an archive directory that includes an old version of Ownable",
|
|
||||||
"pr": 2019
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"note": "Remove `LibAssetProxyIds` contract",
|
|
||||||
"pr": 2055
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"note": "Compile and export all contracts, artifacts, and wrappers by default",
|
|
||||||
"pr": 2055
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"note": "Remove unused dependency on IAuthorizable in IAssetProxy",
|
|
||||||
"pr": 1910
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"note": "Add `ERC20BridgeProxy`",
|
|
||||||
"pr": 2220
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"note": "Add `Eth2DaiBridge`",
|
|
||||||
"pr": 2221
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"note": "Add `UniswapBridge`",
|
|
||||||
"pr": 2233
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"note": "Replaced `SafeMath` with `LibSafeMath`",
|
|
||||||
"pr": 2254
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"timestamp": 1575296764
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"version": "2.3.0-beta.4",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Implement `KyberBridge`.",
|
|
||||||
"pr": 2352
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"note": "Implement `DydxBridge`.",
|
|
||||||
"pr": 2365
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"timestamp": 1575290197
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"version": "2.3.0-beta.3",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Dependencies updated"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"timestamp": 1574238768
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"version": "2.3.0-beta.2",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils",
|
|
||||||
"pr": 2330
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"timestamp": 1574030254
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"version": "2.3.0-beta.1",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "ERC20Wrapper and ERC1155ProxyWrapper constructors now require an instance of DevUtilsContract",
|
|
||||||
"pr": 2034
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"timestamp": 1573159180
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"version": "2.3.0-beta.0",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Disallow the zero address from being made an authorized address in MixinAuthorizable, and created an archive directory that includes an old version of Ownable",
|
|
||||||
"pr": 2019
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"note": "Remove `LibAssetProxyIds` contract",
|
|
||||||
"pr": 2055
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"note": "Compile and export all contracts, artifacts, and wrappers by default",
|
|
||||||
"pr": 2055
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"note": "Remove unused dependency on IAuthorizable in IAssetProxy",
|
|
||||||
"pr": 1910
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"note": "Add `ERC20BridgeProxy`",
|
|
||||||
"pr": 2220
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"note": "Add `Eth2DaiBridge`",
|
|
||||||
"pr": 2221
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"note": "Add `UniswapBridge`",
|
|
||||||
"pr": 2233
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"note": "Replaced `SafeMath` with `LibSafeMath`",
|
|
||||||
"pr": 2254
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"timestamp": 1570135330
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"timestamp": 1568744790,
|
|
||||||
"version": "2.2.8",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Dependencies updated"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"timestamp": 1567521715,
|
|
||||||
"version": "2.2.7",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Dependencies updated"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"timestamp": 1566446343,
|
|
||||||
"version": "2.2.6",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Dependencies updated"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"timestamp": 1565296576,
|
|
||||||
"version": "2.2.5",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Dependencies updated"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"version": "2.2.4",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Updated calls to <contract wrapper>.deployFrom0xArtifactAsync to include artifact dependencies.",
|
|
||||||
"pr": 1995
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"timestamp": 1564607468
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"timestamp": 1563957393,
|
|
||||||
"version": "2.2.3",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Dependencies updated"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"timestamp": 1563193019,
|
|
||||||
"version": "2.2.2",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Dependencies updated"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"timestamp": 1563047529,
|
|
||||||
"version": "2.2.1",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Dependencies updated"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"version": "2.2.0",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Add `LibAssetProxyIds` contract",
|
|
||||||
"pr": 1835
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"note": "Updated ERC1155 Asset Proxy. Less optimization. More explicit handling of edge cases.",
|
|
||||||
"pr": 1852
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"note": "Implement StaticCallProxy",
|
|
||||||
"pr": 1863
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"timestamp": 1563006338
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"timestamp": 1558712885,
|
|
||||||
"version": "2.1.5",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Dependencies updated"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"timestamp": 1557961111,
|
|
||||||
"version": "2.1.4",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Dependencies updated"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"timestamp": 1557799313,
|
|
||||||
"version": "2.1.3",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Dependencies updated"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"version": "2.1.2",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Update tests to use contract-built-in `awaitTransactionSuccessAsync`",
|
|
||||||
"pr": 1797
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"note": "Make `ERC721Wrapper.setApprovalForAll()` take an owner address instead of a token ID",
|
|
||||||
"pr": 1819
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"note": "Automatically set unlimited proxy allowances in `ERC721.setBalancesAndAllowancesAsync()`",
|
|
||||||
"pr": 1819
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"note": "Add `setProxyAllowanceForAllAsync()` to `ERC1155ProxyWrapper`.",
|
|
||||||
"pr": 1819
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"timestamp": 1557507213
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"version": "2.1.1",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Dependencies updated"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"timestamp": 1554997931
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"version": "2.1.0",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Run Web3ProviderEngine without excess block polling",
|
|
||||||
"pr": 1695
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"timestamp": 1553183790
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"version": "2.0.0",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Do not reexport external dependencies",
|
|
||||||
"pr": 1682
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"note": "Add ERC1155Proxy",
|
|
||||||
"pr": 1661
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"note": "Bumped solidity version to ^0.5.5",
|
|
||||||
"pr": 1701
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"note": "Integration testing for ERC1155Proxy",
|
|
||||||
"pr": 1673
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"timestamp": 1553091633
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"timestamp": 1551479279,
|
|
||||||
"version": "1.0.9",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Dependencies updated"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"timestamp": 1551299797,
|
|
||||||
"version": "1.0.8",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Dependencies updated"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"timestamp": 1551220833,
|
|
||||||
"version": "1.0.7",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Dependencies updated"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"timestamp": 1551130135,
|
|
||||||
"version": "1.0.6",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Dependencies updated"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"timestamp": 1549733923,
|
|
||||||
"version": "1.0.5",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Dependencies updated"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"version": "1.0.4",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Dependencies updated"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"timestamp": 1549547375
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"version": "1.0.3",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Fake publish to enable pinning"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"timestamp": 1549504360
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"timestamp": 1549452781,
|
|
||||||
"version": "1.0.2",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Dependencies updated"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"timestamp": 1549373905,
|
|
||||||
"version": "1.0.1",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Dependencies updated"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"version": "1.0.0",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Move all AssetProxy contracts out of contracts-protocol to new package",
|
|
||||||
"pr": 1539
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@@ -1,282 +0,0 @@
|
|||||||
<!--
|
|
||||||
changelogUtils.file is auto-generated using the monorepo-scripts package. Don't edit directly.
|
|
||||||
Edit the package's CHANGELOG.json file only.
|
|
||||||
-->
|
|
||||||
|
|
||||||
CHANGELOG
|
|
||||||
|
|
||||||
## v3.6.9 - _December 9, 2020_
|
|
||||||
|
|
||||||
* Dependencies updated
|
|
||||||
|
|
||||||
## v3.6.8 - _December 7, 2020_
|
|
||||||
|
|
||||||
* Dependencies updated
|
|
||||||
|
|
||||||
## v3.6.7 - _December 3, 2020_
|
|
||||||
|
|
||||||
* Dependencies updated
|
|
||||||
|
|
||||||
## v3.6.6 - _November 19, 2020_
|
|
||||||
|
|
||||||
* Dependencies updated
|
|
||||||
|
|
||||||
## v3.6.5 - _November 13, 2020_
|
|
||||||
|
|
||||||
* Dependencies updated
|
|
||||||
|
|
||||||
## v3.6.4 - _November 3, 2020_
|
|
||||||
|
|
||||||
* Dependencies updated
|
|
||||||
|
|
||||||
## v3.6.3 - _November 3, 2020_
|
|
||||||
|
|
||||||
* Dependencies updated
|
|
||||||
|
|
||||||
## v3.6.2 - _November 2, 2020_
|
|
||||||
|
|
||||||
* Dependencies updated
|
|
||||||
|
|
||||||
## v3.6.1 - _October 28, 2020_
|
|
||||||
|
|
||||||
* Dependencies updated
|
|
||||||
|
|
||||||
## v3.6.0 - _October 27, 2020_
|
|
||||||
|
|
||||||
* Add `SwerveBridge` and `SnowSwapBridge` (duplicate of `CurveBridge`) (#2707)
|
|
||||||
|
|
||||||
## v3.5.0 - _October 21, 2020_
|
|
||||||
|
|
||||||
* Update `CurveBridge` to support more varied curves (#2633)
|
|
||||||
* Export DexForwarderBridgeContract (#2656)
|
|
||||||
* Add BancorBridge and IBancorNetwork, (#2650)
|
|
||||||
* Added `MStableBridge` (#2662)
|
|
||||||
* Added `MooniswapBridge` (#2675)
|
|
||||||
* Reworked `KyberBridge` (#2683)
|
|
||||||
* Added `CreamBridge` (#2715)
|
|
||||||
* Added `ShellBridge` (#2722)
|
|
||||||
* Added `DODOBridge` (#2701)
|
|
||||||
|
|
||||||
## v3.4.0 - _July 15, 2020_
|
|
||||||
|
|
||||||
* Fix instability with DFB. (#2616)
|
|
||||||
* Add `BalancerBridge` (#2613)
|
|
||||||
|
|
||||||
## 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_
|
|
||||||
|
|
||||||
* Dependencies updated
|
|
||||||
|
|
||||||
## v3.1.2 - _February 4, 2020_
|
|
||||||
|
|
||||||
* Dependencies updated
|
|
||||||
|
|
||||||
## v3.1.1 - _January 22, 2020_
|
|
||||||
|
|
||||||
* Dependencies updated
|
|
||||||
|
|
||||||
## v3.1.0 - _January 6, 2020_
|
|
||||||
|
|
||||||
* Integration tests for DydxBridge with ERC20BridgeProxy. (#2401)
|
|
||||||
* Fix `UniswapBridge` token -> token transfer call. (#2412)
|
|
||||||
* Fix `KyberBridge` incorrect `minConversionRate` calculation. (#2412)
|
|
||||||
|
|
||||||
## v3.0.2 - _December 17, 2019_
|
|
||||||
|
|
||||||
* Dependencies updated
|
|
||||||
|
|
||||||
## v3.0.1 - _December 9, 2019_
|
|
||||||
|
|
||||||
* Dependencies updated
|
|
||||||
|
|
||||||
## v3.0.0 - _December 2, 2019_
|
|
||||||
|
|
||||||
* Implement `KyberBridge`. (#2352)
|
|
||||||
* Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330)
|
|
||||||
* ERC20Wrapper and ERC1155ProxyWrapper constructors now require an instance of DevUtilsContract (#2034)
|
|
||||||
* Disallow the zero address from being made an authorized address in MixinAuthorizable, and created an archive directory that includes an old version of Ownable (#2019)
|
|
||||||
* Remove `LibAssetProxyIds` contract (#2055)
|
|
||||||
* Compile and export all contracts, artifacts, and wrappers by default (#2055)
|
|
||||||
* Remove unused dependency on IAuthorizable in IAssetProxy (#1910)
|
|
||||||
* Add `ERC20BridgeProxy` (#2220)
|
|
||||||
* Add `Eth2DaiBridge` (#2221)
|
|
||||||
* Add `UniswapBridge` (#2233)
|
|
||||||
* Replaced `SafeMath` with `LibSafeMath` (#2254)
|
|
||||||
|
|
||||||
## v2.3.0-beta.4 - _December 2, 2019_
|
|
||||||
|
|
||||||
* Implement `KyberBridge`. (#2352)
|
|
||||||
* Implement `DydxBridge`. (#2365)
|
|
||||||
|
|
||||||
## v2.3.0-beta.3 - _November 20, 2019_
|
|
||||||
|
|
||||||
* Dependencies updated
|
|
||||||
|
|
||||||
## v2.3.0-beta.2 - _November 17, 2019_
|
|
||||||
|
|
||||||
* Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330)
|
|
||||||
|
|
||||||
## v2.3.0-beta.1 - _November 7, 2019_
|
|
||||||
|
|
||||||
* ERC20Wrapper and ERC1155ProxyWrapper constructors now require an instance of DevUtilsContract (#2034)
|
|
||||||
|
|
||||||
## v2.3.0-beta.0 - _October 3, 2019_
|
|
||||||
|
|
||||||
* Disallow the zero address from being made an authorized address in MixinAuthorizable, and created an archive directory that includes an old version of Ownable (#2019)
|
|
||||||
* Remove `LibAssetProxyIds` contract (#2055)
|
|
||||||
* Compile and export all contracts, artifacts, and wrappers by default (#2055)
|
|
||||||
* Remove unused dependency on IAuthorizable in IAssetProxy (#1910)
|
|
||||||
* Add `ERC20BridgeProxy` (#2220)
|
|
||||||
* Add `Eth2DaiBridge` (#2221)
|
|
||||||
* Add `UniswapBridge` (#2233)
|
|
||||||
* Replaced `SafeMath` with `LibSafeMath` (#2254)
|
|
||||||
|
|
||||||
## v2.2.8 - _September 17, 2019_
|
|
||||||
|
|
||||||
* Dependencies updated
|
|
||||||
|
|
||||||
## v2.2.7 - _September 3, 2019_
|
|
||||||
|
|
||||||
* Dependencies updated
|
|
||||||
|
|
||||||
## v2.2.6 - _August 22, 2019_
|
|
||||||
|
|
||||||
* Dependencies updated
|
|
||||||
|
|
||||||
## v2.2.5 - _August 8, 2019_
|
|
||||||
|
|
||||||
* Dependencies updated
|
|
||||||
|
|
||||||
## v2.2.4 - _July 31, 2019_
|
|
||||||
|
|
||||||
* Updated calls to <contract wrapper>.deployFrom0xArtifactAsync to include artifact dependencies. (#1995)
|
|
||||||
|
|
||||||
## v2.2.3 - _July 24, 2019_
|
|
||||||
|
|
||||||
* Dependencies updated
|
|
||||||
|
|
||||||
## v2.2.2 - _July 15, 2019_
|
|
||||||
|
|
||||||
* Dependencies updated
|
|
||||||
|
|
||||||
## v2.2.1 - _July 13, 2019_
|
|
||||||
|
|
||||||
* Dependencies updated
|
|
||||||
|
|
||||||
## v2.2.0 - _July 13, 2019_
|
|
||||||
|
|
||||||
* Add `LibAssetProxyIds` contract (#1835)
|
|
||||||
* Updated ERC1155 Asset Proxy. Less optimization. More explicit handling of edge cases. (#1852)
|
|
||||||
* Implement StaticCallProxy (#1863)
|
|
||||||
|
|
||||||
## v2.1.5 - _May 24, 2019_
|
|
||||||
|
|
||||||
* Dependencies updated
|
|
||||||
|
|
||||||
## v2.1.4 - _May 15, 2019_
|
|
||||||
|
|
||||||
* Dependencies updated
|
|
||||||
|
|
||||||
## v2.1.3 - _May 14, 2019_
|
|
||||||
|
|
||||||
* Dependencies updated
|
|
||||||
|
|
||||||
## v2.1.2 - _May 10, 2019_
|
|
||||||
|
|
||||||
* Update tests to use contract-built-in `awaitTransactionSuccessAsync` (#1797)
|
|
||||||
* Make `ERC721Wrapper.setApprovalForAll()` take an owner address instead of a token ID (#1819)
|
|
||||||
* Automatically set unlimited proxy allowances in `ERC721.setBalancesAndAllowancesAsync()` (#1819)
|
|
||||||
* Add `setProxyAllowanceForAllAsync()` to `ERC1155ProxyWrapper`. (#1819)
|
|
||||||
|
|
||||||
## v2.1.1 - _April 11, 2019_
|
|
||||||
|
|
||||||
* Dependencies updated
|
|
||||||
|
|
||||||
## v2.1.0 - _March 21, 2019_
|
|
||||||
|
|
||||||
* Run Web3ProviderEngine without excess block polling (#1695)
|
|
||||||
|
|
||||||
## v2.0.0 - _March 20, 2019_
|
|
||||||
|
|
||||||
* Do not reexport external dependencies (#1682)
|
|
||||||
* Add ERC1155Proxy (#1661)
|
|
||||||
* Bumped solidity version to ^0.5.5 (#1701)
|
|
||||||
* Integration testing for ERC1155Proxy (#1673)
|
|
||||||
|
|
||||||
## v1.0.9 - _March 1, 2019_
|
|
||||||
|
|
||||||
* Dependencies updated
|
|
||||||
|
|
||||||
## v1.0.8 - _February 27, 2019_
|
|
||||||
|
|
||||||
* Dependencies updated
|
|
||||||
|
|
||||||
## v1.0.7 - _February 26, 2019_
|
|
||||||
|
|
||||||
* Dependencies updated
|
|
||||||
|
|
||||||
## v1.0.6 - _February 25, 2019_
|
|
||||||
|
|
||||||
* Dependencies updated
|
|
||||||
|
|
||||||
## v1.0.5 - _February 9, 2019_
|
|
||||||
|
|
||||||
* Dependencies updated
|
|
||||||
|
|
||||||
## v1.0.4 - _February 7, 2019_
|
|
||||||
|
|
||||||
* Dependencies updated
|
|
||||||
|
|
||||||
## v1.0.3 - _February 7, 2019_
|
|
||||||
|
|
||||||
* Fake publish to enable pinning
|
|
||||||
|
|
||||||
## v1.0.2 - _February 6, 2019_
|
|
||||||
|
|
||||||
* Dependencies updated
|
|
||||||
|
|
||||||
## v1.0.1 - _February 5, 2019_
|
|
||||||
|
|
||||||
* Dependencies updated
|
|
||||||
|
|
||||||
## v1.0.0 - _Invalid date_
|
|
||||||
|
|
||||||
* Move all AssetProxy contracts out of contracts-protocol to new package (#1539)
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"name": "MultiAssetProxy",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "Add MultiAssetProxy implementation",
|
|
||||||
"pr": 1224,
|
|
||||||
"networks": {
|
|
||||||
"3": "0xab8fbd189c569ccdee3a4d929bb7f557be4028f6",
|
|
||||||
"4": "0xb34cde0ad3a83d04abebc0b66e75196f22216621",
|
|
||||||
"42": "0xf6313a772c222f51c28f2304c0703b8cf5428fd8"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "ERC20Proxy",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "protocol v2 deploy",
|
|
||||||
"networks": {
|
|
||||||
"1": "0x2240dab907db71e64d3e0dba4800c83b5c502d4e",
|
|
||||||
"3": "0xb1408f4c245a23c31b98d2c626777d4c0d766caa",
|
|
||||||
"4": "0x3e809c563c15a295e832e37053798ddc8d6c8dab",
|
|
||||||
"42": "0xf1ec01d6236d3cd881a0bf0130ea25fe4234003e"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "ERC721Proxy",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"changes": [
|
|
||||||
{
|
|
||||||
"note": "protocol v2 deploy",
|
|
||||||
"networks": {
|
|
||||||
"1": "0x208e41fb445f1bb1b6780d58356e81405f3e6127",
|
|
||||||
"3": "0xe654aac058bfbf9f83fcaee7793311dd82f6ddb4",
|
|
||||||
"4": "0x8e1ff02637cb5e39f2fa36c14706aa348b065b09",
|
|
||||||
"42": "0x2a9127c745688a165106c11cd4d647d2220af821"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
## AssetProxy
|
|
||||||
|
|
||||||
This package contains the implementations of all of the [`AssetProxy`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#assetproxy) contracts available within the 0x protocol. These contracts are responsible for decoding the `assetData` sent to them and performing the actual transfer of assets. Addresses of the deployed contracts can be found in this 0x [guide](https://0x.org/docs/guides/0x-cheat-sheet) or the [DEPLOYS](./DEPLOYS.json) file within this package.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
**Install**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm install @0x/contracts-asset-proxy --save
|
|
||||||
```
|
|
||||||
|
|
||||||
## Bug bounty
|
|
||||||
|
|
||||||
A bug bounty for the 2.0.0 contracts is ongoing! Instructions can be found [here](https://0x.org/docs/guides/bug-bounty-program).
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
We strongly recommend that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository.
|
|
||||||
|
|
||||||
For proposals regarding the 0x protocol's smart contract architecture, message format, or additional functionality, go to the [0x Improvement Proposals (ZEIPs)](https://github.com/0xProject/ZEIPs) repository and follow the contribution guidelines provided therein.
|
|
||||||
|
|
||||||
Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started.
|
|
||||||
|
|
||||||
### Install Dependencies
|
|
||||||
|
|
||||||
If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
yarn config set workspaces-experimental true
|
|
||||||
```
|
|
||||||
|
|
||||||
Then install dependencies
|
|
||||||
|
|
||||||
```bash
|
|
||||||
yarn install
|
|
||||||
```
|
|
||||||
|
|
||||||
### Build
|
|
||||||
|
|
||||||
To build this package and all other monorepo packages that it depends on, run the following from the monorepo root directory:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
PKG=@0x/contracts-asset-proxy yarn build
|
|
||||||
```
|
|
||||||
|
|
||||||
Or continuously rebuild on change:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
PKG=@0x/contracts-asset-proxy yarn watch
|
|
||||||
```
|
|
||||||
|
|
||||||
### Clean
|
|
||||||
|
|
||||||
```bash
|
|
||||||
yarn clean
|
|
||||||
```
|
|
||||||
|
|
||||||
### Lint
|
|
||||||
|
|
||||||
```bash
|
|
||||||
yarn lint
|
|
||||||
```
|
|
||||||
|
|
||||||
### Run Tests
|
|
||||||
|
|
||||||
```bash
|
|
||||||
yarn test
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Testing options
|
|
||||||
|
|
||||||
Contracts testing options like coverage, profiling, revert traces or backing node choosing - are described [here](../TESTING.md).
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
{
|
|
||||||
"artifactsDir": "./test/generated-artifacts",
|
|
||||||
"contractsDir": "./contracts",
|
|
||||||
"useDockerisedSolc": false,
|
|
||||||
"isOfflineMode": false,
|
|
||||||
"shouldSaveStandardInput": true,
|
|
||||||
"compilerSettings": {
|
|
||||||
"evmVersion": "istanbul",
|
|
||||||
"optimizer": {
|
|
||||||
"enabled": true,
|
|
||||||
"runs": 1000000,
|
|
||||||
"details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
|
|
||||||
},
|
|
||||||
"outputSelection": {
|
|
||||||
"*": {
|
|
||||||
"*": [
|
|
||||||
"abi",
|
|
||||||
"devdoc",
|
|
||||||
"evm.bytecode.object",
|
|
||||||
"evm.bytecode.sourceMap",
|
|
||||||
"evm.deployedBytecode.object",
|
|
||||||
"evm.deployedBytecode.sourceMap"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,172 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
import "../archive/Ownable.sol";
|
|
||||||
import "../src/interfaces/IAssetProxy.sol";
|
|
||||||
import "../src/interfaces/IAssetProxyDispatcher.sol";
|
|
||||||
|
|
||||||
|
|
||||||
contract MixinAssetProxyDispatcher is
|
|
||||||
Ownable,
|
|
||||||
IAssetProxyDispatcher
|
|
||||||
{
|
|
||||||
// Mapping from Asset Proxy Id's to their respective Asset Proxy
|
|
||||||
mapping (bytes4 => address) public assetProxies;
|
|
||||||
|
|
||||||
/// @dev Registers an asset proxy to its asset proxy id.
|
|
||||||
/// Once an asset proxy is registered, it cannot be unregistered.
|
|
||||||
/// @param assetProxy Address of new asset proxy to register.
|
|
||||||
function registerAssetProxy(address assetProxy)
|
|
||||||
external
|
|
||||||
onlyOwner
|
|
||||||
{
|
|
||||||
// Ensure that no asset proxy exists with current id.
|
|
||||||
bytes4 assetProxyId = IAssetProxy(assetProxy).getProxyId();
|
|
||||||
address currentAssetProxy = assetProxies[assetProxyId];
|
|
||||||
require(
|
|
||||||
currentAssetProxy == address(0),
|
|
||||||
"ASSET_PROXY_ALREADY_EXISTS"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Add asset proxy and log registration.
|
|
||||||
assetProxies[assetProxyId] = assetProxy;
|
|
||||||
emit AssetProxyRegistered(
|
|
||||||
assetProxyId,
|
|
||||||
assetProxy
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Gets an asset proxy.
|
|
||||||
/// @param assetProxyId Id of the asset proxy.
|
|
||||||
/// @return The asset proxy registered to assetProxyId. Returns 0x0 if no proxy is registered.
|
|
||||||
function getAssetProxy(bytes4 assetProxyId)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (address)
|
|
||||||
{
|
|
||||||
return assetProxies[assetProxyId];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Forwards arguments to assetProxy and calls `transferFrom`. Either succeeds or throws.
|
|
||||||
/// @param assetData Byte array encoded for the asset.
|
|
||||||
/// @param from Address to transfer token from.
|
|
||||||
/// @param to Address to transfer token to.
|
|
||||||
/// @param amount Amount of token to transfer.
|
|
||||||
function _dispatchTransferFrom(
|
|
||||||
bytes memory assetData,
|
|
||||||
address from,
|
|
||||||
address to,
|
|
||||||
uint256 amount
|
|
||||||
)
|
|
||||||
internal
|
|
||||||
{
|
|
||||||
// Do nothing if no amount should be transferred.
|
|
||||||
if (amount > 0 && from != to) {
|
|
||||||
// Ensure assetData length is valid
|
|
||||||
require(
|
|
||||||
assetData.length > 3,
|
|
||||||
"LENGTH_GREATER_THAN_3_REQUIRED"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Lookup assetProxy. We do not use `LibBytes.readBytes4` for gas efficiency reasons.
|
|
||||||
bytes4 assetProxyId;
|
|
||||||
assembly {
|
|
||||||
assetProxyId := and(mload(
|
|
||||||
add(assetData, 32)),
|
|
||||||
0xFFFFFFFF00000000000000000000000000000000000000000000000000000000
|
|
||||||
)
|
|
||||||
}
|
|
||||||
address assetProxy = assetProxies[assetProxyId];
|
|
||||||
|
|
||||||
// Ensure that assetProxy exists
|
|
||||||
require(
|
|
||||||
assetProxy != address(0),
|
|
||||||
"ASSET_PROXY_DOES_NOT_EXIST"
|
|
||||||
);
|
|
||||||
|
|
||||||
// We construct calldata for the `assetProxy.transferFrom` ABI.
|
|
||||||
// The layout of this calldata is in the table below.
|
|
||||||
//
|
|
||||||
// | Area | Offset | Length | Contents |
|
|
||||||
// | -------- |--------|---------|-------------------------------------------- |
|
|
||||||
// | Header | 0 | 4 | function selector |
|
|
||||||
// | Params | | 4 * 32 | function parameters: |
|
|
||||||
// | | 4 | | 1. offset to assetData (*) |
|
|
||||||
// | | 36 | | 2. from |
|
|
||||||
// | | 68 | | 3. to |
|
|
||||||
// | | 100 | | 4. amount |
|
|
||||||
// | Data | | | assetData: |
|
|
||||||
// | | 132 | 32 | assetData Length |
|
|
||||||
// | | 164 | ** | assetData Contents |
|
|
||||||
|
|
||||||
assembly {
|
|
||||||
/////// Setup State ///////
|
|
||||||
// `cdStart` is the start of the calldata for `assetProxy.transferFrom` (equal to free memory ptr).
|
|
||||||
let cdStart := mload(64)
|
|
||||||
// `dataAreaLength` is the total number of words needed to store `assetData`
|
|
||||||
// As-per the ABI spec, this value is padded up to the nearest multiple of 32,
|
|
||||||
// and includes 32-bytes for length.
|
|
||||||
let dataAreaLength := and(add(mload(assetData), 63), 0xFFFFFFFFFFFE0)
|
|
||||||
// `cdEnd` is the end of the calldata for `assetProxy.transferFrom`.
|
|
||||||
let cdEnd := add(cdStart, add(132, dataAreaLength))
|
|
||||||
|
|
||||||
|
|
||||||
/////// Setup Header Area ///////
|
|
||||||
// This area holds the 4-byte `transferFromSelector`.
|
|
||||||
// bytes4(keccak256("transferFrom(bytes,address,address,uint256)")) = 0xa85e59e4
|
|
||||||
mstore(cdStart, 0xa85e59e400000000000000000000000000000000000000000000000000000000)
|
|
||||||
|
|
||||||
/////// Setup Params Area ///////
|
|
||||||
// Each parameter is padded to 32-bytes. The entire Params Area is 128 bytes.
|
|
||||||
// Notes:
|
|
||||||
// 1. The offset to `assetData` is the length of the Params Area (128 bytes).
|
|
||||||
// 2. A 20-byte mask is applied to addresses to zero-out the unused bytes.
|
|
||||||
mstore(add(cdStart, 4), 128)
|
|
||||||
mstore(add(cdStart, 36), and(from, 0xffffffffffffffffffffffffffffffffffffffff))
|
|
||||||
mstore(add(cdStart, 68), and(to, 0xffffffffffffffffffffffffffffffffffffffff))
|
|
||||||
mstore(add(cdStart, 100), amount)
|
|
||||||
|
|
||||||
/////// Setup Data Area ///////
|
|
||||||
// This area holds `assetData`.
|
|
||||||
let dataArea := add(cdStart, 132)
|
|
||||||
// solhint-disable-next-line no-empty-blocks
|
|
||||||
for {} lt(dataArea, cdEnd) {} {
|
|
||||||
mstore(dataArea, mload(assetData))
|
|
||||||
dataArea := add(dataArea, 32)
|
|
||||||
assetData := add(assetData, 32)
|
|
||||||
}
|
|
||||||
|
|
||||||
/////// Call `assetProxy.transferFrom` using the constructed calldata ///////
|
|
||||||
let success := call(
|
|
||||||
gas, // forward all gas
|
|
||||||
assetProxy, // call address of asset proxy
|
|
||||||
0, // don't send any ETH
|
|
||||||
cdStart, // pointer to start of input
|
|
||||||
sub(cdEnd, cdStart), // length of input
|
|
||||||
cdStart, // write output over input
|
|
||||||
512 // reserve 512 bytes for output
|
|
||||||
)
|
|
||||||
if iszero(success) {
|
|
||||||
revert(cdStart, returndatasize())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
import "../archive/Ownable.sol";
|
|
||||||
import "../src/interfaces/IAuthorizable.sol";
|
|
||||||
|
|
||||||
|
|
||||||
contract MixinAuthorizable is
|
|
||||||
Ownable,
|
|
||||||
IAuthorizable
|
|
||||||
{
|
|
||||||
/// @dev Only authorized addresses can invoke functions with this modifier.
|
|
||||||
modifier onlyAuthorized {
|
|
||||||
require(
|
|
||||||
authorized[msg.sender],
|
|
||||||
"SENDER_NOT_AUTHORIZED"
|
|
||||||
);
|
|
||||||
_;
|
|
||||||
}
|
|
||||||
|
|
||||||
mapping (address => bool) public authorized;
|
|
||||||
address[] public authorities;
|
|
||||||
|
|
||||||
/// @dev Authorizes an address.
|
|
||||||
/// @param target Address to authorize.
|
|
||||||
function addAuthorizedAddress(address target)
|
|
||||||
external
|
|
||||||
onlyOwner
|
|
||||||
{
|
|
||||||
require(
|
|
||||||
!authorized[target],
|
|
||||||
"TARGET_ALREADY_AUTHORIZED"
|
|
||||||
);
|
|
||||||
|
|
||||||
authorized[target] = true;
|
|
||||||
authorities.push(target);
|
|
||||||
emit AuthorizedAddressAdded(target, msg.sender);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Removes authorizion of an address.
|
|
||||||
/// @param target Address to remove authorization from.
|
|
||||||
function removeAuthorizedAddress(address target)
|
|
||||||
external
|
|
||||||
onlyOwner
|
|
||||||
{
|
|
||||||
require(
|
|
||||||
authorized[target],
|
|
||||||
"TARGET_NOT_AUTHORIZED"
|
|
||||||
);
|
|
||||||
|
|
||||||
delete authorized[target];
|
|
||||||
for (uint256 i = 0; i < authorities.length; i++) {
|
|
||||||
if (authorities[i] == target) {
|
|
||||||
authorities[i] = authorities[authorities.length - 1];
|
|
||||||
authorities.length -= 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
emit AuthorizedAddressRemoved(target, msg.sender);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Removes authorizion of an address.
|
|
||||||
/// @param target Address to remove authorization from.
|
|
||||||
/// @param index Index of target in authorities array.
|
|
||||||
function removeAuthorizedAddressAtIndex(
|
|
||||||
address target,
|
|
||||||
uint256 index
|
|
||||||
)
|
|
||||||
external
|
|
||||||
onlyOwner
|
|
||||||
{
|
|
||||||
require(
|
|
||||||
authorized[target],
|
|
||||||
"TARGET_NOT_AUTHORIZED"
|
|
||||||
);
|
|
||||||
require(
|
|
||||||
index < authorities.length,
|
|
||||||
"INDEX_OUT_OF_BOUNDS"
|
|
||||||
);
|
|
||||||
require(
|
|
||||||
authorities[index] == target,
|
|
||||||
"AUTHORIZED_ADDRESS_MISMATCH"
|
|
||||||
);
|
|
||||||
|
|
||||||
delete authorized[target];
|
|
||||||
authorities[index] = authorities[authorities.length - 1];
|
|
||||||
authorities.length -= 1;
|
|
||||||
emit AuthorizedAddressRemoved(target, msg.sender);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Gets all authorized addresses.
|
|
||||||
/// @return Array of authorized addresses.
|
|
||||||
function getAuthorizedAddresses()
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (address[] memory)
|
|
||||||
{
|
|
||||||
return authorities;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
pragma solidity ^0.5.9;
|
|
||||||
|
|
||||||
import "@0x/contracts-utils/contracts/src/interfaces/IOwnable.sol";
|
|
||||||
|
|
||||||
|
|
||||||
contract Ownable is
|
|
||||||
IOwnable
|
|
||||||
{
|
|
||||||
address public owner;
|
|
||||||
|
|
||||||
constructor ()
|
|
||||||
public
|
|
||||||
{
|
|
||||||
owner = msg.sender;
|
|
||||||
}
|
|
||||||
|
|
||||||
modifier onlyOwner() {
|
|
||||||
require(
|
|
||||||
msg.sender == owner,
|
|
||||||
"ONLY_CONTRACT_OWNER"
|
|
||||||
);
|
|
||||||
_;
|
|
||||||
}
|
|
||||||
|
|
||||||
function transferOwnership(address newOwner)
|
|
||||||
public
|
|
||||||
onlyOwner
|
|
||||||
{
|
|
||||||
if (newOwner != address(0)) {
|
|
||||||
owner = newOwner;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
|
||||||
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
|
||||||
import "@0x/contracts-erc1155/contracts/src/interfaces/IERC1155.sol";
|
|
||||||
import "../archive/MixinAuthorizable.sol";
|
|
||||||
import "./interfaces/IAssetProxy.sol";
|
|
||||||
|
|
||||||
|
|
||||||
contract ERC1155Proxy is
|
|
||||||
MixinAuthorizable,
|
|
||||||
IAssetProxy
|
|
||||||
{
|
|
||||||
using LibBytes for bytes;
|
|
||||||
using LibSafeMath for uint256;
|
|
||||||
|
|
||||||
// Id of this proxy.
|
|
||||||
bytes4 constant internal PROXY_ID = bytes4(keccak256("ERC1155Assets(address,uint256[],uint256[],bytes)"));
|
|
||||||
|
|
||||||
/// @dev Transfers batch of ERC1155 assets. Either succeeds or throws.
|
|
||||||
/// @param assetData Byte array encoded with ERC1155 token address, array of ids, array of values, and callback data.
|
|
||||||
/// @param from Address to transfer assets from.
|
|
||||||
/// @param to Address to transfer assets to.
|
|
||||||
/// @param amount Amount that will be multiplied with each element of `assetData.values` to scale the
|
|
||||||
/// values that will be transferred.
|
|
||||||
function transferFrom(
|
|
||||||
bytes calldata assetData,
|
|
||||||
address from,
|
|
||||||
address to,
|
|
||||||
uint256 amount
|
|
||||||
)
|
|
||||||
external
|
|
||||||
onlyAuthorized
|
|
||||||
{
|
|
||||||
// Decode params from `assetData`
|
|
||||||
// solhint-disable indent
|
|
||||||
(
|
|
||||||
address erc1155TokenAddress,
|
|
||||||
uint256[] memory ids,
|
|
||||||
uint256[] memory values,
|
|
||||||
bytes memory data
|
|
||||||
) = abi.decode(
|
|
||||||
assetData.sliceDestructive(4, assetData.length),
|
|
||||||
(address, uint256[], uint256[], bytes)
|
|
||||||
);
|
|
||||||
// solhint-enable indent
|
|
||||||
|
|
||||||
// Scale values up by `amount`
|
|
||||||
uint256 length = values.length;
|
|
||||||
uint256[] memory scaledValues = new uint256[](length);
|
|
||||||
for (uint256 i = 0; i != length; i++) {
|
|
||||||
// We write the scaled values to an unused location in memory in order
|
|
||||||
// to avoid copying over `ids` or `data`. This is possible if they are
|
|
||||||
// identical to `values` and the offsets for each are pointing to the
|
|
||||||
// same location in the ABI encoded calldata.
|
|
||||||
scaledValues[i] = values[i].safeMul(amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute `safeBatchTransferFrom` call
|
|
||||||
// Either succeeds or throws
|
|
||||||
IERC1155(erc1155TokenAddress).safeBatchTransferFrom(
|
|
||||||
from,
|
|
||||||
to,
|
|
||||||
ids,
|
|
||||||
scaledValues,
|
|
||||||
data
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Gets the proxy id associated with the proxy address.
|
|
||||||
/// @return Proxy id.
|
|
||||||
function getProxyId()
|
|
||||||
external
|
|
||||||
pure
|
|
||||||
returns (bytes4)
|
|
||||||
{
|
|
||||||
return PROXY_ID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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-utils/contracts/src/LibBytes.sol";
|
|
||||||
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
|
||||||
import "@0x/contracts-utils/contracts/src/Authorizable.sol";
|
|
||||||
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
|
||||||
import "./interfaces/IAssetProxy.sol";
|
|
||||||
import "./interfaces/IERC20Bridge.sol";
|
|
||||||
|
|
||||||
|
|
||||||
contract ERC20BridgeProxy is
|
|
||||||
IAssetProxy,
|
|
||||||
Authorizable
|
|
||||||
{
|
|
||||||
using LibBytes for bytes;
|
|
||||||
using LibSafeMath for uint256;
|
|
||||||
|
|
||||||
// @dev Id of this proxy. Also the result of a successful bridge call.
|
|
||||||
// bytes4(keccak256("ERC20Bridge(address,address,bytes)"))
|
|
||||||
bytes4 constant private PROXY_ID = 0xdc1600f3;
|
|
||||||
|
|
||||||
/// @dev Calls a bridge contract to transfer `amount` of ERC20 from `from`
|
|
||||||
/// to `to`. Asserts that the balance of `to` has increased by `amount`.
|
|
||||||
/// @param assetData Abi-encoded data for this asset proxy encoded as:
|
|
||||||
/// abi.encodeWithSelector(
|
|
||||||
/// bytes4 PROXY_ID,
|
|
||||||
/// address tokenAddress,
|
|
||||||
/// address bridgeAddress,
|
|
||||||
/// bytes bridgeData
|
|
||||||
/// )
|
|
||||||
/// @param from Address to transfer asset from.
|
|
||||||
/// @param to Address to transfer asset to.
|
|
||||||
/// @param amount Amount of asset to transfer.
|
|
||||||
function transferFrom(
|
|
||||||
bytes calldata assetData,
|
|
||||||
address from,
|
|
||||||
address to,
|
|
||||||
uint256 amount
|
|
||||||
)
|
|
||||||
external
|
|
||||||
onlyAuthorized
|
|
||||||
{
|
|
||||||
// Extract asset data fields.
|
|
||||||
(
|
|
||||||
address tokenAddress,
|
|
||||||
address bridgeAddress,
|
|
||||||
bytes memory bridgeData
|
|
||||||
) = abi.decode(
|
|
||||||
assetData.sliceDestructive(4, assetData.length),
|
|
||||||
(address, address, bytes)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Remember the balance of `to` before calling the bridge.
|
|
||||||
uint256 balanceBefore = balanceOf(tokenAddress, to);
|
|
||||||
// Call the bridge, who should transfer `amount` of `tokenAddress` to
|
|
||||||
// `to`.
|
|
||||||
bytes4 success = IERC20Bridge(bridgeAddress).bridgeTransferFrom(
|
|
||||||
tokenAddress,
|
|
||||||
from,
|
|
||||||
to,
|
|
||||||
amount,
|
|
||||||
bridgeData
|
|
||||||
);
|
|
||||||
// Bridge must return the proxy ID to indicate success.
|
|
||||||
require(success == PROXY_ID, "BRIDGE_FAILED");
|
|
||||||
// Ensure that the balance of `to` has increased by at least `amount`.
|
|
||||||
require(
|
|
||||||
balanceBefore.safeAdd(amount) <= balanceOf(tokenAddress, to),
|
|
||||||
"BRIDGE_UNDERPAY"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Gets the proxy id associated with this asset proxy.
|
|
||||||
/// @return proxyId The proxy id.
|
|
||||||
function getProxyId()
|
|
||||||
external
|
|
||||||
pure
|
|
||||||
returns (bytes4 proxyId)
|
|
||||||
{
|
|
||||||
return PROXY_ID;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Retrieves the balance of `owner` for this asset.
|
|
||||||
/// @return balance The balance of the ERC20 token being transferred by this
|
|
||||||
/// asset proxy.
|
|
||||||
function balanceOf(bytes calldata assetData, address owner)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (uint256 balance)
|
|
||||||
{
|
|
||||||
(address tokenAddress) = abi.decode(
|
|
||||||
assetData.sliceDestructive(4, assetData.length),
|
|
||||||
(address)
|
|
||||||
);
|
|
||||||
return balanceOf(tokenAddress, owner);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Retrieves the balance of `owner` given an ERC20 address.
|
|
||||||
/// @return balance The balance of the ERC20 token for `owner`.
|
|
||||||
function balanceOf(address tokenAddress, address owner)
|
|
||||||
private
|
|
||||||
view
|
|
||||||
returns (uint256 balance)
|
|
||||||
{
|
|
||||||
return IERC20Token(tokenAddress).balanceOf(owner);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,184 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
import "../archive/MixinAuthorizable.sol";
|
|
||||||
|
|
||||||
|
|
||||||
contract ERC20Proxy is
|
|
||||||
MixinAuthorizable
|
|
||||||
{
|
|
||||||
// Id of this proxy.
|
|
||||||
bytes4 constant internal PROXY_ID = bytes4(keccak256("ERC20Token(address)"));
|
|
||||||
|
|
||||||
// solhint-disable-next-line payable-fallback
|
|
||||||
function ()
|
|
||||||
external
|
|
||||||
{
|
|
||||||
assembly {
|
|
||||||
// The first 4 bytes of calldata holds the function selector
|
|
||||||
let selector := and(calldataload(0), 0xffffffff00000000000000000000000000000000000000000000000000000000)
|
|
||||||
|
|
||||||
// `transferFrom` will be called with the following parameters:
|
|
||||||
// assetData Encoded byte array.
|
|
||||||
// from Address to transfer asset from.
|
|
||||||
// to Address to transfer asset to.
|
|
||||||
// amount Amount of asset to transfer.
|
|
||||||
// bytes4(keccak256("transferFrom(bytes,address,address,uint256)")) = 0xa85e59e4
|
|
||||||
if eq(selector, 0xa85e59e400000000000000000000000000000000000000000000000000000000) {
|
|
||||||
|
|
||||||
// To lookup a value in a mapping, we load from the storage location keccak256(k, p),
|
|
||||||
// where k is the key left padded to 32 bytes and p is the storage slot
|
|
||||||
let start := mload(64)
|
|
||||||
mstore(start, and(caller, 0xffffffffffffffffffffffffffffffffffffffff))
|
|
||||||
mstore(add(start, 32), authorized_slot)
|
|
||||||
|
|
||||||
// Revert if authorized[msg.sender] == false
|
|
||||||
if iszero(sload(keccak256(start, 64))) {
|
|
||||||
// Revert with `Error("SENDER_NOT_AUTHORIZED")`
|
|
||||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
|
||||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
|
||||||
mstore(64, 0x0000001553454e4445525f4e4f545f415554484f52495a454400000000000000)
|
|
||||||
mstore(96, 0)
|
|
||||||
revert(0, 100)
|
|
||||||
}
|
|
||||||
|
|
||||||
// `transferFrom`.
|
|
||||||
// The function is marked `external`, so no abi decodeding is done for
|
|
||||||
// us. Instead, we expect the `calldata` memory to contain the
|
|
||||||
// following:
|
|
||||||
//
|
|
||||||
// | Area | Offset | Length | Contents |
|
|
||||||
// |----------|--------|---------|-------------------------------------|
|
|
||||||
// | Header | 0 | 4 | function selector |
|
|
||||||
// | Params | | 4 * 32 | function parameters: |
|
|
||||||
// | | 4 | | 1. offset to assetData (*) |
|
|
||||||
// | | 36 | | 2. from |
|
|
||||||
// | | 68 | | 3. to |
|
|
||||||
// | | 100 | | 4. amount |
|
|
||||||
// | Data | | | assetData: |
|
|
||||||
// | | 132 | 32 | assetData Length |
|
|
||||||
// | | 164 | ** | assetData Contents |
|
|
||||||
//
|
|
||||||
// (*): offset is computed from start of function parameters, so offset
|
|
||||||
// by an additional 4 bytes in the calldata.
|
|
||||||
//
|
|
||||||
// (**): see table below to compute length of assetData Contents
|
|
||||||
//
|
|
||||||
// WARNING: The ABIv2 specification allows additional padding between
|
|
||||||
// the Params and Data section. This will result in a larger
|
|
||||||
// offset to assetData.
|
|
||||||
|
|
||||||
// Asset data itself is encoded as follows:
|
|
||||||
//
|
|
||||||
// | Area | Offset | Length | Contents |
|
|
||||||
// |----------|--------|---------|-------------------------------------|
|
|
||||||
// | Header | 0 | 4 | function selector |
|
|
||||||
// | Params | | 1 * 32 | function parameters: |
|
|
||||||
// | | 4 | 12 + 20 | 1. token address |
|
|
||||||
|
|
||||||
// We construct calldata for the `token.transferFrom` ABI.
|
|
||||||
// The layout of this calldata is in the table below.
|
|
||||||
//
|
|
||||||
// | Area | Offset | Length | Contents |
|
|
||||||
// |----------|--------|---------|-------------------------------------|
|
|
||||||
// | Header | 0 | 4 | function selector |
|
|
||||||
// | Params | | 3 * 32 | function parameters: |
|
|
||||||
// | | 4 | | 1. from |
|
|
||||||
// | | 36 | | 2. to |
|
|
||||||
// | | 68 | | 3. amount |
|
|
||||||
|
|
||||||
/////// Read token address from calldata ///////
|
|
||||||
// * The token address is stored in `assetData`.
|
|
||||||
//
|
|
||||||
// * The "offset to assetData" is stored at offset 4 in the calldata (table 1).
|
|
||||||
// [assetDataOffsetFromParams = calldataload(4)]
|
|
||||||
//
|
|
||||||
// * Notes that the "offset to assetData" is relative to the "Params" area of calldata;
|
|
||||||
// add 4 bytes to account for the length of the "Header" area (table 1).
|
|
||||||
// [assetDataOffsetFromHeader = assetDataOffsetFromParams + 4]
|
|
||||||
//
|
|
||||||
// * The "token address" is offset 32+4=36 bytes into "assetData" (tables 1 & 2).
|
|
||||||
// [tokenOffset = assetDataOffsetFromHeader + 36 = calldataload(4) + 4 + 36]
|
|
||||||
let token := calldataload(add(calldataload(4), 40))
|
|
||||||
|
|
||||||
/////// Setup Header Area ///////
|
|
||||||
// This area holds the 4-byte `transferFrom` selector.
|
|
||||||
// Any trailing data in transferFromSelector will be
|
|
||||||
// overwritten in the next `mstore` call.
|
|
||||||
mstore(0, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
|
|
||||||
|
|
||||||
/////// Setup Params Area ///////
|
|
||||||
// We copy the fields `from`, `to` and `amount` in bulk
|
|
||||||
// from our own calldata to the new calldata.
|
|
||||||
calldatacopy(4, 36, 96)
|
|
||||||
|
|
||||||
/////// Call `token.transferFrom` using the calldata ///////
|
|
||||||
let success := call(
|
|
||||||
gas, // forward all gas
|
|
||||||
token, // call address of token contract
|
|
||||||
0, // don't send any ETH
|
|
||||||
0, // pointer to start of input
|
|
||||||
100, // length of input
|
|
||||||
0, // write output over input
|
|
||||||
32 // output size should be 32 bytes
|
|
||||||
)
|
|
||||||
|
|
||||||
/////// Check return data. ///////
|
|
||||||
// If there is no return data, we assume the token incorrectly
|
|
||||||
// does not return a bool. In this case we expect it to revert
|
|
||||||
// on failure, which was handled above.
|
|
||||||
// If the token does return data, we require that it is a single
|
|
||||||
// nonzero 32 bytes value.
|
|
||||||
// So the transfer succeeded if the call succeeded and either
|
|
||||||
// returned nothing, or returned a non-zero 32 byte value.
|
|
||||||
success := and(success, or(
|
|
||||||
iszero(returndatasize),
|
|
||||||
and(
|
|
||||||
eq(returndatasize, 32),
|
|
||||||
gt(mload(0), 0)
|
|
||||||
)
|
|
||||||
))
|
|
||||||
if success {
|
|
||||||
return(0, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Revert with `Error("TRANSFER_FAILED")`
|
|
||||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
|
||||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
|
||||||
mstore(64, 0x0000000f5452414e534645525f4641494c454400000000000000000000000000)
|
|
||||||
mstore(96, 0)
|
|
||||||
revert(0, 100)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Revert if undefined function is called
|
|
||||||
revert(0, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Gets the proxy id associated with the proxy address.
|
|
||||||
/// @return Proxy id.
|
|
||||||
function getProxyId()
|
|
||||||
external
|
|
||||||
pure
|
|
||||||
returns (bytes4)
|
|
||||||
{
|
|
||||||
return PROXY_ID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,171 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
import "../archive/MixinAuthorizable.sol";
|
|
||||||
|
|
||||||
|
|
||||||
contract ERC721Proxy is
|
|
||||||
MixinAuthorizable
|
|
||||||
{
|
|
||||||
// Id of this proxy.
|
|
||||||
bytes4 constant internal PROXY_ID = bytes4(keccak256("ERC721Token(address,uint256)"));
|
|
||||||
|
|
||||||
// solhint-disable-next-line payable-fallback
|
|
||||||
function ()
|
|
||||||
external
|
|
||||||
{
|
|
||||||
assembly {
|
|
||||||
// The first 4 bytes of calldata holds the function selector
|
|
||||||
let selector := and(calldataload(0), 0xffffffff00000000000000000000000000000000000000000000000000000000)
|
|
||||||
|
|
||||||
// `transferFrom` will be called with the following parameters:
|
|
||||||
// assetData Encoded byte array.
|
|
||||||
// from Address to transfer asset from.
|
|
||||||
// to Address to transfer asset to.
|
|
||||||
// amount Amount of asset to transfer.
|
|
||||||
// bytes4(keccak256("transferFrom(bytes,address,address,uint256)")) = 0xa85e59e4
|
|
||||||
if eq(selector, 0xa85e59e400000000000000000000000000000000000000000000000000000000) {
|
|
||||||
|
|
||||||
// To lookup a value in a mapping, we load from the storage location keccak256(k, p),
|
|
||||||
// where k is the key left padded to 32 bytes and p is the storage slot
|
|
||||||
let start := mload(64)
|
|
||||||
mstore(start, and(caller, 0xffffffffffffffffffffffffffffffffffffffff))
|
|
||||||
mstore(add(start, 32), authorized_slot)
|
|
||||||
|
|
||||||
// Revert if authorized[msg.sender] == false
|
|
||||||
if iszero(sload(keccak256(start, 64))) {
|
|
||||||
// Revert with `Error("SENDER_NOT_AUTHORIZED")`
|
|
||||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
|
||||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
|
||||||
mstore(64, 0x0000001553454e4445525f4e4f545f415554484f52495a454400000000000000)
|
|
||||||
mstore(96, 0)
|
|
||||||
revert(0, 100)
|
|
||||||
}
|
|
||||||
|
|
||||||
// `transferFrom`.
|
|
||||||
// The function is marked `external`, so no abi decodeding is done for
|
|
||||||
// us. Instead, we expect the `calldata` memory to contain the
|
|
||||||
// following:
|
|
||||||
//
|
|
||||||
// | Area | Offset | Length | Contents |
|
|
||||||
// |----------|--------|---------|-------------------------------------|
|
|
||||||
// | Header | 0 | 4 | function selector |
|
|
||||||
// | Params | | 4 * 32 | function parameters: |
|
|
||||||
// | | 4 | | 1. offset to assetData (*) |
|
|
||||||
// | | 36 | | 2. from |
|
|
||||||
// | | 68 | | 3. to |
|
|
||||||
// | | 100 | | 4. amount |
|
|
||||||
// | Data | | | assetData: |
|
|
||||||
// | | 132 | 32 | assetData Length |
|
|
||||||
// | | 164 | ** | assetData Contents |
|
|
||||||
//
|
|
||||||
// (*): offset is computed from start of function parameters, so offset
|
|
||||||
// by an additional 4 bytes in the calldata.
|
|
||||||
//
|
|
||||||
// (**): see table below to compute length of assetData Contents
|
|
||||||
//
|
|
||||||
// WARNING: The ABIv2 specification allows additional padding between
|
|
||||||
// the Params and Data section. This will result in a larger
|
|
||||||
// offset to assetData.
|
|
||||||
|
|
||||||
// Asset data itself is encoded as follows:
|
|
||||||
//
|
|
||||||
// | Area | Offset | Length | Contents |
|
|
||||||
// |----------|--------|---------|-------------------------------------|
|
|
||||||
// | Header | 0 | 4 | function selector |
|
|
||||||
// | Params | | 2 * 32 | function parameters: |
|
|
||||||
// | | 4 | 12 + 20 | 1. token address |
|
|
||||||
// | | 36 | | 2. tokenId |
|
|
||||||
|
|
||||||
// We construct calldata for the `token.transferFrom` ABI.
|
|
||||||
// The layout of this calldata is in the table below.
|
|
||||||
//
|
|
||||||
// | Area | Offset | Length | Contents |
|
|
||||||
// |----------|--------|---------|-------------------------------------|
|
|
||||||
// | Header | 0 | 4 | function selector |
|
|
||||||
// | Params | | 3 * 32 | function parameters: |
|
|
||||||
// | | 4 | | 1. from |
|
|
||||||
// | | 36 | | 2. to |
|
|
||||||
// | | 68 | | 3. tokenId |
|
|
||||||
|
|
||||||
// There exists only 1 of each token.
|
|
||||||
// require(amount == 1, "INVALID_AMOUNT")
|
|
||||||
if sub(calldataload(100), 1) {
|
|
||||||
// Revert with `Error("INVALID_AMOUNT")`
|
|
||||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
|
||||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
|
||||||
mstore(64, 0x0000000e494e56414c49445f414d4f554e540000000000000000000000000000)
|
|
||||||
mstore(96, 0)
|
|
||||||
revert(0, 100)
|
|
||||||
}
|
|
||||||
|
|
||||||
/////// Setup Header Area ///////
|
|
||||||
// This area holds the 4-byte `transferFrom` selector.
|
|
||||||
// Any trailing data in transferFromSelector will be
|
|
||||||
// overwritten in the next `mstore` call.
|
|
||||||
mstore(0, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
|
|
||||||
|
|
||||||
/////// Setup Params Area ///////
|
|
||||||
// We copy the fields `from` and `to` in bulk
|
|
||||||
// from our own calldata to the new calldata.
|
|
||||||
calldatacopy(4, 36, 64)
|
|
||||||
|
|
||||||
// Copy `tokenId` field from our own calldata to the new calldata.
|
|
||||||
let assetDataOffset := calldataload(4)
|
|
||||||
calldatacopy(68, add(assetDataOffset, 72), 32)
|
|
||||||
|
|
||||||
/////// Call `token.transferFrom` using the calldata ///////
|
|
||||||
let token := calldataload(add(assetDataOffset, 40))
|
|
||||||
let success := call(
|
|
||||||
gas, // forward all gas
|
|
||||||
token, // call address of token contract
|
|
||||||
0, // don't send any ETH
|
|
||||||
0, // pointer to start of input
|
|
||||||
100, // length of input
|
|
||||||
0, // write output to null
|
|
||||||
0 // output size is 0 bytes
|
|
||||||
)
|
|
||||||
if success {
|
|
||||||
return(0, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Revert with `Error("TRANSFER_FAILED")`
|
|
||||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
|
||||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
|
||||||
mstore(64, 0x0000000f5452414e534645525f4641494c454400000000000000000000000000)
|
|
||||||
mstore(96, 0)
|
|
||||||
revert(0, 100)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Revert if undefined function is called
|
|
||||||
revert(0, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Gets the proxy id associated with the proxy address.
|
|
||||||
/// @return Proxy id.
|
|
||||||
function getProxyId()
|
|
||||||
external
|
|
||||||
pure
|
|
||||||
returns (bytes4)
|
|
||||||
{
|
|
||||||
return PROXY_ID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,335 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
import "../archive/MixinAssetProxyDispatcher.sol";
|
|
||||||
import "../archive/MixinAuthorizable.sol";
|
|
||||||
|
|
||||||
|
|
||||||
contract MultiAssetProxy is
|
|
||||||
MixinAssetProxyDispatcher,
|
|
||||||
MixinAuthorizable
|
|
||||||
{
|
|
||||||
// Id of this proxy.
|
|
||||||
bytes4 constant internal PROXY_ID = bytes4(keccak256("MultiAsset(uint256[],bytes[])"));
|
|
||||||
|
|
||||||
// solhint-disable-next-line payable-fallback
|
|
||||||
function ()
|
|
||||||
external
|
|
||||||
{
|
|
||||||
// NOTE: The below assembly assumes that clients do some input validation and that the input is properly encoded according to the AbiV2 specification.
|
|
||||||
// It is technically possible for inputs with very large lengths and offsets to cause overflows. However, this would make the calldata prohibitively
|
|
||||||
// expensive and we therefore do not check for overflows in these scenarios.
|
|
||||||
assembly {
|
|
||||||
// The first 4 bytes of calldata holds the function selector
|
|
||||||
let selector := and(calldataload(0), 0xffffffff00000000000000000000000000000000000000000000000000000000)
|
|
||||||
|
|
||||||
// `transferFrom` will be called with the following parameters:
|
|
||||||
// assetData Encoded byte array.
|
|
||||||
// from Address to transfer asset from.
|
|
||||||
// to Address to transfer asset to.
|
|
||||||
// amount Amount of asset to transfer.
|
|
||||||
// bytes4(keccak256("transferFrom(bytes,address,address,uint256)")) = 0xa85e59e4
|
|
||||||
if eq(selector, 0xa85e59e400000000000000000000000000000000000000000000000000000000) {
|
|
||||||
|
|
||||||
// To lookup a value in a mapping, we load from the storage location keccak256(k, p),
|
|
||||||
// where k is the key left padded to 32 bytes and p is the storage slot
|
|
||||||
mstore(0, caller)
|
|
||||||
mstore(32, authorized_slot)
|
|
||||||
|
|
||||||
// Revert if authorized[msg.sender] == false
|
|
||||||
if iszero(sload(keccak256(0, 64))) {
|
|
||||||
// Revert with `Error("SENDER_NOT_AUTHORIZED")`
|
|
||||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
|
||||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
|
||||||
mstore(64, 0x0000001553454e4445525f4e4f545f415554484f52495a454400000000000000)
|
|
||||||
mstore(96, 0)
|
|
||||||
revert(0, 100)
|
|
||||||
}
|
|
||||||
|
|
||||||
// `transferFrom`.
|
|
||||||
// The function is marked `external`, so no abi decoding is done for
|
|
||||||
// us. Instead, we expect the `calldata` memory to contain the
|
|
||||||
// following:
|
|
||||||
//
|
|
||||||
// | Area | Offset | Length | Contents |
|
|
||||||
// |----------|--------|---------|-------------------------------------|
|
|
||||||
// | Header | 0 | 4 | function selector |
|
|
||||||
// | Params | | 4 * 32 | function parameters: |
|
|
||||||
// | | 4 | | 1. offset to assetData (*) |
|
|
||||||
// | | 36 | | 2. from |
|
|
||||||
// | | 68 | | 3. to |
|
|
||||||
// | | 100 | | 4. amount |
|
|
||||||
// | Data | | | assetData: |
|
|
||||||
// | | 132 | 32 | assetData Length |
|
|
||||||
// | | 164 | ** | assetData Contents |
|
|
||||||
//
|
|
||||||
// (*): offset is computed from start of function parameters, so offset
|
|
||||||
// by an additional 4 bytes in the calldata.
|
|
||||||
//
|
|
||||||
// (**): see table below to compute length of assetData Contents
|
|
||||||
//
|
|
||||||
// WARNING: The ABIv2 specification allows additional padding between
|
|
||||||
// the Params and Data section. This will result in a larger
|
|
||||||
// offset to assetData.
|
|
||||||
|
|
||||||
// Load offset to `assetData`
|
|
||||||
let assetDataOffset := add(calldataload(4), 4)
|
|
||||||
|
|
||||||
// Load length in bytes of `assetData`
|
|
||||||
let assetDataLength := calldataload(assetDataOffset)
|
|
||||||
|
|
||||||
// Asset data itself is encoded as follows:
|
|
||||||
//
|
|
||||||
// | Area | Offset | Length | Contents |
|
|
||||||
// |----------|-------------|---------|-------------------------------------|
|
|
||||||
// | Header | 0 | 4 | assetProxyId |
|
|
||||||
// | Params | | 2 * 32 | function parameters: |
|
|
||||||
// | | 4 | | 1. offset to amounts (*) |
|
|
||||||
// | | 36 | | 2. offset to nestedAssetData (*) |
|
|
||||||
// | Data | | | amounts: |
|
|
||||||
// | | 68 | 32 | amounts Length |
|
|
||||||
// | | 100 | a | amounts Contents |
|
|
||||||
// | | | | nestedAssetData: |
|
|
||||||
// | | 100 + a | 32 | nestedAssetData Length |
|
|
||||||
// | | 132 + a | b | nestedAssetData Contents (offsets) |
|
|
||||||
// | | 132 + a + b | | nestedAssetData[0, ..., len] |
|
|
||||||
|
|
||||||
// Assert that the length of asset data:
|
|
||||||
// 1. Must be at least 68 bytes (see table above)
|
|
||||||
// 2. Must be a multiple of 32 (excluding the 4-byte selector)
|
|
||||||
if or(lt(assetDataLength, 68), mod(sub(assetDataLength, 4), 32)) {
|
|
||||||
// Revert with `Error("INVALID_ASSET_DATA_LENGTH")`
|
|
||||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
|
||||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
|
||||||
mstore(64, 0x00000019494e56414c49445f41535345545f444154415f4c454e475448000000)
|
|
||||||
mstore(96, 0)
|
|
||||||
revert(0, 100)
|
|
||||||
}
|
|
||||||
|
|
||||||
// End of asset data in calldata
|
|
||||||
// assetDataOffset
|
|
||||||
// + 32 (assetData len)
|
|
||||||
let assetDataEnd := add(assetDataOffset, add(assetDataLength, 32))
|
|
||||||
if gt(assetDataEnd, calldatasize()) {
|
|
||||||
// Revert with `Error("INVALID_ASSET_DATA_END")`
|
|
||||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
|
||||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
|
||||||
mstore(64, 0x00000016494e56414c49445f41535345545f444154415f454e44000000000000)
|
|
||||||
mstore(96, 0)
|
|
||||||
revert(0, 100)
|
|
||||||
}
|
|
||||||
|
|
||||||
// In order to find the offset to `amounts`, we must add:
|
|
||||||
// assetDataOffset
|
|
||||||
// + 32 (assetData len)
|
|
||||||
// + 4 (assetProxyId)
|
|
||||||
let amountsOffset := calldataload(add(assetDataOffset, 36))
|
|
||||||
|
|
||||||
// In order to find the offset to `nestedAssetData`, we must add:
|
|
||||||
// assetDataOffset
|
|
||||||
// + 32 (assetData len)
|
|
||||||
// + 4 (assetProxyId)
|
|
||||||
// + 32 (amounts offset)
|
|
||||||
let nestedAssetDataOffset := calldataload(add(assetDataOffset, 68))
|
|
||||||
|
|
||||||
// In order to find the start of the `amounts` contents, we must add:
|
|
||||||
// assetDataOffset
|
|
||||||
// + 32 (assetData len)
|
|
||||||
// + 4 (assetProxyId)
|
|
||||||
// + amountsOffset
|
|
||||||
// + 32 (amounts len)
|
|
||||||
let amountsContentsStart := add(assetDataOffset, add(amountsOffset, 68))
|
|
||||||
|
|
||||||
// Load number of elements in `amounts`
|
|
||||||
let amountsLen := calldataload(sub(amountsContentsStart, 32))
|
|
||||||
|
|
||||||
// In order to find the start of the `nestedAssetData` contents, we must add:
|
|
||||||
// assetDataOffset
|
|
||||||
// + 32 (assetData len)
|
|
||||||
// + 4 (assetProxyId)
|
|
||||||
// + nestedAssetDataOffset
|
|
||||||
// + 32 (nestedAssetData len)
|
|
||||||
let nestedAssetDataContentsStart := add(assetDataOffset, add(nestedAssetDataOffset, 68))
|
|
||||||
|
|
||||||
// Load number of elements in `nestedAssetData`
|
|
||||||
let nestedAssetDataLen := calldataload(sub(nestedAssetDataContentsStart, 32))
|
|
||||||
|
|
||||||
// Revert if number of elements in `amounts` differs from number of elements in `nestedAssetData`
|
|
||||||
if sub(amountsLen, nestedAssetDataLen) {
|
|
||||||
// Revert with `Error("LENGTH_MISMATCH")`
|
|
||||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
|
||||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
|
||||||
mstore(64, 0x0000000f4c454e4754485f4d49534d4154434800000000000000000000000000)
|
|
||||||
mstore(96, 0)
|
|
||||||
revert(0, 100)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy `transferFrom` selector, offset to `assetData`, `from`, and `to` from calldata to memory
|
|
||||||
calldatacopy(
|
|
||||||
0, // memory can safely be overwritten from beginning
|
|
||||||
0, // start of calldata
|
|
||||||
100 // length of selector (4) and 3 params (32 * 3)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Overwrite existing offset to `assetData` with our own
|
|
||||||
mstore(4, 128)
|
|
||||||
|
|
||||||
// Load `amount`
|
|
||||||
let amount := calldataload(100)
|
|
||||||
|
|
||||||
// Calculate number of bytes in `amounts` contents
|
|
||||||
let amountsByteLen := mul(amountsLen, 32)
|
|
||||||
|
|
||||||
// Initialize `assetProxyId` and `assetProxy` to 0
|
|
||||||
let assetProxyId := 0
|
|
||||||
let assetProxy := 0
|
|
||||||
|
|
||||||
// Loop through `amounts` and `nestedAssetData`, calling `transferFrom` for each respective element
|
|
||||||
for {let i := 0} lt(i, amountsByteLen) {i := add(i, 32)} {
|
|
||||||
|
|
||||||
// Calculate the total amount
|
|
||||||
let amountsElement := calldataload(add(amountsContentsStart, i))
|
|
||||||
let totalAmount := mul(amountsElement, amount)
|
|
||||||
|
|
||||||
// Revert if `amount` != 0 and multiplication resulted in an overflow
|
|
||||||
if iszero(or(
|
|
||||||
iszero(amount),
|
|
||||||
eq(div(totalAmount, amount), amountsElement)
|
|
||||||
)) {
|
|
||||||
// Revert with `Error("UINT256_OVERFLOW")`
|
|
||||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
|
||||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
|
||||||
mstore(64, 0x0000001055494e543235365f4f564552464c4f57000000000000000000000000)
|
|
||||||
mstore(96, 0)
|
|
||||||
revert(0, 100)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write `totalAmount` to memory
|
|
||||||
mstore(100, totalAmount)
|
|
||||||
|
|
||||||
// Load offset to `nestedAssetData[i]`
|
|
||||||
let nestedAssetDataElementOffset := calldataload(add(nestedAssetDataContentsStart, i))
|
|
||||||
|
|
||||||
// In order to find the start of the `nestedAssetData[i]` contents, we must add:
|
|
||||||
// assetDataOffset
|
|
||||||
// + 32 (assetData len)
|
|
||||||
// + 4 (assetProxyId)
|
|
||||||
// + nestedAssetDataOffset
|
|
||||||
// + 32 (nestedAssetData len)
|
|
||||||
// + nestedAssetDataElementOffset
|
|
||||||
// + 32 (nestedAssetDataElement len)
|
|
||||||
let nestedAssetDataElementContentsStart := add(
|
|
||||||
assetDataOffset,
|
|
||||||
add(
|
|
||||||
nestedAssetDataOffset,
|
|
||||||
add(nestedAssetDataElementOffset, 100)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Load length of `nestedAssetData[i]`
|
|
||||||
let nestedAssetDataElementLenStart := sub(nestedAssetDataElementContentsStart, 32)
|
|
||||||
let nestedAssetDataElementLen := calldataload(nestedAssetDataElementLenStart)
|
|
||||||
|
|
||||||
// Revert if the `nestedAssetData` does not contain a 4 byte `assetProxyId`
|
|
||||||
if lt(nestedAssetDataElementLen, 4) {
|
|
||||||
// Revert with `Error("LENGTH_GREATER_THAN_3_REQUIRED")`
|
|
||||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
|
||||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
|
||||||
mstore(64, 0x0000001e4c454e4754485f475245415445525f5448414e5f335f524551554952)
|
|
||||||
mstore(96, 0x4544000000000000000000000000000000000000000000000000000000000000)
|
|
||||||
revert(0, 100)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load AssetProxy id
|
|
||||||
let currentAssetProxyId := and(
|
|
||||||
calldataload(nestedAssetDataElementContentsStart),
|
|
||||||
0xffffffff00000000000000000000000000000000000000000000000000000000
|
|
||||||
)
|
|
||||||
|
|
||||||
// Only load `assetProxy` if `currentAssetProxyId` does not equal `assetProxyId`
|
|
||||||
// We do not need to check if `currentAssetProxyId` is 0 since `assetProxy` is also initialized to 0
|
|
||||||
if sub(currentAssetProxyId, assetProxyId) {
|
|
||||||
// Update `assetProxyId`
|
|
||||||
assetProxyId := currentAssetProxyId
|
|
||||||
// To lookup a value in a mapping, we load from the storage location keccak256(k, p),
|
|
||||||
// where k is the key left padded to 32 bytes and p is the storage slot
|
|
||||||
mstore(132, assetProxyId)
|
|
||||||
mstore(164, assetProxies_slot)
|
|
||||||
assetProxy := sload(keccak256(132, 64))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Revert if AssetProxy with given id does not exist
|
|
||||||
if iszero(assetProxy) {
|
|
||||||
// Revert with `Error("ASSET_PROXY_DOES_NOT_EXIST")`
|
|
||||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
|
||||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
|
||||||
mstore(64, 0x0000001a41535345545f50524f58595f444f45535f4e4f545f45584953540000)
|
|
||||||
mstore(96, 0)
|
|
||||||
revert(0, 100)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy `nestedAssetData[i]` from calldata to memory
|
|
||||||
calldatacopy(
|
|
||||||
132, // memory slot after `amounts[i]`
|
|
||||||
nestedAssetDataElementLenStart, // location of `nestedAssetData[i]` in calldata
|
|
||||||
add(nestedAssetDataElementLen, 32) // `nestedAssetData[i].length` plus 32 byte length
|
|
||||||
)
|
|
||||||
|
|
||||||
// call `assetProxy.transferFrom`
|
|
||||||
let success := call(
|
|
||||||
gas, // forward all gas
|
|
||||||
assetProxy, // call address of asset proxy
|
|
||||||
0, // don't send any ETH
|
|
||||||
0, // pointer to start of input
|
|
||||||
add(164, nestedAssetDataElementLen), // length of input
|
|
||||||
0, // write output over memory that won't be reused
|
|
||||||
0 // don't copy output to memory
|
|
||||||
)
|
|
||||||
|
|
||||||
// Revert with reason given by AssetProxy if `transferFrom` call failed
|
|
||||||
if iszero(success) {
|
|
||||||
returndatacopy(
|
|
||||||
0, // copy to memory at 0
|
|
||||||
0, // copy from return data at 0
|
|
||||||
returndatasize() // copy all return data
|
|
||||||
)
|
|
||||||
revert(0, returndatasize())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return if no `transferFrom` calls reverted
|
|
||||||
return(0, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Revert if undefined function is called
|
|
||||||
revert(0, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Gets the proxy id associated with the proxy address.
|
|
||||||
/// @return Proxy id.
|
|
||||||
function getProxyId()
|
|
||||||
external
|
|
||||||
pure
|
|
||||||
returns (bytes4)
|
|
||||||
{
|
|
||||||
return PROXY_ID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
|
||||||
|
|
||||||
|
|
||||||
// solhint-disable no-unused-vars
|
|
||||||
contract StaticCallProxy {
|
|
||||||
|
|
||||||
using LibBytes for bytes;
|
|
||||||
|
|
||||||
// Id of this proxy.
|
|
||||||
bytes4 constant internal PROXY_ID = bytes4(keccak256("StaticCall(address,bytes,bytes32)"));
|
|
||||||
|
|
||||||
/// @dev Makes a staticcall to a target address and verifies that the data returned matches the expected return data.
|
|
||||||
/// @param assetData Byte array encoded with staticCallTarget, staticCallData, and expectedCallResultHash
|
|
||||||
/// @param from This value is ignored.
|
|
||||||
/// @param to This value is ignored.
|
|
||||||
/// @param amount This value is ignored.
|
|
||||||
function transferFrom(
|
|
||||||
bytes calldata assetData,
|
|
||||||
address from,
|
|
||||||
address to,
|
|
||||||
uint256 amount
|
|
||||||
)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
{
|
|
||||||
// Decode params from `assetData`
|
|
||||||
(
|
|
||||||
address staticCallTarget,
|
|
||||||
bytes memory staticCallData,
|
|
||||||
bytes32 expectedReturnDataHash
|
|
||||||
) = abi.decode(
|
|
||||||
assetData.sliceDestructive(4, assetData.length),
|
|
||||||
(address, bytes, bytes32)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Execute staticcall
|
|
||||||
(bool success, bytes memory returnData) = staticCallTarget.staticcall(staticCallData);
|
|
||||||
|
|
||||||
// Revert with returned data if staticcall is unsuccessful
|
|
||||||
if (!success) {
|
|
||||||
assembly {
|
|
||||||
revert(add(returnData, 32), mload(returnData))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Revert if hash of return data is not as expected
|
|
||||||
bytes32 returnDataHash = keccak256(returnData);
|
|
||||||
require(
|
|
||||||
expectedReturnDataHash == returnDataHash,
|
|
||||||
"UNEXPECTED_STATIC_CALL_RESULT"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Gets the proxy id associated with the proxy address.
|
|
||||||
/// @return Proxy id.
|
|
||||||
function getProxyId()
|
|
||||||
external
|
|
||||||
pure
|
|
||||||
returns (bytes4)
|
|
||||||
{
|
|
||||||
return PROXY_ID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,103 +0,0 @@
|
|||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
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-utils/contracts/src/DeploymentConstants.sol";
|
|
||||||
import "../interfaces/IERC20Bridge.sol";
|
|
||||||
import "../interfaces/IBalancerPool.sol";
|
|
||||||
|
|
||||||
|
|
||||||
contract BalancerBridge is
|
|
||||||
IERC20Bridge,
|
|
||||||
IWallet,
|
|
||||||
DeploymentConstants
|
|
||||||
{
|
|
||||||
/// @dev Callback for `IERC20Bridge`. Tries to buy `amount` of
|
|
||||||
/// `toTokenAddress` tokens by selling the entirety of the `fromTokenAddress`
|
|
||||||
/// token encoded in the bridge data, then transfers the bought
|
|
||||||
/// tokens to `to`.
|
|
||||||
/// @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 addresses of the "from" token and Balancer pool.
|
|
||||||
/// @return success The magic bytes if successful.
|
|
||||||
function bridgeTransferFrom(
|
|
||||||
address toTokenAddress,
|
|
||||||
address from,
|
|
||||||
address to,
|
|
||||||
uint256 amount,
|
|
||||||
bytes calldata bridgeData
|
|
||||||
)
|
|
||||||
external
|
|
||||||
returns (bytes4 success)
|
|
||||||
{
|
|
||||||
// Decode the bridge data.
|
|
||||||
(address fromTokenAddress, address poolAddress) = abi.decode(
|
|
||||||
bridgeData,
|
|
||||||
(address, address)
|
|
||||||
);
|
|
||||||
require(toTokenAddress != fromTokenAddress, "BalancerBridge/INVALID_PAIR");
|
|
||||||
|
|
||||||
uint256 fromTokenBalance = IERC20Token(fromTokenAddress).balanceOf(address(this));
|
|
||||||
// Grant an allowance to the exchange to spend `fromTokenAddress` token.
|
|
||||||
LibERC20Token.approveIfBelow(fromTokenAddress, poolAddress, fromTokenBalance);
|
|
||||||
|
|
||||||
// Sell all of this contract's `fromTokenAddress` token balance.
|
|
||||||
(uint256 boughtAmount,) = IBalancerPool(poolAddress).swapExactAmountIn(
|
|
||||||
fromTokenAddress, // tokenIn
|
|
||||||
fromTokenBalance, // tokenAmountIn
|
|
||||||
toTokenAddress, // tokenOut
|
|
||||||
amount, // minAmountOut
|
|
||||||
uint256(-1) // maxPrice
|
|
||||||
);
|
|
||||||
|
|
||||||
// Transfer the converted `toToken`s to `to`.
|
|
||||||
LibERC20Token.transfer(toTokenAddress, to, boughtAmount);
|
|
||||||
|
|
||||||
emit ERC20BridgeTransfer(
|
|
||||||
fromTokenAddress,
|
|
||||||
toTokenAddress,
|
|
||||||
fromTokenBalance,
|
|
||||||
boughtAmount,
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,122 +0,0 @@
|
|||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
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-utils/contracts/src/DeploymentConstants.sol";
|
|
||||||
import "../interfaces/IERC20Bridge.sol";
|
|
||||||
import "../interfaces/IBancorNetwork.sol";
|
|
||||||
|
|
||||||
|
|
||||||
contract BancorBridge is
|
|
||||||
IERC20Bridge,
|
|
||||||
IWallet,
|
|
||||||
DeploymentConstants
|
|
||||||
{
|
|
||||||
struct TransferState {
|
|
||||||
address bancorNetworkAddress;
|
|
||||||
address[] path;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Callback for `IERC20Bridge`. Tries to buy `amount` of
|
|
||||||
/// `toTokenAddress` tokens by selling the entirety of the `fromTokenAddress`
|
|
||||||
/// token encoded in the bridge data, then transfers the bought
|
|
||||||
/// tokens to `to`.
|
|
||||||
/// @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 conversion path addresses and Bancor network address
|
|
||||||
/// @return success The magic bytes if successful.
|
|
||||||
function bridgeTransferFrom(
|
|
||||||
address toTokenAddress,
|
|
||||||
address from,
|
|
||||||
address to,
|
|
||||||
uint256 amount,
|
|
||||||
bytes calldata bridgeData
|
|
||||||
)
|
|
||||||
external
|
|
||||||
returns (bytes4 success)
|
|
||||||
{
|
|
||||||
// hold variables to get around stack depth limitations
|
|
||||||
TransferState memory state;
|
|
||||||
|
|
||||||
// Decode the bridge data.
|
|
||||||
(
|
|
||||||
state.path,
|
|
||||||
state.bancorNetworkAddress
|
|
||||||
// solhint-disable indent
|
|
||||||
) = abi.decode(bridgeData, (address[], address));
|
|
||||||
// solhint-enable indent
|
|
||||||
|
|
||||||
require(state.path.length > 0, "BancorBridge/PATH_MUST_EXIST");
|
|
||||||
// Just transfer the tokens if they're the same.
|
|
||||||
if (state.path[0] == toTokenAddress) {
|
|
||||||
LibERC20Token.transfer(state.path[0], to, amount);
|
|
||||||
return BRIDGE_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise use Bancor to convert
|
|
||||||
require(state.path.length > 2, "BancorBridge/PATH_LENGTH_MUST_BE_GREATER_THAN_TWO");
|
|
||||||
require(state.path[state.path.length - 1] == toTokenAddress, "BancorBridge/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN");
|
|
||||||
|
|
||||||
// // Grant an allowance to the Bancor Network to spend `fromTokenAddress` token.
|
|
||||||
uint256 fromTokenBalance = IERC20Token(state.path[0]).balanceOf(address(this));
|
|
||||||
LibERC20Token.approveIfBelow(state.path[0], state.bancorNetworkAddress, fromTokenBalance);
|
|
||||||
|
|
||||||
// Convert the tokens
|
|
||||||
uint256 boughtAmount = IBancorNetwork(state.bancorNetworkAddress).convertByPath(
|
|
||||||
state.path, // path originating with source token and terminating in destination token
|
|
||||||
fromTokenBalance, // amount of source token to trade
|
|
||||||
amount, // minimum amount of destination token expected to receive
|
|
||||||
to, // beneficiary
|
|
||||||
address(0), // affiliateAccount; no fee paid
|
|
||||||
0 // affiliateFee; no fee paid
|
|
||||||
);
|
|
||||||
|
|
||||||
emit ERC20BridgeTransfer(
|
|
||||||
state.path[0], // fromTokenAddress
|
|
||||||
toTokenAddress,
|
|
||||||
fromTokenBalance,
|
|
||||||
boughtAmount,
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright 2019 ZeroEx Intl.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
pragma solidity ^0.5.9;
|
|
||||||
pragma experimental ABIEncoderV2;
|
|
||||||
|
|
||||||
import "../interfaces/IERC20Bridge.sol";
|
|
||||||
import "../interfaces/IChai.sol";
|
|
||||||
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
|
|
||||||
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
|
||||||
|
|
||||||
|
|
||||||
// solhint-disable space-after-comma
|
|
||||||
contract ChaiBridge is
|
|
||||||
IERC20Bridge,
|
|
||||||
DeploymentConstants
|
|
||||||
{
|
|
||||||
/// @dev Withdraws `amount` of `from` address's Dai from the Chai contract.
|
|
||||||
/// Transfers `amount` of Dai to `to` address.
|
|
||||||
/// @param from Address to transfer asset from.
|
|
||||||
/// @param to Address to transfer asset to.
|
|
||||||
/// @param amount Amount of asset to transfer.
|
|
||||||
/// @return success The magic bytes `0xdc1600f3` if successful.
|
|
||||||
function bridgeTransferFrom(
|
|
||||||
address /* tokenAddress */,
|
|
||||||
address from,
|
|
||||||
address to,
|
|
||||||
uint256 amount,
|
|
||||||
bytes calldata /* bridgeData */
|
|
||||||
)
|
|
||||||
external
|
|
||||||
returns (bytes4 success)
|
|
||||||
{
|
|
||||||
// Ensure that only the `ERC20BridgeProxy` can call this function.
|
|
||||||
require(
|
|
||||||
msg.sender == _getERC20BridgeProxyAddress(),
|
|
||||||
"ChaiBridge/ONLY_CALLABLE_BY_ERC20_BRIDGE_PROXY"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Withdraw `from` address's Dai.
|
|
||||||
// NOTE: This contract must be approved to spend Chai on behalf of `from`.
|
|
||||||
bytes memory drawCalldata = abi.encodeWithSelector(
|
|
||||||
IChai(address(0)).draw.selector,
|
|
||||||
from,
|
|
||||||
amount
|
|
||||||
);
|
|
||||||
|
|
||||||
(bool success,) = _getChaiAddress().call(drawCalldata);
|
|
||||||
require(
|
|
||||||
success,
|
|
||||||
"ChaiBridge/DRAW_DAI_FAILED"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Transfer Dai to `to`
|
|
||||||
// This will never fail if the `draw` call was successful
|
|
||||||
IERC20Token(_getDaiAddress()).transfer(to, amount);
|
|
||||||
|
|
||||||
return BRIDGE_SUCCESS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,103 +0,0 @@
|
|||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
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-utils/contracts/src/DeploymentConstants.sol";
|
|
||||||
import "../interfaces/IERC20Bridge.sol";
|
|
||||||
import "../interfaces/IBalancerPool.sol";
|
|
||||||
|
|
||||||
|
|
||||||
contract CreamBridge is
|
|
||||||
IERC20Bridge,
|
|
||||||
IWallet,
|
|
||||||
DeploymentConstants
|
|
||||||
{
|
|
||||||
/// @dev Callback for `IERC20Bridge`. Tries to buy `amount` of
|
|
||||||
/// `toTokenAddress` tokens by selling the entirety of the `fromTokenAddress`
|
|
||||||
/// token encoded in the bridge data, then transfers the bought
|
|
||||||
/// tokens to `to`.
|
|
||||||
/// @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 addresses of the "from" token and Balancer pool.
|
|
||||||
/// @return success The magic bytes if successful.
|
|
||||||
function bridgeTransferFrom(
|
|
||||||
address toTokenAddress,
|
|
||||||
address from,
|
|
||||||
address to,
|
|
||||||
uint256 amount,
|
|
||||||
bytes calldata bridgeData
|
|
||||||
)
|
|
||||||
external
|
|
||||||
returns (bytes4 success)
|
|
||||||
{
|
|
||||||
// Decode the bridge data.
|
|
||||||
(address fromTokenAddress, address poolAddress) = abi.decode(
|
|
||||||
bridgeData,
|
|
||||||
(address, address)
|
|
||||||
);
|
|
||||||
require(toTokenAddress != fromTokenAddress, "CreamBridge/INVALID_PAIR");
|
|
||||||
|
|
||||||
uint256 fromTokenBalance = IERC20Token(fromTokenAddress).balanceOf(address(this));
|
|
||||||
// Grant an allowance to the exchange to spend `fromTokenAddress` token.
|
|
||||||
LibERC20Token.approveIfBelow(fromTokenAddress, poolAddress, fromTokenBalance);
|
|
||||||
|
|
||||||
// Sell all of this contract's `fromTokenAddress` token balance.
|
|
||||||
(uint256 boughtAmount,) = IBalancerPool(poolAddress).swapExactAmountIn(
|
|
||||||
fromTokenAddress, // tokenIn
|
|
||||||
fromTokenBalance, // tokenAmountIn
|
|
||||||
toTokenAddress, // tokenOut
|
|
||||||
amount, // minAmountOut
|
|
||||||
uint256(-1) // maxPrice
|
|
||||||
);
|
|
||||||
|
|
||||||
// Transfer the converted `toToken`s to `to`.
|
|
||||||
LibERC20Token.transfer(toTokenAddress, to, boughtAmount);
|
|
||||||
|
|
||||||
emit ERC20BridgeTransfer(
|
|
||||||
fromTokenAddress,
|
|
||||||
toTokenAddress,
|
|
||||||
fromTokenBalance,
|
|
||||||
boughtAmount,
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,136 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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 CryptoComBridge is
|
|
||||||
IERC20Bridge,
|
|
||||||
IWallet,
|
|
||||||
DeploymentConstants
|
|
||||||
{
|
|
||||||
struct TransferState {
|
|
||||||
address[] path;
|
|
||||||
address router;
|
|
||||||
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, state.router) = abi.decode(bridgeData, (address[], address));
|
|
||||||
// solhint-enable indent
|
|
||||||
|
|
||||||
require(state.path.length >= 2, "CryptoComBridge/PATH_LENGTH_MUST_BE_AT_LEAST_TWO");
|
|
||||||
require(state.path[state.path.length - 1] == toTokenAddress, "CryptoComBridge/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 SushiSwap router an allowance.
|
|
||||||
LibERC20Token.approveIfBelow(
|
|
||||||
state.path[0],
|
|
||||||
state.router,
|
|
||||||
state.fromTokenBalance
|
|
||||||
);
|
|
||||||
|
|
||||||
// Buy as much `toTokenAddress` token with `fromTokenAddress` token
|
|
||||||
// and transfer it to `to`.
|
|
||||||
IUniswapV2Router01 router = IUniswapV2Router01(state.router);
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,119 +0,0 @@
|
|||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
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";
|
|
||||||
|
|
||||||
|
|
||||||
// solhint-disable not-rely-on-time
|
|
||||||
// solhint-disable space-after-comma
|
|
||||||
contract CurveBridge is
|
|
||||||
IERC20Bridge,
|
|
||||||
IWallet,
|
|
||||||
DeploymentConstants
|
|
||||||
{
|
|
||||||
struct CurveBridgeData {
|
|
||||||
address curveAddress;
|
|
||||||
bytes4 exchangeFunctionSelector;
|
|
||||||
address fromTokenAddress;
|
|
||||||
int128 fromCoinIdx;
|
|
||||||
int128 toCoinIdx;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @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
|
|
||||||
returns (bytes4 success)
|
|
||||||
{
|
|
||||||
// Decode the bridge data to get the Curve metadata.
|
|
||||||
CurveBridgeData memory data = abi.decode(bridgeData, (CurveBridgeData));
|
|
||||||
|
|
||||||
require(toTokenAddress != data.fromTokenAddress, "CurveBridge/INVALID_PAIR");
|
|
||||||
uint256 fromTokenBalance = IERC20Token(data.fromTokenAddress).balanceOf(address(this));
|
|
||||||
// Grant an allowance to the exchange to spend `fromTokenAddress` token.
|
|
||||||
LibERC20Token.approveIfBelow(data.fromTokenAddress, data.curveAddress, fromTokenBalance);
|
|
||||||
|
|
||||||
// Try to sell all of this contract's `fromTokenAddress` token balance.
|
|
||||||
{
|
|
||||||
(bool didSucceed, bytes memory resultData) =
|
|
||||||
data.curveAddress.call(abi.encodeWithSelector(
|
|
||||||
data.exchangeFunctionSelector,
|
|
||||||
data.fromCoinIdx,
|
|
||||||
data.toCoinIdx,
|
|
||||||
// dx
|
|
||||||
fromTokenBalance,
|
|
||||||
// min dy
|
|
||||||
amount
|
|
||||||
));
|
|
||||||
if (!didSucceed) {
|
|
||||||
assembly { revert(add(resultData, 32), mload(resultData)) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint256 toTokenBalance = IERC20Token(toTokenAddress).balanceOf(address(this));
|
|
||||||
// Transfer the converted `toToken`s to `to`.
|
|
||||||
LibERC20Token.transfer(toTokenAddress, to, toTokenBalance);
|
|
||||||
|
|
||||||
emit ERC20BridgeTransfer(
|
|
||||||
data.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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,147 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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-utils/contracts/src/DeploymentConstants.sol";
|
|
||||||
import "../interfaces/IERC20Bridge.sol";
|
|
||||||
|
|
||||||
|
|
||||||
interface IDODOHelper {
|
|
||||||
|
|
||||||
function querySellQuoteToken(address dodo, uint256 amount) external view returns (uint256);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
interface IDODO {
|
|
||||||
|
|
||||||
function sellBaseToken(uint256 amount, uint256 minReceiveQuote, bytes calldata data) external returns (uint256);
|
|
||||||
|
|
||||||
function buyBaseToken(uint256 amount, uint256 maxPayQuote, bytes calldata data) external returns (uint256);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
contract DODOBridge is
|
|
||||||
IERC20Bridge,
|
|
||||||
IWallet,
|
|
||||||
DeploymentConstants
|
|
||||||
{
|
|
||||||
|
|
||||||
struct TransferState {
|
|
||||||
address fromTokenAddress;
|
|
||||||
uint256 fromTokenBalance;
|
|
||||||
address pool;
|
|
||||||
bool isSellBase;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @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)
|
|
||||||
{
|
|
||||||
TransferState memory state;
|
|
||||||
// Decode the bridge data to get the `fromTokenAddress`.
|
|
||||||
(state.fromTokenAddress, state.pool, state.isSellBase) = abi.decode(bridgeData, (address, address, bool));
|
|
||||||
require(state.pool != address(0), "DODOBridge/InvalidPool");
|
|
||||||
IDODO exchange = IDODO(state.pool);
|
|
||||||
// Get our balance of `fromTokenAddress` token.
|
|
||||||
state.fromTokenBalance = IERC20Token(state.fromTokenAddress).balanceOf(address(this));
|
|
||||||
|
|
||||||
// Grant the pool an allowance.
|
|
||||||
LibERC20Token.approveIfBelow(
|
|
||||||
state.fromTokenAddress,
|
|
||||||
address(exchange),
|
|
||||||
state.fromTokenBalance
|
|
||||||
);
|
|
||||||
|
|
||||||
uint256 boughtAmount;
|
|
||||||
if (state.isSellBase) {
|
|
||||||
boughtAmount = exchange.sellBaseToken(
|
|
||||||
// amount to sell
|
|
||||||
state.fromTokenBalance,
|
|
||||||
// min receive amount
|
|
||||||
1,
|
|
||||||
new bytes(0)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// Need to re-calculate the sell quote amount into buyBase
|
|
||||||
boughtAmount = IDODOHelper(_getDODOHelperAddress()).querySellQuoteToken(
|
|
||||||
address(exchange),
|
|
||||||
state.fromTokenBalance
|
|
||||||
);
|
|
||||||
exchange.buyBaseToken(
|
|
||||||
// amount to buy
|
|
||||||
boughtAmount,
|
|
||||||
// max pay amount
|
|
||||||
state.fromTokenBalance,
|
|
||||||
new bytes(0)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// Transfer funds to `to`
|
|
||||||
IERC20Token(toTokenAddress).transfer(to, boughtAmount);
|
|
||||||
|
|
||||||
|
|
||||||
emit ERC20BridgeTransfer(
|
|
||||||
// input token
|
|
||||||
state.fromTokenAddress,
|
|
||||||
// output token
|
|
||||||
toTokenAddress,
|
|
||||||
// input token amount
|
|
||||||
state.fromTokenBalance,
|
|
||||||
// output token amount
|
|
||||||
boughtAmount,
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,200 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,242 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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-utils/contracts/src/DeploymentConstants.sol";
|
|
||||||
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
|
||||||
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
|
|
||||||
import "../interfaces/IERC20Bridge.sol";
|
|
||||||
import "../interfaces/IDydxBridge.sol";
|
|
||||||
import "../interfaces/IDydx.sol";
|
|
||||||
|
|
||||||
|
|
||||||
contract DydxBridge is
|
|
||||||
IERC20Bridge,
|
|
||||||
IDydxBridge,
|
|
||||||
DeploymentConstants
|
|
||||||
{
|
|
||||||
|
|
||||||
using LibSafeMath for uint256;
|
|
||||||
|
|
||||||
/// @dev Callback for `IERC20Bridge`. Deposits or withdraws tokens from a dydx account.
|
|
||||||
/// Notes:
|
|
||||||
/// 1. This bridge must be set as an operator of the input dydx account.
|
|
||||||
/// 2. This function may only be called in the context of the 0x Exchange.
|
|
||||||
/// 3. The maker or taker of the 0x order must be the dydx account owner.
|
|
||||||
/// 4. Deposits into dydx are made from the `from` address.
|
|
||||||
/// 5. Withdrawals from dydx are made to the `to` address.
|
|
||||||
/// 6. Calling this function must always withdraw at least `amount`,
|
|
||||||
/// otherwise the `ERC20Bridge` will revert.
|
|
||||||
/// @param from The sender of the tokens and owner of the dydx account.
|
|
||||||
/// @param to The recipient of the tokens.
|
|
||||||
/// @param amount Minimum amount of `toTokenAddress` tokens to deposit or withdraw.
|
|
||||||
/// @param encodedBridgeData An abi-encoded `BridgeData` struct.
|
|
||||||
/// @return success The magic bytes if successful.
|
|
||||||
function bridgeTransferFrom(
|
|
||||||
address, /* toTokenAddress */
|
|
||||||
address from,
|
|
||||||
address to,
|
|
||||||
uint256 amount,
|
|
||||||
bytes calldata encodedBridgeData
|
|
||||||
)
|
|
||||||
external
|
|
||||||
returns (bytes4 success)
|
|
||||||
{
|
|
||||||
// Ensure that only the `ERC20BridgeProxy` can call this function.
|
|
||||||
require(
|
|
||||||
msg.sender == _getERC20BridgeProxyAddress(),
|
|
||||||
"DydxBridge/ONLY_CALLABLE_BY_ERC20_BRIDGE_PROXY"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Decode bridge data.
|
|
||||||
(BridgeData memory bridgeData) = abi.decode(encodedBridgeData, (BridgeData));
|
|
||||||
|
|
||||||
// The dydx accounts are owned by the `from` address.
|
|
||||||
IDydx.AccountInfo[] memory accounts = _createAccounts(from, bridgeData);
|
|
||||||
|
|
||||||
// Create dydx actions to run on the dydx accounts.
|
|
||||||
IDydx.ActionArgs[] memory actions = _createActions(
|
|
||||||
from,
|
|
||||||
to,
|
|
||||||
amount,
|
|
||||||
bridgeData
|
|
||||||
);
|
|
||||||
|
|
||||||
// Run operation. This will revert on failure.
|
|
||||||
IDydx(_getDydxAddress()).operate(accounts, actions);
|
|
||||||
|
|
||||||
return BRIDGE_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Creates an array of accounts for dydx to operate on.
|
|
||||||
/// All accounts must belong to the same owner.
|
|
||||||
/// @param accountOwner Owner of the dydx account.
|
|
||||||
/// @param bridgeData A `BridgeData` struct.
|
|
||||||
function _createAccounts(
|
|
||||||
address accountOwner,
|
|
||||||
BridgeData memory bridgeData
|
|
||||||
)
|
|
||||||
internal
|
|
||||||
returns (IDydx.AccountInfo[] memory accounts)
|
|
||||||
{
|
|
||||||
uint256[] memory accountNumbers = bridgeData.accountNumbers;
|
|
||||||
uint256 nAccounts = accountNumbers.length;
|
|
||||||
accounts = new IDydx.AccountInfo[](nAccounts);
|
|
||||||
for (uint256 i = 0; i < nAccounts; ++i) {
|
|
||||||
accounts[i] = IDydx.AccountInfo({
|
|
||||||
owner: accountOwner,
|
|
||||||
number: accountNumbers[i]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Creates an array of actions to carry out on dydx.
|
|
||||||
/// @param depositFrom Deposit value from this address (owner of the dydx account).
|
|
||||||
/// @param withdrawTo Withdraw value to this address.
|
|
||||||
/// @param amount The amount of value available to operate on.
|
|
||||||
/// @param bridgeData A `BridgeData` struct.
|
|
||||||
function _createActions(
|
|
||||||
address depositFrom,
|
|
||||||
address withdrawTo,
|
|
||||||
uint256 amount,
|
|
||||||
BridgeData memory bridgeData
|
|
||||||
)
|
|
||||||
internal
|
|
||||||
returns (IDydx.ActionArgs[] memory actions)
|
|
||||||
{
|
|
||||||
BridgeAction[] memory bridgeActions = bridgeData.actions;
|
|
||||||
uint256 nBridgeActions = bridgeActions.length;
|
|
||||||
actions = new IDydx.ActionArgs[](nBridgeActions);
|
|
||||||
for (uint256 i = 0; i < nBridgeActions; ++i) {
|
|
||||||
// Cache current bridge action.
|
|
||||||
BridgeAction memory bridgeAction = bridgeActions[i];
|
|
||||||
|
|
||||||
// Scale amount, if conversion rate is set.
|
|
||||||
uint256 scaledAmount;
|
|
||||||
if (bridgeAction.conversionRateDenominator > 0) {
|
|
||||||
scaledAmount = LibMath.safeGetPartialAmountFloor(
|
|
||||||
bridgeAction.conversionRateNumerator,
|
|
||||||
bridgeAction.conversionRateDenominator,
|
|
||||||
amount
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
scaledAmount = amount;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construct dydx action.
|
|
||||||
if (bridgeAction.actionType == BridgeActionType.Deposit) {
|
|
||||||
// Deposit tokens from the account owner into their dydx account.
|
|
||||||
actions[i] = _createDepositAction(
|
|
||||||
depositFrom,
|
|
||||||
scaledAmount,
|
|
||||||
bridgeAction
|
|
||||||
);
|
|
||||||
} else if (bridgeAction.actionType == BridgeActionType.Withdraw) {
|
|
||||||
// Withdraw tokens from dydx to the `otherAccount`.
|
|
||||||
actions[i] = _createWithdrawAction(
|
|
||||||
withdrawTo,
|
|
||||||
scaledAmount,
|
|
||||||
bridgeAction
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// If all values in the `Action` enum are handled then this
|
|
||||||
// revert is unreachable: Solidity will revert when casting
|
|
||||||
// from `uint8` to `Action`.
|
|
||||||
revert("DydxBridge/UNRECOGNIZED_BRIDGE_ACTION");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Returns a dydx `DepositAction`.
|
|
||||||
/// @param depositFrom Deposit tokens from this address who is also the account owner.
|
|
||||||
/// @param amount of tokens to deposit.
|
|
||||||
/// @param bridgeAction A `BridgeAction` struct.
|
|
||||||
/// @return depositAction The encoded dydx action.
|
|
||||||
function _createDepositAction(
|
|
||||||
address depositFrom,
|
|
||||||
uint256 amount,
|
|
||||||
BridgeAction memory bridgeAction
|
|
||||||
)
|
|
||||||
internal
|
|
||||||
pure
|
|
||||||
returns (
|
|
||||||
IDydx.ActionArgs memory depositAction
|
|
||||||
)
|
|
||||||
{
|
|
||||||
// Create dydx amount.
|
|
||||||
IDydx.AssetAmount memory dydxAmount = IDydx.AssetAmount({
|
|
||||||
sign: true, // true if positive.
|
|
||||||
denomination: IDydx.AssetDenomination.Wei, // Wei => actual token amount held in account.
|
|
||||||
ref: IDydx.AssetReference.Delta, // Delta => a relative amount.
|
|
||||||
value: amount // amount to deposit.
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create dydx deposit action.
|
|
||||||
depositAction = IDydx.ActionArgs({
|
|
||||||
actionType: IDydx.ActionType.Deposit, // deposit tokens.
|
|
||||||
amount: dydxAmount, // amount to deposit.
|
|
||||||
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,
|
|
||||||
otherAccountIdx: 0,
|
|
||||||
data: hex''
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Returns a dydx `WithdrawAction`.
|
|
||||||
/// @param withdrawTo Withdraw tokens to this address.
|
|
||||||
/// @param amount of tokens to withdraw.
|
|
||||||
/// @param bridgeAction A `BridgeAction` struct.
|
|
||||||
/// @return withdrawAction The encoded dydx action.
|
|
||||||
function _createWithdrawAction(
|
|
||||||
address withdrawTo,
|
|
||||||
uint256 amount,
|
|
||||||
BridgeAction memory bridgeAction
|
|
||||||
)
|
|
||||||
internal
|
|
||||||
pure
|
|
||||||
returns (
|
|
||||||
IDydx.ActionArgs memory withdrawAction
|
|
||||||
)
|
|
||||||
{
|
|
||||||
// Create dydx amount.
|
|
||||||
IDydx.AssetAmount memory amountToWithdraw = IDydx.AssetAmount({
|
|
||||||
sign: false, // false if negative.
|
|
||||||
denomination: IDydx.AssetDenomination.Wei, // Wei => actual token amount held in account.
|
|
||||||
ref: IDydx.AssetReference.Delta, // Delta => a relative amount.
|
|
||||||
value: amount // amount to withdraw.
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create withdraw action.
|
|
||||||
withdrawAction = IDydx.ActionArgs({
|
|
||||||
actionType: IDydx.ActionType.Withdraw, // withdraw tokens.
|
|
||||||
amount: amountToWithdraw, // amount to withdraw.
|
|
||||||
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,
|
|
||||||
otherAccountIdx: 0,
|
|
||||||
data: hex''
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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/IEth2Dai.sol";
|
|
||||||
|
|
||||||
|
|
||||||
// solhint-disable space-after-comma
|
|
||||||
contract Eth2DaiBridge is
|
|
||||||
IERC20Bridge,
|
|
||||||
IWallet,
|
|
||||||
DeploymentConstants
|
|
||||||
{
|
|
||||||
/// @dev Callback for `IERC20Bridge`. Tries to buy `amount` of
|
|
||||||
/// `toTokenAddress` tokens by selling the entirety of the opposing asset
|
|
||||||
/// (DAI or WETH) to the Eth2Dai contract, then transfers the bought
|
|
||||||
/// 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 to,
|
|
||||||
uint256 amount,
|
|
||||||
bytes calldata bridgeData
|
|
||||||
)
|
|
||||||
external
|
|
||||||
returns (bytes4 success)
|
|
||||||
{
|
|
||||||
// Decode the bridge data to get the `fromTokenAddress`.
|
|
||||||
(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.approveIfBelow(fromTokenAddress, address(exchange), fromTokenBalance);
|
|
||||||
|
|
||||||
// Try to sell all of this contract's `fromTokenAddress` token balance.
|
|
||||||
uint256 boughtAmount = exchange.sellAllAmount(
|
|
||||||
fromTokenAddress,
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,166 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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/DeploymentConstants.sol";
|
|
||||||
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
|
||||||
import "../interfaces/IERC20Bridge.sol";
|
|
||||||
import "../interfaces/IKyberNetworkProxy.sol";
|
|
||||||
|
|
||||||
|
|
||||||
// solhint-disable space-after-comma
|
|
||||||
contract KyberBridge is
|
|
||||||
IERC20Bridge,
|
|
||||||
IWallet,
|
|
||||||
DeploymentConstants
|
|
||||||
{
|
|
||||||
using LibSafeMath for uint256;
|
|
||||||
|
|
||||||
// @dev Structure used internally to get around stack limits.
|
|
||||||
struct TradeState {
|
|
||||||
IKyberNetworkProxy kyber;
|
|
||||||
IEtherToken weth;
|
|
||||||
address fromTokenAddress;
|
|
||||||
uint256 fromTokenBalance;
|
|
||||||
uint256 payableAmount;
|
|
||||||
uint256 conversionRate;
|
|
||||||
bytes hint;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Kyber ETH pseudo-address.
|
|
||||||
address constant public KYBER_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
|
|
||||||
/// @dev `bridgeTransferFrom()` failure result.
|
|
||||||
bytes4 constant private BRIDGE_FAILED = 0x0;
|
|
||||||
/// @dev Precision of Kyber rates.
|
|
||||||
uint256 constant private KYBER_RATE_BASE = 10 ** 18;
|
|
||||||
|
|
||||||
// solhint-disable no-empty-blocks
|
|
||||||
/// @dev Payable fallback to receive ETH from Kyber.
|
|
||||||
function ()
|
|
||||||
external
|
|
||||||
payable
|
|
||||||
{}
|
|
||||||
|
|
||||||
/// @dev Callback for `IKyberBridge`. Tries to buy `amount` of
|
|
||||||
/// `toTokenAddress` tokens by selling the entirety of the opposing asset
|
|
||||||
/// to the `KyberNetworkProxy` contract, then transfers the bought
|
|
||||||
/// tokens to `to`.
|
|
||||||
/// @param toTokenAddress The token to give to `to`.
|
|
||||||
/// @param 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
|
|
||||||
returns (bytes4 success)
|
|
||||||
{
|
|
||||||
TradeState memory state;
|
|
||||||
state.kyber = IKyberNetworkProxy(_getKyberNetworkProxyAddress());
|
|
||||||
state.weth = IEtherToken(_getWethAddress());
|
|
||||||
// Decode the bridge data to get the `fromTokenAddress`.
|
|
||||||
(state.fromTokenAddress, state.hint) = abi.decode(bridgeData, (address, bytes));
|
|
||||||
// Query the balance of "from" tokens.
|
|
||||||
state.fromTokenBalance = IERC20Token(state.fromTokenAddress).balanceOf(address(this));
|
|
||||||
if (state.fromTokenBalance == 0) {
|
|
||||||
// Return failure if no input tokens.
|
|
||||||
return BRIDGE_FAILED;
|
|
||||||
}
|
|
||||||
if (state.fromTokenAddress == toTokenAddress) {
|
|
||||||
// Just transfer the tokens if they're the same.
|
|
||||||
LibERC20Token.transfer(state.fromTokenAddress, to, state.fromTokenBalance);
|
|
||||||
return BRIDGE_SUCCESS;
|
|
||||||
}
|
|
||||||
if (state.fromTokenAddress == address(state.weth)) {
|
|
||||||
// From WETH
|
|
||||||
state.fromTokenAddress = KYBER_ETH_ADDRESS;
|
|
||||||
state.payableAmount = state.fromTokenBalance;
|
|
||||||
state.weth.withdraw(state.fromTokenBalance);
|
|
||||||
} else {
|
|
||||||
LibERC20Token.approveIfBelow(
|
|
||||||
state.fromTokenAddress,
|
|
||||||
address(state.kyber),
|
|
||||||
state.fromTokenBalance
|
|
||||||
);
|
|
||||||
}
|
|
||||||
bool isToTokenWeth = toTokenAddress == address(state.weth);
|
|
||||||
|
|
||||||
// Try to sell all of this contract's input token balance through
|
|
||||||
// `KyberNetworkProxy.trade()`.
|
|
||||||
uint256 boughtAmount = state.kyber.tradeWithHint.value(state.payableAmount)(
|
|
||||||
// Input token.
|
|
||||||
state.fromTokenAddress,
|
|
||||||
// Sell amount.
|
|
||||||
state.fromTokenBalance,
|
|
||||||
// Output token.
|
|
||||||
isToTokenWeth ? KYBER_ETH_ADDRESS : toTokenAddress,
|
|
||||||
// Transfer to this contract if converting to ETH, otherwise
|
|
||||||
// transfer directly to the recipient.
|
|
||||||
isToTokenWeth ? address(uint160(address(this))) : address(uint160(to)),
|
|
||||||
// Buy as much as possible.
|
|
||||||
uint256(-1),
|
|
||||||
// The minimum conversion rate
|
|
||||||
1,
|
|
||||||
// No affiliate address.
|
|
||||||
address(0),
|
|
||||||
state.hint
|
|
||||||
);
|
|
||||||
// Wrap ETH output and transfer to recipient.
|
|
||||||
if (isToTokenWeth) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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/IMStable.sol";
|
|
||||||
|
|
||||||
|
|
||||||
contract MStableBridge is
|
|
||||||
IERC20Bridge,
|
|
||||||
IWallet,
|
|
||||||
DeploymentConstants
|
|
||||||
{
|
|
||||||
|
|
||||||
/// @dev Swaps specified tokens against the mStable mUSD contract
|
|
||||||
/// @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-encoded "from" token address.
|
|
||||||
/// @return success The magic bytes if successful.
|
|
||||||
// solhint-disable no-unused-vars
|
|
||||||
function bridgeTransferFrom(
|
|
||||||
address toTokenAddress,
|
|
||||||
address from,
|
|
||||||
address to,
|
|
||||||
uint256 amount,
|
|
||||||
bytes calldata bridgeData
|
|
||||||
)
|
|
||||||
external
|
|
||||||
returns (bytes4 success)
|
|
||||||
{
|
|
||||||
// Decode the bridge data to get the `fromTokenAddress`.
|
|
||||||
(address fromTokenAddress) = abi.decode(bridgeData, (address));
|
|
||||||
|
|
||||||
IMStable exchange = IMStable(_getMUsdAddress());
|
|
||||||
uint256 fromTokenBalance = IERC20Token(fromTokenAddress).balanceOf(address(this));
|
|
||||||
// Grant an allowance to the exchange to spend `fromTokenAddress` token.
|
|
||||||
LibERC20Token.approveIfBelow(fromTokenAddress, address(exchange), fromTokenBalance);
|
|
||||||
|
|
||||||
// Try to sell all of this contract's `fromTokenAddress` token balance.
|
|
||||||
uint256 boughtAmount = exchange.swap(
|
|
||||||
fromTokenAddress,
|
|
||||||
toTokenAddress,
|
|
||||||
fromTokenBalance,
|
|
||||||
to
|
|
||||||
);
|
|
||||||
|
|
||||||
emit ERC20BridgeTransfer(
|
|
||||||
fromTokenAddress,
|
|
||||||
toTokenAddress,
|
|
||||||
fromTokenBalance,
|
|
||||||
boughtAmount,
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,148 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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/DeploymentConstants.sol";
|
|
||||||
import "../interfaces/IERC20Bridge.sol";
|
|
||||||
import "../interfaces/IMooniswap.sol";
|
|
||||||
|
|
||||||
|
|
||||||
// solhint-disable space-after-comma
|
|
||||||
// solhint-disable not-rely-on-time
|
|
||||||
contract MooniswapBridge is
|
|
||||||
IERC20Bridge,
|
|
||||||
IWallet,
|
|
||||||
DeploymentConstants
|
|
||||||
{
|
|
||||||
|
|
||||||
struct TransferState {
|
|
||||||
IMooniswap pool;
|
|
||||||
uint256 fromTokenBalance;
|
|
||||||
IEtherToken weth;
|
|
||||||
uint256 boughtAmount;
|
|
||||||
address fromTokenAddress;
|
|
||||||
address toTokenAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
// solhint-disable no-empty-blocks
|
|
||||||
/// @dev Payable fallback to receive ETH from uniswap.
|
|
||||||
function ()
|
|
||||||
external
|
|
||||||
payable
|
|
||||||
{}
|
|
||||||
|
|
||||||
/// @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)
|
|
||||||
{
|
|
||||||
// State memory object to avoid stack overflows.
|
|
||||||
TransferState memory state;
|
|
||||||
// Decode the bridge data to get the `fromTokenAddress`.
|
|
||||||
address fromTokenAddress = abi.decode(bridgeData, (address));
|
|
||||||
// Get the weth contract.
|
|
||||||
state.weth = IEtherToken(_getWethAddress());
|
|
||||||
// Get our balance of `fromTokenAddress` token.
|
|
||||||
state.fromTokenBalance = IERC20Token(fromTokenAddress).balanceOf(address(this));
|
|
||||||
|
|
||||||
state.fromTokenAddress = fromTokenAddress == address(state.weth) ? address(0) : fromTokenAddress;
|
|
||||||
state.toTokenAddress = toTokenAddress == address(state.weth) ? address(0) : toTokenAddress;
|
|
||||||
state.pool = IMooniswap(
|
|
||||||
IMooniswapRegistry(_getMooniswapAddress()).pools(
|
|
||||||
state.fromTokenAddress,
|
|
||||||
state.toTokenAddress
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
// withdraw WETH to ETH
|
|
||||||
if (state.fromTokenAddress == address(0)) {
|
|
||||||
state.weth.withdraw(state.fromTokenBalance);
|
|
||||||
} else {
|
|
||||||
// Grant the pool an allowance.
|
|
||||||
LibERC20Token.approveIfBelow(
|
|
||||||
state.fromTokenAddress,
|
|
||||||
address(state.pool),
|
|
||||||
state.fromTokenBalance
|
|
||||||
);
|
|
||||||
}
|
|
||||||
uint256 ethValue = state.fromTokenAddress == address(0) ? state.fromTokenBalance : 0;
|
|
||||||
state.boughtAmount = state.pool.swap.value(ethValue)(
|
|
||||||
state.fromTokenAddress,
|
|
||||||
state.toTokenAddress,
|
|
||||||
state.fromTokenBalance,
|
|
||||||
amount,
|
|
||||||
address(0)
|
|
||||||
);
|
|
||||||
// Deposit to WETH
|
|
||||||
if (state.toTokenAddress == address(0)) {
|
|
||||||
state.weth.deposit.value(state.boughtAmount)();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transfer funds to `to`
|
|
||||||
LibERC20Token.transfer(toTokenAddress, to, state.boughtAmount);
|
|
||||||
|
|
||||||
emit ERC20BridgeTransfer(
|
|
||||||
// input token
|
|
||||||
fromTokenAddress,
|
|
||||||
// output token
|
|
||||||
toTokenAddress,
|
|
||||||
// input token amount
|
|
||||||
state.fromTokenBalance,
|
|
||||||
// output token amount
|
|
||||||
state.boughtAmount,
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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/IShell.sol";
|
|
||||||
|
|
||||||
|
|
||||||
contract ShellBridge is
|
|
||||||
IERC20Bridge,
|
|
||||||
IWallet,
|
|
||||||
DeploymentConstants
|
|
||||||
{
|
|
||||||
|
|
||||||
/// @dev Swaps specified tokens against the Shell contract
|
|
||||||
/// @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-encoded "from" token address.
|
|
||||||
/// @return success The magic bytes if successful.
|
|
||||||
// solhint-disable no-unused-vars
|
|
||||||
function bridgeTransferFrom(
|
|
||||||
address toTokenAddress,
|
|
||||||
address from,
|
|
||||||
address to,
|
|
||||||
uint256 amount,
|
|
||||||
bytes calldata bridgeData
|
|
||||||
)
|
|
||||||
external
|
|
||||||
returns (bytes4 success)
|
|
||||||
{
|
|
||||||
// Decode the bridge data to get the `fromTokenAddress` and `pool`.
|
|
||||||
(address fromTokenAddress, address pool) = abi.decode(bridgeData, (address, address));
|
|
||||||
|
|
||||||
uint256 fromTokenBalance = IERC20Token(fromTokenAddress).balanceOf(address(this));
|
|
||||||
// Grant an allowance to the exchange to spend `fromTokenAddress` token.
|
|
||||||
LibERC20Token.approveIfBelow(fromTokenAddress, pool, fromTokenBalance);
|
|
||||||
|
|
||||||
// Try to sell all of this contract's `fromTokenAddress` token balance.
|
|
||||||
uint256 boughtAmount = IShell(pool).originSwap(
|
|
||||||
fromTokenAddress,
|
|
||||||
toTokenAddress,
|
|
||||||
fromTokenBalance,
|
|
||||||
amount, // min amount
|
|
||||||
block.timestamp + 1
|
|
||||||
);
|
|
||||||
LibERC20Token.transfer(toTokenAddress, to, boughtAmount);
|
|
||||||
|
|
||||||
emit ERC20BridgeTransfer(
|
|
||||||
fromTokenAddress,
|
|
||||||
toTokenAddress,
|
|
||||||
fromTokenBalance,
|
|
||||||
boughtAmount,
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,119 +0,0 @@
|
|||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
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";
|
|
||||||
|
|
||||||
|
|
||||||
// solhint-disable not-rely-on-time
|
|
||||||
// solhint-disable space-after-comma
|
|
||||||
contract SnowSwapBridge is
|
|
||||||
IERC20Bridge,
|
|
||||||
IWallet,
|
|
||||||
DeploymentConstants
|
|
||||||
{
|
|
||||||
struct SnowSwapBridgeData {
|
|
||||||
address curveAddress;
|
|
||||||
bytes4 exchangeFunctionSelector;
|
|
||||||
address fromTokenAddress;
|
|
||||||
int128 fromCoinIdx;
|
|
||||||
int128 toCoinIdx;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @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
|
|
||||||
returns (bytes4 success)
|
|
||||||
{
|
|
||||||
// Decode the bridge data to get the SnowSwap metadata.
|
|
||||||
SnowSwapBridgeData memory data = abi.decode(bridgeData, (SnowSwapBridgeData));
|
|
||||||
|
|
||||||
require(toTokenAddress != data.fromTokenAddress, "SnowSwapBridge/INVALID_PAIR");
|
|
||||||
uint256 fromTokenBalance = IERC20Token(data.fromTokenAddress).balanceOf(address(this));
|
|
||||||
// Grant an allowance to the exchange to spend `fromTokenAddress` token.
|
|
||||||
LibERC20Token.approveIfBelow(data.fromTokenAddress, data.curveAddress, fromTokenBalance);
|
|
||||||
|
|
||||||
// Try to sell all of this contract's `fromTokenAddress` token balance.
|
|
||||||
{
|
|
||||||
(bool didSucceed, bytes memory resultData) =
|
|
||||||
data.curveAddress.call(abi.encodeWithSelector(
|
|
||||||
data.exchangeFunctionSelector,
|
|
||||||
data.fromCoinIdx,
|
|
||||||
data.toCoinIdx,
|
|
||||||
// dx
|
|
||||||
fromTokenBalance,
|
|
||||||
// min dy
|
|
||||||
amount
|
|
||||||
));
|
|
||||||
if (!didSucceed) {
|
|
||||||
assembly { revert(add(resultData, 32), mload(resultData)) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint256 toTokenBalance = IERC20Token(toTokenAddress).balanceOf(address(this));
|
|
||||||
// Transfer the converted `toToken`s to `to`.
|
|
||||||
LibERC20Token.transfer(toTokenAddress, to, toTokenBalance);
|
|
||||||
|
|
||||||
emit ERC20BridgeTransfer(
|
|
||||||
data.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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,136 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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 SushiSwapBridge is
|
|
||||||
IERC20Bridge,
|
|
||||||
IWallet,
|
|
||||||
DeploymentConstants
|
|
||||||
{
|
|
||||||
struct TransferState {
|
|
||||||
address[] path;
|
|
||||||
address router;
|
|
||||||
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, state.router) = abi.decode(bridgeData, (address[], address));
|
|
||||||
// solhint-enable indent
|
|
||||||
|
|
||||||
require(state.path.length >= 2, "SushiSwapBridge/PATH_LENGTH_MUST_BE_AT_LEAST_TWO");
|
|
||||||
require(state.path[state.path.length - 1] == toTokenAddress, "SushiSwapBridge/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 SushiSwap router an allowance.
|
|
||||||
LibERC20Token.approveIfBelow(
|
|
||||||
state.path[0],
|
|
||||||
state.router,
|
|
||||||
state.fromTokenBalance
|
|
||||||
);
|
|
||||||
|
|
||||||
// Buy as much `toTokenAddress` token with `fromTokenAddress` token
|
|
||||||
// and transfer it to `to`.
|
|
||||||
IUniswapV2Router01 router = IUniswapV2Router01(state.router);
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,119 +0,0 @@
|
|||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
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";
|
|
||||||
|
|
||||||
|
|
||||||
// solhint-disable not-rely-on-time
|
|
||||||
// solhint-disable space-after-comma
|
|
||||||
contract SwerveBridge is
|
|
||||||
IERC20Bridge,
|
|
||||||
IWallet,
|
|
||||||
DeploymentConstants
|
|
||||||
{
|
|
||||||
struct SwerveBridgeData {
|
|
||||||
address curveAddress;
|
|
||||||
bytes4 exchangeFunctionSelector;
|
|
||||||
address fromTokenAddress;
|
|
||||||
int128 fromCoinIdx;
|
|
||||||
int128 toCoinIdx;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @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
|
|
||||||
returns (bytes4 success)
|
|
||||||
{
|
|
||||||
// Decode the bridge data to get the SwerveBridgeData metadata.
|
|
||||||
SwerveBridgeData memory data = abi.decode(bridgeData, (SwerveBridgeData));
|
|
||||||
|
|
||||||
require(toTokenAddress != data.fromTokenAddress, "SwerveBridge/INVALID_PAIR");
|
|
||||||
uint256 fromTokenBalance = IERC20Token(data.fromTokenAddress).balanceOf(address(this));
|
|
||||||
// Grant an allowance to the exchange to spend `fromTokenAddress` token.
|
|
||||||
LibERC20Token.approveIfBelow(data.fromTokenAddress, data.curveAddress, fromTokenBalance);
|
|
||||||
|
|
||||||
// Try to sell all of this contract's `fromTokenAddress` token balance.
|
|
||||||
{
|
|
||||||
(bool didSucceed, bytes memory resultData) =
|
|
||||||
data.curveAddress.call(abi.encodeWithSelector(
|
|
||||||
data.exchangeFunctionSelector,
|
|
||||||
data.fromCoinIdx,
|
|
||||||
data.toCoinIdx,
|
|
||||||
// dx
|
|
||||||
fromTokenBalance,
|
|
||||||
// min dy
|
|
||||||
amount
|
|
||||||
));
|
|
||||||
if (!didSucceed) {
|
|
||||||
assembly { revert(add(resultData, 32), mload(resultData)) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint256 toTokenBalance = IERC20Token(toTokenAddress).balanceOf(address(this));
|
|
||||||
// Transfer the converted `toToken`s to `to`.
|
|
||||||
LibERC20Token.transfer(toTokenAddress, to, toTokenBalance);
|
|
||||||
|
|
||||||
emit ERC20BridgeTransfer(
|
|
||||||
data.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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,220 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright 2019 ZeroEx Intl.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
pragma solidity ^0.5.9;
|
|
||||||
pragma experimental ABIEncoderV2;
|
|
||||||
|
|
||||||
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
|
||||||
import "@0x/contracts-erc20/contracts/src/interfaces/IEtherToken.sol";
|
|
||||||
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
|
|
||||||
import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol";
|
|
||||||
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
|
|
||||||
import "../interfaces/IUniswapExchangeFactory.sol";
|
|
||||||
import "../interfaces/IUniswapExchange.sol";
|
|
||||||
import "../interfaces/IERC20Bridge.sol";
|
|
||||||
|
|
||||||
|
|
||||||
// solhint-disable space-after-comma
|
|
||||||
// solhint-disable not-rely-on-time
|
|
||||||
contract UniswapBridge is
|
|
||||||
IERC20Bridge,
|
|
||||||
IWallet,
|
|
||||||
DeploymentConstants
|
|
||||||
{
|
|
||||||
// Struct to hold `bridgeTransferFrom()` local variables in memory and to avoid
|
|
||||||
// stack overflows.
|
|
||||||
struct TransferState {
|
|
||||||
IUniswapExchange exchange;
|
|
||||||
uint256 fromTokenBalance;
|
|
||||||
IEtherToken weth;
|
|
||||||
uint256 boughtAmount;
|
|
||||||
}
|
|
||||||
|
|
||||||
// solhint-disable no-empty-blocks
|
|
||||||
/// @dev Payable fallback to receive ETH from uniswap.
|
|
||||||
function ()
|
|
||||||
external
|
|
||||||
payable
|
|
||||||
{}
|
|
||||||
|
|
||||||
/// @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 "from" token address.
|
|
||||||
/// @return success The magic bytes if successful.
|
|
||||||
function bridgeTransferFrom(
|
|
||||||
address toTokenAddress,
|
|
||||||
address from,
|
|
||||||
address to,
|
|
||||||
uint256 amount,
|
|
||||||
bytes calldata bridgeData
|
|
||||||
)
|
|
||||||
external
|
|
||||||
returns (bytes4 success)
|
|
||||||
{
|
|
||||||
// State memory object to avoid stack overflows.
|
|
||||||
TransferState memory state;
|
|
||||||
// Decode the bridge data to get the `fromTokenAddress`.
|
|
||||||
(address fromTokenAddress) = abi.decode(bridgeData, (address));
|
|
||||||
|
|
||||||
// Just transfer the tokens if they're the same.
|
|
||||||
if (fromTokenAddress == toTokenAddress) {
|
|
||||||
LibERC20Token.transfer(fromTokenAddress, to, amount);
|
|
||||||
return BRIDGE_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the exchange for the token pair.
|
|
||||||
state.exchange = _getUniswapExchangeForTokenPair(
|
|
||||||
fromTokenAddress,
|
|
||||||
toTokenAddress
|
|
||||||
);
|
|
||||||
// Get our balance of `fromTokenAddress` token.
|
|
||||||
state.fromTokenBalance = IERC20Token(fromTokenAddress).balanceOf(address(this));
|
|
||||||
// Get the weth contract.
|
|
||||||
state.weth = IEtherToken(_getWethAddress());
|
|
||||||
|
|
||||||
// Convert from WETH to a token.
|
|
||||||
if (fromTokenAddress == address(state.weth)) {
|
|
||||||
// Unwrap the WETH.
|
|
||||||
state.weth.withdraw(state.fromTokenBalance);
|
|
||||||
// Buy as much of `toTokenAddress` token with ETH as possible and
|
|
||||||
// transfer it to `to`.
|
|
||||||
state.boughtAmount = state.exchange.ethToTokenTransferInput.value(state.fromTokenBalance)(
|
|
||||||
// Minimum buy amount.
|
|
||||||
amount,
|
|
||||||
// Expires after this block.
|
|
||||||
block.timestamp,
|
|
||||||
// Recipient is `to`.
|
|
||||||
to
|
|
||||||
);
|
|
||||||
|
|
||||||
// Convert from a token to WETH.
|
|
||||||
} else if (toTokenAddress == address(state.weth)) {
|
|
||||||
// Grant the exchange an allowance.
|
|
||||||
_grantExchangeAllowance(state.exchange, fromTokenAddress, state.fromTokenBalance);
|
|
||||||
// Buy as much ETH with `fromTokenAddress` token as possible.
|
|
||||||
state.boughtAmount = state.exchange.tokenToEthSwapInput(
|
|
||||||
// Sell all tokens we hold.
|
|
||||||
state.fromTokenBalance,
|
|
||||||
// Minimum buy amount.
|
|
||||||
amount,
|
|
||||||
// Expires after this block.
|
|
||||||
block.timestamp
|
|
||||||
);
|
|
||||||
// Wrap the ETH.
|
|
||||||
state.weth.deposit.value(state.boughtAmount)();
|
|
||||||
// Transfer the WETH to `to`.
|
|
||||||
IEtherToken(toTokenAddress).transfer(to, state.boughtAmount);
|
|
||||||
|
|
||||||
// Convert from one token to another.
|
|
||||||
} else {
|
|
||||||
// Grant the exchange an allowance.
|
|
||||||
_grantExchangeAllowance(state.exchange, fromTokenAddress, state.fromTokenBalance);
|
|
||||||
// Buy as much `toTokenAddress` token with `fromTokenAddress` token
|
|
||||||
// and transfer it to `to`.
|
|
||||||
state.boughtAmount = state.exchange.tokenToTokenTransferInput(
|
|
||||||
// Sell all tokens we hold.
|
|
||||||
state.fromTokenBalance,
|
|
||||||
// Minimum buy amount.
|
|
||||||
amount,
|
|
||||||
// Must buy at least 1 intermediate ETH.
|
|
||||||
1,
|
|
||||||
// Expires after this block.
|
|
||||||
block.timestamp,
|
|
||||||
// Recipient is `to`.
|
|
||||||
to,
|
|
||||||
// Convert to `toTokenAddress`.
|
|
||||||
toTokenAddress
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
emit ERC20BridgeTransfer(
|
|
||||||
fromTokenAddress,
|
|
||||||
toTokenAddress,
|
|
||||||
state.fromTokenBalance,
|
|
||||||
state.boughtAmount,
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Grants an unlimited allowance to the exchange for its token
|
|
||||||
/// on behalf of this contract.
|
|
||||||
/// @param exchange The Uniswap token exchange.
|
|
||||||
/// @param tokenAddress The token address for the exchange.
|
|
||||||
/// @param minimumAllowance The minimum necessary allowance.
|
|
||||||
function _grantExchangeAllowance(
|
|
||||||
IUniswapExchange exchange,
|
|
||||||
address tokenAddress,
|
|
||||||
uint256 minimumAllowance
|
|
||||||
)
|
|
||||||
private
|
|
||||||
{
|
|
||||||
LibERC20Token.approveIfBelow(
|
|
||||||
tokenAddress,
|
|
||||||
address(exchange),
|
|
||||||
minimumAllowance
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Retrieves the uniswap exchange for a given token pair.
|
|
||||||
/// In the case of a WETH-token exchange, this will be the non-WETH token.
|
|
||||||
/// In th ecase of a token-token exchange, this will be the first token.
|
|
||||||
/// @param fromTokenAddress The address of the token we are converting from.
|
|
||||||
/// @param toTokenAddress The address of the token we are converting to.
|
|
||||||
/// @return exchange The uniswap exchange.
|
|
||||||
function _getUniswapExchangeForTokenPair(
|
|
||||||
address fromTokenAddress,
|
|
||||||
address toTokenAddress
|
|
||||||
)
|
|
||||||
private
|
|
||||||
view
|
|
||||||
returns (IUniswapExchange exchange)
|
|
||||||
{
|
|
||||||
address exchangeTokenAddress = fromTokenAddress;
|
|
||||||
// Whichever isn't WETH is the exchange token.
|
|
||||||
if (fromTokenAddress == _getWethAddress()) {
|
|
||||||
exchangeTokenAddress = toTokenAddress;
|
|
||||||
}
|
|
||||||
exchange = IUniswapExchange(
|
|
||||||
IUniswapExchangeFactory(_getUniswapExchangeFactoryAddress())
|
|
||||||
.getExchange(exchangeTokenAddress)
|
|
||||||
);
|
|
||||||
require(address(exchange) != address(0), "NO_UNISWAP_EXCHANGE_FOR_TOKEN");
|
|
||||||
return exchange;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,135 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
// solhint-disable
|
|
||||||
pragma solidity ^0.5.9;
|
|
||||||
pragma experimental ABIEncoderV2;
|
|
||||||
|
|
||||||
|
|
||||||
// @dev Interface of the asset proxy's assetData.
|
|
||||||
// The asset proxies take an ABI encoded `bytes assetData` as argument.
|
|
||||||
// This argument is ABI encoded as one of the methods of this interface.
|
|
||||||
interface IAssetData {
|
|
||||||
|
|
||||||
/// @dev Function signature for encoding ERC20 assetData.
|
|
||||||
/// @param tokenAddress Address of ERC20Token contract.
|
|
||||||
function ERC20Token(address tokenAddress)
|
|
||||||
external;
|
|
||||||
|
|
||||||
/// @dev Function signature for encoding ERC721 assetData.
|
|
||||||
/// @param tokenAddress Address of ERC721 token contract.
|
|
||||||
/// @param tokenId Id of ERC721 token to be transferred.
|
|
||||||
function ERC721Token(
|
|
||||||
address tokenAddress,
|
|
||||||
uint256 tokenId
|
|
||||||
)
|
|
||||||
external;
|
|
||||||
|
|
||||||
/// @dev Function signature for encoding ERC1155 assetData.
|
|
||||||
/// @param tokenAddress Address of ERC1155 token contract.
|
|
||||||
/// @param tokenIds Array of ids of tokens to be transferred.
|
|
||||||
/// @param values Array of values that correspond to each token id to be transferred.
|
|
||||||
/// Note that each value will be multiplied by the amount being filled in the order before transferring.
|
|
||||||
/// @param callbackData Extra data to be passed to receiver's `onERC1155Received` callback function.
|
|
||||||
function ERC1155Assets(
|
|
||||||
address tokenAddress,
|
|
||||||
uint256[] calldata tokenIds,
|
|
||||||
uint256[] calldata values,
|
|
||||||
bytes calldata callbackData
|
|
||||||
)
|
|
||||||
external;
|
|
||||||
|
|
||||||
/// @dev Function signature for encoding MultiAsset assetData.
|
|
||||||
/// @param values Array of amounts that correspond to each asset to be transferred.
|
|
||||||
/// Note that each value will be multiplied by the amount being filled in the order before transferring.
|
|
||||||
/// @param nestedAssetData Array of assetData fields that will be be dispatched to their correspnding AssetProxy contract.
|
|
||||||
function MultiAsset(
|
|
||||||
uint256[] calldata values,
|
|
||||||
bytes[] calldata nestedAssetData
|
|
||||||
)
|
|
||||||
external;
|
|
||||||
|
|
||||||
/// @dev Function signature for encoding StaticCall assetData.
|
|
||||||
/// @param staticCallTargetAddress Address that will execute the staticcall.
|
|
||||||
/// @param staticCallData Data that will be executed via staticcall on the staticCallTargetAddress.
|
|
||||||
/// @param expectedReturnDataHash Keccak-256 hash of the expected staticcall return data.
|
|
||||||
function StaticCall(
|
|
||||||
address staticCallTargetAddress,
|
|
||||||
bytes calldata staticCallData,
|
|
||||||
bytes32 expectedReturnDataHash
|
|
||||||
)
|
|
||||||
external;
|
|
||||||
|
|
||||||
/// @dev Function signature for encoding ERC20Bridge assetData.
|
|
||||||
/// @param tokenAddress Address of token to transfer.
|
|
||||||
/// @param bridgeAddress Address of the bridge contract.
|
|
||||||
/// @param bridgeData Arbitrary data to be passed to the bridge contract.
|
|
||||||
function ERC20Bridge(
|
|
||||||
address tokenAddress,
|
|
||||||
address bridgeAddress,
|
|
||||||
bytes calldata bridgeData
|
|
||||||
)
|
|
||||||
external;
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
|
|
||||||
contract IAssetProxy {
|
|
||||||
|
|
||||||
/// @dev Transfers assets. Either succeeds or throws.
|
|
||||||
/// @param assetData Byte array encoded for the respective asset proxy.
|
|
||||||
/// @param from Address to transfer asset from.
|
|
||||||
/// @param to Address to transfer asset to.
|
|
||||||
/// @param amount Amount of asset to transfer.
|
|
||||||
function transferFrom(
|
|
||||||
bytes calldata assetData,
|
|
||||||
address from,
|
|
||||||
address to,
|
|
||||||
uint256 amount
|
|
||||||
)
|
|
||||||
external;
|
|
||||||
|
|
||||||
/// @dev Gets the proxy id associated with the proxy address.
|
|
||||||
/// @return Proxy id.
|
|
||||||
function getProxyId()
|
|
||||||
external
|
|
||||||
pure
|
|
||||||
returns (bytes4);
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
|
|
||||||
contract IAssetProxyDispatcher {
|
|
||||||
|
|
||||||
// Logs registration of new asset proxy
|
|
||||||
event AssetProxyRegistered(
|
|
||||||
bytes4 id, // Id of new registered AssetProxy.
|
|
||||||
address assetProxy // Address of new registered AssetProxy.
|
|
||||||
);
|
|
||||||
|
|
||||||
/// @dev Registers an asset proxy to its asset proxy id.
|
|
||||||
/// Once an asset proxy is registered, it cannot be unregistered.
|
|
||||||
/// @param assetProxy Address of new asset proxy to register.
|
|
||||||
function registerAssetProxy(address assetProxy)
|
|
||||||
external;
|
|
||||||
|
|
||||||
/// @dev Gets an asset proxy.
|
|
||||||
/// @param assetProxyId Id of the asset proxy.
|
|
||||||
/// @return The asset proxy registered to assetProxyId. Returns 0x0 if no proxy is registered.
|
|
||||||
function getAssetProxy(bytes4 assetProxyId)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (address);
|
|
||||||
}
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
import "@0x/contracts-utils/contracts/src/interfaces/IOwnable.sol";
|
|
||||||
|
|
||||||
|
|
||||||
contract IAuthorizable is
|
|
||||||
IOwnable
|
|
||||||
{
|
|
||||||
// Event logged when a new address is authorized.
|
|
||||||
event AuthorizedAddressAdded(
|
|
||||||
address indexed target,
|
|
||||||
address indexed caller
|
|
||||||
);
|
|
||||||
|
|
||||||
// Event logged when a currently authorized address is unauthorized.
|
|
||||||
event AuthorizedAddressRemoved(
|
|
||||||
address indexed target,
|
|
||||||
address indexed caller
|
|
||||||
);
|
|
||||||
|
|
||||||
/// @dev Authorizes an address.
|
|
||||||
/// @param target Address to authorize.
|
|
||||||
function addAuthorizedAddress(address target)
|
|
||||||
external;
|
|
||||||
|
|
||||||
/// @dev Removes authorizion of an address.
|
|
||||||
/// @param target Address to remove authorization from.
|
|
||||||
function removeAuthorizedAddress(address target)
|
|
||||||
external;
|
|
||||||
|
|
||||||
/// @dev Removes authorizion of an address.
|
|
||||||
/// @param target Address to remove authorization from.
|
|
||||||
/// @param index Index of target in authorities array.
|
|
||||||
function removeAuthorizedAddressAtIndex(
|
|
||||||
address target,
|
|
||||||
uint256 index
|
|
||||||
)
|
|
||||||
external;
|
|
||||||
|
|
||||||
/// @dev Gets all authorized addresses.
|
|
||||||
/// @return Array of authorized addresses.
|
|
||||||
function getAuthorizedAddresses()
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (address[] memory);
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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 IBalancerPool {
|
|
||||||
/// @dev Sell `tokenAmountIn` of `tokenIn` and receive `tokenOut`.
|
|
||||||
/// @param tokenIn The token being sold
|
|
||||||
/// @param tokenAmountIn The amount of `tokenIn` to sell.
|
|
||||||
/// @param tokenOut The token being bought.
|
|
||||||
/// @param minAmountOut The minimum amount of `tokenOut` to buy.
|
|
||||||
/// @param maxPrice The maximum value for `spotPriceAfter`.
|
|
||||||
/// @return tokenAmountOut The amount of `tokenOut` bought.
|
|
||||||
/// @return spotPriceAfter The new marginal spot price of the given
|
|
||||||
/// token pair for this pool.
|
|
||||||
function swapExactAmountIn(
|
|
||||||
address tokenIn,
|
|
||||||
uint tokenAmountIn,
|
|
||||||
address tokenOut,
|
|
||||||
uint minAmountOut,
|
|
||||||
uint maxPrice
|
|
||||||
) external returns (uint tokenAmountOut, uint spotPriceAfter);
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
|
|
||||||
contract IContractRegistry {
|
|
||||||
function addressOf(
|
|
||||||
bytes32 contractName
|
|
||||||
) external returns(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
contract IBancorNetwork {
|
|
||||||
function convertByPath(
|
|
||||||
address[] calldata _path,
|
|
||||||
uint256 _amount,
|
|
||||||
uint256 _minReturn,
|
|
||||||
address _beneficiary,
|
|
||||||
address _affiliateAccount,
|
|
||||||
uint256 _affiliateFee
|
|
||||||
) external payable returns (uint256);
|
|
||||||
}
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
|
||||||
|
|
||||||
|
|
||||||
contract PotLike {
|
|
||||||
function chi() external returns (uint256);
|
|
||||||
function rho() external returns (uint256);
|
|
||||||
function drip() external returns (uint256);
|
|
||||||
function join(uint256) external;
|
|
||||||
function exit(uint256) external;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// The actual Chai contract can be found here: https://github.com/dapphub/chai
|
|
||||||
contract IChai is
|
|
||||||
IERC20Token
|
|
||||||
{
|
|
||||||
/// @dev Withdraws Dai owned by `src`
|
|
||||||
/// @param src Address that owns Dai.
|
|
||||||
/// @param wad Amount of Dai to withdraw.
|
|
||||||
function draw(
|
|
||||||
address src,
|
|
||||||
uint256 wad
|
|
||||||
)
|
|
||||||
external;
|
|
||||||
|
|
||||||
/// @dev Queries Dai balance of Chai holder.
|
|
||||||
/// @param usr Address of Chai holder.
|
|
||||||
/// @return Dai balance.
|
|
||||||
function dai(address usr)
|
|
||||||
external
|
|
||||||
returns (uint256);
|
|
||||||
|
|
||||||
/// @dev Queries the Pot contract used by the Chai contract.
|
|
||||||
function pot()
|
|
||||||
external
|
|
||||||
returns (PotLike);
|
|
||||||
|
|
||||||
/// @dev Deposits Dai in exchange for Chai
|
|
||||||
/// @param dst Address to receive Chai.
|
|
||||||
/// @param wad Amount of Dai to deposit.
|
|
||||||
function join(
|
|
||||||
address dst,
|
|
||||||
uint256 wad
|
|
||||||
)
|
|
||||||
external;
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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 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);
|
|
||||||
}
|
|
||||||
@@ -1,192 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
|
|
||||||
interface IDydx {
|
|
||||||
|
|
||||||
/// @dev Represents the unique key that specifies an account
|
|
||||||
struct AccountInfo {
|
|
||||||
address owner; // The address that owns the account
|
|
||||||
uint256 number; // A nonce that allows a single address to control many accounts
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ActionType {
|
|
||||||
Deposit, // supply tokens
|
|
||||||
Withdraw, // borrow tokens
|
|
||||||
Transfer, // transfer balance between accounts
|
|
||||||
Buy, // buy an amount of some token (externally)
|
|
||||||
Sell, // sell an amount of some token (externally)
|
|
||||||
Trade, // trade tokens against another account
|
|
||||||
Liquidate, // liquidate an undercollateralized or expiring account
|
|
||||||
Vaporize, // use excess tokens to zero-out a completely negative account
|
|
||||||
Call // send arbitrary data to an address
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Arguments that are passed to Solo in an ordered list as part of a single operation.
|
|
||||||
/// Each ActionArgs has an actionType which specifies which action struct that this data will be
|
|
||||||
/// parsed into before being processed.
|
|
||||||
struct ActionArgs {
|
|
||||||
ActionType actionType;
|
|
||||||
uint256 accountIdx;
|
|
||||||
AssetAmount amount;
|
|
||||||
uint256 primaryMarketId;
|
|
||||||
uint256 secondaryMarketId;
|
|
||||||
address otherAddress;
|
|
||||||
uint256 otherAccountIdx;
|
|
||||||
bytes data;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum AssetDenomination {
|
|
||||||
Wei, // the amount is denominated in wei
|
|
||||||
Par // the amount is denominated in par
|
|
||||||
}
|
|
||||||
|
|
||||||
enum AssetReference {
|
|
||||||
Delta, // the amount is given as a delta from the current value
|
|
||||||
Target // the amount is given as an exact number to end up at
|
|
||||||
}
|
|
||||||
|
|
||||||
struct AssetAmount {
|
|
||||||
bool sign; // true if positive
|
|
||||||
AssetDenomination denomination;
|
|
||||||
AssetReference ref;
|
|
||||||
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.
|
|
||||||
/// One call to operate() is considered a singular "operation". Account collateralization is
|
|
||||||
/// ensured only after the completion of the entire operation.
|
|
||||||
/// @param accounts A list of all accounts that will be used in this operation. Cannot contain
|
|
||||||
/// duplicates. In each action, the relevant account will be referred-to by its
|
|
||||||
/// index in the list.
|
|
||||||
/// @param actions An ordered list of all actions that will be taken in this operation. The
|
|
||||||
/// actions will be processed in order.
|
|
||||||
function operate(
|
|
||||||
AccountInfo[] calldata accounts,
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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 IDydxBridge {
|
|
||||||
|
|
||||||
/// @dev This is the subset of `IDydx.ActionType` that are supported by the bridge.
|
|
||||||
enum BridgeActionType {
|
|
||||||
Deposit, // Deposit tokens into dydx account.
|
|
||||||
Withdraw // Withdraw tokens from dydx account.
|
|
||||||
}
|
|
||||||
|
|
||||||
struct BridgeAction {
|
|
||||||
BridgeActionType actionType; // Action to run on dydx account.
|
|
||||||
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).
|
|
||||||
}
|
|
||||||
|
|
||||||
struct BridgeData {
|
|
||||||
uint256[] accountNumbers; // Account number used to identify the owner's specific account.
|
|
||||||
BridgeAction[] actions; // Actions to carry out on the owner's accounts.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
|
|
||||||
contract IERC20Bridge {
|
|
||||||
|
|
||||||
/// @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 `0xdc1600f3` if successful.
|
|
||||||
function bridgeTransferFrom(
|
|
||||||
address tokenAddress,
|
|
||||||
address from,
|
|
||||||
address to,
|
|
||||||
uint256 amount,
|
|
||||||
bytes calldata bridgeData
|
|
||||||
)
|
|
||||||
external
|
|
||||||
returns (bytes4 success);
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright 2019 ZeroEx Intl.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
pragma solidity ^0.5.9;
|
|
||||||
|
|
||||||
|
|
||||||
interface IEth2Dai {
|
|
||||||
|
|
||||||
/// @dev Sell `sellAmount` of `fromToken` token and receive `toToken` token.
|
|
||||||
/// @param fromToken The token being sold.
|
|
||||||
/// @param sellAmount The amount of `fromToken` token being sold.
|
|
||||||
/// @param toToken The token being bought.
|
|
||||||
/// @param minFillAmount Minimum amount of `toToken` token to buy.
|
|
||||||
/// @return fillAmount Amount of `toToken` bought.
|
|
||||||
function sellAllAmount(
|
|
||||||
address fromToken,
|
|
||||||
uint256 sellAmount,
|
|
||||||
address toToken,
|
|
||||||
uint256 minFillAmount
|
|
||||||
)
|
|
||||||
external
|
|
||||||
returns (uint256 fillAmount);
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright 2019 ZeroEx Intl.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
pragma solidity ^0.5.9;
|
|
||||||
|
|
||||||
|
|
||||||
interface IKyberNetworkProxy {
|
|
||||||
|
|
||||||
/// @dev Sells `sellTokenAddress` tokens for `buyTokenAddress` tokens.
|
|
||||||
/// @param sellTokenAddress Token to sell.
|
|
||||||
/// @param sellAmount Amount of tokens to sell.
|
|
||||||
/// @param buyTokenAddress Token to buy.
|
|
||||||
/// @param recipientAddress Address to send bought tokens to.
|
|
||||||
/// @param maxBuyTokenAmount A limit on the amount of tokens to buy.
|
|
||||||
/// @param minConversionRate The minimal conversion rate. If actual rate
|
|
||||||
/// is lower, trade is canceled.
|
|
||||||
/// @param walletId The wallet ID to send part of the fees
|
|
||||||
/// @return boughtAmount Amount of tokens bought.
|
|
||||||
function trade(
|
|
||||||
address sellTokenAddress,
|
|
||||||
uint256 sellAmount,
|
|
||||||
address buyTokenAddress,
|
|
||||||
address payable recipientAddress,
|
|
||||||
uint256 maxBuyTokenAmount,
|
|
||||||
uint256 minConversionRate,
|
|
||||||
address walletId
|
|
||||||
)
|
|
||||||
external
|
|
||||||
payable
|
|
||||||
returns (uint256 boughtAmount);
|
|
||||||
|
|
||||||
/// @dev Sells `sellTokenAddress` tokens for `buyTokenAddress` tokens
|
|
||||||
/// using a hint for the reserve.
|
|
||||||
/// @param sellTokenAddress Token to sell.
|
|
||||||
/// @param sellAmount Amount of tokens to sell.
|
|
||||||
/// @param buyTokenAddress Token to buy.
|
|
||||||
/// @param recipientAddress Address to send bought tokens to.
|
|
||||||
/// @param maxBuyTokenAmount A limit on the amount of tokens to buy.
|
|
||||||
/// @param minConversionRate The minimal conversion rate. If actual rate
|
|
||||||
/// is lower, trade is canceled.
|
|
||||||
/// @param walletId The wallet ID to send part of the fees
|
|
||||||
/// @param hint The hint for the selective inclusion (or exclusion) of reserves
|
|
||||||
/// @return boughtAmount Amount of tokens bought.
|
|
||||||
function tradeWithHint(
|
|
||||||
address sellTokenAddress,
|
|
||||||
uint256 sellAmount,
|
|
||||||
address buyTokenAddress,
|
|
||||||
address payable recipientAddress,
|
|
||||||
uint256 maxBuyTokenAmount,
|
|
||||||
uint256 minConversionRate,
|
|
||||||
address payable walletId,
|
|
||||||
bytes calldata hint
|
|
||||||
)
|
|
||||||
external
|
|
||||||
payable
|
|
||||||
returns (uint256 boughtAmount);
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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 IMStable {
|
|
||||||
|
|
||||||
function swap(
|
|
||||||
address _input,
|
|
||||||
address _output,
|
|
||||||
uint256 _quantity,
|
|
||||||
address _recipient
|
|
||||||
)
|
|
||||||
external
|
|
||||||
returns (uint256 output);
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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 IUniswapExchange {
|
|
||||||
|
|
||||||
/// @dev Buys at least `minTokensBought` tokens with ETH and transfer them
|
|
||||||
/// to `recipient`.
|
|
||||||
/// @param minTokensBought The minimum number of tokens to buy.
|
|
||||||
/// @param deadline Time when this order expires.
|
|
||||||
/// @param recipient Who to transfer the tokens to.
|
|
||||||
/// @return tokensBought Amount of tokens bought.
|
|
||||||
function ethToTokenTransferInput(
|
|
||||||
uint256 minTokensBought,
|
|
||||||
uint256 deadline,
|
|
||||||
address recipient
|
|
||||||
)
|
|
||||||
external
|
|
||||||
payable
|
|
||||||
returns (uint256 tokensBought);
|
|
||||||
|
|
||||||
/// @dev Buys at least `minEthBought` ETH with tokens.
|
|
||||||
/// @param tokensSold Amount of tokens to sell.
|
|
||||||
/// @param minEthBought The minimum amount of ETH to buy.
|
|
||||||
/// @param deadline Time when this order expires.
|
|
||||||
/// @return ethBought Amount of tokens bought.
|
|
||||||
function tokenToEthSwapInput(
|
|
||||||
uint256 tokensSold,
|
|
||||||
uint256 minEthBought,
|
|
||||||
uint256 deadline
|
|
||||||
)
|
|
||||||
external
|
|
||||||
returns (uint256 ethBought);
|
|
||||||
|
|
||||||
/// @dev Buys at least `minTokensBought` tokens with the exchange token
|
|
||||||
/// and transfer them to `recipient`.
|
|
||||||
/// @param minTokensBought The minimum number of tokens to buy.
|
|
||||||
/// @param minEthBought The minimum amount of intermediate ETH to buy.
|
|
||||||
/// @param deadline Time when this order expires.
|
|
||||||
/// @param recipient Who to transfer the tokens to.
|
|
||||||
/// @param toTokenAddress The token being bought.
|
|
||||||
/// @return tokensBought Amount of tokens bought.
|
|
||||||
function tokenToTokenTransferInput(
|
|
||||||
uint256 tokensSold,
|
|
||||||
uint256 minTokensBought,
|
|
||||||
uint256 minEthBought,
|
|
||||||
uint256 deadline,
|
|
||||||
address recipient,
|
|
||||||
address toTokenAddress
|
|
||||||
)
|
|
||||||
external
|
|
||||||
returns (uint256 tokensBought);
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
import "./IUniswapExchange.sol";
|
|
||||||
|
|
||||||
|
|
||||||
interface IUniswapExchangeFactory {
|
|
||||||
|
|
||||||
/// @dev Get the exchange for a token.
|
|
||||||
/// @param tokenAddress The address of the token contract.
|
|
||||||
function getExchange(address tokenAddress)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (address);
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
@@ -1,247 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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/BancorBridge.sol";
|
|
||||||
import "../src/interfaces/IBancorNetwork.sol";
|
|
||||||
|
|
||||||
|
|
||||||
contract TestEventsRaiser {
|
|
||||||
|
|
||||||
event TokenTransfer(
|
|
||||||
address token,
|
|
||||||
address from,
|
|
||||||
address to,
|
|
||||||
uint256 amount
|
|
||||||
);
|
|
||||||
|
|
||||||
event TokenApprove(
|
|
||||||
address spender,
|
|
||||||
uint256 allowance
|
|
||||||
);
|
|
||||||
|
|
||||||
event ConvertByPathInput(
|
|
||||||
uint amountIn,
|
|
||||||
uint amountOutMin,
|
|
||||||
address toTokenAddress,
|
|
||||||
address to,
|
|
||||||
address feeRecipient,
|
|
||||||
uint256 feeAmount
|
|
||||||
);
|
|
||||||
|
|
||||||
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 raiseConvertByPathInput(
|
|
||||||
uint amountIn,
|
|
||||||
uint amountOutMin,
|
|
||||||
address toTokenAddress,
|
|
||||||
address to,
|
|
||||||
address feeRecipient,
|
|
||||||
uint256 feeAmount
|
|
||||||
) external
|
|
||||||
{
|
|
||||||
emit ConvertByPathInput(
|
|
||||||
amountIn,
|
|
||||||
amountOutMin,
|
|
||||||
toTokenAddress,
|
|
||||||
to,
|
|
||||||
feeRecipient,
|
|
||||||
feeAmount
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// @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 BancorNetwork contract
|
|
||||||
contract TestBancorNetwork is
|
|
||||||
IBancorNetwork
|
|
||||||
{
|
|
||||||
string private _nextRevertReason;
|
|
||||||
|
|
||||||
/// @dev Set the revert reason for `swapExactTokensForTokens`.
|
|
||||||
function setRevertReason(string calldata reason)
|
|
||||||
external
|
|
||||||
{
|
|
||||||
_nextRevertReason = reason;
|
|
||||||
}
|
|
||||||
|
|
||||||
function convertByPath(
|
|
||||||
address[] calldata _path,
|
|
||||||
uint256 _amount,
|
|
||||||
uint256 _minReturn,
|
|
||||||
address _beneficiary,
|
|
||||||
address _affiliateAccount,
|
|
||||||
uint256 _affiliateFee
|
|
||||||
) external payable returns (uint256)
|
|
||||||
{
|
|
||||||
_revertIfReasonExists();
|
|
||||||
|
|
||||||
TestEventsRaiser(msg.sender).raiseConvertByPathInput(
|
|
||||||
// tokens sold
|
|
||||||
_amount,
|
|
||||||
// tokens bought
|
|
||||||
_minReturn,
|
|
||||||
// output token
|
|
||||||
_path[_path.length - 1],
|
|
||||||
// recipient
|
|
||||||
_beneficiary,
|
|
||||||
// fee recipient
|
|
||||||
_affiliateAccount,
|
|
||||||
// fee amount
|
|
||||||
_affiliateFee
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _revertIfReasonExists()
|
|
||||||
private
|
|
||||||
view
|
|
||||||
{
|
|
||||||
if (bytes(_nextRevertReason).length != 0) {
|
|
||||||
revert(_nextRevertReason);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// @dev BancorBridge overridden to mock tokens and BancorNetwork
|
|
||||||
contract TestBancorBridge is
|
|
||||||
BancorBridge,
|
|
||||||
TestEventsRaiser
|
|
||||||
{
|
|
||||||
|
|
||||||
// Token address to TestToken instance.
|
|
||||||
mapping (address => TestToken) private _testTokens;
|
|
||||||
// TestRouter instance.
|
|
||||||
TestBancorNetwork private _testNetwork;
|
|
||||||
|
|
||||||
constructor() public {
|
|
||||||
_testNetwork = new TestBancorNetwork();
|
|
||||||
}
|
|
||||||
|
|
||||||
function setNetworkRevertReason(string calldata revertReason)
|
|
||||||
external
|
|
||||||
{
|
|
||||||
_testNetwork.setRevertReason(revertReason);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Sets the balance of this contract for an existing token.
|
|
||||||
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 getNetworkAddress()
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (address)
|
|
||||||
{
|
|
||||||
return address(_testNetwork);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright 2019 ZeroEx Intl.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
pragma solidity ^0.5.9;
|
|
||||||
pragma experimental ABIEncoderV2;
|
|
||||||
|
|
||||||
import "../src/bridges/ChaiBridge.sol";
|
|
||||||
import "@0x/contracts-erc20/contracts/src/ERC20Token.sol";
|
|
||||||
|
|
||||||
|
|
||||||
contract TestChaiDai is
|
|
||||||
ERC20Token
|
|
||||||
{
|
|
||||||
address private constant ALWAYS_REVERT_ADDRESS = address(1);
|
|
||||||
|
|
||||||
function draw(
|
|
||||||
address from,
|
|
||||||
uint256 amount
|
|
||||||
)
|
|
||||||
external
|
|
||||||
{
|
|
||||||
if (from == ALWAYS_REVERT_ADDRESS) {
|
|
||||||
revert();
|
|
||||||
}
|
|
||||||
balances[msg.sender] += amount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
contract TestChaiBridge is
|
|
||||||
ChaiBridge
|
|
||||||
{
|
|
||||||
address public testChaiDai;
|
|
||||||
address private constant ALWAYS_REVERT_ADDRESS = address(1);
|
|
||||||
|
|
||||||
constructor()
|
|
||||||
public
|
|
||||||
{
|
|
||||||
testChaiDai = address(new TestChaiDai());
|
|
||||||
}
|
|
||||||
|
|
||||||
function _getDaiAddress()
|
|
||||||
internal
|
|
||||||
view
|
|
||||||
returns (address)
|
|
||||||
{
|
|
||||||
return testChaiDai;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _getChaiAddress()
|
|
||||||
internal
|
|
||||||
view
|
|
||||||
returns (address)
|
|
||||||
{
|
|
||||||
return testChaiDai;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _getERC20BridgeProxyAddress()
|
|
||||||
internal
|
|
||||||
view
|
|
||||||
returns (address)
|
|
||||||
{
|
|
||||||
return msg.sender == ALWAYS_REVERT_ADDRESS ? address(0) : msg.sender;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,244 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,246 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright 2019 ZeroEx Intl.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
pragma solidity ^0.5.9;
|
|
||||||
pragma experimental ABIEncoderV2;
|
|
||||||
|
|
||||||
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
|
||||||
import "../src/bridges/DydxBridge.sol";
|
|
||||||
|
|
||||||
|
|
||||||
// solhint-disable no-empty-blocks
|
|
||||||
contract TestDydxBridgeToken {
|
|
||||||
|
|
||||||
uint256 private constant INIT_HOLDER_BALANCE = 10 * 10**18; // 10 tokens
|
|
||||||
mapping (address => uint256) private _balances;
|
|
||||||
|
|
||||||
/// @dev Sets initial balance of token holders.
|
|
||||||
constructor(address[] memory holders)
|
|
||||||
public
|
|
||||||
{
|
|
||||||
for (uint256 i = 0; i != holders.length; ++i) {
|
|
||||||
_balances[holders[i]] = INIT_HOLDER_BALANCE;
|
|
||||||
}
|
|
||||||
_balances[msg.sender] = INIT_HOLDER_BALANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Basic transferFrom implementation.
|
|
||||||
function transferFrom(address from, address to, uint256 amount)
|
|
||||||
external
|
|
||||||
returns (bool)
|
|
||||||
{
|
|
||||||
if (_balances[from] < amount || _balances[to] + amount < _balances[to]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
_balances[from] -= amount;
|
|
||||||
_balances[to] += amount;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Returns balance of `holder`.
|
|
||||||
function balanceOf(address holder)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (uint256)
|
|
||||||
{
|
|
||||||
return _balances[holder];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// solhint-disable space-after-comma
|
|
||||||
contract TestDydxBridge is
|
|
||||||
IDydx,
|
|
||||||
DydxBridge
|
|
||||||
{
|
|
||||||
|
|
||||||
address private constant ALWAYS_REVERT_ADDRESS = address(1);
|
|
||||||
address private _testTokenAddress;
|
|
||||||
bool private _shouldRevertOnOperate;
|
|
||||||
|
|
||||||
event OperateAccount(
|
|
||||||
address owner,
|
|
||||||
uint256 number
|
|
||||||
);
|
|
||||||
|
|
||||||
event OperateAction(
|
|
||||||
ActionType actionType,
|
|
||||||
uint256 accountIdx,
|
|
||||||
bool amountSign,
|
|
||||||
AssetDenomination amountDenomination,
|
|
||||||
AssetReference amountRef,
|
|
||||||
uint256 amountValue,
|
|
||||||
uint256 primaryMarketId,
|
|
||||||
uint256 secondaryMarketId,
|
|
||||||
address otherAddress,
|
|
||||||
uint256 otherAccountId,
|
|
||||||
bytes data
|
|
||||||
);
|
|
||||||
|
|
||||||
constructor(address[] memory holders)
|
|
||||||
public
|
|
||||||
{
|
|
||||||
// Deploy a test token. This represents the asset being deposited/withdrawn from dydx.
|
|
||||||
_testTokenAddress = address(new TestDydxBridgeToken(holders));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Simulates `operate` in dydx contract.
|
|
||||||
/// Emits events so that arguments can be validated client-side.
|
|
||||||
function operate(
|
|
||||||
AccountInfo[] calldata accounts,
|
|
||||||
ActionArgs[] calldata actions
|
|
||||||
)
|
|
||||||
external
|
|
||||||
{
|
|
||||||
if (_shouldRevertOnOperate) {
|
|
||||||
revert("TestDydxBridge/SHOULD_REVERT_ON_OPERATE");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (uint i = 0; i < accounts.length; ++i) {
|
|
||||||
emit OperateAccount(
|
|
||||||
accounts[i].owner,
|
|
||||||
accounts[i].number
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (uint i = 0; i < actions.length; ++i) {
|
|
||||||
emit OperateAction(
|
|
||||||
actions[i].actionType,
|
|
||||||
actions[i].accountIdx,
|
|
||||||
actions[i].amount.sign,
|
|
||||||
actions[i].amount.denomination,
|
|
||||||
actions[i].amount.ref,
|
|
||||||
actions[i].amount.value,
|
|
||||||
actions[i].primaryMarketId,
|
|
||||||
actions[i].secondaryMarketId,
|
|
||||||
actions[i].otherAddress,
|
|
||||||
actions[i].otherAccountIdx,
|
|
||||||
actions[i].data
|
|
||||||
);
|
|
||||||
|
|
||||||
if (actions[i].actionType == IDydx.ActionType.Withdraw) {
|
|
||||||
require(
|
|
||||||
IERC20Token(_testTokenAddress).transferFrom(
|
|
||||||
address(this),
|
|
||||||
actions[i].otherAddress,
|
|
||||||
actions[i].amount.value
|
|
||||||
),
|
|
||||||
"TestDydxBridge/WITHDRAW_FAILED"
|
|
||||||
);
|
|
||||||
} else if (actions[i].actionType == IDydx.ActionType.Deposit) {
|
|
||||||
require(
|
|
||||||
IERC20Token(_testTokenAddress).transferFrom(
|
|
||||||
actions[i].otherAddress,
|
|
||||||
address(this),
|
|
||||||
actions[i].amount.value
|
|
||||||
),
|
|
||||||
"TestDydxBridge/DEPOSIT_FAILED"
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
revert("TestDydxBridge/UNSUPPORTED_ACTION");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev If `true` then subsequent calls to `operate` will revert.
|
|
||||||
function setRevertOnOperate(bool shouldRevert)
|
|
||||||
external
|
|
||||||
{
|
|
||||||
_shouldRevertOnOperate = shouldRevert;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Returns test token.
|
|
||||||
function getTestToken()
|
|
||||||
external
|
|
||||||
returns (address)
|
|
||||||
{
|
|
||||||
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
|
|
||||||
view
|
|
||||||
returns (address)
|
|
||||||
{
|
|
||||||
return address(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev overrides `_getERC20BridgeProxyAddress()` from `DeploymentConstants` for testing.
|
|
||||||
function _getERC20BridgeProxyAddress()
|
|
||||||
internal
|
|
||||||
view
|
|
||||||
returns (address)
|
|
||||||
{
|
|
||||||
return msg.sender == ALWAYS_REVERT_ADDRESS ? address(0) : msg.sender;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright 2019 ZeroEx Intl.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
pragma solidity ^0.5.9;
|
|
||||||
pragma experimental ABIEncoderV2;
|
|
||||||
|
|
||||||
import "../src/interfaces/IERC20Bridge.sol";
|
|
||||||
|
|
||||||
|
|
||||||
/// @dev Test bridge token
|
|
||||||
contract TestERC20BridgeToken {
|
|
||||||
mapping (address => uint256) private _balances;
|
|
||||||
|
|
||||||
function addBalance(address owner, int256 amount)
|
|
||||||
external
|
|
||||||
{
|
|
||||||
setBalance(owner, uint256(int256(balanceOf(owner)) + amount));
|
|
||||||
}
|
|
||||||
|
|
||||||
function setBalance(address owner, uint256 balance)
|
|
||||||
public
|
|
||||||
{
|
|
||||||
_balances[owner] = balance;
|
|
||||||
}
|
|
||||||
|
|
||||||
function balanceOf(address owner)
|
|
||||||
public
|
|
||||||
view
|
|
||||||
returns (uint256)
|
|
||||||
{
|
|
||||||
return _balances[owner];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// @dev Test bridge contract.
|
|
||||||
contract TestERC20Bridge is
|
|
||||||
IERC20Bridge
|
|
||||||
{
|
|
||||||
TestERC20BridgeToken public testToken;
|
|
||||||
|
|
||||||
event BridgeWithdrawTo(
|
|
||||||
address tokenAddress,
|
|
||||||
address from,
|
|
||||||
address to,
|
|
||||||
uint256 amount,
|
|
||||||
bytes bridgeData
|
|
||||||
);
|
|
||||||
|
|
||||||
constructor() public {
|
|
||||||
testToken = new TestERC20BridgeToken();
|
|
||||||
}
|
|
||||||
|
|
||||||
function setTestTokenBalance(address owner, uint256 balance)
|
|
||||||
external
|
|
||||||
{
|
|
||||||
testToken.setBalance(owner, balance);
|
|
||||||
}
|
|
||||||
|
|
||||||
function bridgeTransferFrom(
|
|
||||||
address tokenAddress,
|
|
||||||
address from,
|
|
||||||
address to,
|
|
||||||
uint256 amount,
|
|
||||||
bytes calldata bridgeData
|
|
||||||
)
|
|
||||||
external
|
|
||||||
returns (bytes4)
|
|
||||||
{
|
|
||||||
emit BridgeWithdrawTo(
|
|
||||||
tokenAddress,
|
|
||||||
from,
|
|
||||||
to,
|
|
||||||
amount,
|
|
||||||
bridgeData
|
|
||||||
);
|
|
||||||
// Unpack the bridgeData.
|
|
||||||
(
|
|
||||||
int256 transferAmount,
|
|
||||||
bytes memory revertData,
|
|
||||||
bytes memory returnData
|
|
||||||
) = abi.decode(bridgeData, (int256, bytes, bytes));
|
|
||||||
|
|
||||||
// If `revertData` is set, revert.
|
|
||||||
if (revertData.length != 0) {
|
|
||||||
assembly { revert(add(revertData, 0x20), mload(revertData)) }
|
|
||||||
}
|
|
||||||
// Increase `to`'s balance by `transferAmount`.
|
|
||||||
TestERC20BridgeToken(tokenAddress).addBalance(to, transferAmount);
|
|
||||||
// Return `returnData`.
|
|
||||||
assembly { return(add(returnData, 0x20), mload(returnData)) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,206 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright 2019 ZeroEx Intl.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
pragma solidity ^0.5.9;
|
|
||||||
pragma experimental ABIEncoderV2;
|
|
||||||
|
|
||||||
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
|
||||||
import "../src/bridges/Eth2DaiBridge.sol";
|
|
||||||
import "../src/interfaces/IEth2Dai.sol";
|
|
||||||
|
|
||||||
|
|
||||||
// solhint-disable no-simple-event-func-name
|
|
||||||
contract TestEvents {
|
|
||||||
|
|
||||||
event TokenTransfer(
|
|
||||||
address token,
|
|
||||||
address from,
|
|
||||||
address to,
|
|
||||||
uint256 amount
|
|
||||||
);
|
|
||||||
|
|
||||||
event TokenApprove(
|
|
||||||
address token,
|
|
||||||
address spender,
|
|
||||||
uint256 allowance
|
|
||||||
);
|
|
||||||
|
|
||||||
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(msg.sender, spender, allowance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// @dev A minimalist ERC20 token.
|
|
||||||
contract TestToken {
|
|
||||||
|
|
||||||
mapping (address => uint256) public balances;
|
|
||||||
string private _nextTransferRevertReason;
|
|
||||||
bytes private _nextTransferReturnData;
|
|
||||||
|
|
||||||
/// @dev Just calls `raiseTokenTransfer()` on the caller.
|
|
||||||
function transfer(address to, uint256 amount)
|
|
||||||
external
|
|
||||||
returns (bool)
|
|
||||||
{
|
|
||||||
TestEvents(msg.sender).raiseTokenTransfer(msg.sender, to, amount);
|
|
||||||
if (bytes(_nextTransferRevertReason).length != 0) {
|
|
||||||
revert(_nextTransferRevertReason);
|
|
||||||
}
|
|
||||||
bytes memory returnData = _nextTransferReturnData;
|
|
||||||
assembly { return(add(returnData, 0x20), mload(returnData)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Set the balance for `owner`.
|
|
||||||
function setBalance(address owner, uint256 balance)
|
|
||||||
external
|
|
||||||
{
|
|
||||||
balances[owner] = balance;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Set the behavior of the `transfer()` call.
|
|
||||||
function setTransferBehavior(
|
|
||||||
string calldata revertReason,
|
|
||||||
bytes calldata returnData
|
|
||||||
)
|
|
||||||
external
|
|
||||||
{
|
|
||||||
_nextTransferRevertReason = revertReason;
|
|
||||||
_nextTransferReturnData = returnData;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Just calls `raiseTokenApprove()` on the caller.
|
|
||||||
function approve(address spender, uint256 allowance)
|
|
||||||
external
|
|
||||||
returns (bool)
|
|
||||||
{
|
|
||||||
TestEvents(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 Eth2DaiBridge overridden to mock tokens and
|
|
||||||
/// implement IEth2Dai.
|
|
||||||
contract TestEth2DaiBridge is
|
|
||||||
TestEvents,
|
|
||||||
IEth2Dai,
|
|
||||||
Eth2DaiBridge
|
|
||||||
{
|
|
||||||
event SellAllAmount(
|
|
||||||
address sellToken,
|
|
||||||
uint256 sellTokenAmount,
|
|
||||||
address buyToken,
|
|
||||||
uint256 minimumFillAmount
|
|
||||||
);
|
|
||||||
|
|
||||||
mapping (address => TestToken) public testTokens;
|
|
||||||
string private _nextRevertReason;
|
|
||||||
uint256 private _nextFillAmount;
|
|
||||||
|
|
||||||
/// @dev Create a token and set this contract's balance.
|
|
||||||
function createToken(uint256 balance)
|
|
||||||
external
|
|
||||||
returns (address tokenAddress)
|
|
||||||
{
|
|
||||||
TestToken token = new TestToken();
|
|
||||||
testTokens[address(token)] = token;
|
|
||||||
token.setBalance(address(this), balance);
|
|
||||||
return address(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Set the behavior for `IEth2Dai.sellAllAmount()`.
|
|
||||||
function setFillBehavior(string calldata revertReason, uint256 fillAmount)
|
|
||||||
external
|
|
||||||
{
|
|
||||||
_nextRevertReason = revertReason;
|
|
||||||
_nextFillAmount = fillAmount;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Set the behavior of a token's `transfer()`.
|
|
||||||
function setTransferBehavior(
|
|
||||||
address tokenAddress,
|
|
||||||
string calldata revertReason,
|
|
||||||
bytes calldata returnData
|
|
||||||
)
|
|
||||||
external
|
|
||||||
{
|
|
||||||
testTokens[tokenAddress].setTransferBehavior(revertReason, returnData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Implementation of `IEth2Dai.sellAllAmount()`
|
|
||||||
function sellAllAmount(
|
|
||||||
address sellTokenAddress,
|
|
||||||
uint256 sellTokenAmount,
|
|
||||||
address buyTokenAddress,
|
|
||||||
uint256 minimumFillAmount
|
|
||||||
)
|
|
||||||
external
|
|
||||||
returns (uint256 fillAmount)
|
|
||||||
{
|
|
||||||
emit SellAllAmount(
|
|
||||||
sellTokenAddress,
|
|
||||||
sellTokenAmount,
|
|
||||||
buyTokenAddress,
|
|
||||||
minimumFillAmount
|
|
||||||
);
|
|
||||||
if (bytes(_nextRevertReason).length != 0) {
|
|
||||||
revert(_nextRevertReason);
|
|
||||||
}
|
|
||||||
return _nextFillAmount;
|
|
||||||
}
|
|
||||||
|
|
||||||
// @dev This contract will double as the Eth2Dai contract.
|
|
||||||
function _getEth2DaiAddress()
|
|
||||||
internal
|
|
||||||
view
|
|
||||||
returns (address)
|
|
||||||
{
|
|
||||||
return address(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,355 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright 2019 ZeroEx Intl.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
pragma solidity ^0.5.9;
|
|
||||||
pragma experimental ABIEncoderV2;
|
|
||||||
|
|
||||||
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
|
||||||
import "../src/bridges/KyberBridge.sol";
|
|
||||||
import "../src/interfaces/IKyberNetworkProxy.sol";
|
|
||||||
|
|
||||||
|
|
||||||
// solhint-disable no-simple-event-func-name
|
|
||||||
interface ITestContract {
|
|
||||||
|
|
||||||
function wethWithdraw(
|
|
||||||
address payable ownerAddress,
|
|
||||||
uint256 amount
|
|
||||||
)
|
|
||||||
external;
|
|
||||||
|
|
||||||
function wethDeposit(
|
|
||||||
address ownerAddress
|
|
||||||
)
|
|
||||||
external
|
|
||||||
payable;
|
|
||||||
|
|
||||||
function tokenTransfer(
|
|
||||||
address ownerAddress,
|
|
||||||
address recipientAddress,
|
|
||||||
uint256 amount
|
|
||||||
)
|
|
||||||
external
|
|
||||||
returns (bool success);
|
|
||||||
|
|
||||||
function tokenApprove(
|
|
||||||
address ownerAddress,
|
|
||||||
address spenderAddress,
|
|
||||||
uint256 allowance
|
|
||||||
)
|
|
||||||
external
|
|
||||||
returns (bool success);
|
|
||||||
|
|
||||||
function tokenBalanceOf(
|
|
||||||
address ownerAddress
|
|
||||||
)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (uint256 balance);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// @dev A minimalist ERC20/WETH token.
|
|
||||||
contract TestToken {
|
|
||||||
|
|
||||||
uint8 public decimals;
|
|
||||||
ITestContract private _testContract;
|
|
||||||
|
|
||||||
constructor(uint8 decimals_) public {
|
|
||||||
decimals = decimals_;
|
|
||||||
_testContract = ITestContract(msg.sender);
|
|
||||||
}
|
|
||||||
|
|
||||||
function approve(address spender, uint256 allowance)
|
|
||||||
external
|
|
||||||
returns (bool)
|
|
||||||
{
|
|
||||||
return _testContract.tokenApprove(
|
|
||||||
msg.sender,
|
|
||||||
spender,
|
|
||||||
allowance
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function transfer(address recipient, uint256 amount)
|
|
||||||
external
|
|
||||||
returns (bool)
|
|
||||||
{
|
|
||||||
return _testContract.tokenTransfer(
|
|
||||||
msg.sender,
|
|
||||||
recipient,
|
|
||||||
amount
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function withdraw(uint256 amount)
|
|
||||||
external
|
|
||||||
{
|
|
||||||
return _testContract.wethWithdraw(msg.sender, amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
function deposit()
|
|
||||||
external
|
|
||||||
payable
|
|
||||||
{
|
|
||||||
return _testContract.wethDeposit.value(msg.value)(msg.sender);
|
|
||||||
}
|
|
||||||
|
|
||||||
function allowance(address, address) external view returns (uint256) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function balanceOf(address owner)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (uint256)
|
|
||||||
{
|
|
||||||
return _testContract.tokenBalanceOf(owner);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// @dev KyberBridge overridden to mock tokens and implement IKyberBridge.
|
|
||||||
contract TestKyberBridge is
|
|
||||||
KyberBridge,
|
|
||||||
ITestContract,
|
|
||||||
IKyberNetworkProxy
|
|
||||||
{
|
|
||||||
event KyberBridgeTrade(
|
|
||||||
uint256 msgValue,
|
|
||||||
address sellTokenAddress,
|
|
||||||
uint256 sellAmount,
|
|
||||||
address buyTokenAddress,
|
|
||||||
address payable recipientAddress,
|
|
||||||
uint256 maxBuyTokenAmount,
|
|
||||||
uint256 minConversionRate,
|
|
||||||
address walletId
|
|
||||||
);
|
|
||||||
|
|
||||||
event KyberBridgeWethWithdraw(
|
|
||||||
address ownerAddress,
|
|
||||||
uint256 amount
|
|
||||||
);
|
|
||||||
|
|
||||||
event KyberBridgeWethDeposit(
|
|
||||||
uint256 msgValue,
|
|
||||||
address ownerAddress,
|
|
||||||
uint256 amount
|
|
||||||
);
|
|
||||||
|
|
||||||
event KyberBridgeTokenApprove(
|
|
||||||
address tokenAddress,
|
|
||||||
address ownerAddress,
|
|
||||||
address spenderAddress,
|
|
||||||
uint256 allowance
|
|
||||||
);
|
|
||||||
|
|
||||||
event KyberBridgeTokenTransfer(
|
|
||||||
address tokenAddress,
|
|
||||||
address ownerAddress,
|
|
||||||
address recipientAddress,
|
|
||||||
uint256 amount
|
|
||||||
);
|
|
||||||
|
|
||||||
IEtherToken public weth;
|
|
||||||
mapping (address => mapping (address => uint256)) private _tokenBalances;
|
|
||||||
uint256 private _nextFillAmount;
|
|
||||||
|
|
||||||
constructor() public {
|
|
||||||
weth = IEtherToken(address(new TestToken(18)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Implementation of `IKyberNetworkProxy.trade()`
|
|
||||||
function trade(
|
|
||||||
address sellTokenAddress,
|
|
||||||
uint256 sellAmount,
|
|
||||||
address buyTokenAddress,
|
|
||||||
address payable recipientAddress,
|
|
||||||
uint256 maxBuyTokenAmount,
|
|
||||||
uint256 minConversionRate,
|
|
||||||
address walletId
|
|
||||||
)
|
|
||||||
external
|
|
||||||
payable
|
|
||||||
returns(uint256 boughtAmount)
|
|
||||||
{
|
|
||||||
emit KyberBridgeTrade(
|
|
||||||
msg.value,
|
|
||||||
sellTokenAddress,
|
|
||||||
sellAmount,
|
|
||||||
buyTokenAddress,
|
|
||||||
recipientAddress,
|
|
||||||
maxBuyTokenAmount,
|
|
||||||
minConversionRate,
|
|
||||||
walletId
|
|
||||||
);
|
|
||||||
return _nextFillAmount;
|
|
||||||
}
|
|
||||||
|
|
||||||
function tradeWithHint(
|
|
||||||
address sellTokenAddress,
|
|
||||||
uint256 sellAmount,
|
|
||||||
address buyTokenAddress,
|
|
||||||
address payable recipientAddress,
|
|
||||||
uint256 maxBuyTokenAmount,
|
|
||||||
uint256 minConversionRate,
|
|
||||||
address payable walletId,
|
|
||||||
bytes calldata hint
|
|
||||||
)
|
|
||||||
external
|
|
||||||
payable
|
|
||||||
returns (uint256 boughtAmount)
|
|
||||||
{
|
|
||||||
emit KyberBridgeTrade(
|
|
||||||
msg.value,
|
|
||||||
sellTokenAddress,
|
|
||||||
sellAmount,
|
|
||||||
buyTokenAddress,
|
|
||||||
recipientAddress,
|
|
||||||
maxBuyTokenAmount,
|
|
||||||
minConversionRate,
|
|
||||||
walletId
|
|
||||||
);
|
|
||||||
return _nextFillAmount;
|
|
||||||
}
|
|
||||||
|
|
||||||
function createToken(uint8 decimals)
|
|
||||||
external
|
|
||||||
returns (address tokenAddress)
|
|
||||||
{
|
|
||||||
return address(new TestToken(decimals));
|
|
||||||
}
|
|
||||||
|
|
||||||
function setNextFillAmount(uint256 amount)
|
|
||||||
external
|
|
||||||
payable
|
|
||||||
{
|
|
||||||
if (msg.value != 0) {
|
|
||||||
require(amount == msg.value, "VALUE_AMOUNT_MISMATCH");
|
|
||||||
grantTokensTo(address(weth), address(this), msg.value);
|
|
||||||
}
|
|
||||||
_nextFillAmount = amount;
|
|
||||||
}
|
|
||||||
|
|
||||||
function wethDeposit(
|
|
||||||
address ownerAddress
|
|
||||||
)
|
|
||||||
external
|
|
||||||
payable
|
|
||||||
{
|
|
||||||
require(msg.sender == address(weth), "ONLY_WETH");
|
|
||||||
grantTokensTo(address(weth), ownerAddress, msg.value);
|
|
||||||
emit KyberBridgeWethDeposit(
|
|
||||||
msg.value,
|
|
||||||
ownerAddress,
|
|
||||||
msg.value
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function wethWithdraw(
|
|
||||||
address payable ownerAddress,
|
|
||||||
uint256 amount
|
|
||||||
)
|
|
||||||
external
|
|
||||||
{
|
|
||||||
require(msg.sender == address(weth), "ONLY_WETH");
|
|
||||||
_tokenBalances[address(weth)][ownerAddress] -= amount;
|
|
||||||
ownerAddress.transfer(amount);
|
|
||||||
emit KyberBridgeWethWithdraw(
|
|
||||||
ownerAddress,
|
|
||||||
amount
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function tokenApprove(
|
|
||||||
address ownerAddress,
|
|
||||||
address spenderAddress,
|
|
||||||
uint256 allowance
|
|
||||||
)
|
|
||||||
external
|
|
||||||
returns (bool success)
|
|
||||||
{
|
|
||||||
emit KyberBridgeTokenApprove(
|
|
||||||
msg.sender,
|
|
||||||
ownerAddress,
|
|
||||||
spenderAddress,
|
|
||||||
allowance
|
|
||||||
);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function tokenTransfer(
|
|
||||||
address ownerAddress,
|
|
||||||
address recipientAddress,
|
|
||||||
uint256 amount
|
|
||||||
)
|
|
||||||
external
|
|
||||||
returns (bool success)
|
|
||||||
{
|
|
||||||
_tokenBalances[msg.sender][ownerAddress] -= amount;
|
|
||||||
_tokenBalances[msg.sender][recipientAddress] += amount;
|
|
||||||
emit KyberBridgeTokenTransfer(
|
|
||||||
msg.sender,
|
|
||||||
ownerAddress,
|
|
||||||
recipientAddress,
|
|
||||||
amount
|
|
||||||
);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function tokenBalanceOf(
|
|
||||||
address ownerAddress
|
|
||||||
)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (uint256 balance)
|
|
||||||
{
|
|
||||||
return _tokenBalances[msg.sender][ownerAddress];
|
|
||||||
}
|
|
||||||
|
|
||||||
function grantTokensTo(address tokenAddress, address ownerAddress, uint256 amount)
|
|
||||||
public
|
|
||||||
payable
|
|
||||||
{
|
|
||||||
_tokenBalances[tokenAddress][ownerAddress] += amount;
|
|
||||||
if (tokenAddress != address(weth)) {
|
|
||||||
// Send back ether if not WETH.
|
|
||||||
msg.sender.transfer(msg.value);
|
|
||||||
} else {
|
|
||||||
require(msg.value == amount, "VALUE_AMOUNT_MISMATCH");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// @dev overridden to point to this contract.
|
|
||||||
function _getKyberNetworkProxyAddress()
|
|
||||||
internal
|
|
||||||
view
|
|
||||||
returns (address)
|
|
||||||
{
|
|
||||||
return address(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// @dev overridden to point to test WETH.
|
|
||||||
function _getWethAddress()
|
|
||||||
internal
|
|
||||||
view
|
|
||||||
returns (address)
|
|
||||||
{
|
|
||||||
return address(weth);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
|
||||||
|
|
||||||
|
|
||||||
contract TestStaticCallTarget {
|
|
||||||
|
|
||||||
using LibBytes for bytes;
|
|
||||||
|
|
||||||
uint256 internal _state;
|
|
||||||
|
|
||||||
function updateState()
|
|
||||||
external
|
|
||||||
{
|
|
||||||
_state++;
|
|
||||||
}
|
|
||||||
|
|
||||||
function assertEvenNumber(uint256 target)
|
|
||||||
external
|
|
||||||
pure
|
|
||||||
{
|
|
||||||
require(
|
|
||||||
target % 2 == 0,
|
|
||||||
"TARGET_NOT_EVEN"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isOddNumber(uint256 target)
|
|
||||||
external
|
|
||||||
pure
|
|
||||||
returns (bool isOdd)
|
|
||||||
{
|
|
||||||
isOdd = target % 2 == 1;
|
|
||||||
return isOdd;
|
|
||||||
}
|
|
||||||
|
|
||||||
function noInputFunction()
|
|
||||||
external
|
|
||||||
pure
|
|
||||||
{
|
|
||||||
assert(msg.data.length == 4 && msg.data.readBytes4(0) == bytes4(keccak256("noInputFunction()")));
|
|
||||||
}
|
|
||||||
|
|
||||||
function dynamicInputFunction(bytes calldata a)
|
|
||||||
external
|
|
||||||
pure
|
|
||||||
{
|
|
||||||
bytes memory abiEncodedData = abi.encodeWithSignature("dynamicInputFunction(bytes)", a);
|
|
||||||
assert(msg.data.equals(abiEncodedData));
|
|
||||||
}
|
|
||||||
|
|
||||||
function returnComplexType(uint256 a, uint256 b)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (bytes memory result)
|
|
||||||
{
|
|
||||||
result = abi.encodePacked(
|
|
||||||
address(this),
|
|
||||||
a,
|
|
||||||
b
|
|
||||||
);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,436 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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 "../src/bridges/UniswapBridge.sol";
|
|
||||||
import "../src/interfaces/IUniswapExchangeFactory.sol";
|
|
||||||
import "../src/interfaces/IUniswapExchange.sol";
|
|
||||||
|
|
||||||
|
|
||||||
// solhint-disable no-simple-event-func-name
|
|
||||||
contract TestEventsRaiser {
|
|
||||||
|
|
||||||
event TokenTransfer(
|
|
||||||
address token,
|
|
||||||
address from,
|
|
||||||
address to,
|
|
||||||
uint256 amount
|
|
||||||
);
|
|
||||||
|
|
||||||
event TokenApprove(
|
|
||||||
address spender,
|
|
||||||
uint256 allowance
|
|
||||||
);
|
|
||||||
|
|
||||||
event WethDeposit(
|
|
||||||
uint256 amount
|
|
||||||
);
|
|
||||||
|
|
||||||
event WethWithdraw(
|
|
||||||
uint256 amount
|
|
||||||
);
|
|
||||||
|
|
||||||
event EthToTokenTransferInput(
|
|
||||||
address exchange,
|
|
||||||
uint256 minTokensBought,
|
|
||||||
uint256 deadline,
|
|
||||||
address recipient
|
|
||||||
);
|
|
||||||
|
|
||||||
event TokenToEthSwapInput(
|
|
||||||
address exchange,
|
|
||||||
uint256 tokensSold,
|
|
||||||
uint256 minEthBought,
|
|
||||||
uint256 deadline
|
|
||||||
);
|
|
||||||
|
|
||||||
event TokenToTokenTransferInput(
|
|
||||||
address exchange,
|
|
||||||
uint256 tokensSold,
|
|
||||||
uint256 minTokensBought,
|
|
||||||
uint256 minEthBought,
|
|
||||||
uint256 deadline,
|
|
||||||
address recipient,
|
|
||||||
address toTokenAddress
|
|
||||||
);
|
|
||||||
|
|
||||||
function raiseEthToTokenTransferInput(
|
|
||||||
uint256 minTokensBought,
|
|
||||||
uint256 deadline,
|
|
||||||
address recipient
|
|
||||||
)
|
|
||||||
external
|
|
||||||
{
|
|
||||||
emit EthToTokenTransferInput(
|
|
||||||
msg.sender,
|
|
||||||
minTokensBought,
|
|
||||||
deadline,
|
|
||||||
recipient
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function raiseTokenToEthSwapInput(
|
|
||||||
uint256 tokensSold,
|
|
||||||
uint256 minEthBought,
|
|
||||||
uint256 deadline
|
|
||||||
)
|
|
||||||
external
|
|
||||||
{
|
|
||||||
emit TokenToEthSwapInput(
|
|
||||||
msg.sender,
|
|
||||||
tokensSold,
|
|
||||||
minEthBought,
|
|
||||||
deadline
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function raiseTokenToTokenTransferInput(
|
|
||||||
uint256 tokensSold,
|
|
||||||
uint256 minTokensBought,
|
|
||||||
uint256 minEthBought,
|
|
||||||
uint256 deadline,
|
|
||||||
address recipient,
|
|
||||||
address toTokenAddress
|
|
||||||
)
|
|
||||||
external
|
|
||||||
{
|
|
||||||
emit TokenToTokenTransferInput(
|
|
||||||
msg.sender,
|
|
||||||
tokensSold,
|
|
||||||
minTokensBought,
|
|
||||||
minEthBought,
|
|
||||||
deadline,
|
|
||||||
recipient,
|
|
||||||
toTokenAddress
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 raiseWethDeposit(uint256 amount)
|
|
||||||
external
|
|
||||||
{
|
|
||||||
emit WethDeposit(amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
function raiseWethWithdraw(uint256 amount)
|
|
||||||
external
|
|
||||||
{
|
|
||||||
emit WethWithdraw(amount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// @dev A minimalist ERC20/WETH 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)
|
|
||||||
external
|
|
||||||
payable
|
|
||||||
{
|
|
||||||
balances[owner] = msg.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Set the revert reason for `transfer()`,
|
|
||||||
/// `deposit()`, and `withdraw()`.
|
|
||||||
function setRevertReason(string calldata reason)
|
|
||||||
external
|
|
||||||
{
|
|
||||||
_nextRevertReason = reason;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Just calls `raiseTokenTransfer()` on the caller.
|
|
||||||
function transfer(address to, uint256 amount)
|
|
||||||
external
|
|
||||||
returns (bool)
|
|
||||||
{
|
|
||||||
_revertIfReasonExists();
|
|
||||||
TestEventsRaiser(msg.sender).raiseTokenTransfer(msg.sender, to, amount);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Just calls `raiseTokenApprove()` on the caller.
|
|
||||||
function approve(address spender, uint256 allowance)
|
|
||||||
external
|
|
||||||
returns (bool)
|
|
||||||
{
|
|
||||||
TestEventsRaiser(msg.sender).raiseTokenApprove(spender, allowance);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev `IWETH.deposit()` that increases balances and calls
|
|
||||||
/// `raiseWethDeposit()` on the caller.
|
|
||||||
function deposit()
|
|
||||||
external
|
|
||||||
payable
|
|
||||||
{
|
|
||||||
_revertIfReasonExists();
|
|
||||||
balances[msg.sender] += balances[msg.sender].safeAdd(msg.value);
|
|
||||||
TestEventsRaiser(msg.sender).raiseWethDeposit(msg.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev `IWETH.withdraw()` that just reduces balances and calls
|
|
||||||
/// `raiseWethWithdraw()` on the caller.
|
|
||||||
function withdraw(uint256 amount)
|
|
||||||
external
|
|
||||||
{
|
|
||||||
_revertIfReasonExists();
|
|
||||||
balances[msg.sender] = balances[msg.sender].safeSub(amount);
|
|
||||||
msg.sender.transfer(amount);
|
|
||||||
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
|
|
||||||
view
|
|
||||||
returns (uint256)
|
|
||||||
{
|
|
||||||
return balances[owner];
|
|
||||||
}
|
|
||||||
|
|
||||||
function _revertIfReasonExists()
|
|
||||||
private
|
|
||||||
view
|
|
||||||
{
|
|
||||||
if (bytes(_nextRevertReason).length != 0) {
|
|
||||||
revert(_nextRevertReason);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
contract TestExchange is
|
|
||||||
IUniswapExchange
|
|
||||||
{
|
|
||||||
address public tokenAddress;
|
|
||||||
string private _nextRevertReason;
|
|
||||||
|
|
||||||
constructor(address _tokenAddress) public {
|
|
||||||
tokenAddress = _tokenAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setFillBehavior(
|
|
||||||
string calldata revertReason
|
|
||||||
)
|
|
||||||
external
|
|
||||||
payable
|
|
||||||
{
|
|
||||||
_nextRevertReason = revertReason;
|
|
||||||
}
|
|
||||||
|
|
||||||
function ethToTokenTransferInput(
|
|
||||||
uint256 minTokensBought,
|
|
||||||
uint256 deadline,
|
|
||||||
address recipient
|
|
||||||
)
|
|
||||||
external
|
|
||||||
payable
|
|
||||||
returns (uint256 tokensBought)
|
|
||||||
{
|
|
||||||
TestEventsRaiser(msg.sender).raiseEthToTokenTransferInput(
|
|
||||||
minTokensBought,
|
|
||||||
deadline,
|
|
||||||
recipient
|
|
||||||
);
|
|
||||||
_revertIfReasonExists();
|
|
||||||
return address(this).balance;
|
|
||||||
}
|
|
||||||
|
|
||||||
function tokenToEthSwapInput(
|
|
||||||
uint256 tokensSold,
|
|
||||||
uint256 minEthBought,
|
|
||||||
uint256 deadline
|
|
||||||
)
|
|
||||||
external
|
|
||||||
returns (uint256 ethBought)
|
|
||||||
{
|
|
||||||
TestEventsRaiser(msg.sender).raiseTokenToEthSwapInput(
|
|
||||||
tokensSold,
|
|
||||||
minEthBought,
|
|
||||||
deadline
|
|
||||||
);
|
|
||||||
_revertIfReasonExists();
|
|
||||||
uint256 fillAmount = address(this).balance;
|
|
||||||
msg.sender.transfer(fillAmount);
|
|
||||||
return fillAmount;
|
|
||||||
}
|
|
||||||
|
|
||||||
function tokenToTokenTransferInput(
|
|
||||||
uint256 tokensSold,
|
|
||||||
uint256 minTokensBought,
|
|
||||||
uint256 minEthBought,
|
|
||||||
uint256 deadline,
|
|
||||||
address recipient,
|
|
||||||
address toTokenAddress
|
|
||||||
)
|
|
||||||
external
|
|
||||||
returns (uint256 tokensBought)
|
|
||||||
{
|
|
||||||
TestEventsRaiser(msg.sender).raiseTokenToTokenTransferInput(
|
|
||||||
tokensSold,
|
|
||||||
minTokensBought,
|
|
||||||
minEthBought,
|
|
||||||
deadline,
|
|
||||||
recipient,
|
|
||||||
toTokenAddress
|
|
||||||
);
|
|
||||||
_revertIfReasonExists();
|
|
||||||
return address(this).balance;
|
|
||||||
}
|
|
||||||
|
|
||||||
function toTokenAddress()
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (address _tokenAddress)
|
|
||||||
{
|
|
||||||
return tokenAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _revertIfReasonExists()
|
|
||||||
private
|
|
||||||
view
|
|
||||||
{
|
|
||||||
if (bytes(_nextRevertReason).length != 0) {
|
|
||||||
revert(_nextRevertReason);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// @dev UniswapBridge overridden to mock tokens and implement IUniswapExchangeFactory.
|
|
||||||
contract TestUniswapBridge is
|
|
||||||
IUniswapExchangeFactory,
|
|
||||||
TestEventsRaiser,
|
|
||||||
UniswapBridge
|
|
||||||
{
|
|
||||||
TestToken public wethToken;
|
|
||||||
// Token address to TestToken instance.
|
|
||||||
mapping (address => TestToken) private _testTokens;
|
|
||||||
// Token address to TestExchange instance.
|
|
||||||
mapping (address => TestExchange) private _testExchanges;
|
|
||||||
|
|
||||||
constructor() public {
|
|
||||||
wethToken = new TestToken();
|
|
||||||
_testTokens[address(wethToken)] = wethToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Sets the balance of this contract for an existing token.
|
|
||||||
/// The wei attached will be the balance.
|
|
||||||
function setTokenBalance(address tokenAddress)
|
|
||||||
external
|
|
||||||
payable
|
|
||||||
{
|
|
||||||
TestToken token = _testTokens[tokenAddress];
|
|
||||||
token.deposit.value(msg.value)();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Sets the revert reason for an existing token.
|
|
||||||
function setTokenRevertReason(address tokenAddress, string calldata revertReason)
|
|
||||||
external
|
|
||||||
{
|
|
||||||
TestToken token = _testTokens[tokenAddress];
|
|
||||||
token.setRevertReason(revertReason);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Create a token and exchange (if they don't exist) for a new token
|
|
||||||
/// and sets the exchange revert and fill behavior. The wei attached
|
|
||||||
/// will be the fill amount for the exchange.
|
|
||||||
/// @param tokenAddress The token address. If zero, one will be created.
|
|
||||||
/// @param revertReason The revert reason for exchange operations.
|
|
||||||
function createTokenAndExchange(
|
|
||||||
address tokenAddress,
|
|
||||||
string calldata revertReason
|
|
||||||
)
|
|
||||||
external
|
|
||||||
payable
|
|
||||||
returns (TestToken token, TestExchange exchange)
|
|
||||||
{
|
|
||||||
token = TestToken(tokenAddress);
|
|
||||||
if (tokenAddress == address(0)) {
|
|
||||||
token = new TestToken();
|
|
||||||
}
|
|
||||||
_testTokens[address(token)] = token;
|
|
||||||
exchange = _testExchanges[address(token)];
|
|
||||||
if (address(exchange) == address(0)) {
|
|
||||||
_testExchanges[address(token)] = exchange = new TestExchange(address(token));
|
|
||||||
}
|
|
||||||
exchange.setFillBehavior.value(msg.value)(revertReason);
|
|
||||||
return (token, exchange);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev `IUniswapExchangeFactory.getExchange`
|
|
||||||
function getExchange(address tokenAddress)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (address)
|
|
||||||
{
|
|
||||||
return address(_testExchanges[tokenAddress]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// @dev Use `wethToken`.
|
|
||||||
function _getWethAddress()
|
|
||||||
internal
|
|
||||||
view
|
|
||||||
returns (address)
|
|
||||||
{
|
|
||||||
return address(wethToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
// @dev This contract will double as the Uniswap contract.
|
|
||||||
function _getUniswapExchangeFactoryAddress()
|
|
||||||
internal
|
|
||||||
view
|
|
||||||
returns (address)
|
|
||||||
{
|
|
||||||
return address(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,253 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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,99 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@0x/contracts-asset-proxy",
|
|
||||||
"version": "3.6.9",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6.12"
|
|
||||||
},
|
|
||||||
"description": "Smart contract components of 0x protocol",
|
|
||||||
"main": "lib/src/index.js",
|
|
||||||
"directories": {
|
|
||||||
"test": "test"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"build": "yarn pre_build && tsc -b",
|
|
||||||
"build:ci": "yarn build",
|
|
||||||
"pre_build": "run-s compile contracts:gen generate_contract_wrappers contracts:copy",
|
|
||||||
"test": "yarn run_mocha",
|
|
||||||
"rebuild_and_test": "run-s build test",
|
|
||||||
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
|
||||||
"test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html",
|
|
||||||
"test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha",
|
|
||||||
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
|
|
||||||
"compile": "sol-compiler",
|
|
||||||
"watch": "sol-compiler -w",
|
|
||||||
"clean": "shx rm -rf lib test/generated-artifacts test/generated-wrappers generated-artifacts generated-wrappers",
|
|
||||||
"generate_contract_wrappers": "abi-gen --debug --abis ${npm_package_config_abis} --output test/generated-wrappers --backend ethers",
|
|
||||||
"lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./test/generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude ./test/generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
|
||||||
"fix": "tslint --fix --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./test/generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude ./test/generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
|
||||||
"coverage:report:text": "istanbul report text",
|
|
||||||
"coverage:report:html": "istanbul report html && open coverage/index.html",
|
|
||||||
"profiler:report:html": "istanbul report html && open coverage/index.html",
|
|
||||||
"coverage:report:lcov": "istanbul report lcov",
|
|
||||||
"test:circleci": "yarn test",
|
|
||||||
"contracts:gen": "contracts-gen generate",
|
|
||||||
"contracts:copy": "contracts-gen copy",
|
|
||||||
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol",
|
|
||||||
"compile:truffle": "truffle compile",
|
|
||||||
"docs:md": "ts-doc-gen --sourceDir='$PROJECT_FILES' --output=$MD_FILE_DIR --fileExtension=mdx --tsconfig=./typedoc-tsconfig.json",
|
|
||||||
"docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES"
|
|
||||||
},
|
|
||||||
"config": {
|
|
||||||
"abis": "./test/generated-artifacts/@(BalancerBridge|BancorBridge|ChaiBridge|CreamBridge|CryptoComBridge|CurveBridge|DODOBridge|DexForwarderBridge|DydxBridge|ERC1155Proxy|ERC20BridgeProxy|ERC20Proxy|ERC721Proxy|Eth2DaiBridge|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|IBalancerPool|IBancorNetwork|IChai|ICurve|IDydx|IDydxBridge|IERC20Bridge|IEth2Dai|IGasToken|IKyberNetworkProxy|IMStable|IMooniswap|IShell|IUniswapExchange|IUniswapExchangeFactory|IUniswapV2Router01|KyberBridge|MStableBridge|MixinAssetProxyDispatcher|MixinAuthorizable|MixinGasToken|MooniswapBridge|MultiAssetProxy|Ownable|ShellBridge|SnowSwapBridge|StaticCallProxy|SushiSwapBridge|SwerveBridge|TestBancorBridge|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": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/0xProject/protocol.git"
|
|
||||||
},
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/0xProject/protocol/issues"
|
|
||||||
},
|
|
||||||
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/protocol",
|
|
||||||
"devDependencies": {
|
|
||||||
"@0x/abi-gen": "^5.4.13",
|
|
||||||
"@0x/contract-wrappers": "^13.11.0",
|
|
||||||
"@0x/contracts-gen": "^2.0.24",
|
|
||||||
"@0x/contracts-test-utils": "^5.3.15",
|
|
||||||
"@0x/contracts-utils": "^4.6.3",
|
|
||||||
"@0x/dev-utils": "^4.1.3",
|
|
||||||
"@0x/sol-compiler": "^4.4.1",
|
|
||||||
"@0x/ts-doc-gen": "^0.0.28",
|
|
||||||
"@0x/tslint-config": "^4.1.3",
|
|
||||||
"@types/lodash": "4.14.104",
|
|
||||||
"@types/mocha": "^5.2.7",
|
|
||||||
"@types/node": "12.12.54",
|
|
||||||
"chai": "^4.0.1",
|
|
||||||
"chai-as-promised": "^7.1.0",
|
|
||||||
"chai-bignumber": "^3.0.0",
|
|
||||||
"dirty-chai": "^2.0.1",
|
|
||||||
"ethereumjs-util": "^5.1.1",
|
|
||||||
"make-promises-safe": "^1.1.0",
|
|
||||||
"mocha": "^6.2.0",
|
|
||||||
"npm-run-all": "^4.1.2",
|
|
||||||
"shx": "^0.2.2",
|
|
||||||
"solhint": "^1.4.1",
|
|
||||||
"truffle": "^5.0.32",
|
|
||||||
"tslint": "5.11.0",
|
|
||||||
"typedoc": "~0.16.11",
|
|
||||||
"typescript": "3.0.1"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@0x/base-contract": "^6.2.14",
|
|
||||||
"@0x/contracts-erc1155": "^2.1.18",
|
|
||||||
"@0x/contracts-erc20": "^3.2.12",
|
|
||||||
"@0x/contracts-erc721": "^3.1.18",
|
|
||||||
"@0x/contracts-exchange-libs": "^4.3.18",
|
|
||||||
"@0x/order-utils": "^10.4.10",
|
|
||||||
"@0x/types": "^3.3.1",
|
|
||||||
"@0x/typescript-typings": "^5.1.6",
|
|
||||||
"@0x/utils": "^6.1.1",
|
|
||||||
"@0x/web3-wrapper": "^7.3.0",
|
|
||||||
"ethereum-types": "^3.4.0",
|
|
||||||
"lodash": "^4.17.11"
|
|
||||||
},
|
|
||||||
"publishConfig": {
|
|
||||||
"access": "public"
|
|
||||||
},
|
|
||||||
"gitHead": "4f91bfd907996b2f4dd383778b50c479c2602b56"
|
|
||||||
}
|
|
||||||
@@ -1,127 +0,0 @@
|
|||||||
/*
|
|
||||||
* -----------------------------------------------------------------------------
|
|
||||||
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
|
||||||
* -----------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
import { ContractArtifact } from 'ethereum-types';
|
|
||||||
|
|
||||||
import * as BalancerBridge from '../generated-artifacts/BalancerBridge.json';
|
|
||||||
import * as BancorBridge from '../generated-artifacts/BancorBridge.json';
|
|
||||||
import * as ChaiBridge from '../generated-artifacts/ChaiBridge.json';
|
|
||||||
import * as CreamBridge from '../generated-artifacts/CreamBridge.json';
|
|
||||||
import * as CryptoComBridge from '../generated-artifacts/CryptoComBridge.json';
|
|
||||||
import * as CurveBridge from '../generated-artifacts/CurveBridge.json';
|
|
||||||
import * as DexForwarderBridge from '../generated-artifacts/DexForwarderBridge.json';
|
|
||||||
import * as DODOBridge from '../generated-artifacts/DODOBridge.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';
|
|
||||||
import * as ERC20Proxy from '../generated-artifacts/ERC20Proxy.json';
|
|
||||||
import * as ERC721Proxy from '../generated-artifacts/ERC721Proxy.json';
|
|
||||||
import * as Eth2DaiBridge from '../generated-artifacts/Eth2DaiBridge.json';
|
|
||||||
import * as IAssetData from '../generated-artifacts/IAssetData.json';
|
|
||||||
import * as IAssetProxy from '../generated-artifacts/IAssetProxy.json';
|
|
||||||
import * as IAssetProxyDispatcher from '../generated-artifacts/IAssetProxyDispatcher.json';
|
|
||||||
import * as IAuthorizable from '../generated-artifacts/IAuthorizable.json';
|
|
||||||
import * as IBalancerPool from '../generated-artifacts/IBalancerPool.json';
|
|
||||||
import * as IBancorNetwork from '../generated-artifacts/IBancorNetwork.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 IMooniswap from '../generated-artifacts/IMooniswap.json';
|
|
||||||
import * as IMStable from '../generated-artifacts/IMStable.json';
|
|
||||||
import * as IShell from '../generated-artifacts/IShell.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 MooniswapBridge from '../generated-artifacts/MooniswapBridge.json';
|
|
||||||
import * as MStableBridge from '../generated-artifacts/MStableBridge.json';
|
|
||||||
import * as MultiAssetProxy from '../generated-artifacts/MultiAssetProxy.json';
|
|
||||||
import * as Ownable from '../generated-artifacts/Ownable.json';
|
|
||||||
import * as ShellBridge from '../generated-artifacts/ShellBridge.json';
|
|
||||||
import * as SnowSwapBridge from '../generated-artifacts/SnowSwapBridge.json';
|
|
||||||
import * as StaticCallProxy from '../generated-artifacts/StaticCallProxy.json';
|
|
||||||
import * as SushiSwapBridge from '../generated-artifacts/SushiSwapBridge.json';
|
|
||||||
import * as SwerveBridge from '../generated-artifacts/SwerveBridge.json';
|
|
||||||
import * as TestBancorBridge from '../generated-artifacts/TestBancorBridge.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,
|
|
||||||
Ownable: Ownable as ContractArtifact,
|
|
||||||
ERC1155Proxy: ERC1155Proxy as ContractArtifact,
|
|
||||||
ERC20BridgeProxy: ERC20BridgeProxy as ContractArtifact,
|
|
||||||
ERC20Proxy: ERC20Proxy as ContractArtifact,
|
|
||||||
ERC721Proxy: ERC721Proxy as ContractArtifact,
|
|
||||||
MultiAssetProxy: MultiAssetProxy as ContractArtifact,
|
|
||||||
StaticCallProxy: StaticCallProxy as ContractArtifact,
|
|
||||||
BalancerBridge: BalancerBridge as ContractArtifact,
|
|
||||||
BancorBridge: BancorBridge as ContractArtifact,
|
|
||||||
ChaiBridge: ChaiBridge as ContractArtifact,
|
|
||||||
CreamBridge: CreamBridge as ContractArtifact,
|
|
||||||
CryptoComBridge: CryptoComBridge as ContractArtifact,
|
|
||||||
CurveBridge: CurveBridge as ContractArtifact,
|
|
||||||
DODOBridge: DODOBridge as ContractArtifact,
|
|
||||||
DexForwarderBridge: DexForwarderBridge as ContractArtifact,
|
|
||||||
DydxBridge: DydxBridge as ContractArtifact,
|
|
||||||
Eth2DaiBridge: Eth2DaiBridge as ContractArtifact,
|
|
||||||
KyberBridge: KyberBridge as ContractArtifact,
|
|
||||||
MStableBridge: MStableBridge as ContractArtifact,
|
|
||||||
MixinGasToken: MixinGasToken as ContractArtifact,
|
|
||||||
MooniswapBridge: MooniswapBridge as ContractArtifact,
|
|
||||||
ShellBridge: ShellBridge as ContractArtifact,
|
|
||||||
SnowSwapBridge: SnowSwapBridge as ContractArtifact,
|
|
||||||
SushiSwapBridge: SushiSwapBridge as ContractArtifact,
|
|
||||||
SwerveBridge: SwerveBridge 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,
|
|
||||||
IBalancerPool: IBalancerPool as ContractArtifact,
|
|
||||||
IBancorNetwork: IBancorNetwork 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,
|
|
||||||
IMStable: IMStable as ContractArtifact,
|
|
||||||
IMooniswap: IMooniswap as ContractArtifact,
|
|
||||||
IShell: IShell as ContractArtifact,
|
|
||||||
IUniswapExchange: IUniswapExchange as ContractArtifact,
|
|
||||||
IUniswapExchangeFactory: IUniswapExchangeFactory as ContractArtifact,
|
|
||||||
IUniswapV2Router01: IUniswapV2Router01 as ContractArtifact,
|
|
||||||
TestBancorBridge: TestBancorBridge 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,
|
|
||||||
};
|
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
import { AssetProxyId } from '@0x/types';
|
|
||||||
import { BigNumber, hexUtils } from '@0x/utils';
|
|
||||||
|
|
||||||
import { IAssetDataContract } from './wrappers';
|
|
||||||
|
|
||||||
const assetDataIface = new IAssetDataContract('0x0000000000000000000000000000000000000000', { isEIP1193: true } as any);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the proxy ID from encoded asset data.
|
|
||||||
*/
|
|
||||||
export function getAssetDataProxyId(encoded: string): AssetProxyId {
|
|
||||||
// tslint:disable-next-line: no-unnecessary-type-assertion
|
|
||||||
return hexUtils.slice(encoded, 0, 4) as AssetProxyId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decode ERC20 asset data.
|
|
||||||
*/
|
|
||||||
export function decodeERC20AssetData(encoded: string): string {
|
|
||||||
return assetDataIface.getABIDecodedTransactionData<string>('ERC20Token', encoded);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decode ERC721 asset data.
|
|
||||||
*/
|
|
||||||
export function decodeERC721AssetData(encoded: string): [string, BigNumber] {
|
|
||||||
return assetDataIface.getABIDecodedTransactionData<[string, BigNumber]>('ERC721Token', encoded);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decode ERC1155 asset data.
|
|
||||||
*/
|
|
||||||
export function decodeERC1155AssetData(encoded: string): [string, BigNumber[], BigNumber[], string] {
|
|
||||||
return assetDataIface.getABIDecodedTransactionData<[string, BigNumber[], BigNumber[], string]>(
|
|
||||||
'ERC1155Assets',
|
|
||||||
encoded,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decode MultiAsset asset data.
|
|
||||||
*/
|
|
||||||
export function decodeMultiAssetData(encoded: string): [BigNumber[], string[]] {
|
|
||||||
return assetDataIface.getABIDecodedTransactionData<[BigNumber[], string[]]>('MultiAsset', encoded);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decode StaticCall asset data.
|
|
||||||
*/
|
|
||||||
export function decodeStaticCallAssetData(encoded: string): [string, string, string] {
|
|
||||||
return assetDataIface.getABIDecodedTransactionData<[string, string, string]>('StaticCall', encoded);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decode ERC20Bridge asset data.
|
|
||||||
*/
|
|
||||||
export function decodeERC20BridgeAssetData(encoded: string): [string, string, string] {
|
|
||||||
return assetDataIface.getABIDecodedTransactionData<[string, string, string]>('ERC20Bridge', encoded);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode ERC20 asset data.
|
|
||||||
*/
|
|
||||||
export function encodeERC20AssetData(tokenAddress: string): string {
|
|
||||||
return assetDataIface.ERC20Token(tokenAddress).getABIEncodedTransactionData();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode ERC721 asset data.
|
|
||||||
*/
|
|
||||||
export function encodeERC721AssetData(tokenAddress: string, tokenId: BigNumber): string {
|
|
||||||
return assetDataIface.ERC721Token(tokenAddress, tokenId).getABIEncodedTransactionData();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode ERC1155 asset data.
|
|
||||||
*/
|
|
||||||
export function encodeERC1155AssetData(
|
|
||||||
tokenAddress: string,
|
|
||||||
tokenIds: BigNumber[],
|
|
||||||
values: BigNumber[],
|
|
||||||
callbackData: string,
|
|
||||||
): string {
|
|
||||||
return assetDataIface.ERC1155Assets(tokenAddress, tokenIds, values, callbackData).getABIEncodedTransactionData();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode MultiAsset asset data.
|
|
||||||
*/
|
|
||||||
export function encodeMultiAssetData(values: BigNumber[], nestedAssetData: string[]): string {
|
|
||||||
return assetDataIface.MultiAsset(values, nestedAssetData).getABIEncodedTransactionData();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode StaticCall asset data.
|
|
||||||
*/
|
|
||||||
export function encodeStaticCallAssetData(
|
|
||||||
staticCallTargetAddress: string,
|
|
||||||
staticCallData: string,
|
|
||||||
expectedReturnDataHash: string,
|
|
||||||
): string {
|
|
||||||
return assetDataIface
|
|
||||||
.StaticCall(staticCallTargetAddress, staticCallData, expectedReturnDataHash)
|
|
||||||
.getABIEncodedTransactionData();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode ERC20Bridge asset data.
|
|
||||||
*/
|
|
||||||
export function encodeERC20BridgeAssetData(tokenAddress: string, bridgeAddress: string, bridgeData: string): string {
|
|
||||||
return assetDataIface.ERC20Bridge(tokenAddress, bridgeAddress, bridgeData).getABIEncodedTransactionData();
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
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' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
import { AbiEncoder, BigNumber } from '@0x/utils';
|
|
||||||
|
|
||||||
export enum DydxBridgeActionType {
|
|
||||||
Deposit,
|
|
||||||
Withdraw,
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DydxBridgeAction {
|
|
||||||
actionType: DydxBridgeActionType;
|
|
||||||
accountIdx: BigNumber;
|
|
||||||
marketId: BigNumber;
|
|
||||||
conversionRateNumerator: BigNumber;
|
|
||||||
conversionRateDenominator: BigNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DydxBridgeData {
|
|
||||||
accountNumbers: BigNumber[];
|
|
||||||
actions: DydxBridgeAction[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export const dydxBridgeDataEncoder = AbiEncoder.create([
|
|
||||||
{
|
|
||||||
name: 'bridgeData',
|
|
||||||
type: 'tuple',
|
|
||||||
components: [
|
|
||||||
{ name: 'accountNumbers', type: 'uint256[]' },
|
|
||||||
{
|
|
||||||
name: 'actions',
|
|
||||||
type: 'tuple[]',
|
|
||||||
components: [
|
|
||||||
{ name: 'actionType', type: 'uint8' },
|
|
||||||
{ name: 'accountIdx', type: 'uint256' },
|
|
||||||
{ name: 'marketId', type: 'uint256' },
|
|
||||||
{ name: 'conversionRateNumerator', type: 'uint256' },
|
|
||||||
{ name: 'conversionRateDenominator', type: 'uint256' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
@@ -1,410 +0,0 @@
|
|||||||
import { artifacts as erc1155Artifacts, ERC1155MintableContract, Erc1155Wrapper } from '@0x/contracts-erc1155';
|
|
||||||
import {
|
|
||||||
constants,
|
|
||||||
ERC1155FungibleHoldingsByOwner,
|
|
||||||
ERC1155HoldingsByOwner,
|
|
||||||
ERC1155NonFungibleHoldingsByOwner,
|
|
||||||
LogDecoder,
|
|
||||||
txDefaults,
|
|
||||||
} from '@0x/contracts-test-utils';
|
|
||||||
import { BigNumber } from '@0x/utils';
|
|
||||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
|
||||||
import { Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
|
||||||
import * as _ from 'lodash';
|
|
||||||
|
|
||||||
import { artifacts } from './artifacts';
|
|
||||||
|
|
||||||
import { ERC1155ProxyContract, IAssetDataContract, IAssetProxyContract } from './wrappers';
|
|
||||||
|
|
||||||
export class ERC1155ProxyWrapper {
|
|
||||||
private readonly _tokenOwnerAddresses: string[];
|
|
||||||
private readonly _fungibleTokenIds: string[];
|
|
||||||
private readonly _nonFungibleTokenIds: string[];
|
|
||||||
private readonly _nfts: Array<{ id: BigNumber; tokenId: BigNumber }>;
|
|
||||||
private readonly _contractOwnerAddress: string;
|
|
||||||
private readonly _web3Wrapper: Web3Wrapper;
|
|
||||||
private readonly _provider: Provider;
|
|
||||||
private readonly _logDecoder: LogDecoder;
|
|
||||||
private readonly _dummyTokenWrappers: Erc1155Wrapper[];
|
|
||||||
private readonly _assetProxyInterface: IAssetProxyContract;
|
|
||||||
private readonly _assetDataInterface: IAssetDataContract;
|
|
||||||
private _proxyContract?: ERC1155ProxyContract;
|
|
||||||
private _proxyIdIfExists?: string;
|
|
||||||
private _initialTokenIdsByOwner: ERC1155HoldingsByOwner = { fungible: {}, nonFungible: {} };
|
|
||||||
|
|
||||||
constructor(provider: Provider, tokenOwnerAddresses: string[], contractOwnerAddress: string) {
|
|
||||||
this._web3Wrapper = new Web3Wrapper(provider);
|
|
||||||
this._provider = provider;
|
|
||||||
const allArtifacts = _.merge(artifacts, erc1155Artifacts);
|
|
||||||
this._logDecoder = new LogDecoder(this._web3Wrapper, allArtifacts);
|
|
||||||
this._dummyTokenWrappers = [];
|
|
||||||
this._assetProxyInterface = new IAssetProxyContract(constants.NULL_ADDRESS, provider);
|
|
||||||
this._assetDataInterface = new IAssetDataContract(constants.NULL_ADDRESS, provider);
|
|
||||||
this._tokenOwnerAddresses = tokenOwnerAddresses;
|
|
||||||
this._contractOwnerAddress = contractOwnerAddress;
|
|
||||||
this._fungibleTokenIds = [];
|
|
||||||
this._nonFungibleTokenIds = [];
|
|
||||||
this._nfts = [];
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @dev Deploys dummy ERC1155 contracts
|
|
||||||
* @return An array of ERC1155 wrappers; one for each deployed contract.
|
|
||||||
*/
|
|
||||||
public async deployDummyContractsAsync(): Promise<Erc1155Wrapper[]> {
|
|
||||||
// tslint:disable-next-line:no-unused-variable
|
|
||||||
for (const i of _.times(constants.NUM_DUMMY_ERC1155_CONTRACTS_TO_DEPLOY)) {
|
|
||||||
const erc1155Contract = await ERC1155MintableContract.deployFrom0xArtifactAsync(
|
|
||||||
erc1155Artifacts.ERC1155Mintable,
|
|
||||||
this._provider,
|
|
||||||
txDefaults,
|
|
||||||
artifacts,
|
|
||||||
);
|
|
||||||
const erc1155Wrapper = new Erc1155Wrapper(erc1155Contract, this._contractOwnerAddress);
|
|
||||||
this._dummyTokenWrappers.push(erc1155Wrapper);
|
|
||||||
}
|
|
||||||
return this._dummyTokenWrappers;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @dev Deploys the ERC1155 proxy
|
|
||||||
* @return Deployed ERC1155 proxy contract instance
|
|
||||||
*/
|
|
||||||
public async deployProxyAsync(): Promise<ERC1155ProxyContract> {
|
|
||||||
this._proxyContract = await ERC1155ProxyContract.deployFrom0xArtifactAsync(
|
|
||||||
artifacts.ERC1155Proxy,
|
|
||||||
this._provider,
|
|
||||||
txDefaults,
|
|
||||||
artifacts,
|
|
||||||
);
|
|
||||||
this._proxyIdIfExists = await this._proxyContract.getProxyId().callAsync();
|
|
||||||
return this._proxyContract;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @dev Gets the ERC1155 proxy id
|
|
||||||
*/
|
|
||||||
public getProxyId(): string {
|
|
||||||
this._validateProxyContractExistsOrThrow();
|
|
||||||
return this._proxyIdIfExists as string;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @dev generates abi-encoded tx data for transferring erc1155 fungible/non-fungible tokens.
|
|
||||||
* @param from source address
|
|
||||||
* @param to destination address
|
|
||||||
* @param contractAddress address of erc155 contract
|
|
||||||
* @param tokensToTransfer array of erc1155 tokens to transfer
|
|
||||||
* @param valuesToTransfer array of corresponding values for each erc1155 token to transfer
|
|
||||||
* @param valueMultiplier each value in `valuesToTransfer` is multiplied by this
|
|
||||||
* @param receiverCallbackData callback data if `to` is a contract
|
|
||||||
* @param authorizedSender sender of `transferFrom` transaction
|
|
||||||
* @param extraData extra data to append to `transferFrom` transaction. Optional.
|
|
||||||
* @return abi encoded tx data.
|
|
||||||
*/
|
|
||||||
public async getTransferFromAbiEncodedTxDataAsync(
|
|
||||||
from: string,
|
|
||||||
to: string,
|
|
||||||
contractAddress: string,
|
|
||||||
tokensToTransfer: BigNumber[],
|
|
||||||
valuesToTransfer: BigNumber[],
|
|
||||||
valueMultiplier: BigNumber,
|
|
||||||
receiverCallbackData: string,
|
|
||||||
authorizedSender: string,
|
|
||||||
assetData_?: string,
|
|
||||||
): Promise<string> {
|
|
||||||
this._validateProxyContractExistsOrThrow();
|
|
||||||
const assetData =
|
|
||||||
assetData_ === undefined
|
|
||||||
? this._assetDataInterface
|
|
||||||
.ERC1155Assets(contractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
|
|
||||||
.getABIEncodedTransactionData()
|
|
||||||
: assetData_;
|
|
||||||
const data = this._assetProxyInterface
|
|
||||||
.transferFrom(assetData, from, to, valueMultiplier)
|
|
||||||
.getABIEncodedTransactionData();
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @dev transfers erc1155 fungible/non-fungible tokens.
|
|
||||||
* @param txData: abi-encoded tx data
|
|
||||||
* @param authorizedSender sender of `transferFrom` transaction
|
|
||||||
*/
|
|
||||||
public async transferFromRawAsync(
|
|
||||||
txData: string,
|
|
||||||
authorizedSender: string,
|
|
||||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
|
||||||
const txHash = await this._web3Wrapper.sendTransactionAsync({
|
|
||||||
to: (this._proxyContract as ERC1155ProxyContract).address,
|
|
||||||
data: txData,
|
|
||||||
from: authorizedSender,
|
|
||||||
gas: 300000,
|
|
||||||
});
|
|
||||||
const txReceipt = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
|
|
||||||
return txReceipt;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @dev transfers erc1155 fungible/non-fungible tokens.
|
|
||||||
* @param from source address
|
|
||||||
* @param to destination address
|
|
||||||
* @param contractAddress address of erc155 contract
|
|
||||||
* @param tokensToTransfer array of erc1155 tokens to transfer
|
|
||||||
* @param valuesToTransfer array of corresponding values for each erc1155 token to transfer
|
|
||||||
* @param valueMultiplier each value in `valuesToTransfer` is multiplied by this
|
|
||||||
* @param receiverCallbackData callback data if `to` is a contract
|
|
||||||
* @param authorizedSender sender of `transferFrom` transaction
|
|
||||||
* @param extraData extra data to append to `transferFrom` transaction. Optional.
|
|
||||||
* @return tranasction hash.
|
|
||||||
*/
|
|
||||||
public async transferFromAsync(
|
|
||||||
from: string,
|
|
||||||
to: string,
|
|
||||||
contractAddress: string,
|
|
||||||
tokensToTransfer: BigNumber[],
|
|
||||||
valuesToTransfer: BigNumber[],
|
|
||||||
valueMultiplier: BigNumber,
|
|
||||||
receiverCallbackData: string,
|
|
||||||
authorizedSender: string,
|
|
||||||
assetData_?: string,
|
|
||||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
|
||||||
this._validateProxyContractExistsOrThrow();
|
|
||||||
const assetData =
|
|
||||||
assetData_ === undefined
|
|
||||||
? this._assetDataInterface
|
|
||||||
.ERC1155Assets(contractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
|
|
||||||
.getABIEncodedTransactionData()
|
|
||||||
: assetData_;
|
|
||||||
const data = this._assetProxyInterface
|
|
||||||
.transferFrom(assetData, from, to, valueMultiplier)
|
|
||||||
.getABIEncodedTransactionData();
|
|
||||||
const txHash = await this._web3Wrapper.sendTransactionAsync({
|
|
||||||
to: (this._proxyContract as ERC1155ProxyContract).address,
|
|
||||||
data,
|
|
||||||
from: authorizedSender,
|
|
||||||
gas: 300000,
|
|
||||||
});
|
|
||||||
const txReceipt = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
|
|
||||||
return txReceipt;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @dev For each deployed ERC1155 contract, this function mints a set of fungible/non-fungible
|
|
||||||
* tokens for each token owner address (`_tokenOwnerAddresses`).
|
|
||||||
* @return Balances of each token owner, across all ERC1155 contracts and tokens.
|
|
||||||
*/
|
|
||||||
public async setBalancesAndAllowancesAsync(): Promise<ERC1155HoldingsByOwner> {
|
|
||||||
this._validateDummyTokenContractsExistOrThrow();
|
|
||||||
this._validateProxyContractExistsOrThrow();
|
|
||||||
this._initialTokenIdsByOwner = {
|
|
||||||
fungible: {},
|
|
||||||
nonFungible: {},
|
|
||||||
};
|
|
||||||
const fungibleHoldingsByOwner: ERC1155FungibleHoldingsByOwner = {};
|
|
||||||
const nonFungibleHoldingsByOwner: ERC1155NonFungibleHoldingsByOwner = {};
|
|
||||||
// Set balances accordingly
|
|
||||||
for (const dummyWrapper of this._dummyTokenWrappers) {
|
|
||||||
const dummyAddress = dummyWrapper.getContract().address;
|
|
||||||
// tslint:disable-next-line:no-unused-variable
|
|
||||||
for (const i of _.times(constants.NUM_ERC1155_FUNGIBLE_TOKENS_MINT)) {
|
|
||||||
// Create a fungible token
|
|
||||||
const tokenId = await dummyWrapper.mintFungibleTokensAsync(
|
|
||||||
this._tokenOwnerAddresses,
|
|
||||||
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
|
||||||
);
|
|
||||||
const tokenIdAsString = tokenId.toString();
|
|
||||||
this._fungibleTokenIds.push(tokenIdAsString);
|
|
||||||
// Mint tokens for each owner for this token
|
|
||||||
for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
|
|
||||||
// tslint:disable-next-line:no-unused-variable
|
|
||||||
if (fungibleHoldingsByOwner[tokenOwnerAddress] === undefined) {
|
|
||||||
fungibleHoldingsByOwner[tokenOwnerAddress] = {};
|
|
||||||
}
|
|
||||||
if (fungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress] === undefined) {
|
|
||||||
fungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress] = {};
|
|
||||||
}
|
|
||||||
fungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress][tokenIdAsString] =
|
|
||||||
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE;
|
|
||||||
await dummyWrapper.setApprovalForAllAsync(
|
|
||||||
tokenOwnerAddress,
|
|
||||||
(this._proxyContract as ERC1155ProxyContract).address,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Non-fungible tokens
|
|
||||||
// tslint:disable-next-line:no-unused-variable
|
|
||||||
for (const j of _.times(constants.NUM_ERC1155_NONFUNGIBLE_TOKENS_MINT)) {
|
|
||||||
const [tokenId, nftIds] = await dummyWrapper.mintNonFungibleTokensAsync(this._tokenOwnerAddresses);
|
|
||||||
const tokenIdAsString = tokenId.toString();
|
|
||||||
this._nonFungibleTokenIds.push(tokenIdAsString);
|
|
||||||
_.each(this._tokenOwnerAddresses, async (tokenOwnerAddress: string, i: number) => {
|
|
||||||
if (nonFungibleHoldingsByOwner[tokenOwnerAddress] === undefined) {
|
|
||||||
nonFungibleHoldingsByOwner[tokenOwnerAddress] = {};
|
|
||||||
}
|
|
||||||
if (nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress] === undefined) {
|
|
||||||
nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress] = {};
|
|
||||||
}
|
|
||||||
if (nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress][tokenIdAsString] === undefined) {
|
|
||||||
nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress][tokenIdAsString] = [];
|
|
||||||
}
|
|
||||||
this._nfts.push({ id: nftIds[i], tokenId });
|
|
||||||
nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress][tokenIdAsString].push(nftIds[i]);
|
|
||||||
await dummyWrapper.setApprovalForAllAsync(
|
|
||||||
tokenOwnerAddress,
|
|
||||||
(this._proxyContract as ERC1155ProxyContract).address,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this._initialTokenIdsByOwner = {
|
|
||||||
fungible: fungibleHoldingsByOwner,
|
|
||||||
nonFungible: nonFungibleHoldingsByOwner,
|
|
||||||
};
|
|
||||||
return this._initialTokenIdsByOwner;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @dev For each deployed ERC1155 contract, this function quieries the set of fungible/non-fungible
|
|
||||||
* tokens for each token owner address (`_tokenOwnerAddresses`).
|
|
||||||
* @return Balances of each token owner, across all ERC1155 contracts and tokens.
|
|
||||||
*/
|
|
||||||
public async getBalancesAsync(): Promise<ERC1155HoldingsByOwner> {
|
|
||||||
this._validateDummyTokenContractsExistOrThrow();
|
|
||||||
this._validateBalancesAndAllowancesSetOrThrow();
|
|
||||||
const tokenHoldingsByOwner: ERC1155FungibleHoldingsByOwner = {};
|
|
||||||
const nonFungibleHoldingsByOwner: ERC1155NonFungibleHoldingsByOwner = {};
|
|
||||||
for (const dummyTokenWrapper of this._dummyTokenWrappers) {
|
|
||||||
const tokenContract = dummyTokenWrapper.getContract();
|
|
||||||
const tokenAddress = tokenContract.address;
|
|
||||||
// Construct batch balance call
|
|
||||||
const tokenOwners: string[] = [];
|
|
||||||
const tokenIds: BigNumber[] = [];
|
|
||||||
for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
|
|
||||||
for (const tokenId of this._fungibleTokenIds) {
|
|
||||||
tokenOwners.push(tokenOwnerAddress);
|
|
||||||
tokenIds.push(new BigNumber(tokenId));
|
|
||||||
}
|
|
||||||
for (const nft of this._nfts) {
|
|
||||||
tokenOwners.push(tokenOwnerAddress);
|
|
||||||
tokenIds.push(nft.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const balances = await dummyTokenWrapper.getBalancesAsync(tokenOwners, tokenIds);
|
|
||||||
// Parse out balances into fungible / non-fungible token holdings
|
|
||||||
let i = 0;
|
|
||||||
for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
|
|
||||||
// Fungible tokens
|
|
||||||
for (const tokenId of this._fungibleTokenIds) {
|
|
||||||
if (tokenHoldingsByOwner[tokenOwnerAddress] === undefined) {
|
|
||||||
tokenHoldingsByOwner[tokenOwnerAddress] = {};
|
|
||||||
}
|
|
||||||
if (tokenHoldingsByOwner[tokenOwnerAddress][tokenAddress] === undefined) {
|
|
||||||
tokenHoldingsByOwner[tokenOwnerAddress][tokenAddress] = {};
|
|
||||||
}
|
|
||||||
tokenHoldingsByOwner[tokenOwnerAddress][tokenAddress][tokenId] = balances[i++];
|
|
||||||
}
|
|
||||||
// Non-fungible tokens
|
|
||||||
for (const nft of this._nfts) {
|
|
||||||
if (nonFungibleHoldingsByOwner[tokenOwnerAddress] === undefined) {
|
|
||||||
nonFungibleHoldingsByOwner[tokenOwnerAddress] = {};
|
|
||||||
}
|
|
||||||
if (nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress] === undefined) {
|
|
||||||
nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress] = {};
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress][nft.tokenId.toString()] ===
|
|
||||||
undefined
|
|
||||||
) {
|
|
||||||
nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress][nft.tokenId.toString()] = [];
|
|
||||||
}
|
|
||||||
const isOwner = balances[i++];
|
|
||||||
if (isOwner.isEqualTo(1)) {
|
|
||||||
nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress][nft.tokenId.toString()].push(
|
|
||||||
nft.id,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const holdingsByOwner = {
|
|
||||||
fungible: tokenHoldingsByOwner,
|
|
||||||
nonFungible: nonFungibleHoldingsByOwner,
|
|
||||||
};
|
|
||||||
return holdingsByOwner;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @dev Set the approval for the proxy on behalf of `userAddress` .
|
|
||||||
* @param userAddress owner of ERC1155 tokens.
|
|
||||||
* @param contractAddress address of ERC1155 contract.
|
|
||||||
* @param isApproved Whether to approve the proxy for all or not.
|
|
||||||
*/
|
|
||||||
public async setProxyAllowanceForAllAsync(
|
|
||||||
userAddress: string,
|
|
||||||
contractAddress: string,
|
|
||||||
isApproved: boolean,
|
|
||||||
): Promise<void> {
|
|
||||||
this._validateProxyContractExistsOrThrow();
|
|
||||||
const tokenWrapper = this.getContractWrapper(contractAddress);
|
|
||||||
const operator = (this._proxyContract as ERC1155ProxyContract).address;
|
|
||||||
await tokenWrapper.setApprovalForAllAsync(userAddress, operator, isApproved);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @dev Checks if proxy is approved to transfer tokens on behalf of `userAddress`.
|
|
||||||
* @param userAddress owner of ERC1155 tokens.
|
|
||||||
* @param contractAddress address of ERC1155 contract.
|
|
||||||
* @return True iff the proxy is approved for all. False otherwise.
|
|
||||||
*/
|
|
||||||
public async isProxyApprovedForAllAsync(userAddress: string, contractAddress: string): Promise<boolean> {
|
|
||||||
this._validateProxyContractExistsOrThrow();
|
|
||||||
const tokenContract = this._getContractFromAddress(contractAddress);
|
|
||||||
const operator = (this._proxyContract as ERC1155ProxyContract).address;
|
|
||||||
const didApproveAll = await tokenContract.isApprovedForAll(userAddress, operator).callAsync();
|
|
||||||
return didApproveAll;
|
|
||||||
}
|
|
||||||
public getFungibleTokenIds(): BigNumber[] {
|
|
||||||
const fungibleTokenIds = _.map(this._fungibleTokenIds, (tokenIdAsString: string) => {
|
|
||||||
return new BigNumber(tokenIdAsString);
|
|
||||||
});
|
|
||||||
return fungibleTokenIds;
|
|
||||||
}
|
|
||||||
public getNonFungibleTokenIds(): BigNumber[] {
|
|
||||||
const nonFungibleTokenIds = _.map(this._nonFungibleTokenIds, (tokenIdAsString: string) => {
|
|
||||||
return new BigNumber(tokenIdAsString);
|
|
||||||
});
|
|
||||||
return nonFungibleTokenIds;
|
|
||||||
}
|
|
||||||
public getTokenOwnerAddresses(): string[] {
|
|
||||||
return this._tokenOwnerAddresses;
|
|
||||||
}
|
|
||||||
public getContractWrapper(contractAddress: string): Erc1155Wrapper {
|
|
||||||
const tokenWrapper = _.find(this._dummyTokenWrappers, (wrapper: Erc1155Wrapper) => {
|
|
||||||
return wrapper.getContract().address === contractAddress;
|
|
||||||
});
|
|
||||||
if (tokenWrapper === undefined) {
|
|
||||||
throw new Error(`Contract: ${contractAddress} was not deployed through ERC1155ProxyWrapper`);
|
|
||||||
}
|
|
||||||
return tokenWrapper;
|
|
||||||
}
|
|
||||||
private _getContractFromAddress(tokenAddress: string): ERC1155MintableContract {
|
|
||||||
const tokenContractIfExists = _.find(this._dummyTokenWrappers, c => c.getContract().address === tokenAddress);
|
|
||||||
if (tokenContractIfExists === undefined) {
|
|
||||||
throw new Error(`Token: ${tokenAddress} was not deployed through ERC1155ProxyWrapper`);
|
|
||||||
}
|
|
||||||
return tokenContractIfExists.getContract();
|
|
||||||
}
|
|
||||||
private _validateDummyTokenContractsExistOrThrow(): void {
|
|
||||||
if (this._dummyTokenWrappers === undefined) {
|
|
||||||
throw new Error('Dummy ERC1155 tokens not yet deployed, please call "deployDummyTokensAsync"');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private _validateProxyContractExistsOrThrow(): void {
|
|
||||||
if (this._proxyContract === undefined) {
|
|
||||||
throw new Error('ERC1155 proxy contract not yet deployed, please call "deployProxyAsync"');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private _validateBalancesAndAllowancesSetOrThrow(): void {
|
|
||||||
if (
|
|
||||||
_.keys(this._initialTokenIdsByOwner.fungible).length === 0 ||
|
|
||||||
_.keys(this._initialTokenIdsByOwner.nonFungible).length === 0
|
|
||||||
) {
|
|
||||||
throw new Error(
|
|
||||||
'Dummy ERC1155 balances and allowances not yet set, please call "setBalancesAndAllowancesAsync"',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,164 +0,0 @@
|
|||||||
import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contracts-erc20';
|
|
||||||
import { constants, ERC20BalancesByOwner, txDefaults } from '@0x/contracts-test-utils';
|
|
||||||
import { BigNumber } from '@0x/utils';
|
|
||||||
import { ZeroExProvider } from 'ethereum-types';
|
|
||||||
import * as _ from 'lodash';
|
|
||||||
|
|
||||||
import { artifacts } from './artifacts';
|
|
||||||
|
|
||||||
import { ERC20ProxyContract, IAssetDataContract } from './wrappers';
|
|
||||||
|
|
||||||
export class ERC20Wrapper {
|
|
||||||
private readonly _tokenOwnerAddresses: string[];
|
|
||||||
private readonly _contractOwnerAddress: string;
|
|
||||||
private readonly _provider: ZeroExProvider;
|
|
||||||
private readonly _dummyTokenContracts: DummyERC20TokenContract[];
|
|
||||||
private readonly _assetDataInterface: IAssetDataContract;
|
|
||||||
private _proxyContract?: ERC20ProxyContract;
|
|
||||||
private _proxyIdIfExists?: string;
|
|
||||||
/**
|
|
||||||
* Instanitates an ERC20Wrapper
|
|
||||||
* @param provider Web3 provider to use for all JSON RPC requests
|
|
||||||
* @param tokenOwnerAddresses Addresses that we want to endow as owners for dummy ERC20 tokens
|
|
||||||
* @param contractOwnerAddress Desired owner of the contract
|
|
||||||
* Instance of ERC20Wrapper
|
|
||||||
*/
|
|
||||||
constructor(provider: ZeroExProvider, tokenOwnerAddresses: string[], contractOwnerAddress: string) {
|
|
||||||
this._dummyTokenContracts = [];
|
|
||||||
this._provider = provider;
|
|
||||||
this._tokenOwnerAddresses = tokenOwnerAddresses;
|
|
||||||
this._contractOwnerAddress = contractOwnerAddress;
|
|
||||||
this._assetDataInterface = new IAssetDataContract(constants.NULL_ADDRESS, provider);
|
|
||||||
}
|
|
||||||
public async deployDummyTokensAsync(
|
|
||||||
numberToDeploy: number,
|
|
||||||
decimals: BigNumber,
|
|
||||||
): Promise<DummyERC20TokenContract[]> {
|
|
||||||
for (let i = 0; i < numberToDeploy; i++) {
|
|
||||||
this._dummyTokenContracts.push(
|
|
||||||
await DummyERC20TokenContract.deployFrom0xArtifactAsync(
|
|
||||||
erc20Artifacts.DummyERC20Token,
|
|
||||||
this._provider,
|
|
||||||
txDefaults,
|
|
||||||
artifacts,
|
|
||||||
constants.DUMMY_TOKEN_NAME,
|
|
||||||
constants.DUMMY_TOKEN_SYMBOL,
|
|
||||||
decimals,
|
|
||||||
constants.DUMMY_TOKEN_TOTAL_SUPPLY,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return this._dummyTokenContracts;
|
|
||||||
}
|
|
||||||
public async deployProxyAsync(): Promise<ERC20ProxyContract> {
|
|
||||||
this._proxyContract = await ERC20ProxyContract.deployFrom0xArtifactAsync(
|
|
||||||
artifacts.ERC20Proxy,
|
|
||||||
this._provider,
|
|
||||||
txDefaults,
|
|
||||||
artifacts,
|
|
||||||
);
|
|
||||||
this._proxyIdIfExists = await this._proxyContract.getProxyId().callAsync();
|
|
||||||
return this._proxyContract;
|
|
||||||
}
|
|
||||||
public getProxyId(): string {
|
|
||||||
this._validateProxyContractExistsOrThrow();
|
|
||||||
return this._proxyIdIfExists as string;
|
|
||||||
}
|
|
||||||
public async setBalancesAndAllowancesAsync(): Promise<void> {
|
|
||||||
this._validateDummyTokenContractsExistOrThrow();
|
|
||||||
this._validateProxyContractExistsOrThrow();
|
|
||||||
for (const dummyTokenContract of this._dummyTokenContracts) {
|
|
||||||
for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
|
|
||||||
await dummyTokenContract
|
|
||||||
.setBalance(tokenOwnerAddress, constants.INITIAL_ERC20_BALANCE)
|
|
||||||
.awaitTransactionSuccessAsync({ from: this._contractOwnerAddress });
|
|
||||||
await dummyTokenContract
|
|
||||||
.approve((this._proxyContract as ERC20ProxyContract).address, constants.INITIAL_ERC20_ALLOWANCE)
|
|
||||||
.awaitTransactionSuccessAsync({ from: tokenOwnerAddress });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public async getBalanceAsync(userAddress: string, assetData: string): Promise<BigNumber> {
|
|
||||||
const tokenContract = await this._getTokenContractFromAssetDataAsync(assetData);
|
|
||||||
const balance = new BigNumber(await tokenContract.balanceOf(userAddress).callAsync());
|
|
||||||
return balance;
|
|
||||||
}
|
|
||||||
public async setBalanceAsync(userAddress: string, assetData: string, amount: BigNumber): Promise<void> {
|
|
||||||
const tokenContract = await this._getTokenContractFromAssetDataAsync(assetData);
|
|
||||||
await tokenContract
|
|
||||||
.setBalance(userAddress, amount)
|
|
||||||
.awaitTransactionSuccessAsync(
|
|
||||||
{ from: this._contractOwnerAddress },
|
|
||||||
{ pollingIntervalMs: constants.AWAIT_TRANSACTION_MINED_MS },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
public async getProxyAllowanceAsync(userAddress: string, assetData: string): Promise<BigNumber> {
|
|
||||||
const tokenContract = await this._getTokenContractFromAssetDataAsync(assetData);
|
|
||||||
const proxyAddress = (this._proxyContract as ERC20ProxyContract).address;
|
|
||||||
const allowance = new BigNumber(await tokenContract.allowance(userAddress, proxyAddress).callAsync());
|
|
||||||
return allowance;
|
|
||||||
}
|
|
||||||
public async setAllowanceAsync(userAddress: string, assetData: string, amount: BigNumber): Promise<void> {
|
|
||||||
const tokenContract = await this._getTokenContractFromAssetDataAsync(assetData);
|
|
||||||
const proxyAddress = (this._proxyContract as ERC20ProxyContract).address;
|
|
||||||
await tokenContract.approve(proxyAddress, amount).awaitTransactionSuccessAsync({ from: userAddress });
|
|
||||||
}
|
|
||||||
public async getBalancesAsync(): Promise<ERC20BalancesByOwner> {
|
|
||||||
this._validateDummyTokenContractsExistOrThrow();
|
|
||||||
const balancesByOwner: ERC20BalancesByOwner = {};
|
|
||||||
const balances: BigNumber[] = [];
|
|
||||||
const balanceInfo: Array<{ tokenOwnerAddress: string; tokenAddress: string }> = [];
|
|
||||||
for (const dummyTokenContract of this._dummyTokenContracts) {
|
|
||||||
for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
|
|
||||||
balances.push(await dummyTokenContract.balanceOf(tokenOwnerAddress).callAsync());
|
|
||||||
balanceInfo.push({
|
|
||||||
tokenOwnerAddress,
|
|
||||||
tokenAddress: dummyTokenContract.address,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_.forEach(balances, (balance, balanceIndex) => {
|
|
||||||
const tokenAddress = balanceInfo[balanceIndex].tokenAddress;
|
|
||||||
const tokenOwnerAddress = balanceInfo[balanceIndex].tokenOwnerAddress;
|
|
||||||
if (balancesByOwner[tokenOwnerAddress] === undefined) {
|
|
||||||
balancesByOwner[tokenOwnerAddress] = {};
|
|
||||||
}
|
|
||||||
const wrappedBalance = new BigNumber(balance);
|
|
||||||
balancesByOwner[tokenOwnerAddress][tokenAddress] = wrappedBalance;
|
|
||||||
});
|
|
||||||
return balancesByOwner;
|
|
||||||
}
|
|
||||||
public addDummyTokenContract(dummy: DummyERC20TokenContract): void {
|
|
||||||
if (this._dummyTokenContracts !== undefined) {
|
|
||||||
this._dummyTokenContracts.push(dummy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public addTokenOwnerAddress(address: string): void {
|
|
||||||
this._tokenOwnerAddresses.push(address);
|
|
||||||
}
|
|
||||||
public getTokenOwnerAddresses(): string[] {
|
|
||||||
return this._tokenOwnerAddresses;
|
|
||||||
}
|
|
||||||
public getTokenAddresses(): string[] {
|
|
||||||
const tokenAddresses = _.map(this._dummyTokenContracts, dummyTokenContract => dummyTokenContract.address);
|
|
||||||
return tokenAddresses;
|
|
||||||
}
|
|
||||||
private async _getTokenContractFromAssetDataAsync(assetData: string): Promise<DummyERC20TokenContract> {
|
|
||||||
const tokenAddress = this._assetDataInterface.getABIDecodedTransactionData<string>('ERC20Token', assetData); // tslint:disable-line:no-unused-variable
|
|
||||||
const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === tokenAddress);
|
|
||||||
if (tokenContractIfExists === undefined) {
|
|
||||||
throw new Error(`Token: ${tokenAddress} was not deployed through ERC20Wrapper`);
|
|
||||||
}
|
|
||||||
return tokenContractIfExists;
|
|
||||||
}
|
|
||||||
private _validateDummyTokenContractsExistOrThrow(): void {
|
|
||||||
if (this._dummyTokenContracts === undefined) {
|
|
||||||
throw new Error('Dummy ERC20 tokens not yet deployed, please call "deployDummyTokensAsync"');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private _validateProxyContractExistsOrThrow(): void {
|
|
||||||
if (this._proxyContract === undefined) {
|
|
||||||
throw new Error('ERC20 proxy contract not yet deployed, please call "deployProxyAsync"');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,220 +0,0 @@
|
|||||||
import { artifacts as erc721Artifacts, DummyERC721TokenContract } from '@0x/contracts-erc721';
|
|
||||||
import { constants, ERC721TokenIdsByOwner, txDefaults } from '@0x/contracts-test-utils';
|
|
||||||
import { generatePseudoRandomSalt } from '@0x/order-utils';
|
|
||||||
import { BigNumber } from '@0x/utils';
|
|
||||||
import { ZeroExProvider } from 'ethereum-types';
|
|
||||||
import * as _ from 'lodash';
|
|
||||||
|
|
||||||
import { artifacts } from './artifacts';
|
|
||||||
|
|
||||||
import { ERC721ProxyContract } from './wrappers';
|
|
||||||
|
|
||||||
export class ERC721Wrapper {
|
|
||||||
private readonly _tokenOwnerAddresses: string[];
|
|
||||||
private readonly _contractOwnerAddress: string;
|
|
||||||
private readonly _provider: ZeroExProvider;
|
|
||||||
private readonly _dummyTokenContracts: DummyERC721TokenContract[];
|
|
||||||
private _proxyContract?: ERC721ProxyContract;
|
|
||||||
private _proxyIdIfExists?: string;
|
|
||||||
private _initialTokenIdsByOwner: ERC721TokenIdsByOwner = {};
|
|
||||||
constructor(provider: ZeroExProvider, tokenOwnerAddresses: string[], contractOwnerAddress: string) {
|
|
||||||
this._provider = provider;
|
|
||||||
this._dummyTokenContracts = [];
|
|
||||||
this._tokenOwnerAddresses = tokenOwnerAddresses;
|
|
||||||
this._contractOwnerAddress = contractOwnerAddress;
|
|
||||||
}
|
|
||||||
public async deployDummyTokensAsync(): Promise<DummyERC721TokenContract[]> {
|
|
||||||
// tslint:disable-next-line:no-unused-variable
|
|
||||||
for (const i of _.times(constants.NUM_DUMMY_ERC721_TO_DEPLOY)) {
|
|
||||||
this._dummyTokenContracts.push(
|
|
||||||
await DummyERC721TokenContract.deployFrom0xArtifactAsync(
|
|
||||||
erc721Artifacts.DummyERC721Token,
|
|
||||||
this._provider,
|
|
||||||
txDefaults,
|
|
||||||
artifacts,
|
|
||||||
constants.DUMMY_TOKEN_NAME,
|
|
||||||
constants.DUMMY_TOKEN_SYMBOL,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return this._dummyTokenContracts;
|
|
||||||
}
|
|
||||||
public async deployProxyAsync(): Promise<ERC721ProxyContract> {
|
|
||||||
this._proxyContract = await ERC721ProxyContract.deployFrom0xArtifactAsync(
|
|
||||||
artifacts.ERC721Proxy,
|
|
||||||
this._provider,
|
|
||||||
txDefaults,
|
|
||||||
artifacts,
|
|
||||||
);
|
|
||||||
this._proxyIdIfExists = await this._proxyContract.getProxyId().callAsync();
|
|
||||||
return this._proxyContract;
|
|
||||||
}
|
|
||||||
public getProxyId(): string {
|
|
||||||
this._validateProxyContractExistsOrThrow();
|
|
||||||
return this._proxyIdIfExists as string;
|
|
||||||
}
|
|
||||||
public async setBalancesAndAllowancesAsync(): Promise<void> {
|
|
||||||
this._validateDummyTokenContractsExistOrThrow();
|
|
||||||
this._validateProxyContractExistsOrThrow();
|
|
||||||
this._initialTokenIdsByOwner = {};
|
|
||||||
for (const dummyTokenContract of this._dummyTokenContracts) {
|
|
||||||
for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
|
|
||||||
// tslint:disable-next-line:no-unused-variable
|
|
||||||
for (const i of _.times(constants.NUM_ERC721_TOKENS_TO_MINT)) {
|
|
||||||
const tokenId = generatePseudoRandomSalt();
|
|
||||||
await this.mintAsync(dummyTokenContract.address, tokenId, tokenOwnerAddress);
|
|
||||||
if (this._initialTokenIdsByOwner[tokenOwnerAddress] === undefined) {
|
|
||||||
this._initialTokenIdsByOwner[tokenOwnerAddress] = {
|
|
||||||
[dummyTokenContract.address]: [],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (this._initialTokenIdsByOwner[tokenOwnerAddress][dummyTokenContract.address] === undefined) {
|
|
||||||
this._initialTokenIdsByOwner[tokenOwnerAddress][dummyTokenContract.address] = [];
|
|
||||||
}
|
|
||||||
this._initialTokenIdsByOwner[tokenOwnerAddress][dummyTokenContract.address].push(tokenId);
|
|
||||||
|
|
||||||
await this.approveProxyForAllAsync(dummyTokenContract.address, tokenOwnerAddress, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public async doesTokenExistAsync(tokenAddress: string, tokenId: BigNumber): Promise<boolean> {
|
|
||||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
|
||||||
const owner = await tokenContract.ownerOf(tokenId).callAsync();
|
|
||||||
const doesExist = owner !== constants.NULL_ADDRESS;
|
|
||||||
return doesExist;
|
|
||||||
}
|
|
||||||
public async approveProxyAsync(tokenAddress: string, tokenId: BigNumber): Promise<void> {
|
|
||||||
const proxyAddress = (this._proxyContract as ERC721ProxyContract).address;
|
|
||||||
await this.approveAsync(proxyAddress, tokenAddress, tokenId);
|
|
||||||
}
|
|
||||||
public async approveProxyForAllAsync(
|
|
||||||
tokenAddress: string,
|
|
||||||
ownerAddress: string,
|
|
||||||
isApproved: boolean,
|
|
||||||
): Promise<void> {
|
|
||||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
|
||||||
const proxyAddress = (this._proxyContract as ERC721ProxyContract).address;
|
|
||||||
await tokenContract.setApprovalForAll(proxyAddress, isApproved).awaitTransactionSuccessAsync({
|
|
||||||
from: ownerAddress,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
public async approveAsync(to: string, tokenAddress: string, tokenId: BigNumber): Promise<void> {
|
|
||||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
|
||||||
const tokenOwner = await this.ownerOfAsync(tokenAddress, tokenId);
|
|
||||||
await tokenContract.approve(to, tokenId).awaitTransactionSuccessAsync({ from: tokenOwner });
|
|
||||||
}
|
|
||||||
public async transferFromAsync(
|
|
||||||
tokenAddress: string,
|
|
||||||
tokenId: BigNumber,
|
|
||||||
currentOwner: string,
|
|
||||||
userAddress: string,
|
|
||||||
): Promise<void> {
|
|
||||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
|
||||||
await tokenContract.transferFrom(currentOwner, userAddress, tokenId).awaitTransactionSuccessAsync({
|
|
||||||
from: currentOwner,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
public async mintAsync(tokenAddress: string, tokenId: BigNumber, userAddress: string): Promise<void> {
|
|
||||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
|
||||||
await tokenContract.mint(userAddress, tokenId).awaitTransactionSuccessAsync({
|
|
||||||
from: this._contractOwnerAddress,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
public async burnAsync(tokenAddress: string, tokenId: BigNumber, owner: string): Promise<void> {
|
|
||||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
|
||||||
await tokenContract.burn(owner, tokenId).awaitTransactionSuccessAsync({ from: this._contractOwnerAddress });
|
|
||||||
}
|
|
||||||
public async ownerOfAsync(tokenAddress: string, tokenId: BigNumber): Promise<string> {
|
|
||||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
|
||||||
const owner = await tokenContract.ownerOf(tokenId).callAsync();
|
|
||||||
return owner;
|
|
||||||
}
|
|
||||||
public async isOwnerAsync(userAddress: string, tokenAddress: string, tokenId: BigNumber): Promise<boolean> {
|
|
||||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
|
||||||
const tokenOwner = await tokenContract.ownerOf(tokenId).callAsync();
|
|
||||||
const isOwner = tokenOwner === userAddress;
|
|
||||||
return isOwner;
|
|
||||||
}
|
|
||||||
public async isProxyApprovedForAllAsync(userAddress: string, tokenAddress: string): Promise<boolean> {
|
|
||||||
this._validateProxyContractExistsOrThrow();
|
|
||||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
|
||||||
const operator = (this._proxyContract as ERC721ProxyContract).address;
|
|
||||||
const didApproveAll = await tokenContract.isApprovedForAll(userAddress, operator).callAsync();
|
|
||||||
return didApproveAll;
|
|
||||||
}
|
|
||||||
public async isProxyApprovedAsync(tokenAddress: string, tokenId: BigNumber): Promise<boolean> {
|
|
||||||
this._validateProxyContractExistsOrThrow();
|
|
||||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
|
||||||
const approvedAddress = await tokenContract.getApproved(tokenId).callAsync();
|
|
||||||
const proxyAddress = (this._proxyContract as ERC721ProxyContract).address;
|
|
||||||
const isProxyAnApprovedOperator = approvedAddress === proxyAddress;
|
|
||||||
return isProxyAnApprovedOperator;
|
|
||||||
}
|
|
||||||
public async getBalancesAsync(): Promise<ERC721TokenIdsByOwner> {
|
|
||||||
this._validateDummyTokenContractsExistOrThrow();
|
|
||||||
this._validateBalancesAndAllowancesSetOrThrow();
|
|
||||||
const tokenIdsByOwner: ERC721TokenIdsByOwner = {};
|
|
||||||
const tokenOwnerAddresses: string[] = [];
|
|
||||||
const tokenInfo: Array<{ tokenId: BigNumber; tokenAddress: string }> = [];
|
|
||||||
for (const dummyTokenContract of this._dummyTokenContracts) {
|
|
||||||
for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
|
|
||||||
const initialTokenOwnerIds = this._initialTokenIdsByOwner[tokenOwnerAddress][
|
|
||||||
dummyTokenContract.address
|
|
||||||
];
|
|
||||||
for (const tokenId of initialTokenOwnerIds) {
|
|
||||||
tokenOwnerAddresses.push(await dummyTokenContract.ownerOf(tokenId).callAsync());
|
|
||||||
tokenInfo.push({
|
|
||||||
tokenId,
|
|
||||||
tokenAddress: dummyTokenContract.address,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_.forEach(tokenOwnerAddresses, (tokenOwnerAddress, ownerIndex) => {
|
|
||||||
const tokenAddress = tokenInfo[ownerIndex].tokenAddress;
|
|
||||||
const tokenId = tokenInfo[ownerIndex].tokenId;
|
|
||||||
if (tokenIdsByOwner[tokenOwnerAddress] === undefined) {
|
|
||||||
tokenIdsByOwner[tokenOwnerAddress] = {
|
|
||||||
[tokenAddress]: [],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (tokenIdsByOwner[tokenOwnerAddress][tokenAddress] === undefined) {
|
|
||||||
tokenIdsByOwner[tokenOwnerAddress][tokenAddress] = [];
|
|
||||||
}
|
|
||||||
tokenIdsByOwner[tokenOwnerAddress][tokenAddress].push(tokenId);
|
|
||||||
});
|
|
||||||
return tokenIdsByOwner;
|
|
||||||
}
|
|
||||||
public getTokenOwnerAddresses(): string[] {
|
|
||||||
return this._tokenOwnerAddresses;
|
|
||||||
}
|
|
||||||
public getTokenAddresses(): string[] {
|
|
||||||
const tokenAddresses = _.map(this._dummyTokenContracts, dummyTokenContract => dummyTokenContract.address);
|
|
||||||
return tokenAddresses;
|
|
||||||
}
|
|
||||||
private _getTokenContractFromAssetData(tokenAddress: string): DummyERC721TokenContract {
|
|
||||||
const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === tokenAddress);
|
|
||||||
if (tokenContractIfExists === undefined) {
|
|
||||||
throw new Error(`Token: ${tokenAddress} was not deployed through ERC20Wrapper`);
|
|
||||||
}
|
|
||||||
return tokenContractIfExists;
|
|
||||||
}
|
|
||||||
private _validateDummyTokenContractsExistOrThrow(): void {
|
|
||||||
if (this._dummyTokenContracts === undefined) {
|
|
||||||
throw new Error('Dummy ERC721 tokens not yet deployed, please call "deployDummyTokensAsync"');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private _validateProxyContractExistsOrThrow(): void {
|
|
||||||
if (this._proxyContract === undefined) {
|
|
||||||
throw new Error('ERC721 proxy contract not yet deployed, please call "deployProxyAsync"');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private _validateBalancesAndAllowancesSetOrThrow(): void {
|
|
||||||
if (_.keys(this._initialTokenIdsByOwner).length === 0) {
|
|
||||||
throw new Error(
|
|
||||||
'Dummy ERC721 balances and allowances not yet set, please call "setBalancesAndAllowancesAsync"',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
export { artifacts } from './artifacts';
|
|
||||||
export {
|
|
||||||
BalancerBridgeContract,
|
|
||||||
ChaiBridgeContract,
|
|
||||||
ERC1155ProxyContract,
|
|
||||||
ERC20BridgeProxyContract,
|
|
||||||
ERC20ProxyContract,
|
|
||||||
ERC721ProxyContract,
|
|
||||||
Eth2DaiBridgeContract,
|
|
||||||
DydxBridgeContract,
|
|
||||||
IAssetDataContract,
|
|
||||||
IAssetProxyContract,
|
|
||||||
IChaiContract,
|
|
||||||
IDydxContract,
|
|
||||||
KyberBridgeContract,
|
|
||||||
MultiAssetProxyContract,
|
|
||||||
StaticCallProxyContract,
|
|
||||||
TestDydxBridgeContract,
|
|
||||||
TestStaticCallTargetContract,
|
|
||||||
UniswapBridgeContract,
|
|
||||||
DexForwarderBridgeContract,
|
|
||||||
} from './wrappers';
|
|
||||||
|
|
||||||
export { ERC20Wrapper } from './erc20_wrapper';
|
|
||||||
export { ERC721Wrapper } from './erc721_wrapper';
|
|
||||||
export { ERC1155ProxyWrapper } from './erc1155_proxy_wrapper';
|
|
||||||
export { ERC1155MintableContract, Erc1155Wrapper } from '@0x/contracts-erc1155';
|
|
||||||
export { DummyERC20TokenContract } from '@0x/contracts-erc20';
|
|
||||||
export { DummyERC721TokenContract } from '@0x/contracts-erc721';
|
|
||||||
export { AssetProxyId } from '@0x/types';
|
|
||||||
export {
|
|
||||||
ERC1155HoldingsByOwner,
|
|
||||||
ERC20BalancesByOwner,
|
|
||||||
ERC721TokenIdsByOwner,
|
|
||||||
ERC1155FungibleHoldingsByOwner,
|
|
||||||
ERC1155NonFungibleHoldingsByOwner,
|
|
||||||
} from '@0x/contracts-test-utils';
|
|
||||||
export {
|
|
||||||
TransactionReceiptWithDecodedLogs,
|
|
||||||
Provider,
|
|
||||||
ZeroExProvider,
|
|
||||||
JSONRPCRequestPayload,
|
|
||||||
JSONRPCErrorCallback,
|
|
||||||
TransactionReceiptStatus,
|
|
||||||
JSONRPCResponsePayload,
|
|
||||||
JSONRPCResponseError,
|
|
||||||
ContractArtifact,
|
|
||||||
ContractChains,
|
|
||||||
CompilerOpts,
|
|
||||||
StandardContractOutput,
|
|
||||||
CompilerSettings,
|
|
||||||
ContractChainData,
|
|
||||||
ContractAbi,
|
|
||||||
DevdocOutput,
|
|
||||||
EvmOutput,
|
|
||||||
CompilerSettingsMetadata,
|
|
||||||
OptimizerSettings,
|
|
||||||
OutputField,
|
|
||||||
ParamDescription,
|
|
||||||
EvmBytecodeOutput,
|
|
||||||
EvmBytecodeOutputLinkReferences,
|
|
||||||
AbiDefinition,
|
|
||||||
FunctionAbi,
|
|
||||||
EventAbi,
|
|
||||||
RevertErrorAbi,
|
|
||||||
EventParameter,
|
|
||||||
DataItem,
|
|
||||||
MethodAbi,
|
|
||||||
ConstructorAbi,
|
|
||||||
FallbackAbi,
|
|
||||||
ConstructorStateMutability,
|
|
||||||
TupleDataItem,
|
|
||||||
StateMutability,
|
|
||||||
} from 'ethereum-types';
|
|
||||||
|
|
||||||
export {
|
|
||||||
decodeERC1155AssetData,
|
|
||||||
decodeERC20AssetData,
|
|
||||||
decodeERC20BridgeAssetData,
|
|
||||||
decodeERC721AssetData,
|
|
||||||
decodeMultiAssetData,
|
|
||||||
decodeStaticCallAssetData,
|
|
||||||
encodeERC1155AssetData,
|
|
||||||
encodeERC20AssetData,
|
|
||||||
encodeERC20BridgeAssetData,
|
|
||||||
encodeERC721AssetData,
|
|
||||||
encodeMultiAssetData,
|
|
||||||
encodeStaticCallAssetData,
|
|
||||||
getAssetDataProxyId,
|
|
||||||
} from './asset_data';
|
|
||||||
|
|
||||||
export * from './dydx_bridge_encoder';
|
|
||||||
export * from './dex_forwarder_bridge';
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
/*
|
|
||||||
* -----------------------------------------------------------------------------
|
|
||||||
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
|
||||||
* -----------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
export * from '../generated-wrappers/balancer_bridge';
|
|
||||||
export * from '../generated-wrappers/bancor_bridge';
|
|
||||||
export * from '../generated-wrappers/chai_bridge';
|
|
||||||
export * from '../generated-wrappers/cream_bridge';
|
|
||||||
export * from '../generated-wrappers/crypto_com_bridge';
|
|
||||||
export * from '../generated-wrappers/curve_bridge';
|
|
||||||
export * from '../generated-wrappers/d_o_d_o_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';
|
|
||||||
export * from '../generated-wrappers/erc20_proxy';
|
|
||||||
export * from '../generated-wrappers/erc721_proxy';
|
|
||||||
export * from '../generated-wrappers/eth2_dai_bridge';
|
|
||||||
export * from '../generated-wrappers/i_asset_data';
|
|
||||||
export * from '../generated-wrappers/i_asset_proxy';
|
|
||||||
export * from '../generated-wrappers/i_asset_proxy_dispatcher';
|
|
||||||
export * from '../generated-wrappers/i_authorizable';
|
|
||||||
export * from '../generated-wrappers/i_balancer_pool';
|
|
||||||
export * from '../generated-wrappers/i_bancor_network';
|
|
||||||
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_m_stable';
|
|
||||||
export * from '../generated-wrappers/i_mooniswap';
|
|
||||||
export * from '../generated-wrappers/i_shell';
|
|
||||||
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/m_stable_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/mooniswap_bridge';
|
|
||||||
export * from '../generated-wrappers/multi_asset_proxy';
|
|
||||||
export * from '../generated-wrappers/ownable';
|
|
||||||
export * from '../generated-wrappers/shell_bridge';
|
|
||||||
export * from '../generated-wrappers/snow_swap_bridge';
|
|
||||||
export * from '../generated-wrappers/static_call_proxy';
|
|
||||||
export * from '../generated-wrappers/sushi_swap_bridge';
|
|
||||||
export * from '../generated-wrappers/swerve_bridge';
|
|
||||||
export * from '../generated-wrappers/test_bancor_bridge';
|
|
||||||
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';
|
|
||||||
@@ -1,127 +0,0 @@
|
|||||||
/*
|
|
||||||
* -----------------------------------------------------------------------------
|
|
||||||
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
|
||||||
* -----------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
import { ContractArtifact } from 'ethereum-types';
|
|
||||||
|
|
||||||
import * as BalancerBridge from '../test/generated-artifacts/BalancerBridge.json';
|
|
||||||
import * as BancorBridge from '../test/generated-artifacts/BancorBridge.json';
|
|
||||||
import * as ChaiBridge from '../test/generated-artifacts/ChaiBridge.json';
|
|
||||||
import * as CreamBridge from '../test/generated-artifacts/CreamBridge.json';
|
|
||||||
import * as CryptoComBridge from '../test/generated-artifacts/CryptoComBridge.json';
|
|
||||||
import * as CurveBridge from '../test/generated-artifacts/CurveBridge.json';
|
|
||||||
import * as DexForwarderBridge from '../test/generated-artifacts/DexForwarderBridge.json';
|
|
||||||
import * as DODOBridge from '../test/generated-artifacts/DODOBridge.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';
|
|
||||||
import * as ERC20Proxy from '../test/generated-artifacts/ERC20Proxy.json';
|
|
||||||
import * as ERC721Proxy from '../test/generated-artifacts/ERC721Proxy.json';
|
|
||||||
import * as Eth2DaiBridge from '../test/generated-artifacts/Eth2DaiBridge.json';
|
|
||||||
import * as IAssetData from '../test/generated-artifacts/IAssetData.json';
|
|
||||||
import * as IAssetProxy from '../test/generated-artifacts/IAssetProxy.json';
|
|
||||||
import * as IAssetProxyDispatcher from '../test/generated-artifacts/IAssetProxyDispatcher.json';
|
|
||||||
import * as IAuthorizable from '../test/generated-artifacts/IAuthorizable.json';
|
|
||||||
import * as IBalancerPool from '../test/generated-artifacts/IBalancerPool.json';
|
|
||||||
import * as IBancorNetwork from '../test/generated-artifacts/IBancorNetwork.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 IMooniswap from '../test/generated-artifacts/IMooniswap.json';
|
|
||||||
import * as IMStable from '../test/generated-artifacts/IMStable.json';
|
|
||||||
import * as IShell from '../test/generated-artifacts/IShell.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 MooniswapBridge from '../test/generated-artifacts/MooniswapBridge.json';
|
|
||||||
import * as MStableBridge from '../test/generated-artifacts/MStableBridge.json';
|
|
||||||
import * as MultiAssetProxy from '../test/generated-artifacts/MultiAssetProxy.json';
|
|
||||||
import * as Ownable from '../test/generated-artifacts/Ownable.json';
|
|
||||||
import * as ShellBridge from '../test/generated-artifacts/ShellBridge.json';
|
|
||||||
import * as SnowSwapBridge from '../test/generated-artifacts/SnowSwapBridge.json';
|
|
||||||
import * as StaticCallProxy from '../test/generated-artifacts/StaticCallProxy.json';
|
|
||||||
import * as SushiSwapBridge from '../test/generated-artifacts/SushiSwapBridge.json';
|
|
||||||
import * as SwerveBridge from '../test/generated-artifacts/SwerveBridge.json';
|
|
||||||
import * as TestBancorBridge from '../test/generated-artifacts/TestBancorBridge.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,
|
|
||||||
Ownable: Ownable as ContractArtifact,
|
|
||||||
ERC1155Proxy: ERC1155Proxy as ContractArtifact,
|
|
||||||
ERC20BridgeProxy: ERC20BridgeProxy as ContractArtifact,
|
|
||||||
ERC20Proxy: ERC20Proxy as ContractArtifact,
|
|
||||||
ERC721Proxy: ERC721Proxy as ContractArtifact,
|
|
||||||
MultiAssetProxy: MultiAssetProxy as ContractArtifact,
|
|
||||||
StaticCallProxy: StaticCallProxy as ContractArtifact,
|
|
||||||
BalancerBridge: BalancerBridge as ContractArtifact,
|
|
||||||
BancorBridge: BancorBridge as ContractArtifact,
|
|
||||||
ChaiBridge: ChaiBridge as ContractArtifact,
|
|
||||||
CreamBridge: CreamBridge as ContractArtifact,
|
|
||||||
CryptoComBridge: CryptoComBridge as ContractArtifact,
|
|
||||||
CurveBridge: CurveBridge as ContractArtifact,
|
|
||||||
DODOBridge: DODOBridge as ContractArtifact,
|
|
||||||
DexForwarderBridge: DexForwarderBridge as ContractArtifact,
|
|
||||||
DydxBridge: DydxBridge as ContractArtifact,
|
|
||||||
Eth2DaiBridge: Eth2DaiBridge as ContractArtifact,
|
|
||||||
KyberBridge: KyberBridge as ContractArtifact,
|
|
||||||
MStableBridge: MStableBridge as ContractArtifact,
|
|
||||||
MixinGasToken: MixinGasToken as ContractArtifact,
|
|
||||||
MooniswapBridge: MooniswapBridge as ContractArtifact,
|
|
||||||
ShellBridge: ShellBridge as ContractArtifact,
|
|
||||||
SnowSwapBridge: SnowSwapBridge as ContractArtifact,
|
|
||||||
SushiSwapBridge: SushiSwapBridge as ContractArtifact,
|
|
||||||
SwerveBridge: SwerveBridge 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,
|
|
||||||
IBalancerPool: IBalancerPool as ContractArtifact,
|
|
||||||
IBancorNetwork: IBancorNetwork 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,
|
|
||||||
IMStable: IMStable as ContractArtifact,
|
|
||||||
IMooniswap: IMooniswap as ContractArtifact,
|
|
||||||
IShell: IShell as ContractArtifact,
|
|
||||||
IUniswapExchange: IUniswapExchange as ContractArtifact,
|
|
||||||
IUniswapExchangeFactory: IUniswapExchangeFactory as ContractArtifact,
|
|
||||||
IUniswapV2Router01: IUniswapV2Router01 as ContractArtifact,
|
|
||||||
TestBancorBridge: TestBancorBridge 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,
|
|
||||||
};
|
|
||||||
@@ -1,128 +0,0 @@
|
|||||||
import { blockchainTests, expect, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils';
|
|
||||||
import { RevertReason } from '@0x/types';
|
|
||||||
import { BigNumber } from '@0x/utils';
|
|
||||||
|
|
||||||
import { artifacts } from './artifacts';
|
|
||||||
|
|
||||||
import { MixinAuthorizableContract } from './wrappers';
|
|
||||||
|
|
||||||
blockchainTests.resets('Authorizable', () => {
|
|
||||||
let owner: string;
|
|
||||||
let notOwner: string;
|
|
||||||
let address: string;
|
|
||||||
let authorizable: MixinAuthorizableContract;
|
|
||||||
|
|
||||||
before(async () => {
|
|
||||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
|
||||||
[owner, address, notOwner] = accounts.slice(0, 3);
|
|
||||||
authorizable = await MixinAuthorizableContract.deployFrom0xArtifactAsync(
|
|
||||||
artifacts.MixinAuthorizable,
|
|
||||||
provider,
|
|
||||||
txDefaults,
|
|
||||||
artifacts,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('addAuthorizedAddress', () => {
|
|
||||||
it('should revert if not called by owner', async () => {
|
|
||||||
const tx = authorizable.addAuthorizedAddress(notOwner).sendTransactionAsync({ from: notOwner });
|
|
||||||
return expect(tx).to.revertWith(RevertReason.OnlyContractOwner);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should allow owner to add an authorized address', async () => {
|
|
||||||
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
|
||||||
const isAuthorized = await authorizable.authorized(address).callAsync();
|
|
||||||
expect(isAuthorized).to.be.true();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should revert if owner attempts to authorize a duplicate address', async () => {
|
|
||||||
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
|
||||||
const tx = authorizable.addAuthorizedAddress(address).sendTransactionAsync({ from: owner });
|
|
||||||
return expect(tx).to.revertWith(RevertReason.TargetAlreadyAuthorized);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('removeAuthorizedAddress', () => {
|
|
||||||
it('should revert if not called by owner', async () => {
|
|
||||||
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
|
||||||
const tx = authorizable.removeAuthorizedAddress(address).sendTransactionAsync({ from: notOwner });
|
|
||||||
return expect(tx).to.revertWith(RevertReason.OnlyContractOwner);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should allow owner to remove an authorized address', async () => {
|
|
||||||
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
|
||||||
await authorizable.removeAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
|
||||||
const isAuthorized = await authorizable.authorized(address).callAsync();
|
|
||||||
expect(isAuthorized).to.be.false();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should revert if owner attempts to remove an address that is not authorized', async () => {
|
|
||||||
const tx = authorizable.removeAuthorizedAddress(address).sendTransactionAsync({ from: owner });
|
|
||||||
return expect(tx).to.revertWith(RevertReason.TargetNotAuthorized);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('removeAuthorizedAddressAtIndex', () => {
|
|
||||||
it('should revert if not called by owner', async () => {
|
|
||||||
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
|
||||||
const index = new BigNumber(0);
|
|
||||||
const tx = authorizable
|
|
||||||
.removeAuthorizedAddressAtIndex(address, index)
|
|
||||||
.sendTransactionAsync({ from: notOwner });
|
|
||||||
return expect(tx).to.revertWith(RevertReason.OnlyContractOwner);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should revert if index is >= authorities.length', async () => {
|
|
||||||
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
|
||||||
const index = new BigNumber(1);
|
|
||||||
const tx = authorizable
|
|
||||||
.removeAuthorizedAddressAtIndex(address, index)
|
|
||||||
.sendTransactionAsync({ from: owner });
|
|
||||||
return expect(tx).to.revertWith(RevertReason.IndexOutOfBounds);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should revert if owner attempts to remove an address that is not authorized', async () => {
|
|
||||||
const index = new BigNumber(0);
|
|
||||||
const tx = authorizable
|
|
||||||
.removeAuthorizedAddressAtIndex(address, index)
|
|
||||||
.sendTransactionAsync({ from: owner });
|
|
||||||
return expect(tx).to.revertWith(RevertReason.TargetNotAuthorized);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should revert if address at index does not match target', async () => {
|
|
||||||
const address1 = address;
|
|
||||||
const address2 = notOwner;
|
|
||||||
await authorizable.addAuthorizedAddress(address1).awaitTransactionSuccessAsync({ from: owner });
|
|
||||||
await authorizable.addAuthorizedAddress(address2).awaitTransactionSuccessAsync({ from: owner });
|
|
||||||
const address1Index = new BigNumber(0);
|
|
||||||
const tx = authorizable
|
|
||||||
.removeAuthorizedAddressAtIndex(address2, address1Index)
|
|
||||||
.sendTransactionAsync({ from: owner });
|
|
||||||
return expect(tx).to.revertWith(RevertReason.AuthorizedAddressMismatch);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should allow owner to remove an authorized address', async () => {
|
|
||||||
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
|
||||||
const index = new BigNumber(0);
|
|
||||||
await authorizable.removeAuthorizedAddressAtIndex(address, index).awaitTransactionSuccessAsync({
|
|
||||||
from: owner,
|
|
||||||
});
|
|
||||||
const isAuthorized = await authorizable.authorized(address).callAsync();
|
|
||||||
expect(isAuthorized).to.be.false();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('getAuthorizedAddresses', () => {
|
|
||||||
it('should return all authorized addresses', async () => {
|
|
||||||
const initial = await authorizable.getAuthorizedAddresses().callAsync();
|
|
||||||
expect(initial).to.have.length(0);
|
|
||||||
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
|
||||||
const afterAdd = await authorizable.getAuthorizedAddresses().callAsync();
|
|
||||||
expect(afterAdd).to.have.length(1);
|
|
||||||
expect(afterAdd).to.include(address);
|
|
||||||
await authorizable.removeAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
|
||||||
const afterRemove = await authorizable.getAuthorizedAddresses().callAsync();
|
|
||||||
expect(afterRemove).to.have.length(0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,205 +0,0 @@
|
|||||||
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 { TestBancorBridgeContract } from './generated-wrappers/test_bancor_bridge';
|
|
||||||
import {
|
|
||||||
TestBancorBridgeConvertByPathInputEventArgs as ConvertByPathArgs,
|
|
||||||
TestBancorBridgeEvents as ContractEvents,
|
|
||||||
TestBancorBridgeTokenApproveEventArgs as TokenApproveArgs,
|
|
||||||
TestBancorBridgeTokenTransferEventArgs as TokenTransferArgs,
|
|
||||||
} from './wrappers';
|
|
||||||
|
|
||||||
blockchainTests.resets('Bancor 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: TestBancorBridgeContract;
|
|
||||||
|
|
||||||
before(async () => {
|
|
||||||
testContract = await TestBancorBridgeContract.deployFrom0xArtifactAsync(
|
|
||||||
artifacts.TestBancorBridge,
|
|
||||||
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(3).fill(constants.NULL_ADDRESS),
|
|
||||||
amount,
|
|
||||||
toAddress: randomAddress(),
|
|
||||||
fromTokenBalance: getRandomInteger(1, FROM_TOKEN_BASE.times(100)),
|
|
||||||
routerRevertReason: '',
|
|
||||||
...opts,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const bridgeDataEncoder = AbiEncoder.create('(address[], 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.setNetworkRevertReason(_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.
|
|
||||||
bridgeDataEncoder.encode([
|
|
||||||
_opts.tokenAddressesPath,
|
|
||||||
await testContract.getNetworkAddress().callAsync(),
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
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 BancorNetwork.convertByPath()', async () => {
|
|
||||||
const { opts, result, logs } = await transferFromAsync();
|
|
||||||
expect(result).to.eq(AssetProxyId.ERC20Bridge, 'asset proxy id');
|
|
||||||
const transfers = filterLogsToArguments<ConvertByPathArgs>(logs, ContractEvents.ConvertByPathInput);
|
|
||||||
|
|
||||||
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].feeRecipient).to.eq(constants.NULL_ADDRESS);
|
|
||||||
expect(transfers[0].feeAmount).to.bignumber.eq(new BigNumber(0));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('sets allowance for "from" token', async () => {
|
|
||||||
const { logs } = await transferFromAsync();
|
|
||||||
const approvals = filterLogsToArguments<TokenApproveArgs>(logs, ContractEvents.TokenApprove);
|
|
||||||
const networkAddress = await testContract.getNetworkAddress().callAsync();
|
|
||||||
expect(approvals.length).to.eq(1);
|
|
||||||
expect(approvals[0].spender).to.eq(networkAddress);
|
|
||||||
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 BancorNetwork.convertByPath()', async () => {
|
|
||||||
const { opts, result, logs } = await transferFromAsync({
|
|
||||||
tokenAddressesPath: Array(5).fill(constants.NULL_ADDRESS),
|
|
||||||
});
|
|
||||||
expect(result).to.eq(AssetProxyId.ERC20Bridge, 'asset proxy id');
|
|
||||||
const transfers = filterLogsToArguments<ConvertByPathArgs>(logs, ContractEvents.ConvertByPathInput);
|
|
||||||
|
|
||||||
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].feeRecipient).to.eq(constants.NULL_ADDRESS);
|
|
||||||
expect(transfers[0].feeAmount).to.bignumber.eq(new BigNumber(0));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
import { ERC20TokenContract } from '@0x/contracts-erc20';
|
|
||||||
import { blockchainTests, constants, expect, randomAddress } from '@0x/contracts-test-utils';
|
|
||||||
import { AssetProxyId, RevertReason } from '@0x/types';
|
|
||||||
import { BigNumber } from '@0x/utils';
|
|
||||||
|
|
||||||
import { artifacts } from './artifacts';
|
|
||||||
import { TestChaiBridgeContract } from './wrappers';
|
|
||||||
|
|
||||||
blockchainTests.resets('ChaiBridge unit tests', env => {
|
|
||||||
let chaiBridgeContract: TestChaiBridgeContract;
|
|
||||||
let testDaiContract: ERC20TokenContract;
|
|
||||||
let fromAddress: string;
|
|
||||||
let toAddress: string;
|
|
||||||
|
|
||||||
const alwaysRevertAddress = '0x0000000000000000000000000000000000000001';
|
|
||||||
const amount = new BigNumber(1);
|
|
||||||
|
|
||||||
before(async () => {
|
|
||||||
[fromAddress, toAddress] = await env.getAccountAddressesAsync();
|
|
||||||
chaiBridgeContract = await TestChaiBridgeContract.deployFrom0xArtifactAsync(
|
|
||||||
artifacts.TestChaiBridge,
|
|
||||||
env.provider,
|
|
||||||
env.txDefaults,
|
|
||||||
artifacts,
|
|
||||||
);
|
|
||||||
const testChaiDaiAddress = await chaiBridgeContract.testChaiDai().callAsync();
|
|
||||||
testDaiContract = new ERC20TokenContract(testChaiDaiAddress, env.provider, env.txDefaults);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('bridgeTransferFrom()', () => {
|
|
||||||
it('fails if not called by ERC20BridgeProxy', async () => {
|
|
||||||
return expect(
|
|
||||||
chaiBridgeContract
|
|
||||||
.bridgeTransferFrom(randomAddress(), fromAddress, toAddress, amount, constants.NULL_BYTES)
|
|
||||||
.awaitTransactionSuccessAsync({ from: alwaysRevertAddress }),
|
|
||||||
).to.revertWith(RevertReason.ChaiBridgeOnlyCallableByErc20BridgeProxy);
|
|
||||||
});
|
|
||||||
it('returns magic bytes upon success', async () => {
|
|
||||||
const magicBytes = await chaiBridgeContract
|
|
||||||
.bridgeTransferFrom(randomAddress(), fromAddress, toAddress, amount, constants.NULL_BYTES)
|
|
||||||
.callAsync();
|
|
||||||
expect(magicBytes).to.eq(AssetProxyId.ERC20Bridge);
|
|
||||||
});
|
|
||||||
it('should increase the Dai balance of `toAddress` by `amount` if successful', async () => {
|
|
||||||
const initialBalance = await testDaiContract.balanceOf(toAddress).callAsync();
|
|
||||||
await chaiBridgeContract
|
|
||||||
.bridgeTransferFrom(randomAddress(), fromAddress, toAddress, amount, constants.NULL_BYTES)
|
|
||||||
.awaitTransactionSuccessAsync();
|
|
||||||
const endBalance = await testDaiContract.balanceOf(toAddress).callAsync();
|
|
||||||
expect(endBalance).to.bignumber.eq(initialBalance.plus(amount));
|
|
||||||
});
|
|
||||||
it('fails if the `chai.draw` call fails', async () => {
|
|
||||||
return expect(
|
|
||||||
chaiBridgeContract
|
|
||||||
.bridgeTransferFrom(randomAddress(), alwaysRevertAddress, toAddress, amount, constants.NULL_BYTES)
|
|
||||||
.awaitTransactionSuccessAsync(),
|
|
||||||
).to.revertWith(RevertReason.ChaiBridgeDrawDaiFailed);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,354 +0,0 @@
|
|||||||
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 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('succeeds with no bridge calls and an input balance', async () => {
|
|
||||||
const bridgeData = dexForwarderBridgeDataEncoder.encode({
|
|
||||||
inputToken,
|
|
||||||
calls: [],
|
|
||||||
});
|
|
||||||
await callBridgeTransferFromAsync({
|
|
||||||
bridgeData,
|
|
||||||
sellAmount: new BigNumber(1),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('succeeds if entire input token balance is not consumed', async () => {
|
|
||||||
const bridgeData = dexForwarderBridgeDataEncoder.encode({
|
|
||||||
inputToken,
|
|
||||||
calls: allBridgeCalls,
|
|
||||||
});
|
|
||||||
await callBridgeTransferFromAsync({
|
|
||||||
bridgeData,
|
|
||||||
sellAmount: totalFillableInputAmount.plus(1),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
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');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,399 +0,0 @@
|
|||||||
import { LibMathRevertErrors } from '@0x/contracts-exchange-libs';
|
|
||||||
import { blockchainTests, constants, expect, verifyEventsFromLogs } from '@0x/contracts-test-utils';
|
|
||||||
import { AssetProxyId, RevertReason } from '@0x/types';
|
|
||||||
import { BigNumber } from '@0x/utils';
|
|
||||||
import * as _ from 'lodash';
|
|
||||||
|
|
||||||
import { DydxBridgeActionType, DydxBridgeData, dydxBridgeDataEncoder } from '../src/dydx_bridge_encoder';
|
|
||||||
import { ERC20BridgeProxyContract, IAssetDataContract } from '../src/wrappers';
|
|
||||||
|
|
||||||
import { artifacts } from './artifacts';
|
|
||||||
import { TestDydxBridgeContract, TestDydxBridgeEvents } from './wrappers';
|
|
||||||
|
|
||||||
blockchainTests.resets('DydxBridge unit tests', env => {
|
|
||||||
const defaultAccountNumber = new BigNumber(1);
|
|
||||||
const marketId = new BigNumber(2);
|
|
||||||
const defaultAmount = new BigNumber(4);
|
|
||||||
const notAuthorized = '0x0000000000000000000000000000000000000001';
|
|
||||||
const defaultDepositAction = {
|
|
||||||
actionType: DydxBridgeActionType.Deposit,
|
|
||||||
accountIdx: constants.ZERO_AMOUNT,
|
|
||||||
marketId,
|
|
||||||
conversionRateNumerator: constants.ZERO_AMOUNT,
|
|
||||||
conversionRateDenominator: constants.ZERO_AMOUNT,
|
|
||||||
};
|
|
||||||
const defaultWithdrawAction = {
|
|
||||||
actionType: DydxBridgeActionType.Withdraw,
|
|
||||||
accountIdx: constants.ZERO_AMOUNT,
|
|
||||||
marketId,
|
|
||||||
conversionRateNumerator: constants.ZERO_AMOUNT,
|
|
||||||
conversionRateDenominator: constants.ZERO_AMOUNT,
|
|
||||||
};
|
|
||||||
let testContract: TestDydxBridgeContract;
|
|
||||||
let testProxyContract: ERC20BridgeProxyContract;
|
|
||||||
let assetDataEncoder: IAssetDataContract;
|
|
||||||
let owner: string;
|
|
||||||
let authorized: string;
|
|
||||||
let accountOwner: string;
|
|
||||||
let receiver: string;
|
|
||||||
|
|
||||||
before(async () => {
|
|
||||||
// Get accounts
|
|
||||||
const accounts = await env.web3Wrapper.getAvailableAddressesAsync();
|
|
||||||
[owner, authorized, accountOwner, receiver] = accounts;
|
|
||||||
|
|
||||||
// Deploy dydx bridge
|
|
||||||
testContract = await TestDydxBridgeContract.deployFrom0xArtifactAsync(
|
|
||||||
artifacts.TestDydxBridge,
|
|
||||||
env.provider,
|
|
||||||
env.txDefaults,
|
|
||||||
artifacts,
|
|
||||||
[accountOwner, receiver],
|
|
||||||
);
|
|
||||||
|
|
||||||
// Deploy test erc20 bridge proxy
|
|
||||||
testProxyContract = await ERC20BridgeProxyContract.deployFrom0xArtifactAsync(
|
|
||||||
artifacts.ERC20BridgeProxy,
|
|
||||||
env.provider,
|
|
||||||
env.txDefaults,
|
|
||||||
artifacts,
|
|
||||||
);
|
|
||||||
await testProxyContract.addAuthorizedAddress(authorized).awaitTransactionSuccessAsync({ from: owner });
|
|
||||||
|
|
||||||
// Setup asset data encoder
|
|
||||||
assetDataEncoder = new IAssetDataContract(constants.NULL_ADDRESS, env.provider);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('bridgeTransferFrom()', () => {
|
|
||||||
const callBridgeTransferFrom = async (
|
|
||||||
from: string,
|
|
||||||
to: string,
|
|
||||||
amount: BigNumber,
|
|
||||||
bridgeData: DydxBridgeData,
|
|
||||||
sender: string,
|
|
||||||
): Promise<string> => {
|
|
||||||
const returnValue = await testContract
|
|
||||||
.bridgeTransferFrom(
|
|
||||||
constants.NULL_ADDRESS,
|
|
||||||
from,
|
|
||||||
to,
|
|
||||||
amount,
|
|
||||||
dydxBridgeDataEncoder.encode({ bridgeData }),
|
|
||||||
)
|
|
||||||
.callAsync({ from: sender });
|
|
||||||
return returnValue;
|
|
||||||
};
|
|
||||||
const executeBridgeTransferFromAndVerifyEvents = async (
|
|
||||||
from: string,
|
|
||||||
to: string,
|
|
||||||
amount: BigNumber,
|
|
||||||
bridgeData: DydxBridgeData,
|
|
||||||
sender: string,
|
|
||||||
): Promise<void> => {
|
|
||||||
// Execute transaction.
|
|
||||||
const txReceipt = await testContract
|
|
||||||
.bridgeTransferFrom(
|
|
||||||
constants.NULL_ADDRESS,
|
|
||||||
from,
|
|
||||||
to,
|
|
||||||
amount,
|
|
||||||
dydxBridgeDataEncoder.encode({ bridgeData }),
|
|
||||||
)
|
|
||||||
.awaitTransactionSuccessAsync({ from: sender });
|
|
||||||
|
|
||||||
// Verify `OperateAccount` event.
|
|
||||||
const expectedOperateAccountEvents = [];
|
|
||||||
for (const accountNumber of bridgeData.accountNumbers) {
|
|
||||||
expectedOperateAccountEvents.push({
|
|
||||||
owner: accountOwner,
|
|
||||||
number: accountNumber,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
verifyEventsFromLogs(txReceipt.logs, expectedOperateAccountEvents, TestDydxBridgeEvents.OperateAccount);
|
|
||||||
|
|
||||||
// Verify `OperateAction` event.
|
|
||||||
const weiDenomination = 0;
|
|
||||||
const deltaAmountRef = 0;
|
|
||||||
const expectedOperateActionEvents = [];
|
|
||||||
for (const action of bridgeData.actions) {
|
|
||||||
expectedOperateActionEvents.push({
|
|
||||||
actionType: action.actionType as number,
|
|
||||||
accountIdx: action.accountIdx,
|
|
||||||
amountSign: action.actionType === DydxBridgeActionType.Deposit ? true : false,
|
|
||||||
amountDenomination: weiDenomination,
|
|
||||||
amountRef: deltaAmountRef,
|
|
||||||
amountValue: action.conversionRateDenominator.gt(0)
|
|
||||||
? amount
|
|
||||||
.times(action.conversionRateNumerator)
|
|
||||||
.dividedToIntegerBy(action.conversionRateDenominator)
|
|
||||||
: amount,
|
|
||||||
primaryMarketId: marketId,
|
|
||||||
secondaryMarketId: constants.ZERO_AMOUNT,
|
|
||||||
otherAddress: action.actionType === DydxBridgeActionType.Deposit ? from : to,
|
|
||||||
otherAccountId: constants.ZERO_AMOUNT,
|
|
||||||
data: '0x',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
verifyEventsFromLogs(txReceipt.logs, expectedOperateActionEvents, TestDydxBridgeEvents.OperateAction);
|
|
||||||
};
|
|
||||||
it('succeeds when calling with zero amount', async () => {
|
|
||||||
const bridgeData = {
|
|
||||||
accountNumbers: [defaultAccountNumber],
|
|
||||||
actions: [defaultDepositAction],
|
|
||||||
};
|
|
||||||
await executeBridgeTransferFromAndVerifyEvents(
|
|
||||||
accountOwner,
|
|
||||||
receiver,
|
|
||||||
constants.ZERO_AMOUNT,
|
|
||||||
bridgeData,
|
|
||||||
authorized,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('succeeds when calling with no accounts', async () => {
|
|
||||||
const bridgeData = {
|
|
||||||
accountNumbers: [],
|
|
||||||
actions: [defaultDepositAction],
|
|
||||||
};
|
|
||||||
await executeBridgeTransferFromAndVerifyEvents(
|
|
||||||
accountOwner,
|
|
||||||
receiver,
|
|
||||||
defaultAmount,
|
|
||||||
bridgeData,
|
|
||||||
authorized,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('succeeds when calling with no actions', async () => {
|
|
||||||
const bridgeData = {
|
|
||||||
accountNumbers: [defaultAccountNumber],
|
|
||||||
actions: [],
|
|
||||||
};
|
|
||||||
await executeBridgeTransferFromAndVerifyEvents(
|
|
||||||
accountOwner,
|
|
||||||
receiver,
|
|
||||||
defaultAmount,
|
|
||||||
bridgeData,
|
|
||||||
authorized,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('succeeds when calling `operate` with the `deposit` action and a single account', async () => {
|
|
||||||
const bridgeData = {
|
|
||||||
accountNumbers: [defaultAccountNumber],
|
|
||||||
actions: [defaultDepositAction],
|
|
||||||
};
|
|
||||||
await executeBridgeTransferFromAndVerifyEvents(
|
|
||||||
accountOwner,
|
|
||||||
receiver,
|
|
||||||
defaultAmount,
|
|
||||||
bridgeData,
|
|
||||||
authorized,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('succeeds when calling `operate` with the `deposit` action and multiple accounts', async () => {
|
|
||||||
const bridgeData = {
|
|
||||||
accountNumbers: [defaultAccountNumber, defaultAccountNumber.plus(1)],
|
|
||||||
actions: [defaultDepositAction],
|
|
||||||
};
|
|
||||||
await executeBridgeTransferFromAndVerifyEvents(
|
|
||||||
accountOwner,
|
|
||||||
receiver,
|
|
||||||
defaultAmount,
|
|
||||||
bridgeData,
|
|
||||||
authorized,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('succeeds when calling `operate` with the `withdraw` action and a single account', async () => {
|
|
||||||
const bridgeData = {
|
|
||||||
accountNumbers: [defaultAccountNumber],
|
|
||||||
actions: [defaultWithdrawAction],
|
|
||||||
};
|
|
||||||
await executeBridgeTransferFromAndVerifyEvents(
|
|
||||||
accountOwner,
|
|
||||||
receiver,
|
|
||||||
defaultAmount,
|
|
||||||
bridgeData,
|
|
||||||
authorized,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('succeeds when calling `operate` with the `withdraw` action and multiple accounts', async () => {
|
|
||||||
const bridgeData = {
|
|
||||||
accountNumbers: [defaultAccountNumber, defaultAccountNumber.plus(1)],
|
|
||||||
actions: [defaultWithdrawAction],
|
|
||||||
};
|
|
||||||
await executeBridgeTransferFromAndVerifyEvents(
|
|
||||||
accountOwner,
|
|
||||||
receiver,
|
|
||||||
defaultAmount,
|
|
||||||
bridgeData,
|
|
||||||
authorized,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('succeeds when calling `operate` with the `deposit` action and multiple accounts', async () => {
|
|
||||||
const bridgeData = {
|
|
||||||
accountNumbers: [defaultAccountNumber, defaultAccountNumber.plus(1)],
|
|
||||||
actions: [defaultWithdrawAction, defaultDepositAction],
|
|
||||||
};
|
|
||||||
await executeBridgeTransferFromAndVerifyEvents(
|
|
||||||
accountOwner,
|
|
||||||
receiver,
|
|
||||||
defaultAmount,
|
|
||||||
bridgeData,
|
|
||||||
authorized,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('succeeds when calling `operate` with multiple actions under a single account', async () => {
|
|
||||||
const bridgeData = {
|
|
||||||
accountNumbers: [defaultAccountNumber],
|
|
||||||
actions: [defaultWithdrawAction, defaultDepositAction],
|
|
||||||
};
|
|
||||||
await executeBridgeTransferFromAndVerifyEvents(
|
|
||||||
accountOwner,
|
|
||||||
receiver,
|
|
||||||
defaultAmount,
|
|
||||||
bridgeData,
|
|
||||||
authorized,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('succeeds when scaling the `amount` to deposit', async () => {
|
|
||||||
const conversionRateNumerator = new BigNumber(1);
|
|
||||||
const conversionRateDenominator = new BigNumber(2);
|
|
||||||
const bridgeData = {
|
|
||||||
accountNumbers: [defaultAccountNumber],
|
|
||||||
actions: [
|
|
||||||
defaultWithdrawAction,
|
|
||||||
{
|
|
||||||
...defaultDepositAction,
|
|
||||||
conversionRateNumerator,
|
|
||||||
conversionRateDenominator,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
await executeBridgeTransferFromAndVerifyEvents(
|
|
||||||
accountOwner,
|
|
||||||
receiver,
|
|
||||||
defaultAmount,
|
|
||||||
bridgeData,
|
|
||||||
authorized,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('succeeds when scaling the `amount` to withdraw', async () => {
|
|
||||||
const conversionRateNumerator = new BigNumber(1);
|
|
||||||
const conversionRateDenominator = new BigNumber(2);
|
|
||||||
const bridgeData = {
|
|
||||||
accountNumbers: [defaultAccountNumber],
|
|
||||||
actions: [
|
|
||||||
defaultDepositAction,
|
|
||||||
{
|
|
||||||
...defaultWithdrawAction,
|
|
||||||
conversionRateNumerator,
|
|
||||||
conversionRateDenominator,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
await executeBridgeTransferFromAndVerifyEvents(
|
|
||||||
accountOwner,
|
|
||||||
receiver,
|
|
||||||
defaultAmount,
|
|
||||||
bridgeData,
|
|
||||||
authorized,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('reverts if not called by the ERC20 Bridge Proxy', async () => {
|
|
||||||
const bridgeData = {
|
|
||||||
accountNumbers: [defaultAccountNumber],
|
|
||||||
actions: [defaultDepositAction],
|
|
||||||
};
|
|
||||||
const callBridgeTransferFromPromise = callBridgeTransferFrom(
|
|
||||||
accountOwner,
|
|
||||||
receiver,
|
|
||||||
defaultAmount,
|
|
||||||
bridgeData,
|
|
||||||
notAuthorized,
|
|
||||||
);
|
|
||||||
const expectedError = RevertReason.DydxBridgeOnlyCallableByErc20BridgeProxy;
|
|
||||||
return expect(callBridgeTransferFromPromise).to.revertWith(expectedError);
|
|
||||||
});
|
|
||||||
it('should return magic bytes if call succeeds', async () => {
|
|
||||||
const bridgeData = {
|
|
||||||
accountNumbers: [defaultAccountNumber],
|
|
||||||
actions: [defaultDepositAction],
|
|
||||||
};
|
|
||||||
const returnValue = await callBridgeTransferFrom(
|
|
||||||
accountOwner,
|
|
||||||
receiver,
|
|
||||||
defaultAmount,
|
|
||||||
bridgeData,
|
|
||||||
authorized,
|
|
||||||
);
|
|
||||||
expect(returnValue).to.equal(AssetProxyId.ERC20Bridge);
|
|
||||||
});
|
|
||||||
it('should revert when `Operate` reverts', async () => {
|
|
||||||
// Set revert flag.
|
|
||||||
await testContract.setRevertOnOperate(true).awaitTransactionSuccessAsync();
|
|
||||||
|
|
||||||
// Execute transfer.
|
|
||||||
const bridgeData = {
|
|
||||||
accountNumbers: [defaultAccountNumber],
|
|
||||||
actions: [defaultDepositAction],
|
|
||||||
};
|
|
||||||
const tx = callBridgeTransferFrom(accountOwner, receiver, defaultAmount, bridgeData, authorized);
|
|
||||||
const expectedError = 'TestDydxBridge/SHOULD_REVERT_ON_OPERATE';
|
|
||||||
return expect(tx).to.revertWith(expectedError);
|
|
||||||
});
|
|
||||||
it('should revert when there is a rounding error', async () => {
|
|
||||||
// Setup a rounding error
|
|
||||||
const conversionRateNumerator = new BigNumber(5318);
|
|
||||||
const conversionRateDenominator = new BigNumber(47958);
|
|
||||||
const amount = new BigNumber(9000);
|
|
||||||
const bridgeData = {
|
|
||||||
accountNumbers: [defaultAccountNumber],
|
|
||||||
actions: [
|
|
||||||
defaultDepositAction,
|
|
||||||
{
|
|
||||||
...defaultWithdrawAction,
|
|
||||||
conversionRateNumerator,
|
|
||||||
conversionRateDenominator,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
// Execute transfer and assert error.
|
|
||||||
const tx = callBridgeTransferFrom(accountOwner, receiver, amount, bridgeData, authorized);
|
|
||||||
const expectedError = new LibMathRevertErrors.RoundingError(
|
|
||||||
conversionRateNumerator,
|
|
||||||
conversionRateDenominator,
|
|
||||||
amount,
|
|
||||||
);
|
|
||||||
return expect(tx).to.revertWith(expectedError);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('ERC20BridgeProxy.transferFrom()', () => {
|
|
||||||
const bridgeData = {
|
|
||||||
accountNumbers: [defaultAccountNumber],
|
|
||||||
actions: [defaultWithdrawAction],
|
|
||||||
};
|
|
||||||
let assetData: string;
|
|
||||||
|
|
||||||
before(async () => {
|
|
||||||
const testTokenAddress = await testContract.getTestToken().callAsync();
|
|
||||||
assetData = assetDataEncoder
|
|
||||||
.ERC20Bridge(testTokenAddress, testContract.address, dydxBridgeDataEncoder.encode({ bridgeData }))
|
|
||||||
.getABIEncodedTransactionData();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should succeed if `bridgeTransferFrom` succeeds', async () => {
|
|
||||||
await testProxyContract
|
|
||||||
.transferFrom(assetData, accountOwner, receiver, defaultAmount)
|
|
||||||
.awaitTransactionSuccessAsync({ from: authorized });
|
|
||||||
});
|
|
||||||
it('should revert if `bridgeTransferFrom` reverts', async () => {
|
|
||||||
// Set revert flag.
|
|
||||||
await testContract.setRevertOnOperate(true).awaitTransactionSuccessAsync();
|
|
||||||
const tx = testProxyContract
|
|
||||||
.transferFrom(assetData, accountOwner, receiver, defaultAmount)
|
|
||||||
.awaitTransactionSuccessAsync({ from: authorized });
|
|
||||||
const expectedError = 'TestDydxBridge/SHOULD_REVERT_ON_OPERATE';
|
|
||||||
return expect(tx).to.revertWith(expectedError);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,287 +0,0 @@
|
|||||||
import {
|
|
||||||
blockchainTests,
|
|
||||||
constants,
|
|
||||||
expect,
|
|
||||||
getRandomInteger,
|
|
||||||
Numberish,
|
|
||||||
randomAddress,
|
|
||||||
} from '@0x/contracts-test-utils';
|
|
||||||
import { AuthorizableRevertErrors } from '@0x/contracts-utils';
|
|
||||||
import { AssetProxyId } from '@0x/types';
|
|
||||||
import { AbiEncoder, BigNumber, hexUtils, StringRevertError } from '@0x/utils';
|
|
||||||
import { DecodedLogs } from 'ethereum-types';
|
|
||||||
import * as _ from 'lodash';
|
|
||||||
|
|
||||||
import { artifacts } from './artifacts';
|
|
||||||
|
|
||||||
import { ERC20BridgeProxyContract, TestERC20BridgeContract } from './wrappers';
|
|
||||||
|
|
||||||
blockchainTests.resets('ERC20BridgeProxy unit tests', env => {
|
|
||||||
const PROXY_ID = AssetProxyId.ERC20Bridge;
|
|
||||||
const BRIDGE_SUCCESS_RETURN_DATA = hexUtils.rightPad(PROXY_ID);
|
|
||||||
let owner: string;
|
|
||||||
let badCaller: string;
|
|
||||||
let assetProxy: ERC20BridgeProxyContract;
|
|
||||||
let bridgeContract: TestERC20BridgeContract;
|
|
||||||
let testTokenAddress: string;
|
|
||||||
|
|
||||||
before(async () => {
|
|
||||||
[owner, badCaller] = await env.getAccountAddressesAsync();
|
|
||||||
assetProxy = await ERC20BridgeProxyContract.deployFrom0xArtifactAsync(
|
|
||||||
artifacts.ERC20BridgeProxy,
|
|
||||||
env.provider,
|
|
||||||
env.txDefaults,
|
|
||||||
artifacts,
|
|
||||||
);
|
|
||||||
bridgeContract = await TestERC20BridgeContract.deployFrom0xArtifactAsync(
|
|
||||||
artifacts.TestERC20Bridge,
|
|
||||||
env.provider,
|
|
||||||
env.txDefaults,
|
|
||||||
artifacts,
|
|
||||||
);
|
|
||||||
testTokenAddress = await bridgeContract.testToken().callAsync();
|
|
||||||
await assetProxy.addAuthorizedAddress(owner).awaitTransactionSuccessAsync();
|
|
||||||
});
|
|
||||||
|
|
||||||
interface AssetDataOpts {
|
|
||||||
tokenAddress: string;
|
|
||||||
bridgeAddress: string;
|
|
||||||
bridgeData: BridgeDataOpts;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface BridgeDataOpts {
|
|
||||||
transferAmount: Numberish;
|
|
||||||
revertError?: string;
|
|
||||||
returnData: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
function createAssetData(opts?: Partial<AssetDataOpts>): AssetDataOpts {
|
|
||||||
return _.merge(
|
|
||||||
{
|
|
||||||
tokenAddress: testTokenAddress,
|
|
||||||
bridgeAddress: bridgeContract.address,
|
|
||||||
bridgeData: createBridgeData(),
|
|
||||||
},
|
|
||||||
opts,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createBridgeData(opts?: Partial<BridgeDataOpts>): BridgeDataOpts {
|
|
||||||
return _.merge(
|
|
||||||
{
|
|
||||||
transferAmount: constants.ZERO_AMOUNT,
|
|
||||||
returnData: BRIDGE_SUCCESS_RETURN_DATA,
|
|
||||||
},
|
|
||||||
opts,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function encodeAssetData(opts: AssetDataOpts): string {
|
|
||||||
const encoder = AbiEncoder.createMethod('ERC20BridgeProxy', [
|
|
||||||
{ name: 'tokenAddress', type: 'address' },
|
|
||||||
{ name: 'bridgeAddress', type: 'address' },
|
|
||||||
{ name: 'bridgeData', type: 'bytes' },
|
|
||||||
]);
|
|
||||||
return encoder.encode([opts.tokenAddress, opts.bridgeAddress, encodeBridgeData(opts.bridgeData)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function encodeBridgeData(opts: BridgeDataOpts): string {
|
|
||||||
const encoder = AbiEncoder.create([
|
|
||||||
{ name: 'transferAmount', type: 'int256' },
|
|
||||||
{ name: 'revertData', type: 'bytes' },
|
|
||||||
{ name: 'returnData', type: 'bytes' },
|
|
||||||
]);
|
|
||||||
const revertErrorBytes =
|
|
||||||
opts.revertError !== undefined ? new StringRevertError(opts.revertError).encode() : '0x';
|
|
||||||
return encoder.encode([new BigNumber(opts.transferAmount), revertErrorBytes, opts.returnData]);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function setTestTokenBalanceAsync(_owner: string, balance: Numberish): Promise<void> {
|
|
||||||
await bridgeContract.setTestTokenBalance(_owner, new BigNumber(balance)).awaitTransactionSuccessAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('transferFrom()', () => {
|
|
||||||
interface TransferFromOpts {
|
|
||||||
assetData: AssetDataOpts;
|
|
||||||
from: string;
|
|
||||||
to: string;
|
|
||||||
amount: Numberish;
|
|
||||||
}
|
|
||||||
|
|
||||||
function createTransferFromOpts(opts?: Partial<TransferFromOpts>): TransferFromOpts {
|
|
||||||
const transferAmount = _.get(opts, ['amount'], getRandomInteger(1, 100e18)) as BigNumber;
|
|
||||||
return _.merge(
|
|
||||||
{
|
|
||||||
assetData: createAssetData({
|
|
||||||
bridgeData: createBridgeData({
|
|
||||||
transferAmount,
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
from: randomAddress(),
|
|
||||||
to: randomAddress(),
|
|
||||||
amount: transferAmount,
|
|
||||||
},
|
|
||||||
opts,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function transferFromAsync(opts?: Partial<TransferFromOpts>, caller?: string): Promise<DecodedLogs> {
|
|
||||||
const _opts = createTransferFromOpts(opts);
|
|
||||||
const { logs } = await assetProxy
|
|
||||||
.transferFrom(encodeAssetData(_opts.assetData), _opts.from, _opts.to, new BigNumber(_opts.amount))
|
|
||||||
.awaitTransactionSuccessAsync({ from: caller });
|
|
||||||
return (logs as any) as DecodedLogs;
|
|
||||||
}
|
|
||||||
|
|
||||||
it('succeeds if the bridge succeeds and balance increases by `amount`', async () => {
|
|
||||||
const tx = transferFromAsync();
|
|
||||||
return expect(tx).to.be.fulfilled('');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('succeeds if balance increases more than `amount`', async () => {
|
|
||||||
const amount = getRandomInteger(1, 100e18);
|
|
||||||
const tx = transferFromAsync({
|
|
||||||
amount,
|
|
||||||
assetData: createAssetData({
|
|
||||||
bridgeData: createBridgeData({
|
|
||||||
transferAmount: amount.plus(1),
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
return expect(tx).to.be.fulfilled('');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('passes the correct arguments to the bridge contract', async () => {
|
|
||||||
const opts = createTransferFromOpts();
|
|
||||||
const logs = await transferFromAsync(opts);
|
|
||||||
expect(logs.length).to.eq(1);
|
|
||||||
const args = logs[0].args;
|
|
||||||
expect(args.tokenAddress).to.eq(opts.assetData.tokenAddress);
|
|
||||||
expect(args.from).to.eq(opts.from);
|
|
||||||
expect(args.to).to.eq(opts.to);
|
|
||||||
expect(args.amount).to.bignumber.eq(opts.amount);
|
|
||||||
expect(args.bridgeData).to.eq(encodeBridgeData(opts.assetData.bridgeData));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('fails if not called by an authorized address', async () => {
|
|
||||||
const tx = transferFromAsync({}, badCaller);
|
|
||||||
return expect(tx).to.revertWith(new AuthorizableRevertErrors.SenderNotAuthorizedError(badCaller));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('fails if asset data is truncated', async () => {
|
|
||||||
const opts = createTransferFromOpts();
|
|
||||||
const truncatedAssetData = hexUtils.slice(encodeAssetData(opts.assetData), 0, -1);
|
|
||||||
const tx = assetProxy
|
|
||||||
.transferFrom(truncatedAssetData, opts.from, opts.to, new BigNumber(opts.amount))
|
|
||||||
.awaitTransactionSuccessAsync();
|
|
||||||
return expect(tx).to.be.rejected();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('fails if bridge returns nothing', async () => {
|
|
||||||
const tx = transferFromAsync({
|
|
||||||
assetData: createAssetData({
|
|
||||||
bridgeData: createBridgeData({
|
|
||||||
returnData: '0x',
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
// This will actually revert when the AP tries to decode the return
|
|
||||||
// value.
|
|
||||||
return expect(tx).to.be.rejected();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('fails if bridge returns true', async () => {
|
|
||||||
const tx = transferFromAsync({
|
|
||||||
assetData: createAssetData({
|
|
||||||
bridgeData: createBridgeData({
|
|
||||||
returnData: hexUtils.leftPad('0x1'),
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
// This will actually revert when the AP tries to decode the return
|
|
||||||
// value.
|
|
||||||
return expect(tx).to.be.rejected();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('fails if bridge returns 0x1', async () => {
|
|
||||||
const tx = transferFromAsync({
|
|
||||||
assetData: createAssetData({
|
|
||||||
bridgeData: createBridgeData({
|
|
||||||
returnData: hexUtils.rightPad('0x1'),
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
return expect(tx).to.revertWith('BRIDGE_FAILED');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('fails if bridge is an EOA', async () => {
|
|
||||||
const tx = transferFromAsync({
|
|
||||||
assetData: createAssetData({
|
|
||||||
bridgeAddress: randomAddress(),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
// This will actually revert when the AP tries to decode the return
|
|
||||||
// value.
|
|
||||||
return expect(tx).to.be.rejected();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('fails if bridge reverts', async () => {
|
|
||||||
const revertError = 'FOOBAR';
|
|
||||||
const tx = transferFromAsync({
|
|
||||||
assetData: createAssetData({
|
|
||||||
bridgeData: createBridgeData({
|
|
||||||
revertError,
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
return expect(tx).to.revertWith(revertError);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('fails if balance of `to` increases by less than `amount`', async () => {
|
|
||||||
const amount = getRandomInteger(1, 100e18);
|
|
||||||
const tx = transferFromAsync({
|
|
||||||
amount,
|
|
||||||
assetData: createAssetData({
|
|
||||||
bridgeData: createBridgeData({
|
|
||||||
transferAmount: amount.minus(1),
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
return expect(tx).to.revertWith('BRIDGE_UNDERPAY');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('fails if balance of `to` decreases', async () => {
|
|
||||||
const toAddress = randomAddress();
|
|
||||||
await setTestTokenBalanceAsync(toAddress, 1e18);
|
|
||||||
const tx = transferFromAsync({
|
|
||||||
to: toAddress,
|
|
||||||
assetData: createAssetData({
|
|
||||||
bridgeData: createBridgeData({
|
|
||||||
transferAmount: -1,
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
return expect(tx).to.revertWith('BRIDGE_UNDERPAY');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('balanceOf()', () => {
|
|
||||||
it('retrieves the balance of the encoded token', async () => {
|
|
||||||
const _owner = randomAddress();
|
|
||||||
const balance = getRandomInteger(1, 100e18);
|
|
||||||
await bridgeContract.setTestTokenBalance(_owner, balance).awaitTransactionSuccessAsync();
|
|
||||||
const assetData = createAssetData({
|
|
||||||
tokenAddress: testTokenAddress,
|
|
||||||
});
|
|
||||||
const actualBalance = await assetProxy.balanceOf(encodeAssetData(assetData), _owner).callAsync();
|
|
||||||
expect(actualBalance).to.bignumber.eq(balance);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('getProxyId()', () => {
|
|
||||||
it('returns the correct proxy ID', async () => {
|
|
||||||
const proxyId = await assetProxy.getProxyId().callAsync();
|
|
||||||
expect(proxyId).to.eq(PROXY_ID);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,191 +0,0 @@
|
|||||||
import {
|
|
||||||
blockchainTests,
|
|
||||||
constants,
|
|
||||||
expect,
|
|
||||||
filterLogsToArguments,
|
|
||||||
getRandomInteger,
|
|
||||||
Numberish,
|
|
||||||
randomAddress,
|
|
||||||
} from '@0x/contracts-test-utils';
|
|
||||||
import { AssetProxyId } from '@0x/types';
|
|
||||||
import { BigNumber, hexUtils, RawRevertError } from '@0x/utils';
|
|
||||||
import { DecodedLogs } from 'ethereum-types';
|
|
||||||
import * as _ from 'lodash';
|
|
||||||
|
|
||||||
import { artifacts } from './artifacts';
|
|
||||||
|
|
||||||
import {
|
|
||||||
TestEth2DaiBridgeContract,
|
|
||||||
TestEth2DaiBridgeEvents,
|
|
||||||
TestEth2DaiBridgeSellAllAmountEventArgs,
|
|
||||||
TestEth2DaiBridgeTokenApproveEventArgs,
|
|
||||||
TestEth2DaiBridgeTokenTransferEventArgs,
|
|
||||||
} from './wrappers';
|
|
||||||
|
|
||||||
blockchainTests.resets('Eth2DaiBridge unit tests', env => {
|
|
||||||
let testContract: TestEth2DaiBridgeContract;
|
|
||||||
|
|
||||||
before(async () => {
|
|
||||||
testContract = await TestEth2DaiBridgeContract.deployFrom0xArtifactAsync(
|
|
||||||
artifacts.TestEth2DaiBridge,
|
|
||||||
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 WithdrawToOpts {
|
|
||||||
toTokenAddress?: string;
|
|
||||||
fromTokenAddress?: string;
|
|
||||||
toAddress: string;
|
|
||||||
amount: Numberish;
|
|
||||||
fromTokenBalance: Numberish;
|
|
||||||
revertReason: string;
|
|
||||||
fillAmount: Numberish;
|
|
||||||
toTokentransferRevertReason: string;
|
|
||||||
toTokenTransferReturnData: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface WithdrawToResult {
|
|
||||||
opts: WithdrawToOpts;
|
|
||||||
result: string;
|
|
||||||
logs: DecodedLogs;
|
|
||||||
}
|
|
||||||
|
|
||||||
function createWithdrawToOpts(opts?: Partial<WithdrawToOpts>): WithdrawToOpts {
|
|
||||||
return {
|
|
||||||
toAddress: randomAddress(),
|
|
||||||
amount: getRandomInteger(1, 100e18),
|
|
||||||
revertReason: '',
|
|
||||||
fillAmount: getRandomInteger(1, 100e18),
|
|
||||||
fromTokenBalance: getRandomInteger(1, 100e18),
|
|
||||||
toTokentransferRevertReason: '',
|
|
||||||
toTokenTransferReturnData: hexUtils.leftPad(1),
|
|
||||||
...opts,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async function withdrawToAsync(opts?: Partial<WithdrawToOpts>): Promise<WithdrawToResult> {
|
|
||||||
const _opts = createWithdrawToOpts(opts);
|
|
||||||
// Set the fill behavior.
|
|
||||||
await testContract
|
|
||||||
.setFillBehavior(_opts.revertReason, new BigNumber(_opts.fillAmount))
|
|
||||||
.awaitTransactionSuccessAsync();
|
|
||||||
// Create tokens and balances.
|
|
||||||
if (_opts.fromTokenAddress === undefined) {
|
|
||||||
const createTokenFn = testContract.createToken(new BigNumber(_opts.fromTokenBalance));
|
|
||||||
_opts.fromTokenAddress = await createTokenFn.callAsync();
|
|
||||||
await createTokenFn.awaitTransactionSuccessAsync();
|
|
||||||
}
|
|
||||||
if (_opts.toTokenAddress === undefined) {
|
|
||||||
const createTokenFn = testContract.createToken(constants.ZERO_AMOUNT);
|
|
||||||
_opts.toTokenAddress = await createTokenFn.callAsync();
|
|
||||||
await createTokenFn.awaitTransactionSuccessAsync();
|
|
||||||
}
|
|
||||||
// Set the transfer behavior of `toTokenAddress`.
|
|
||||||
await testContract
|
|
||||||
.setTransferBehavior(
|
|
||||||
_opts.toTokenAddress,
|
|
||||||
_opts.toTokentransferRevertReason,
|
|
||||||
_opts.toTokenTransferReturnData,
|
|
||||||
)
|
|
||||||
.awaitTransactionSuccessAsync();
|
|
||||||
// Call bridgeTransferFrom().
|
|
||||||
const bridgeTransferFromFn = testContract.bridgeTransferFrom(
|
|
||||||
// "to" token address
|
|
||||||
_opts.toTokenAddress,
|
|
||||||
// Random from address.
|
|
||||||
randomAddress(),
|
|
||||||
// To address.
|
|
||||||
_opts.toAddress,
|
|
||||||
new BigNumber(_opts.amount),
|
|
||||||
// ABI-encode the "from" token address as the bridge data.
|
|
||||||
hexUtils.leftPad(_opts.fromTokenAddress as string),
|
|
||||||
);
|
|
||||||
const result = await bridgeTransferFromFn.callAsync();
|
|
||||||
const { logs } = await bridgeTransferFromFn.awaitTransactionSuccessAsync();
|
|
||||||
return {
|
|
||||||
opts: _opts,
|
|
||||||
result,
|
|
||||||
logs: (logs as any) as DecodedLogs,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
it('returns magic bytes on success', async () => {
|
|
||||||
const BRIDGE_SUCCESS_RETURN_DATA = AssetProxyId.ERC20Bridge;
|
|
||||||
const { result } = await withdrawToAsync();
|
|
||||||
expect(result).to.eq(BRIDGE_SUCCESS_RETURN_DATA);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('calls `Eth2Dai.sellAllAmount()`', async () => {
|
|
||||||
const { opts, logs } = await withdrawToAsync();
|
|
||||||
const transfers = filterLogsToArguments<TestEth2DaiBridgeSellAllAmountEventArgs>(
|
|
||||||
logs,
|
|
||||||
TestEth2DaiBridgeEvents.SellAllAmount,
|
|
||||||
);
|
|
||||||
expect(transfers.length).to.eq(1);
|
|
||||||
expect(transfers[0].sellToken).to.eq(opts.fromTokenAddress);
|
|
||||||
expect(transfers[0].buyToken).to.eq(opts.toTokenAddress);
|
|
||||||
expect(transfers[0].sellTokenAmount).to.bignumber.eq(opts.fromTokenBalance);
|
|
||||||
expect(transfers[0].minimumFillAmount).to.bignumber.eq(opts.amount);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('sets an unlimited allowance on the `fromTokenAddress` token', async () => {
|
|
||||||
const { opts, logs } = await withdrawToAsync();
|
|
||||||
const approvals = filterLogsToArguments<TestEth2DaiBridgeTokenApproveEventArgs>(
|
|
||||||
logs,
|
|
||||||
TestEth2DaiBridgeEvents.TokenApprove,
|
|
||||||
);
|
|
||||||
expect(approvals.length).to.eq(1);
|
|
||||||
expect(approvals[0].token).to.eq(opts.fromTokenAddress);
|
|
||||||
expect(approvals[0].spender).to.eq(testContract.address);
|
|
||||||
expect(approvals[0].allowance).to.bignumber.eq(constants.MAX_UINT256);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('transfers filled amount to `to`', async () => {
|
|
||||||
const { opts, logs } = await withdrawToAsync();
|
|
||||||
const transfers = filterLogsToArguments<TestEth2DaiBridgeTokenTransferEventArgs>(
|
|
||||||
logs,
|
|
||||||
TestEth2DaiBridgeEvents.TokenTransfer,
|
|
||||||
);
|
|
||||||
expect(transfers.length).to.eq(1);
|
|
||||||
expect(transfers[0].token).to.eq(opts.toTokenAddress);
|
|
||||||
expect(transfers[0].from).to.eq(testContract.address);
|
|
||||||
expect(transfers[0].to).to.eq(opts.toAddress);
|
|
||||||
expect(transfers[0].amount).to.bignumber.eq(opts.fillAmount);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('fails if `Eth2Dai.sellAllAmount()` reverts', async () => {
|
|
||||||
const opts = createWithdrawToOpts({ revertReason: 'FOOBAR' });
|
|
||||||
const tx = withdrawToAsync(opts);
|
|
||||||
return expect(tx).to.revertWith(opts.revertReason);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('fails if `toTokenAddress.transfer()` reverts', async () => {
|
|
||||||
const opts = createWithdrawToOpts({ toTokentransferRevertReason: 'FOOBAR' });
|
|
||||||
const tx = withdrawToAsync(opts);
|
|
||||||
return expect(tx).to.revertWith(opts.toTokentransferRevertReason);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('fails if `toTokenAddress.transfer()` returns false', async () => {
|
|
||||||
const opts = createWithdrawToOpts({ toTokenTransferReturnData: hexUtils.leftPad(0) });
|
|
||||||
const tx = withdrawToAsync(opts);
|
|
||||||
return expect(tx).to.revertWith(new RawRevertError(hexUtils.leftPad(0)));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('succeeds if `toTokenAddress.transfer()` returns true', async () => {
|
|
||||||
await withdrawToAsync({ toTokenTransferReturnData: hexUtils.leftPad(1) });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
import { env, EnvVars } from '@0x/dev-utils';
|
|
||||||
|
|
||||||
import { coverage, profiler, provider } from '@0x/contracts-test-utils';
|
|
||||||
import { providerUtils } from '@0x/utils';
|
|
||||||
|
|
||||||
before('start web3 provider', () => {
|
|
||||||
providerUtils.startProviderEngine(provider);
|
|
||||||
});
|
|
||||||
after('generate coverage report', async () => {
|
|
||||||
if (env.parseBoolean(EnvVars.SolidityCoverage)) {
|
|
||||||
const coverageSubprovider = coverage.getCoverageSubproviderSingleton();
|
|
||||||
await coverageSubprovider.writeCoverageAsync();
|
|
||||||
}
|
|
||||||
if (env.parseBoolean(EnvVars.SolidityProfiler)) {
|
|
||||||
const profilerSubprovider = profiler.getProfilerSubproviderSingleton();
|
|
||||||
await profilerSubprovider.writeProfilerOutputAsync();
|
|
||||||
}
|
|
||||||
provider.stop();
|
|
||||||
});
|
|
||||||
@@ -1,284 +0,0 @@
|
|||||||
import {
|
|
||||||
blockchainTests,
|
|
||||||
constants,
|
|
||||||
expect,
|
|
||||||
getRandomInteger,
|
|
||||||
getRandomPortion,
|
|
||||||
randomAddress,
|
|
||||||
verifyEventsFromLogs,
|
|
||||||
} from '@0x/contracts-test-utils';
|
|
||||||
import { AssetProxyId } from '@0x/types';
|
|
||||||
import { BigNumber, hexUtils } from '@0x/utils';
|
|
||||||
import { DecodedLogs } from 'ethereum-types';
|
|
||||||
import * as _ from 'lodash';
|
|
||||||
|
|
||||||
import { artifacts } from './artifacts';
|
|
||||||
|
|
||||||
import { TestKyberBridgeContract, TestKyberBridgeEvents } from './wrappers';
|
|
||||||
|
|
||||||
// TODO(dorothy-zbornak): Tests need to be updated.
|
|
||||||
blockchainTests.resets.skip('KyberBridge unit tests', env => {
|
|
||||||
const KYBER_ETH_ADDRESS = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee';
|
|
||||||
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);
|
|
||||||
const WETH_BASE = new BigNumber(10).pow(18);
|
|
||||||
const KYBER_RATE_BASE = WETH_BASE;
|
|
||||||
let testContract: TestKyberBridgeContract;
|
|
||||||
|
|
||||||
before(async () => {
|
|
||||||
testContract = await TestKyberBridgeContract.deployFrom0xArtifactAsync(
|
|
||||||
artifacts.TestKyberBridge,
|
|
||||||
env.provider,
|
|
||||||
env.txDefaults,
|
|
||||||
artifacts,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('isValidSignature()', () => {
|
|
||||||
it('returns success bytes', async () => {
|
|
||||||
const LEGACY_WALLET_MAGIC_VALUE = '0xb0671381';
|
|
||||||
const result = await testContract
|
|
||||||
.isValidSignature(hexUtils.random(), hexUtils.random(_.random(0, 32)))
|
|
||||||
.callAsync();
|
|
||||||
expect(result).to.eq(LEGACY_WALLET_MAGIC_VALUE);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('bridgeTransferFrom()', () => {
|
|
||||||
let fromTokenAddress: string;
|
|
||||||
let toTokenAddress: string;
|
|
||||||
let wethAddress: string;
|
|
||||||
|
|
||||||
before(async () => {
|
|
||||||
wethAddress = await testContract.weth().callAsync();
|
|
||||||
fromTokenAddress = await testContract.createToken(FROM_TOKEN_DECIMALS).callAsync();
|
|
||||||
await testContract.createToken(FROM_TOKEN_DECIMALS).awaitTransactionSuccessAsync();
|
|
||||||
toTokenAddress = await testContract.createToken(TO_TOKEN_DECIMALS).callAsync();
|
|
||||||
await testContract.createToken(TO_TOKEN_DECIMALS).awaitTransactionSuccessAsync();
|
|
||||||
});
|
|
||||||
|
|
||||||
const STATIC_KYBER_TRADE_ARGS = {
|
|
||||||
maxBuyTokenAmount: constants.MAX_UINT256,
|
|
||||||
walletId: constants.NULL_ADDRESS,
|
|
||||||
};
|
|
||||||
|
|
||||||
interface TransferFromOpts {
|
|
||||||
toTokenAddress: string;
|
|
||||||
fromTokenAddress: string;
|
|
||||||
toAddress: string;
|
|
||||||
// Amount to pass into `bridgeTransferFrom()`
|
|
||||||
amount: BigNumber;
|
|
||||||
// Amount to convert in `trade()`.
|
|
||||||
fillAmount: BigNumber;
|
|
||||||
// Token balance of the bridge.
|
|
||||||
fromTokenBalance: BigNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface TransferFromResult {
|
|
||||||
opts: TransferFromOpts;
|
|
||||||
result: string;
|
|
||||||
logs: DecodedLogs;
|
|
||||||
}
|
|
||||||
|
|
||||||
function createTransferFromOpts(opts?: Partial<TransferFromOpts>): TransferFromOpts {
|
|
||||||
const amount = getRandomInteger(1, TO_TOKEN_BASE.times(100));
|
|
||||||
return {
|
|
||||||
fromTokenAddress,
|
|
||||||
toTokenAddress,
|
|
||||||
amount,
|
|
||||||
toAddress: randomAddress(),
|
|
||||||
fillAmount: getRandomPortion(amount),
|
|
||||||
fromTokenBalance: getRandomInteger(1, FROM_TOKEN_BASE.times(100)),
|
|
||||||
...opts,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async function withdrawToAsync(opts?: Partial<TransferFromOpts>): Promise<TransferFromResult> {
|
|
||||||
const _opts = createTransferFromOpts(opts);
|
|
||||||
// Fund the contract with input tokens.
|
|
||||||
await testContract
|
|
||||||
.grantTokensTo(_opts.fromTokenAddress, testContract.address, _opts.fromTokenBalance)
|
|
||||||
.awaitTransactionSuccessAsync({ value: _opts.fromTokenBalance });
|
|
||||||
// Fund the contract with output tokens.
|
|
||||||
await testContract.setNextFillAmount(_opts.fillAmount).awaitTransactionSuccessAsync({
|
|
||||||
value: _opts.toTokenAddress === wethAddress ? _opts.fillAmount : constants.ZERO_AMOUNT,
|
|
||||||
});
|
|
||||||
// Call bridgeTransferFrom().
|
|
||||||
const bridgeTransferFromFn = testContract.bridgeTransferFrom(
|
|
||||||
// Output token
|
|
||||||
_opts.toTokenAddress,
|
|
||||||
// Random maker address.
|
|
||||||
randomAddress(),
|
|
||||||
// Recipient address.
|
|
||||||
_opts.toAddress,
|
|
||||||
// Transfer amount.
|
|
||||||
_opts.amount,
|
|
||||||
// ABI-encode the input token address as the bridge data.
|
|
||||||
hexUtils.concat(hexUtils.leftPad(_opts.fromTokenAddress), hexUtils.leftPad(32), hexUtils.leftPad(0)),
|
|
||||||
);
|
|
||||||
const result = await bridgeTransferFromFn.callAsync();
|
|
||||||
const { logs } = await bridgeTransferFromFn.awaitTransactionSuccessAsync();
|
|
||||||
return {
|
|
||||||
opts: _opts,
|
|
||||||
result,
|
|
||||||
logs: (logs as any) as DecodedLogs,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getMinimumConversionRate(opts: TransferFromOpts): BigNumber {
|
|
||||||
const fromBase = opts.fromTokenAddress === wethAddress ? WETH_BASE : FROM_TOKEN_BASE;
|
|
||||||
const toBase = opts.toTokenAddress === wethAddress ? WETH_BASE : TO_TOKEN_BASE;
|
|
||||||
return opts.amount
|
|
||||||
.div(toBase)
|
|
||||||
.div(opts.fromTokenBalance.div(fromBase))
|
|
||||||
.times(KYBER_RATE_BASE)
|
|
||||||
.integerValue(BigNumber.ROUND_DOWN);
|
|
||||||
}
|
|
||||||
|
|
||||||
it('returns magic bytes on success', async () => {
|
|
||||||
const BRIDGE_SUCCESS_RETURN_DATA = AssetProxyId.ERC20Bridge;
|
|
||||||
const { result } = await withdrawToAsync();
|
|
||||||
expect(result).to.eq(BRIDGE_SUCCESS_RETURN_DATA);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can trade token -> token', async () => {
|
|
||||||
const { opts, logs } = await withdrawToAsync();
|
|
||||||
verifyEventsFromLogs(
|
|
||||||
logs,
|
|
||||||
[
|
|
||||||
{
|
|
||||||
sellTokenAddress: opts.fromTokenAddress,
|
|
||||||
buyTokenAddress: opts.toTokenAddress,
|
|
||||||
sellAmount: opts.fromTokenBalance,
|
|
||||||
recipientAddress: opts.toAddress,
|
|
||||||
minConversionRate: getMinimumConversionRate(opts),
|
|
||||||
msgValue: constants.ZERO_AMOUNT,
|
|
||||||
...STATIC_KYBER_TRADE_ARGS,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
TestKyberBridgeEvents.KyberBridgeTrade,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can trade token -> ETH', async () => {
|
|
||||||
const { opts, logs } = await withdrawToAsync({
|
|
||||||
toTokenAddress: wethAddress,
|
|
||||||
});
|
|
||||||
verifyEventsFromLogs(
|
|
||||||
logs,
|
|
||||||
[
|
|
||||||
{
|
|
||||||
sellTokenAddress: opts.fromTokenAddress,
|
|
||||||
buyTokenAddress: KYBER_ETH_ADDRESS,
|
|
||||||
sellAmount: opts.fromTokenBalance,
|
|
||||||
recipientAddress: testContract.address,
|
|
||||||
minConversionRate: getMinimumConversionRate(opts),
|
|
||||||
msgValue: constants.ZERO_AMOUNT,
|
|
||||||
...STATIC_KYBER_TRADE_ARGS,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
TestKyberBridgeEvents.KyberBridgeTrade,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can trade ETH -> token', async () => {
|
|
||||||
const { opts, logs } = await withdrawToAsync({
|
|
||||||
fromTokenAddress: wethAddress,
|
|
||||||
});
|
|
||||||
verifyEventsFromLogs(
|
|
||||||
logs,
|
|
||||||
[
|
|
||||||
{
|
|
||||||
sellTokenAddress: KYBER_ETH_ADDRESS,
|
|
||||||
buyTokenAddress: opts.toTokenAddress,
|
|
||||||
sellAmount: opts.fromTokenBalance,
|
|
||||||
recipientAddress: opts.toAddress,
|
|
||||||
minConversionRate: getMinimumConversionRate(opts),
|
|
||||||
msgValue: opts.fromTokenBalance,
|
|
||||||
...STATIC_KYBER_TRADE_ARGS,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
TestKyberBridgeEvents.KyberBridgeTrade,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('does nothing if bridge has no token balance', async () => {
|
|
||||||
const { logs } = await withdrawToAsync({
|
|
||||||
fromTokenBalance: constants.ZERO_AMOUNT,
|
|
||||||
});
|
|
||||||
expect(logs).to.be.length(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('only transfers the token if trading the same token', async () => {
|
|
||||||
const { opts, logs } = await withdrawToAsync({
|
|
||||||
toTokenAddress: fromTokenAddress,
|
|
||||||
});
|
|
||||||
verifyEventsFromLogs(
|
|
||||||
logs,
|
|
||||||
[
|
|
||||||
{
|
|
||||||
tokenAddress: fromTokenAddress,
|
|
||||||
ownerAddress: testContract.address,
|
|
||||||
recipientAddress: opts.toAddress,
|
|
||||||
amount: opts.fromTokenBalance,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
TestKyberBridgeEvents.KyberBridgeTokenTransfer,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('grants Kyber an allowance when selling non-WETH', async () => {
|
|
||||||
const { opts, logs } = await withdrawToAsync();
|
|
||||||
verifyEventsFromLogs(
|
|
||||||
logs,
|
|
||||||
[
|
|
||||||
{
|
|
||||||
tokenAddress: opts.fromTokenAddress,
|
|
||||||
ownerAddress: testContract.address,
|
|
||||||
spenderAddress: testContract.address,
|
|
||||||
allowance: constants.MAX_UINT256,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
TestKyberBridgeEvents.KyberBridgeTokenApprove,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('does not grant Kyber an allowance when selling WETH', async () => {
|
|
||||||
const { logs } = await withdrawToAsync({
|
|
||||||
fromTokenAddress: wethAddress,
|
|
||||||
});
|
|
||||||
verifyEventsFromLogs(logs, [], TestKyberBridgeEvents.KyberBridgeTokenApprove);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('withdraws WETH and passes it to Kyber when selling WETH', async () => {
|
|
||||||
const { opts, logs } = await withdrawToAsync({
|
|
||||||
fromTokenAddress: wethAddress,
|
|
||||||
});
|
|
||||||
expect(logs[0].event).to.eq(TestKyberBridgeEvents.KyberBridgeWethWithdraw);
|
|
||||||
expect(logs[0].args).to.deep.eq({
|
|
||||||
ownerAddress: testContract.address,
|
|
||||||
amount: opts.fromTokenBalance,
|
|
||||||
});
|
|
||||||
expect(logs[1].event).to.eq(TestKyberBridgeEvents.KyberBridgeTrade);
|
|
||||||
expect(logs[1].args.msgValue).to.bignumber.eq(opts.fromTokenBalance);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('wraps WETH and transfers it to the recipient when buyng WETH', async () => {
|
|
||||||
const { opts, logs } = await withdrawToAsync({
|
|
||||||
toTokenAddress: wethAddress,
|
|
||||||
});
|
|
||||||
expect(logs[0].event).to.eq(TestKyberBridgeEvents.KyberBridgeTokenApprove);
|
|
||||||
expect(logs[0].args.tokenAddress).to.eq(opts.fromTokenAddress);
|
|
||||||
expect(logs[1].event).to.eq(TestKyberBridgeEvents.KyberBridgeTrade);
|
|
||||||
expect(logs[1].args.recipientAddress).to.eq(testContract.address);
|
|
||||||
expect(logs[2].event).to.eq(TestKyberBridgeEvents.KyberBridgeWethDeposit);
|
|
||||||
expect(logs[2].args).to.deep.eq({
|
|
||||||
msgValue: opts.fillAmount,
|
|
||||||
ownerAddress: testContract.address,
|
|
||||||
amount: opts.fillAmount,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,245 +0,0 @@
|
|||||||
import {
|
|
||||||
chaiSetup,
|
|
||||||
constants,
|
|
||||||
expectTransactionFailedWithoutReasonAsync,
|
|
||||||
provider,
|
|
||||||
txDefaults,
|
|
||||||
web3Wrapper,
|
|
||||||
} from '@0x/contracts-test-utils';
|
|
||||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
|
||||||
import { AssetProxyId, RevertReason } from '@0x/types';
|
|
||||||
import { AbiEncoder, BigNumber } from '@0x/utils';
|
|
||||||
import * as chai from 'chai';
|
|
||||||
import * as ethUtil from 'ethereumjs-util';
|
|
||||||
|
|
||||||
import { artifacts } from './artifacts';
|
|
||||||
|
|
||||||
import {
|
|
||||||
IAssetDataContract,
|
|
||||||
IAssetProxyContract,
|
|
||||||
StaticCallProxyContract,
|
|
||||||
TestStaticCallTargetContract,
|
|
||||||
} from './wrappers';
|
|
||||||
|
|
||||||
chaiSetup.configure();
|
|
||||||
const expect = chai.expect;
|
|
||||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
|
||||||
|
|
||||||
describe('StaticCallProxy', () => {
|
|
||||||
const amount = constants.ZERO_AMOUNT;
|
|
||||||
let fromAddress: string;
|
|
||||||
let toAddress: string;
|
|
||||||
|
|
||||||
let assetDataInterface: IAssetDataContract;
|
|
||||||
let staticCallProxy: IAssetProxyContract;
|
|
||||||
let staticCallTarget: TestStaticCallTargetContract;
|
|
||||||
|
|
||||||
before(async () => {
|
|
||||||
await blockchainLifecycle.startAsync();
|
|
||||||
});
|
|
||||||
after(async () => {
|
|
||||||
await blockchainLifecycle.revertAsync();
|
|
||||||
});
|
|
||||||
before(async () => {
|
|
||||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
|
||||||
[fromAddress, toAddress] = accounts.slice(0, 2);
|
|
||||||
const staticCallProxyWithoutTransferFrom = await StaticCallProxyContract.deployFrom0xArtifactAsync(
|
|
||||||
artifacts.StaticCallProxy,
|
|
||||||
provider,
|
|
||||||
txDefaults,
|
|
||||||
artifacts,
|
|
||||||
);
|
|
||||||
assetDataInterface = new IAssetDataContract(constants.NULL_ADDRESS, provider);
|
|
||||||
staticCallProxy = new IAssetProxyContract(
|
|
||||||
staticCallProxyWithoutTransferFrom.address,
|
|
||||||
provider,
|
|
||||||
txDefaults,
|
|
||||||
{},
|
|
||||||
StaticCallProxyContract.deployedBytecode,
|
|
||||||
);
|
|
||||||
staticCallTarget = await TestStaticCallTargetContract.deployFrom0xArtifactAsync(
|
|
||||||
artifacts.TestStaticCallTarget,
|
|
||||||
provider,
|
|
||||||
txDefaults,
|
|
||||||
artifacts,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
beforeEach(async () => {
|
|
||||||
await blockchainLifecycle.startAsync();
|
|
||||||
});
|
|
||||||
afterEach(async () => {
|
|
||||||
await blockchainLifecycle.revertAsync();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('general', () => {
|
|
||||||
it('should revert if undefined function is called', async () => {
|
|
||||||
const undefinedSelector = '0x01020304';
|
|
||||||
await expectTransactionFailedWithoutReasonAsync(
|
|
||||||
web3Wrapper.sendTransactionAsync({
|
|
||||||
from: fromAddress,
|
|
||||||
to: staticCallProxy.address,
|
|
||||||
value: constants.ZERO_AMOUNT,
|
|
||||||
data: undefinedSelector,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('should have an id of 0xc339d10a', async () => {
|
|
||||||
const proxyId = await staticCallProxy.getProxyId().callAsync();
|
|
||||||
const expectedProxyId = AssetProxyId.StaticCall;
|
|
||||||
expect(proxyId).to.equal(expectedProxyId);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
describe('transferFrom', () => {
|
|
||||||
it('should revert if assetData lies outside the bounds of calldata', async () => {
|
|
||||||
const staticCallData = staticCallTarget.noInputFunction().getABIEncodedTransactionData();
|
|
||||||
const expectedResultHash = constants.KECCAK256_NULL;
|
|
||||||
const assetData = assetDataInterface
|
|
||||||
.StaticCall(staticCallTarget.address, staticCallData, expectedResultHash)
|
|
||||||
.getABIEncodedTransactionData();
|
|
||||||
const txData = staticCallProxy
|
|
||||||
.transferFrom(assetData, fromAddress, toAddress, amount)
|
|
||||||
.getABIEncodedTransactionData();
|
|
||||||
const offsetToAssetData = '0000000000000000000000000000000000000000000000000000000000000080';
|
|
||||||
const txDataEndBuffer = ethUtil.toBuffer((txData.length - 2) / 2 - 4);
|
|
||||||
const paddedTxDataEndBuffer = ethUtil.setLengthLeft(txDataEndBuffer, 32);
|
|
||||||
const invalidOffsetToAssetData = ethUtil.bufferToHex(paddedTxDataEndBuffer).slice(2);
|
|
||||||
const newAssetData = '0000000000000000000000000000000000000000000000000000000000000304';
|
|
||||||
const badTxData = `${txData.replace(offsetToAssetData, invalidOffsetToAssetData)}${newAssetData}`;
|
|
||||||
await expectTransactionFailedWithoutReasonAsync(
|
|
||||||
web3Wrapper.sendTransactionAsync({
|
|
||||||
to: staticCallProxy.address,
|
|
||||||
from: fromAddress,
|
|
||||||
data: badTxData,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('should revert if the length of assetData is less than 100 bytes', async () => {
|
|
||||||
const staticCallData = constants.NULL_BYTES;
|
|
||||||
const expectedResultHash = constants.KECCAK256_NULL;
|
|
||||||
const assetData = assetDataInterface
|
|
||||||
.StaticCall(staticCallTarget.address, staticCallData, expectedResultHash)
|
|
||||||
.getABIEncodedTransactionData()
|
|
||||||
.slice(0, -128);
|
|
||||||
const assetDataByteLen = (assetData.length - 2) / 2;
|
|
||||||
expect((assetDataByteLen - 4) % 32).to.equal(0);
|
|
||||||
await expectTransactionFailedWithoutReasonAsync(
|
|
||||||
staticCallProxy.transferFrom(assetData, fromAddress, toAddress, amount).sendTransactionAsync(),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('should revert if the offset to `staticCallData` points to outside of assetData', async () => {
|
|
||||||
const staticCallData = staticCallTarget.noInputFunction().getABIEncodedTransactionData();
|
|
||||||
const expectedResultHash = constants.KECCAK256_NULL;
|
|
||||||
const assetData = assetDataInterface
|
|
||||||
.StaticCall(staticCallTarget.address, staticCallData, expectedResultHash)
|
|
||||||
.getABIEncodedTransactionData();
|
|
||||||
const offsetToStaticCallData = '0000000000000000000000000000000000000000000000000000000000000060';
|
|
||||||
const assetDataEndBuffer = ethUtil.toBuffer((assetData.length - 2) / 2 - 4);
|
|
||||||
const paddedAssetDataEndBuffer = ethUtil.setLengthLeft(assetDataEndBuffer, 32);
|
|
||||||
const invalidOffsetToStaticCallData = ethUtil.bufferToHex(paddedAssetDataEndBuffer).slice(2);
|
|
||||||
const newStaticCallData = '0000000000000000000000000000000000000000000000000000000000000304';
|
|
||||||
const badAssetData = `${assetData.replace(
|
|
||||||
offsetToStaticCallData,
|
|
||||||
invalidOffsetToStaticCallData,
|
|
||||||
)}${newStaticCallData}`;
|
|
||||||
await expectTransactionFailedWithoutReasonAsync(
|
|
||||||
staticCallProxy.transferFrom(badAssetData, fromAddress, toAddress, amount).sendTransactionAsync(),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('should revert if the callTarget attempts to write to state', async () => {
|
|
||||||
const staticCallData = staticCallTarget.updateState().getABIEncodedTransactionData();
|
|
||||||
const expectedResultHash = constants.KECCAK256_NULL;
|
|
||||||
const assetData = assetDataInterface
|
|
||||||
.StaticCall(staticCallTarget.address, staticCallData, expectedResultHash)
|
|
||||||
.getABIEncodedTransactionData();
|
|
||||||
await expectTransactionFailedWithoutReasonAsync(
|
|
||||||
staticCallProxy.transferFrom(assetData, fromAddress, toAddress, amount).sendTransactionAsync(),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('should revert with data provided by the callTarget if the staticcall reverts', async () => {
|
|
||||||
const staticCallData = staticCallTarget.assertEvenNumber(new BigNumber(1)).getABIEncodedTransactionData();
|
|
||||||
const expectedResultHash = constants.KECCAK256_NULL;
|
|
||||||
const assetData = assetDataInterface
|
|
||||||
.StaticCall(staticCallTarget.address, staticCallData, expectedResultHash)
|
|
||||||
.getABIEncodedTransactionData();
|
|
||||||
return expect(
|
|
||||||
staticCallProxy.transferFrom(assetData, fromAddress, toAddress, amount).awaitTransactionSuccessAsync(),
|
|
||||||
).to.revertWith(RevertReason.TargetNotEven);
|
|
||||||
});
|
|
||||||
it('should revert if the hash of the output is different than expected expected', async () => {
|
|
||||||
const staticCallData = staticCallTarget.isOddNumber(new BigNumber(0)).getABIEncodedTransactionData();
|
|
||||||
const trueAsBuffer = ethUtil.toBuffer('0x0000000000000000000000000000000000000000000000000000000000000001');
|
|
||||||
const expectedResultHash = ethUtil.bufferToHex(ethUtil.sha3(trueAsBuffer));
|
|
||||||
const assetData = assetDataInterface
|
|
||||||
.StaticCall(staticCallTarget.address, staticCallData, expectedResultHash)
|
|
||||||
.getABIEncodedTransactionData();
|
|
||||||
return expect(
|
|
||||||
staticCallProxy.transferFrom(assetData, fromAddress, toAddress, amount).awaitTransactionSuccessAsync(),
|
|
||||||
).to.revertWith(RevertReason.UnexpectedStaticCallResult);
|
|
||||||
});
|
|
||||||
it('should be successful if a function call with no inputs and no outputs is successful', async () => {
|
|
||||||
const staticCallData = staticCallTarget.noInputFunction().getABIEncodedTransactionData();
|
|
||||||
const expectedResultHash = constants.KECCAK256_NULL;
|
|
||||||
const assetData = assetDataInterface
|
|
||||||
.StaticCall(staticCallTarget.address, staticCallData, expectedResultHash)
|
|
||||||
.getABIEncodedTransactionData();
|
|
||||||
await staticCallProxy
|
|
||||||
.transferFrom(assetData, fromAddress, toAddress, amount)
|
|
||||||
.awaitTransactionSuccessAsync();
|
|
||||||
});
|
|
||||||
it('should be successful if the staticCallTarget is not a contract and no return value is expected', async () => {
|
|
||||||
const staticCallData = '0x0102030405060708';
|
|
||||||
const expectedResultHash = constants.KECCAK256_NULL;
|
|
||||||
const assetData = assetDataInterface
|
|
||||||
.StaticCall(toAddress, staticCallData, expectedResultHash)
|
|
||||||
.getABIEncodedTransactionData();
|
|
||||||
await staticCallProxy
|
|
||||||
.transferFrom(assetData, fromAddress, toAddress, amount)
|
|
||||||
.awaitTransactionSuccessAsync();
|
|
||||||
});
|
|
||||||
it('should be successful if a function call with one static input returns the correct value', async () => {
|
|
||||||
const staticCallData = staticCallTarget.isOddNumber(new BigNumber(1)).getABIEncodedTransactionData();
|
|
||||||
const trueAsBuffer = ethUtil.toBuffer('0x0000000000000000000000000000000000000000000000000000000000000001');
|
|
||||||
const expectedResultHash = ethUtil.bufferToHex(ethUtil.sha3(trueAsBuffer));
|
|
||||||
const assetData = assetDataInterface
|
|
||||||
.StaticCall(staticCallTarget.address, staticCallData, expectedResultHash)
|
|
||||||
.getABIEncodedTransactionData();
|
|
||||||
await staticCallProxy
|
|
||||||
.transferFrom(assetData, fromAddress, toAddress, amount)
|
|
||||||
.awaitTransactionSuccessAsync();
|
|
||||||
});
|
|
||||||
it('should be successful if a function with one dynamic input is successful', async () => {
|
|
||||||
const dynamicInput = '0x0102030405060708';
|
|
||||||
const staticCallData = staticCallTarget.dynamicInputFunction(dynamicInput).getABIEncodedTransactionData();
|
|
||||||
const expectedResultHash = constants.KECCAK256_NULL;
|
|
||||||
const assetData = assetDataInterface
|
|
||||||
.StaticCall(staticCallTarget.address, staticCallData, expectedResultHash)
|
|
||||||
.getABIEncodedTransactionData();
|
|
||||||
await staticCallProxy
|
|
||||||
.transferFrom(assetData, fromAddress, toAddress, amount)
|
|
||||||
.awaitTransactionSuccessAsync();
|
|
||||||
});
|
|
||||||
it('should be successful if a function call returns a complex type', async () => {
|
|
||||||
const a = new BigNumber(1);
|
|
||||||
const b = new BigNumber(2);
|
|
||||||
const staticCallData = staticCallTarget.returnComplexType(a, b).getABIEncodedTransactionData();
|
|
||||||
const abiEncoder = new AbiEncoder.DynamicBytes({
|
|
||||||
name: '',
|
|
||||||
type: 'bytes',
|
|
||||||
});
|
|
||||||
const aHex = '0000000000000000000000000000000000000000000000000000000000000001';
|
|
||||||
const bHex = '0000000000000000000000000000000000000000000000000000000000000002';
|
|
||||||
const expectedResults = `${staticCallTarget.address}${aHex}${bHex}`;
|
|
||||||
const offset = '0000000000000000000000000000000000000000000000000000000000000020';
|
|
||||||
const encodedExpectedResultWithOffset = `0x${offset}${abiEncoder.encode(expectedResults).slice(2)}`;
|
|
||||||
const expectedResultHash = ethUtil.bufferToHex(
|
|
||||||
ethUtil.sha3(ethUtil.toBuffer(encodedExpectedResultWithOffset)),
|
|
||||||
);
|
|
||||||
const assetData = assetDataInterface
|
|
||||||
.StaticCall(staticCallTarget.address, staticCallData, expectedResultHash)
|
|
||||||
.getABIEncodedTransactionData();
|
|
||||||
await staticCallProxy
|
|
||||||
.transferFrom(assetData, fromAddress, toAddress, amount)
|
|
||||||
.awaitTransactionSuccessAsync();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,370 +0,0 @@
|
|||||||
import {
|
|
||||||
blockchainTests,
|
|
||||||
constants,
|
|
||||||
expect,
|
|
||||||
filterLogs,
|
|
||||||
filterLogsToArguments,
|
|
||||||
getRandomInteger,
|
|
||||||
Numberish,
|
|
||||||
randomAddress,
|
|
||||||
} from '@0x/contracts-test-utils';
|
|
||||||
import { AssetProxyId } from '@0x/types';
|
|
||||||
import { BigNumber, hexUtils } from '@0x/utils';
|
|
||||||
import { DecodedLogs } from 'ethereum-types';
|
|
||||||
import * as _ from 'lodash';
|
|
||||||
|
|
||||||
import { artifacts } from './artifacts';
|
|
||||||
|
|
||||||
import {
|
|
||||||
TestUniswapBridgeContract,
|
|
||||||
TestUniswapBridgeEthToTokenTransferInputEventArgs as EthToTokenTransferInputArgs,
|
|
||||||
TestUniswapBridgeEvents as ContractEvents,
|
|
||||||
TestUniswapBridgeTokenApproveEventArgs as TokenApproveArgs,
|
|
||||||
TestUniswapBridgeTokenToEthSwapInputEventArgs as TokenToEthSwapInputArgs,
|
|
||||||
TestUniswapBridgeTokenToTokenTransferInputEventArgs as TokenToTokenTransferInputArgs,
|
|
||||||
TestUniswapBridgeTokenTransferEventArgs as TokenTransferArgs,
|
|
||||||
TestUniswapBridgeWethDepositEventArgs as WethDepositArgs,
|
|
||||||
TestUniswapBridgeWethWithdrawEventArgs as WethWithdrawArgs,
|
|
||||||
} from './wrappers';
|
|
||||||
|
|
||||||
blockchainTests.resets('UniswapBridge unit tests', env => {
|
|
||||||
let testContract: TestUniswapBridgeContract;
|
|
||||||
let wethTokenAddress: string;
|
|
||||||
|
|
||||||
before(async () => {
|
|
||||||
testContract = await TestUniswapBridgeContract.deployFrom0xArtifactAsync(
|
|
||||||
artifacts.TestUniswapBridge,
|
|
||||||
env.provider,
|
|
||||||
env.txDefaults,
|
|
||||||
artifacts,
|
|
||||||
);
|
|
||||||
wethTokenAddress = await testContract.wethToken().callAsync();
|
|
||||||
});
|
|
||||||
|
|
||||||
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 WithdrawToOpts {
|
|
||||||
fromTokenAddress: string;
|
|
||||||
toTokenAddress: string;
|
|
||||||
fromTokenBalance: Numberish;
|
|
||||||
toAddress: string;
|
|
||||||
amount: Numberish;
|
|
||||||
exchangeRevertReason: string;
|
|
||||||
exchangeFillAmount: Numberish;
|
|
||||||
toTokenRevertReason: string;
|
|
||||||
fromTokenRevertReason: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
function createWithdrawToOpts(opts?: Partial<WithdrawToOpts>): WithdrawToOpts {
|
|
||||||
return {
|
|
||||||
fromTokenAddress: constants.NULL_ADDRESS,
|
|
||||||
toTokenAddress: constants.NULL_ADDRESS,
|
|
||||||
fromTokenBalance: getRandomInteger(1, 1e18),
|
|
||||||
toAddress: randomAddress(),
|
|
||||||
amount: getRandomInteger(1, 1e18),
|
|
||||||
exchangeRevertReason: '',
|
|
||||||
exchangeFillAmount: getRandomInteger(1, 1e18),
|
|
||||||
toTokenRevertReason: '',
|
|
||||||
fromTokenRevertReason: '',
|
|
||||||
...opts,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface WithdrawToResult {
|
|
||||||
opts: WithdrawToOpts;
|
|
||||||
result: string;
|
|
||||||
logs: DecodedLogs;
|
|
||||||
blockTime: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function withdrawToAsync(opts?: Partial<WithdrawToOpts>): Promise<WithdrawToResult> {
|
|
||||||
const _opts = createWithdrawToOpts(opts);
|
|
||||||
const callData = { value: new BigNumber(_opts.exchangeFillAmount) };
|
|
||||||
// Create the "from" token and exchange.
|
|
||||||
const createFromTokenFn = testContract.createTokenAndExchange(
|
|
||||||
_opts.fromTokenAddress,
|
|
||||||
_opts.exchangeRevertReason,
|
|
||||||
);
|
|
||||||
[_opts.fromTokenAddress] = await createFromTokenFn.callAsync(callData);
|
|
||||||
await createFromTokenFn.awaitTransactionSuccessAsync(callData);
|
|
||||||
|
|
||||||
// Create the "to" token and exchange.
|
|
||||||
const createToTokenFn = testContract.createTokenAndExchange(
|
|
||||||
_opts.toTokenAddress,
|
|
||||||
_opts.exchangeRevertReason,
|
|
||||||
);
|
|
||||||
[_opts.toTokenAddress] = await createToTokenFn.callAsync(callData);
|
|
||||||
await createToTokenFn.awaitTransactionSuccessAsync(callData);
|
|
||||||
|
|
||||||
await testContract
|
|
||||||
.setTokenRevertReason(_opts.toTokenAddress, _opts.toTokenRevertReason)
|
|
||||||
.awaitTransactionSuccessAsync();
|
|
||||||
await testContract
|
|
||||||
.setTokenRevertReason(_opts.fromTokenAddress, _opts.fromTokenRevertReason)
|
|
||||||
.awaitTransactionSuccessAsync();
|
|
||||||
// Set the token balance for the token we're converting from.
|
|
||||||
await testContract.setTokenBalance(_opts.fromTokenAddress).awaitTransactionSuccessAsync({
|
|
||||||
value: new BigNumber(_opts.fromTokenBalance),
|
|
||||||
});
|
|
||||||
// Call bridgeTransferFrom().
|
|
||||||
const bridgeTransferFromFn = testContract.bridgeTransferFrom(
|
|
||||||
// The "to" token address.
|
|
||||||
_opts.toTokenAddress,
|
|
||||||
// The "from" address.
|
|
||||||
randomAddress(),
|
|
||||||
// The "to" address.
|
|
||||||
_opts.toAddress,
|
|
||||||
// The amount to transfer to "to"
|
|
||||||
new BigNumber(_opts.amount),
|
|
||||||
// ABI-encoded "from" token address.
|
|
||||||
hexUtils.leftPad(_opts.fromTokenAddress),
|
|
||||||
);
|
|
||||||
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),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getExchangeForTokenAsync(tokenAddress: string): Promise<string> {
|
|
||||||
return testContract.getExchange(tokenAddress).callAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
it('returns magic bytes on success', async () => {
|
|
||||||
const { result } = await withdrawToAsync();
|
|
||||||
expect(result).to.eq(AssetProxyId.ERC20Bridge);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('just transfers tokens to `to` if the same tokens are in play', async () => {
|
|
||||||
const createTokenFn = await testContract.createTokenAndExchange(constants.NULL_ADDRESS, '');
|
|
||||||
const [tokenAddress] = await createTokenFn.callAsync();
|
|
||||||
await createTokenFn.awaitTransactionSuccessAsync();
|
|
||||||
const { opts, result, logs } = await withdrawToAsync({
|
|
||||||
fromTokenAddress: tokenAddress,
|
|
||||||
toTokenAddress: tokenAddress,
|
|
||||||
});
|
|
||||||
expect(result).to.eq(AssetProxyId.ERC20Bridge);
|
|
||||||
const transfers = filterLogsToArguments<TokenTransferArgs>(logs, ContractEvents.TokenTransfer);
|
|
||||||
expect(transfers.length).to.eq(1);
|
|
||||||
expect(transfers[0].token).to.eq(tokenAddress);
|
|
||||||
expect(transfers[0].from).to.eq(testContract.address);
|
|
||||||
expect(transfers[0].to).to.eq(opts.toAddress);
|
|
||||||
expect(transfers[0].amount).to.bignumber.eq(opts.amount);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('token -> token', () => {
|
|
||||||
it('calls `IUniswapExchange.tokenToTokenTransferInput()', async () => {
|
|
||||||
const { opts, logs, blockTime } = await withdrawToAsync();
|
|
||||||
const exchangeAddress = await getExchangeForTokenAsync(opts.fromTokenAddress);
|
|
||||||
const calls = filterLogsToArguments<TokenToTokenTransferInputArgs>(
|
|
||||||
logs,
|
|
||||||
ContractEvents.TokenToTokenTransferInput,
|
|
||||||
);
|
|
||||||
expect(calls.length).to.eq(1);
|
|
||||||
expect(calls[0].exchange).to.eq(exchangeAddress);
|
|
||||||
expect(calls[0].tokensSold).to.bignumber.eq(opts.fromTokenBalance);
|
|
||||||
expect(calls[0].minTokensBought).to.bignumber.eq(opts.amount);
|
|
||||||
expect(calls[0].minEthBought).to.bignumber.eq(1);
|
|
||||||
expect(calls[0].deadline).to.bignumber.eq(blockTime);
|
|
||||||
expect(calls[0].recipient).to.eq(opts.toAddress);
|
|
||||||
expect(calls[0].toTokenAddress).to.eq(opts.toTokenAddress);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('sets allowance for "from" token', async () => {
|
|
||||||
const { opts, logs } = await withdrawToAsync();
|
|
||||||
const approvals = filterLogsToArguments<TokenApproveArgs>(logs, ContractEvents.TokenApprove);
|
|
||||||
const exchangeAddress = await getExchangeForTokenAsync(opts.fromTokenAddress);
|
|
||||||
expect(approvals.length).to.eq(1);
|
|
||||||
expect(approvals[0].spender).to.eq(exchangeAddress);
|
|
||||||
expect(approvals[0].allowance).to.bignumber.eq(constants.MAX_UINT256);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('sets allowance for "from" token on subsequent calls', async () => {
|
|
||||||
const { opts } = await withdrawToAsync();
|
|
||||||
const { logs } = await withdrawToAsync(opts);
|
|
||||||
const approvals = filterLogsToArguments<TokenApproveArgs>(logs, ContractEvents.TokenApprove);
|
|
||||||
const exchangeAddress = await getExchangeForTokenAsync(opts.fromTokenAddress);
|
|
||||||
expect(approvals.length).to.eq(1);
|
|
||||||
expect(approvals[0].spender).to.eq(exchangeAddress);
|
|
||||||
expect(approvals[0].allowance).to.bignumber.eq(constants.MAX_UINT256);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('fails if "from" token does not exist', async () => {
|
|
||||||
const tx = testContract
|
|
||||||
.bridgeTransferFrom(
|
|
||||||
randomAddress(),
|
|
||||||
randomAddress(),
|
|
||||||
randomAddress(),
|
|
||||||
getRandomInteger(1, 1e18),
|
|
||||||
hexUtils.leftPad(randomAddress()),
|
|
||||||
)
|
|
||||||
.awaitTransactionSuccessAsync();
|
|
||||||
return expect(tx).to.eventually.be.rejectedWith('NO_UNISWAP_EXCHANGE_FOR_TOKEN');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('fails if the exchange fails', async () => {
|
|
||||||
const revertReason = 'FOOBAR';
|
|
||||||
const tx = withdrawToAsync({
|
|
||||||
exchangeRevertReason: revertReason,
|
|
||||||
});
|
|
||||||
return expect(tx).to.eventually.be.rejectedWith(revertReason);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('token -> ETH', () => {
|
|
||||||
it('calls `IUniswapExchange.tokenToEthSwapInput()`, `WETH.deposit()`, then `transfer()`', async () => {
|
|
||||||
const { opts, logs, blockTime } = await withdrawToAsync({
|
|
||||||
toTokenAddress: wethTokenAddress,
|
|
||||||
});
|
|
||||||
const exchangeAddress = await getExchangeForTokenAsync(opts.fromTokenAddress);
|
|
||||||
let calls: any = filterLogs<TokenToEthSwapInputArgs>(logs, ContractEvents.TokenToEthSwapInput);
|
|
||||||
expect(calls.length).to.eq(1);
|
|
||||||
expect(calls[0].args.exchange).to.eq(exchangeAddress);
|
|
||||||
expect(calls[0].args.tokensSold).to.bignumber.eq(opts.fromTokenBalance);
|
|
||||||
expect(calls[0].args.minEthBought).to.bignumber.eq(opts.amount);
|
|
||||||
expect(calls[0].args.deadline).to.bignumber.eq(blockTime);
|
|
||||||
calls = filterLogs<WethDepositArgs>(
|
|
||||||
logs.slice(calls[0].logIndex as number),
|
|
||||||
ContractEvents.WethDeposit,
|
|
||||||
);
|
|
||||||
expect(calls.length).to.eq(1);
|
|
||||||
expect(calls[0].args.amount).to.bignumber.eq(opts.exchangeFillAmount);
|
|
||||||
calls = filterLogs<TokenTransferArgs>(
|
|
||||||
logs.slice(calls[0].logIndex as number),
|
|
||||||
ContractEvents.TokenTransfer,
|
|
||||||
);
|
|
||||||
expect(calls.length).to.eq(1);
|
|
||||||
expect(calls[0].args.token).to.eq(opts.toTokenAddress);
|
|
||||||
expect(calls[0].args.from).to.eq(testContract.address);
|
|
||||||
expect(calls[0].args.to).to.eq(opts.toAddress);
|
|
||||||
expect(calls[0].args.amount).to.bignumber.eq(opts.exchangeFillAmount);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('sets allowance for "from" token', async () => {
|
|
||||||
const { opts, logs } = await withdrawToAsync({
|
|
||||||
toTokenAddress: wethTokenAddress,
|
|
||||||
});
|
|
||||||
const transfers = filterLogsToArguments<TokenApproveArgs>(logs, ContractEvents.TokenApprove);
|
|
||||||
const exchangeAddress = await getExchangeForTokenAsync(opts.fromTokenAddress);
|
|
||||||
expect(transfers.length).to.eq(1);
|
|
||||||
expect(transfers[0].spender).to.eq(exchangeAddress);
|
|
||||||
expect(transfers[0].allowance).to.bignumber.eq(constants.MAX_UINT256);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('sets allowance for "from" token on subsequent calls', async () => {
|
|
||||||
const { opts } = await withdrawToAsync({
|
|
||||||
toTokenAddress: wethTokenAddress,
|
|
||||||
});
|
|
||||||
const { logs } = await withdrawToAsync(opts);
|
|
||||||
const approvals = filterLogsToArguments<TokenApproveArgs>(logs, ContractEvents.TokenApprove);
|
|
||||||
const exchangeAddress = await getExchangeForTokenAsync(opts.fromTokenAddress);
|
|
||||||
expect(approvals.length).to.eq(1);
|
|
||||||
expect(approvals[0].spender).to.eq(exchangeAddress);
|
|
||||||
expect(approvals[0].allowance).to.bignumber.eq(constants.MAX_UINT256);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('fails if "from" token does not exist', async () => {
|
|
||||||
const tx = testContract
|
|
||||||
.bridgeTransferFrom(
|
|
||||||
randomAddress(),
|
|
||||||
randomAddress(),
|
|
||||||
randomAddress(),
|
|
||||||
getRandomInteger(1, 1e18),
|
|
||||||
hexUtils.leftPad(wethTokenAddress),
|
|
||||||
)
|
|
||||||
.awaitTransactionSuccessAsync();
|
|
||||||
return expect(tx).to.eventually.be.rejectedWith('NO_UNISWAP_EXCHANGE_FOR_TOKEN');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('fails if `WETH.deposit()` fails', async () => {
|
|
||||||
const revertReason = 'FOOBAR';
|
|
||||||
const tx = withdrawToAsync({
|
|
||||||
toTokenAddress: wethTokenAddress,
|
|
||||||
toTokenRevertReason: revertReason,
|
|
||||||
});
|
|
||||||
return expect(tx).to.eventually.be.rejectedWith(revertReason);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('fails if the exchange fails', async () => {
|
|
||||||
const revertReason = 'FOOBAR';
|
|
||||||
const tx = withdrawToAsync({
|
|
||||||
toTokenAddress: wethTokenAddress,
|
|
||||||
exchangeRevertReason: revertReason,
|
|
||||||
});
|
|
||||||
return expect(tx).to.eventually.be.rejectedWith(revertReason);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('ETH -> token', () => {
|
|
||||||
it('calls `WETH.withdraw()`, then `IUniswapExchange.ethToTokenTransferInput()`', async () => {
|
|
||||||
const { opts, logs, blockTime } = await withdrawToAsync({
|
|
||||||
fromTokenAddress: wethTokenAddress,
|
|
||||||
});
|
|
||||||
const exchangeAddress = await getExchangeForTokenAsync(opts.toTokenAddress);
|
|
||||||
let calls: any = filterLogs<WethWithdrawArgs>(logs, ContractEvents.WethWithdraw);
|
|
||||||
expect(calls.length).to.eq(1);
|
|
||||||
expect(calls[0].args.amount).to.bignumber.eq(opts.fromTokenBalance);
|
|
||||||
calls = filterLogs<EthToTokenTransferInputArgs>(
|
|
||||||
logs.slice(calls[0].logIndex as number),
|
|
||||||
ContractEvents.EthToTokenTransferInput,
|
|
||||||
);
|
|
||||||
expect(calls.length).to.eq(1);
|
|
||||||
expect(calls[0].args.exchange).to.eq(exchangeAddress);
|
|
||||||
expect(calls[0].args.minTokensBought).to.bignumber.eq(opts.amount);
|
|
||||||
expect(calls[0].args.deadline).to.bignumber.eq(blockTime);
|
|
||||||
expect(calls[0].args.recipient).to.eq(opts.toAddress);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('does not set any allowance', async () => {
|
|
||||||
const { logs } = await withdrawToAsync({
|
|
||||||
fromTokenAddress: wethTokenAddress,
|
|
||||||
});
|
|
||||||
const approvals = filterLogsToArguments<TokenApproveArgs>(logs, ContractEvents.TokenApprove);
|
|
||||||
expect(approvals).to.be.empty('');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('fails if "to" token does not exist', async () => {
|
|
||||||
const tx = testContract
|
|
||||||
.bridgeTransferFrom(
|
|
||||||
wethTokenAddress,
|
|
||||||
randomAddress(),
|
|
||||||
randomAddress(),
|
|
||||||
getRandomInteger(1, 1e18),
|
|
||||||
hexUtils.leftPad(randomAddress()),
|
|
||||||
)
|
|
||||||
.awaitTransactionSuccessAsync();
|
|
||||||
return expect(tx).to.eventually.be.rejectedWith('NO_UNISWAP_EXCHANGE_FOR_TOKEN');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('fails if the `WETH.withdraw()` fails', async () => {
|
|
||||||
const revertReason = 'FOOBAR';
|
|
||||||
const tx = withdrawToAsync({
|
|
||||||
fromTokenAddress: wethTokenAddress,
|
|
||||||
fromTokenRevertReason: revertReason,
|
|
||||||
});
|
|
||||||
return expect(tx).to.eventually.be.rejectedWith(revertReason);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('fails if the exchange fails', async () => {
|
|
||||||
const revertReason = 'FOOBAR';
|
|
||||||
const tx = withdrawToAsync({
|
|
||||||
fromTokenAddress: wethTokenAddress,
|
|
||||||
exchangeRevertReason: revertReason,
|
|
||||||
});
|
|
||||||
return expect(tx).to.eventually.be.rejectedWith(revertReason);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,216 +0,0 @@
|
|||||||
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');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
/*
|
|
||||||
* -----------------------------------------------------------------------------
|
|
||||||
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
|
||||||
* -----------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
export * from '../test/generated-wrappers/balancer_bridge';
|
|
||||||
export * from '../test/generated-wrappers/bancor_bridge';
|
|
||||||
export * from '../test/generated-wrappers/chai_bridge';
|
|
||||||
export * from '../test/generated-wrappers/cream_bridge';
|
|
||||||
export * from '../test/generated-wrappers/crypto_com_bridge';
|
|
||||||
export * from '../test/generated-wrappers/curve_bridge';
|
|
||||||
export * from '../test/generated-wrappers/d_o_d_o_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';
|
|
||||||
export * from '../test/generated-wrappers/erc20_proxy';
|
|
||||||
export * from '../test/generated-wrappers/erc721_proxy';
|
|
||||||
export * from '../test/generated-wrappers/eth2_dai_bridge';
|
|
||||||
export * from '../test/generated-wrappers/i_asset_data';
|
|
||||||
export * from '../test/generated-wrappers/i_asset_proxy';
|
|
||||||
export * from '../test/generated-wrappers/i_asset_proxy_dispatcher';
|
|
||||||
export * from '../test/generated-wrappers/i_authorizable';
|
|
||||||
export * from '../test/generated-wrappers/i_balancer_pool';
|
|
||||||
export * from '../test/generated-wrappers/i_bancor_network';
|
|
||||||
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_m_stable';
|
|
||||||
export * from '../test/generated-wrappers/i_mooniswap';
|
|
||||||
export * from '../test/generated-wrappers/i_shell';
|
|
||||||
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/m_stable_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/mooniswap_bridge';
|
|
||||||
export * from '../test/generated-wrappers/multi_asset_proxy';
|
|
||||||
export * from '../test/generated-wrappers/ownable';
|
|
||||||
export * from '../test/generated-wrappers/shell_bridge';
|
|
||||||
export * from '../test/generated-wrappers/snow_swap_bridge';
|
|
||||||
export * from '../test/generated-wrappers/static_call_proxy';
|
|
||||||
export * from '../test/generated-wrappers/sushi_swap_bridge';
|
|
||||||
export * from '../test/generated-wrappers/swerve_bridge';
|
|
||||||
export * from '../test/generated-wrappers/test_bancor_bridge';
|
|
||||||
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';
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
/**
|
|
||||||
* Use this file to configure your truffle project. It's seeded with some
|
|
||||||
* common settings for different networks and features like migrations,
|
|
||||||
* compilation and testing. Uncomment the ones you need or modify
|
|
||||||
* them to suit your project as necessary.
|
|
||||||
*
|
|
||||||
* More information about configuration can be found at:
|
|
||||||
*
|
|
||||||
* truffleframework.com/docs/advanced/configuration
|
|
||||||
*
|
|
||||||
* To deploy via Infura you'll need a wallet provider (like truffle-hdwallet-provider)
|
|
||||||
* to sign your transactions before they're sent to a remote public node. Infura accounts
|
|
||||||
* are available for free at: infura.io/register.
|
|
||||||
*
|
|
||||||
* You'll also need a mnemonic - the twelve word phrase the wallet uses to generate
|
|
||||||
* public/private key pairs. If you're publishing your code to GitHub make sure you load this
|
|
||||||
* phrase from a file you've .gitignored so it doesn't accidentally become public.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
// const HDWalletProvider = require('truffle-hdwallet-provider');
|
|
||||||
// const infuraKey = "fj4jll3k.....";
|
|
||||||
//
|
|
||||||
// const fs = require('fs');
|
|
||||||
// const mnemonic = fs.readFileSync(".secret").toString().trim();
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
/**
|
|
||||||
* Networks define how you connect to your ethereum client and let you set the
|
|
||||||
* defaults web3 uses to send transactions. If you don't specify one truffle
|
|
||||||
* will spin up a development blockchain for you on port 9545 when you
|
|
||||||
* run `develop` or `test`. You can ask a truffle command to use a specific
|
|
||||||
* network from the command line, e.g
|
|
||||||
*
|
|
||||||
* $ truffle test --network <network-name>
|
|
||||||
*/
|
|
||||||
|
|
||||||
networks: {
|
|
||||||
// Useful for testing. The `development` name is special - truffle uses it by default
|
|
||||||
// if it's defined here and no other network is specified at the command line.
|
|
||||||
// You should run a client (like ganache-cli, geth or parity) in a separate terminal
|
|
||||||
// tab if you use this network and you must also set the `host`, `port` and `network_id`
|
|
||||||
// options below to some value.
|
|
||||||
//
|
|
||||||
// development: {
|
|
||||||
// host: "127.0.0.1", // Localhost (default: none)
|
|
||||||
// port: 8545, // Standard Ethereum port (default: none)
|
|
||||||
// network_id: "*", // Any network (default: none)
|
|
||||||
// },
|
|
||||||
// Another network with more advanced options...
|
|
||||||
// advanced: {
|
|
||||||
// port: 8777, // Custom port
|
|
||||||
// network_id: 1342, // Custom network
|
|
||||||
// gas: 8500000, // Gas sent with each transaction (default: ~6700000)
|
|
||||||
// gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei)
|
|
||||||
// from: <address>, // Account to send txs from (default: accounts[0])
|
|
||||||
// websockets: true // Enable EventEmitter interface for web3 (default: false)
|
|
||||||
// },
|
|
||||||
// Useful for deploying to a public network.
|
|
||||||
// NB: It's important to wrap the provider as a function.
|
|
||||||
// ropsten: {
|
|
||||||
// provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/v3/YOUR-PROJECT-ID`),
|
|
||||||
// network_id: 3, // Ropsten's id
|
|
||||||
// gas: 5500000, // Ropsten has a lower block limit than mainnet
|
|
||||||
// confirmations: 2, // # of confs to wait between deployments. (default: 0)
|
|
||||||
// timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50)
|
|
||||||
// skipDryRun: true // Skip dry run before migrations? (default: false for public nets )
|
|
||||||
// },
|
|
||||||
// Useful for private networks
|
|
||||||
// private: {
|
|
||||||
// provider: () => new HDWalletProvider(mnemonic, `https://network.io`),
|
|
||||||
// network_id: 2111, // This network is yours, in the cloud.
|
|
||||||
// production: true // Treats this network as if it was a public net. (default: false)
|
|
||||||
// }
|
|
||||||
},
|
|
||||||
|
|
||||||
// Set default mocha options here, use special reporters etc.
|
|
||||||
mocha: {
|
|
||||||
// timeout: 100000
|
|
||||||
},
|
|
||||||
|
|
||||||
// Configure your compilers
|
|
||||||
compilers: {
|
|
||||||
solc: {
|
|
||||||
version: '0.5.9',
|
|
||||||
settings: {
|
|
||||||
evmVersion: 'istanbul',
|
|
||||||
optimizer: {
|
|
||||||
enabled: true,
|
|
||||||
runs: 1000000,
|
|
||||||
details: { yul: true, deduplicate: true, cse: true, constantOptimizer: true },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user