Compare commits
740 Commits
@0x/contra
...
@0x/contra
Author | SHA1 | Date | |
---|---|---|---|
|
ec92cea598 | ||
|
0e25f8ba32 | ||
|
23602ec6b4 | ||
|
f4da2a129d | ||
|
ab6938f614 | ||
|
09ec6d637b | ||
|
703e8e06a3 | ||
|
47f30d097a | ||
|
5c18b394a4 | ||
|
fb5afafbbe | ||
|
b3f71af850 | ||
|
093fb6e68d | ||
|
a8ae2b7355 | ||
|
d45f64ab06 | ||
|
050343c97a | ||
|
64f5aaccd7 | ||
|
856472452a | ||
|
f06e7a511e | ||
|
f6bd8c939c | ||
|
d09040d1d3 | ||
|
00fcdbd43a | ||
|
4dfd91e2df | ||
|
50f86dd61b | ||
|
9c219159c6 | ||
|
e1195a3444 | ||
|
7fe4a03683 | ||
|
803505cacc | ||
|
e05e118bd2 | ||
|
c883f5ea41 | ||
|
0567b3332b | ||
|
cc2833b372 | ||
|
0165d67dc1 | ||
|
1a4489b96a | ||
|
1ecc4a14ca | ||
|
9eba6d7146 | ||
|
3724ac4b67 | ||
|
d912175a7a | ||
|
5f23eee6dc | ||
|
5fe607557d | ||
|
5d6584b0eb | ||
|
7bdb77d93f | ||
|
2530d47fde | ||
|
2b8226a757 | ||
|
92d5adaac4 | ||
|
0c9daa693e | ||
|
89729e828c | ||
|
c2113caae1 | ||
|
e0adb6624d | ||
|
022855add0 | ||
|
4dd1c48dc8 | ||
|
cdc2393aa6 | ||
|
94f94bdda7 | ||
|
3264bd223d | ||
|
6292c0703c | ||
|
9a28079f2a | ||
|
c612649f02 | ||
|
eda44d1ffb | ||
|
4c2f77c014 | ||
|
6b8bbd3d5f | ||
|
e914e1b7fa | ||
|
3d2ce749a8 | ||
|
9892d8d6d2 | ||
|
218a7ab810 | ||
|
6779e52813 | ||
|
5d51b40541 | ||
|
b12b7069f7 | ||
|
10a5d38446 | ||
|
e39e7a934e | ||
|
b7127d8506 | ||
|
a12e07b1a8 | ||
|
8ff5c0a603 | ||
|
c5c8506cc6 | ||
|
dbef531d66 | ||
|
4e9c9ca079 | ||
|
d7c61fea4c | ||
|
08dc5fafa0 | ||
|
78493a9d98 | ||
|
e274d8994a | ||
|
2dee887e6f | ||
|
f7eb20a235 | ||
|
f3cea0ebd0 | ||
|
05e00d278f | ||
|
107c7a71f9 | ||
|
56698fb09e | ||
|
55bb6f89d3 | ||
|
47d77cbddd | ||
|
2547e0e5b1 | ||
|
1a4699ecac | ||
|
c825643b67 | ||
|
a425d3a234 | ||
|
3041e7459e | ||
|
1fb9d54174 | ||
|
598607f5a3 | ||
|
5c5bb20415 | ||
|
6199a17791 | ||
|
7dd42ad6aa | ||
|
c7fd85633d | ||
|
b3df71bebf | ||
|
88d95ebdbc | ||
|
140cf4d378 | ||
|
451a19117f | ||
|
aa6ab33b93 | ||
|
8ec04271f3 | ||
|
f583de652f | ||
|
96cd9de8a6 | ||
|
9a17cb4c92 | ||
|
021cba9fad | ||
|
47f4321611 | ||
|
775a41a789 | ||
|
f6c61ed79f | ||
|
152e057e08 | ||
|
8b84ecc593 | ||
|
9c10babefd | ||
|
67193d9472 | ||
|
7d04dd9520 | ||
|
93bda7972c | ||
|
92c0e34ed2 | ||
|
c0a471b349 | ||
|
2543076d16 | ||
|
e32bb6fc0f | ||
|
7020166473 | ||
|
16ae47f2ad | ||
|
abf1141ad8 | ||
|
3790e0a741 | ||
|
9e1966b4fe | ||
|
38e06e57a3 | ||
|
7f2fc90677 | ||
|
1f0ab54fc5 | ||
|
7f8c11a74c | ||
|
7a4d64d90d | ||
|
7217bfa35e | ||
|
25077affc8 | ||
|
7a80583655 | ||
|
c21fe0cb50 | ||
|
3d08e79089 | ||
|
c43db2d096 | ||
|
220856d084 | ||
|
a01eafa4e7 | ||
|
f9c2d25352 | ||
|
d2f77d74e6 | ||
|
c3b928c1f5 | ||
|
9dfa9c194e | ||
|
1a3dc6cefc | ||
|
1a73daf82a | ||
|
6b20c9a542 | ||
|
76c996250d | ||
|
52e8de9966 | ||
|
81ab84b087 | ||
|
e34755a1ef | ||
|
6c11b2ad8a | ||
|
ede6ecc3aa | ||
|
9db69f33e4 | ||
|
17d5d4648b | ||
|
e203b5593a | ||
|
de9aa063c0 | ||
|
077d001b42 | ||
|
dfd46d68ac | ||
|
4dbe137999 | ||
|
1ac5cb404f | ||
|
43ffa2dd77 | ||
|
1efa7935b7 | ||
|
b0835b005a | ||
|
30946ac110 | ||
|
9427858755 | ||
|
53864d3817 | ||
|
ab283ddd9b | ||
|
7efcf9066c | ||
|
219e09d157 | ||
|
d816551dba | ||
|
04c5752b8a | ||
|
ed12d8b95f | ||
|
f48852742d | ||
|
b84d89367a | ||
|
d8dab6a070 | ||
|
ac2443690c | ||
|
5df0f13eb0 | ||
|
24d782fb19 | ||
|
01a5472318 | ||
|
4b7a2e9d49 | ||
|
62936e3502 | ||
|
d6b3e4fbaa | ||
|
a5f011f4a4 | ||
|
c6efaab01d | ||
|
0f7282d9a9 | ||
|
a3b414a986 | ||
|
8a6d0b67f1 | ||
|
f78ff91975 | ||
|
c8e416f3b1 | ||
|
d61f67d24a | ||
|
258ffdcc94 | ||
|
1f93f09864 | ||
|
7afddb9309 | ||
|
5eb4dbd5d9 | ||
|
fc39ddfb71 | ||
|
ba04a8bc0c | ||
|
02d4a28402 | ||
|
ea2bf07ea6 | ||
|
b5dc734dc4 | ||
|
7dd9b0ba66 | ||
|
69a5c8a317 | ||
|
b975ac7c31 | ||
|
dcede832c8 | ||
|
f40bbbc238 | ||
|
361f5ca5cc | ||
|
a710ebe5b3 | ||
|
2becef23ff | ||
|
f916d293fa | ||
|
f312a260cc | ||
|
fd9b51c7db | ||
|
1dac6b6157 | ||
|
c2ba6b3a0f | ||
|
576bd5585f | ||
|
ae61a87190 | ||
|
2e8f2ae769 | ||
|
b507a308e6 | ||
|
ad83b17fdf | ||
|
e452cfcd59 | ||
|
6474a4e08c | ||
|
e78288ddfd | ||
|
6e2d0ab13d | ||
|
bfbc78c95c | ||
|
368ae86530 | ||
|
d8ccc1694d | ||
|
b4e10b1e06 | ||
|
05f76958ac | ||
|
1183cf5e6b | ||
|
bf9eb1413b | ||
|
38d48a8f20 | ||
|
ca222a470d | ||
|
49ca8840ea | ||
|
faaeba78bb | ||
|
6c37d47f2f | ||
|
185e53149f | ||
|
4329a252ee | ||
|
8d38d69684 | ||
|
edb5e50253 | ||
|
81ab2e75ca | ||
|
e0d8398cf7 | ||
|
4fd46d1c95 | ||
|
c217764fe0 | ||
|
42124274c7 | ||
|
562342ac99 | ||
|
14b573ebfd | ||
|
e3834c2fc0 | ||
|
c2f3757de7 | ||
|
4b0010be63 | ||
|
078af36e0e | ||
|
b17d12fe23 | ||
|
294c0b449b | ||
|
bc3927e973 | ||
|
a2cfdd2975 | ||
|
53b4f48b2e | ||
|
635b80440a | ||
|
f2d95477e6 | ||
|
1005e4962a | ||
|
487bc1a08b | ||
|
0cf768185e | ||
|
b5558a8cff | ||
|
af2b8dfde5 | ||
|
2bde5f7034 | ||
|
c38f913a84 | ||
|
44b4f91208 | ||
|
9c4ad6ac32 | ||
|
b151c0b701 | ||
|
d4b6db773f | ||
|
7da71c0955 | ||
|
32adb35c2a | ||
|
2f197d128a | ||
|
5415bc4590 | ||
|
f6086b8054 | ||
|
c9d77d7fa0 | ||
|
ab8c457c51 | ||
|
5d91ad3656 | ||
|
78ffca06ea | ||
|
0d71ec93e7 | ||
|
d4c771dc7d | ||
|
68004466bb | ||
|
4dd2d1afaf | ||
|
4947676434 | ||
|
ea5e83da03 | ||
|
0705276ff9 | ||
|
0299abf1b5 | ||
|
132394ffbe | ||
|
40edcef340 | ||
|
bf22eba795 | ||
|
e990272db3 | ||
|
401a0eadb1 | ||
|
5852e0b476 | ||
|
401df5f45d | ||
|
7da40fd7bc | ||
|
89740dc24c | ||
|
4d7ba42f8f | ||
|
bbd9c4ef67 | ||
|
de036ae96a | ||
|
e5985d7c3f | ||
|
fb54c45d7d | ||
|
f1b704a91a | ||
|
b99eab6804 | ||
|
82acc26f97 | ||
|
569a165c87 | ||
|
704adcb03d | ||
|
197cdee604 | ||
|
3dc5de936e | ||
|
d88eb6a5c9 | ||
|
a168f34538 | ||
|
7b150bab73 | ||
|
b0e38f79ea | ||
|
a68ebc27ed | ||
|
25705bd314 | ||
|
85c9b7d9c5 | ||
|
629c2ecba2 | ||
|
be0662a41d | ||
|
1985fec892 | ||
|
2cbdd76aa3 | ||
|
73ae0541d8 | ||
|
22621b9f76 | ||
|
c9f214504a | ||
|
e8a2d1240f | ||
|
f2e0fe49f7 | ||
|
4ce7bf56e7 | ||
|
29be232ae9 | ||
|
794c0342ee | ||
|
d5a22829ac | ||
|
b58d4005d3 | ||
|
c16d9d85a2 | ||
|
92aeca1f30 | ||
|
b81ed67975 | ||
|
2bc6582e6b | ||
|
b27311da2e | ||
|
ab8a0da16a | ||
|
800e37ed03 | ||
|
950e84fe5c | ||
|
fdbc235fd6 | ||
|
ffdb5c06f6 | ||
|
14f0f89798 | ||
|
54b53184b7 | ||
|
75b1cdac66 | ||
|
d21f394531 | ||
|
86d90599ca | ||
|
ec24976789 | ||
|
00eaa8bd34 | ||
|
5c44163d68 | ||
|
f73bad5c13 | ||
|
a063fa6fe0 | ||
|
894ad8af21 | ||
|
c01793599f | ||
|
63db393b60 | ||
|
5846166c85 | ||
|
25e941128a | ||
|
22964ff913 | ||
|
62a58667ba | ||
|
c868015989 | ||
|
565e5e5770 | ||
|
198831d084 | ||
|
d3be097436 | ||
|
1259de5be4 | ||
|
df6be48638 | ||
|
4923fdbb73 | ||
|
66964a5a2f | ||
|
97e24d0e14 | ||
|
fe0b75ef26 | ||
|
4b76efbc28 | ||
|
11cff4d391 | ||
|
59211c1c1e | ||
|
c0ab2e8127 | ||
|
d39e90bfa1 | ||
|
16e55457c8 | ||
|
ea2a453811 | ||
|
7d2a768a0c | ||
|
78304c4369 | ||
|
85f243e2e0 | ||
|
785ca4f5d1 | ||
|
730e8ad151 | ||
|
b3e6e23508 | ||
|
f09d56cdb9 | ||
|
b51933c4d9 | ||
|
477791a600 | ||
|
f0d6476f92 | ||
|
fa4accd0c4 | ||
|
e64754f554 | ||
|
5badb1eb5d | ||
|
9c52fd1f2a | ||
|
27e01b9249 | ||
|
5bf0de5519 | ||
|
139a4acb1b | ||
|
9d8b2d9e0c | ||
|
d16a0f1b56 | ||
|
edb63c0f26 | ||
|
bd3a80bcde | ||
|
9f0da8ec39 | ||
|
42ed4e393f | ||
|
99ffe6bb2d | ||
|
70898be894 | ||
|
9f1859575d | ||
|
0167689374 | ||
|
053c5f0f88 | ||
|
fa6516d0be | ||
|
7fb0e1b39c | ||
|
8ba439c263 | ||
|
1a1f24146c | ||
|
086fa31d04 | ||
|
b5e02d1b74 | ||
|
e88aee6ad9 | ||
|
fb4ead84f5 | ||
|
298967e639 | ||
|
903a9947a3 | ||
|
72beb59d63 | ||
|
75dd1be40e | ||
|
6a7c2918bb | ||
|
9b9ee2415d | ||
|
f1f38fb8b0 | ||
|
1e44bcb7c9 | ||
|
11e689156e | ||
|
6c792e89f9 | ||
|
11026fe36a | ||
|
3133dde3a3 | ||
|
b666ca0271 | ||
|
de5c6c1ed0 | ||
|
34f4cf133b | ||
|
1ba54af4e2 | ||
|
42e0d608c8 | ||
|
2c35d63976 | ||
|
d1ca1e768f | ||
|
2255cc2ebc | ||
|
6512c12f40 | ||
|
fc8d428d1d | ||
|
e07613818d | ||
|
de59ae11bd | ||
|
026690c837 | ||
|
c223a72f5b | ||
|
c66cf83ef1 | ||
|
30cf9ac857 | ||
|
b99b9d5435 | ||
|
81b9ab2b6e | ||
|
78a60a9973 | ||
|
fca6f838d5 | ||
|
4a39eb7931 | ||
|
00ab5f0afb | ||
|
34dfd73aab | ||
|
9e0e12a468 | ||
|
1bdcb4f737 | ||
|
5e5ecdcf32 | ||
|
b316217394 | ||
|
d96e307e2c | ||
|
a7944bb3c5 | ||
|
2dc3885691 | ||
|
027ab98a3e | ||
|
1a5736a498 | ||
|
73f4c036c6 | ||
|
6cbadcf8e9 | ||
|
6a38f231b1 | ||
|
02d63daba5 | ||
|
99074b3c34 | ||
|
bb33609164 | ||
|
27832741e4 | ||
|
0cffdc9868 | ||
|
6055d44120 | ||
|
984305d483 | ||
|
7934624afc | ||
|
e7db5aa4f3 | ||
|
e922299a55 | ||
|
58cbc7a05f | ||
|
43648a2382 | ||
|
c1abaa3293 | ||
|
9f77879198 | ||
|
79279e5614 | ||
|
d100897b20 | ||
|
bfaaefaf0a | ||
|
520c6fa426 | ||
|
d95b520512 | ||
|
10f8637802 | ||
|
b327cc0f52 | ||
|
88acdaff90 | ||
|
d5039809de | ||
|
2746b73416 | ||
|
fdd1d20c5b | ||
|
e2b4670016 | ||
|
336adc6974 | ||
|
7c72ac52e1 | ||
|
d165bb2bb2 | ||
|
12dea02fab | ||
|
5181ee172b | ||
|
def0d9307e | ||
|
45e572388b | ||
|
4898de8d41 | ||
|
1aa2270d97 | ||
|
5abc9a8066 | ||
|
a8deb6cc74 | ||
|
87bcb46f43 | ||
|
3d904aac67 | ||
|
ace63fe83a | ||
|
33320fd758 | ||
|
be5b4b7702 | ||
|
5c55064c0f | ||
|
71ad8dcec0 | ||
|
09fd8bc521 | ||
|
ace0150fcb | ||
|
e627d3ce01 | ||
|
a65f981f55 | ||
|
dbebb3818d | ||
|
2027b74c5f | ||
|
5e921fdd08 | ||
|
6eda017719 | ||
|
9b9960c7b9 | ||
|
9df09e2464 | ||
|
5dacc58a4e | ||
|
fcb18e8d34 | ||
|
9af95a9461 | ||
|
bef662a6e1 | ||
|
04f24f32e2 | ||
|
65743882bb | ||
|
9e82b51eb5 | ||
|
a20c40ca90 | ||
|
745bdda1a3 | ||
|
3c7e538202 | ||
|
786419fee0 | ||
|
739651b917 | ||
|
e374469818 | ||
|
2ecd9672c2 | ||
|
68a4ad2e51 | ||
|
b2e2c27775 | ||
|
8e45d5e137 | ||
|
1ded7cd4f1 | ||
|
8bd2411a89 | ||
|
907fba7d0f | ||
|
bb5afc43b9 | ||
|
a243c9d685 | ||
|
829eeb2374 | ||
|
338de4ffa1 | ||
|
c7fbd6c64c | ||
|
95b7ae3146 | ||
|
ded48fd453 | ||
|
cbe2cf8a85 | ||
|
a12dc5c81b | ||
|
db062154d1 | ||
|
ac3a6426e8 | ||
|
f1f5b57254 | ||
|
3403e8af9b | ||
|
19286db952 | ||
|
40234e5b4a | ||
|
a9f046609c | ||
|
f2e2672e81 | ||
|
784f2674a9 | ||
|
93399165e7 | ||
|
7422485817 | ||
|
2ef19f31db | ||
|
8154209eab | ||
|
d56fb374a7 | ||
|
7bad1d2921 | ||
|
37dd494abd | ||
|
a9748e1b52 | ||
|
cc33101923 | ||
|
31fbbb52a8 | ||
|
d2c5665a30 | ||
|
06744ee7fb | ||
|
3e1db453ff | ||
|
ec76186c23 | ||
|
fcf975a65c | ||
|
e6d2c7db88 | ||
|
d012268953 | ||
|
70b797cb6d | ||
|
28d7cf38c8 | ||
|
6094fa7b6d | ||
|
8f3b7ee522 | ||
|
74f6fb7408 | ||
|
4ccb735282 | ||
|
b94631c84a | ||
|
2544e4fd65 | ||
|
7454a7a6f3 | ||
|
13e262b9cf | ||
|
b4db9d8b7d | ||
|
14ad5ced78 | ||
|
b2e592bb41 | ||
|
1a3281a959 | ||
|
6701c58a10 | ||
|
80fd0db2eb | ||
|
7ca8c5c16d | ||
|
3ed7cc5cab | ||
|
0987ae05a8 | ||
|
3154149d37 | ||
|
6f46109617 | ||
|
b0896408d2 | ||
|
243b478b99 | ||
|
fabbad2b2c | ||
|
1948ffe7bd | ||
|
67baee60f8 | ||
|
8f4c4715e2 | ||
|
3e7cbe6015 | ||
|
0053bde668 | ||
|
363dd31768 | ||
|
f841737adc | ||
|
9adaa7972e | ||
|
ec387f9bb7 | ||
|
aa657776fc | ||
|
f12632a1f2 | ||
|
4f8164dc43 | ||
|
88303d8855 | ||
|
4e8ddafa64 | ||
|
1ebe9d2bba | ||
|
fbae619725 | ||
|
8c5f4c3de7 | ||
|
154841157f | ||
|
6a20d06194 | ||
|
245e118016 | ||
|
fcc9d6749c | ||
|
9e091c5015 | ||
|
01247319c3 | ||
|
1d3d5f7e32 | ||
|
49f2cef5ac | ||
|
926d165321 | ||
|
86218445cd | ||
|
6e0f695699 | ||
|
7b9ff7776d | ||
|
e0f3f53d42 | ||
|
2e911ee709 | ||
|
43afed6654 | ||
|
0dda8328af | ||
|
48052fc3e4 | ||
|
5ac5fed513 | ||
|
60521e8167 | ||
|
510568d4f1 | ||
|
3e3ec3134d | ||
|
c22374893e | ||
|
013eaeeb07 | ||
|
b97b6867d8 | ||
|
87f31ec532 | ||
|
9b12695443 | ||
|
cbb40c1c2b | ||
|
b0e56fc27b | ||
|
d15532227d | ||
|
c3f98e95ad | ||
|
e92e99d6ea | ||
|
f6b67f6c98 | ||
|
108861b6ca | ||
|
80d93e8d75 | ||
|
1634cd53be | ||
|
577df3749d | ||
|
73c53b3da6 | ||
|
9af996a907 | ||
|
bd2e4a8076 | ||
|
ceaa6a592c | ||
|
b503fecccb | ||
|
4161055cc7 | ||
|
b0c9a3bbe1 | ||
|
436bdde461 | ||
|
01853064b0 | ||
|
c1ccb5af0f | ||
|
706f04ee27 | ||
|
549e35e972 | ||
|
1ed66966a8 | ||
|
326539f1f5 | ||
|
67322ba39f | ||
|
41fcc41bd1 | ||
|
7aacff62ca | ||
|
4566ddb037 | ||
|
957e6b1500 | ||
|
5945635d1d | ||
|
045fc0914b | ||
|
72442871aa | ||
|
bbd3c03969 | ||
|
a4405c3d39 | ||
|
0fe4f587d8 | ||
|
d3c714bd17 | ||
|
c399b7a7d5 | ||
|
b9234e94fb | ||
|
417bb87785 | ||
|
0233ae3134 | ||
|
eed0c5dd59 | ||
|
2b3b167095 | ||
|
5d91d19808 | ||
|
0f374ddee9 | ||
|
a65a9913cd | ||
|
1ead32c666 | ||
|
d1af9fc780 | ||
|
0f06737fb6 | ||
|
1676231532 | ||
|
b1caf697c8 | ||
|
51481065fe | ||
|
e367da710c | ||
|
f493d6524d | ||
|
e1b85da2a7 | ||
|
22c6548ed1 | ||
|
afb32c087d | ||
|
bbc1ed1c64 | ||
|
3a46f1a27a | ||
|
90cd364780 | ||
|
6795e6f078 | ||
|
cfb3404349 | ||
|
0212f3ee78 | ||
|
6b2995a4ee | ||
|
09e7ac54d4 | ||
|
f69009d4a8 | ||
|
206802ae33 | ||
|
91d4138fb8 | ||
|
cb455f951a | ||
|
5f25d20cd0 | ||
|
1f0e2cd910 | ||
|
1749d02701 | ||
|
55ace3179c | ||
|
7866d9ccb4 | ||
|
51f73d07fa | ||
|
63d84674ab | ||
|
14066997b2 | ||
|
28561e765a | ||
|
453fbbdc5d | ||
|
1e1e5ec10d | ||
|
2088b0e459 | ||
|
58400d9e01 | ||
|
ac9375f1d2 | ||
|
db061c9355 | ||
|
d5ce6c464b | ||
|
b06205bb7f | ||
|
f528a3e1de | ||
|
bddfdacfad | ||
|
d3cdd3f235 | ||
|
41ae45ea40 | ||
|
657e0895ea | ||
|
b2592d1cc2 | ||
|
aa3524c3b2 | ||
|
39deb1a05f | ||
|
302d08e290 | ||
|
05489dd7f1 | ||
|
55bd076602 | ||
|
7a224fe08f | ||
|
3bdeb82097 | ||
|
f49ab3f919 | ||
|
42d5bdd3ab | ||
|
7228cbfe92 | ||
|
11e2fc5bc4 | ||
|
3e88f820b8 | ||
|
163750f8c2 | ||
|
4aabc5d791 | ||
|
c9a7b9dcc1 | ||
|
98075b5653 | ||
|
57ae5be916 | ||
|
8caf62997f | ||
|
f8656ad376 | ||
|
29c6c2a2ad | ||
|
f2db67ef02 | ||
|
72b8ef33d9 |
@@ -47,41 +47,19 @@ jobs:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: yarn wsrun test:circleci @0x/contracts-multisig @0x/contracts-utils @0x/contracts-exchange-libs @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-extensions @0x/contracts-asset-proxy @0x/contracts-exchange @0x/contracts-exchange-forwarder @0x/contracts-coordinator @0x/contracts-tests @0x/contracts-staking
|
||||
test-exchange-ganache-3.0:
|
||||
resource_class: medium+
|
||||
- run: yarn wsrun test:circleci @0x/contracts-multisig @0x/contracts-utils @0x/contracts-exchange-libs @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-extensions @0x/contracts-asset-proxy @0x/contracts-exchange @0x/contracts-exchange-forwarder @0x/contracts-coordinator @0x/contracts-dev-utils
|
||||
test-contracts-geth:
|
||||
docker:
|
||||
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||
- image: 0xorg/devnet
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: yarn wsrun test:circleci @0x/contracts-exchange
|
||||
test-integrations-ganache-3.0:
|
||||
resource_class: medium+
|
||||
docker:
|
||||
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: yarn wsrun test:circleci @0x/contracts-integrations
|
||||
test-contracts-rest-ganache-3.0:
|
||||
resource_class: medium+
|
||||
docker:
|
||||
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: yarn wsrun test:circleci @0x/contracts-multisig @0x/contracts-utils @0x/contracts-exchange-libs @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-asset-proxy @0x/contracts-exchange-forwarder @0x/contracts-tests @0x/contracts-staking @0x/contracts-coordinator
|
||||
# TODO(dorothy-zbornak): Re-enable after updating this package for
|
||||
# 3.0. At that time, also remove exclusion from monorepo
|
||||
# package.json's test script.
|
||||
# - run: yarn wsrun test:circleci @0x/contracts-extensions
|
||||
# HACK(albrow): we need to sleep 10 seconds to ensure the devnet is
|
||||
# initialized
|
||||
- run: sleep 10 && TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-multisig @0x/contracts-utils @0x/contracts-exchange-libs @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-extensions @0x/contracts-asset-proxy @0x/contracts-exchange @0x/contracts-exchange-forwarder @0x/contracts-coordinator @0x/contracts-dev-utils
|
||||
test-publish:
|
||||
resource_class: medium+
|
||||
docker:
|
||||
@@ -92,9 +70,7 @@ jobs:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run:
|
||||
command: yarn test:publish:circleci
|
||||
no_output_timeout: 1800
|
||||
- run: yarn test:publish:circleci
|
||||
test-doc-generation:
|
||||
docker:
|
||||
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||
@@ -103,9 +79,7 @@ jobs:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run:
|
||||
command: yarn test:generate_docs:circleci
|
||||
no_output_timeout: 1200
|
||||
- run: yarn test:generate_docs:circleci
|
||||
test-rest:
|
||||
docker:
|
||||
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||
@@ -116,23 +90,15 @@ jobs:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: yarn wsrun test:circleci @0x/contracts-test-utils
|
||||
- run: yarn wsrun test:circleci @0x/abi-gen
|
||||
# TODO (xianny): Needs to be updated for 3.0
|
||||
# - run: yarn wsrun test:circleci @0x/asset-buyer
|
||||
# TODO: Needs to be updated for 3.0. At that time, also remove
|
||||
# exclusion from monorepo package.json's test script.
|
||||
# - run: yarn wsrun test:circleci @0x/asset-swapper
|
||||
- run: yarn wsrun test:circleci @0x/asset-buyer
|
||||
- run: yarn wsrun test:circleci @0x/contract-artifacts
|
||||
- run: yarn wsrun test:circleci @0x/assert
|
||||
- run: yarn wsrun test:circleci @0x/base-contract
|
||||
# TODO (xianny): Needs to be updated for 3.0
|
||||
# - run: yarn wsrun test:circleci @0x/connect
|
||||
- run: yarn wsrun test:circleci @0x/connect
|
||||
- run: yarn wsrun test:circleci @0x/contract-wrappers
|
||||
- run: yarn wsrun test:circleci @0x/dev-utils
|
||||
- run: yarn wsrun test:circleci @0x/json-schemas
|
||||
- run: yarn wsrun test:circleci @0x/order-utils
|
||||
# TODO: Needs to be updated for 3.0. At that time, also remove
|
||||
# exclusion from monorepo package.json's test script.
|
||||
# - run: yarn wsrun test:circleci @0x/orderbook
|
||||
- run: yarn wsrun test:circleci @0x/sol-compiler
|
||||
- run: yarn wsrun test:circleci @0x/sol-tracing-utils
|
||||
- run: yarn wsrun test:circleci @0x/sol-doc
|
||||
@@ -200,33 +166,14 @@ jobs:
|
||||
working_directory: ~/repo
|
||||
docker:
|
||||
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||
- image: 0xorg/ganache-cli:4.4.0-beta.1
|
||||
- image: 0xorg/ganache-cli:2.2.2
|
||||
- image: 0xorg/launch-kit-backend:74bcc39
|
||||
environment:
|
||||
VERSION: 4.4.0-beta.1
|
||||
SNAPSHOT_NAME: 0x_ganache_snapshot-v3-beta
|
||||
- image: 0xorg/mesh:6.0.0-beta-0xv3
|
||||
environment:
|
||||
ETHEREUM_RPC_URL: 'http://localhost:8545'
|
||||
ETHEREUM_NETWORK_ID: '50'
|
||||
ETHEREUM_CHAIN_ID: '1337'
|
||||
USE_BOOTSTRAP_LIST: 'true'
|
||||
VERBOSITY: 3
|
||||
PRIVATE_KEY_PATH: ''
|
||||
BLOCK_POLLING_INTERVAL: '5s'
|
||||
P2P_LISTEN_PORT: '60557'
|
||||
command: |
|
||||
sh -c "waitForGanache () { until printf 'POST /\r\nContent-Length: 26\r\n\r\n{\"method\":\"net_listening\"}' | nc localhost 8545 | grep true; do continue; done }; waitForGanache && ./mesh"
|
||||
- image: 0xorg/launch-kit-backend:v3
|
||||
environment:
|
||||
RPC_URL: 'http://localhost:8545'
|
||||
CHAIN_ID: 1337
|
||||
RPC_URL: http://localhost:8545
|
||||
NETWORK_ID: 50
|
||||
WHITELIST_ALL_TOKENS: True
|
||||
FEE_RECIPIENT: '0x0000000000000000000000000000000000000001'
|
||||
MAKER_FEE_UNIT_AMOUNT: 0
|
||||
TAKER_FEE_UNIT_AMOUNT: 0
|
||||
MESH_ENDPOINT: 'ws://localhost:60557'
|
||||
command: |
|
||||
sh -c "waitForMesh () { sleep 5; }; waitForMesh && node_modules/.bin/forever ts/lib/index.js"
|
||||
sh -c "until printf 'POST /\r\nContent-Length: 26\r\n\r\n{\"method\":\"net_listening\"}' | nc localhost 8545 | grep true; do continue; done; node_modules/.bin/forever ts/lib/index.js"
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
@@ -248,14 +195,8 @@ jobs:
|
||||
- run:
|
||||
command: |
|
||||
cd python-packages
|
||||
./parallel coverage run setup.py test
|
||||
./parallel_without_sra_client coverage run setup.py test
|
||||
./build_docs
|
||||
- run:
|
||||
command: |
|
||||
# copy generated wrappers into contract_wrappers/build,
|
||||
# JUST so CircleCI will persist them as build artifacts.
|
||||
cd python-packages/contract_wrappers/src/zero_ex
|
||||
for i in contract_wrappers/[^__]*/; do mkdir -p ../../build/$i; cp $i/__init__.py ../../build/$i; done
|
||||
- save_cache:
|
||||
key: coverage-python-contract-addresses-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
@@ -280,6 +221,8 @@ jobs:
|
||||
key: coverage-python-sra-client-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/python-packages/sra_client/.coverage
|
||||
- store_artifacts:
|
||||
path: ~/repo/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/*/__init__.py
|
||||
- store_artifacts:
|
||||
path: ~/repo/python-packages/contract_addresses/build
|
||||
- store_artifacts:
|
||||
@@ -430,15 +373,14 @@ workflows:
|
||||
main:
|
||||
jobs:
|
||||
- build
|
||||
- test-exchange-ganache-3.0:
|
||||
requires:
|
||||
- build
|
||||
- test-integrations-ganache-3.0:
|
||||
requires:
|
||||
- build
|
||||
- test-contracts-rest-ganache-3.0:
|
||||
- test-contracts-ganache:
|
||||
requires:
|
||||
- build
|
||||
# TODO(albrow): Tests always fail on Geth right now because our fork
|
||||
# is outdated. Uncomment once we have updated our Geth fork.
|
||||
# - test-contracts-geth:
|
||||
# requires:
|
||||
# - build
|
||||
- test-rest:
|
||||
requires:
|
||||
- build
|
||||
@@ -453,15 +395,13 @@ workflows:
|
||||
- build
|
||||
- submit-coverage:
|
||||
requires:
|
||||
- test-contracts-rest-ganache-3.0
|
||||
- test-exchange-ganache-3.0
|
||||
- test-rest
|
||||
- static-tests
|
||||
- test-python
|
||||
- static-tests-python:
|
||||
requires:
|
||||
- test-python
|
||||
- test-python:
|
||||
requires:
|
||||
- build
|
||||
- static-tests-python:
|
||||
requires:
|
||||
- build
|
||||
# skip python tox run for now, as we don't yet have multiple test environments to support.
|
||||
# - test-rest-python
|
||||
#- test-rest-python
|
||||
|
3
.github/autolabeler.yml
vendored
3
.github/autolabeler.yml
vendored
@@ -32,3 +32,6 @@ contracts: ['contracts']
|
||||
@0x/json-schemas: ['packages/json-schemas']
|
||||
@0x/ethereum-types: ['ethereum-types']
|
||||
@0x/connect: ['packages/connect']
|
||||
@0x/fill-scenarios: ['packages/fill-scenarios']
|
||||
@0x/testnet-faucets: ['packages/testnet-faucets']
|
||||
@0x/monorepo-scripts: ['packages/monorepo-scripts']
|
||||
|
63
.gitignore
vendored
63
.gitignore
vendored
@@ -78,84 +78,39 @@ TODO.md
|
||||
# VSCode file
|
||||
.vscode
|
||||
|
||||
# server cli
|
||||
packages/testnet-faucets/server/
|
||||
|
||||
# generated contract artifacts/
|
||||
contracts/integrations/generated-artifacts/
|
||||
contracts/integrations/test/generated-artifacts/
|
||||
contracts/staking/generated-artifacts/
|
||||
contracts/staking/test/generated-artifacts/
|
||||
contracts/coordinator/generated-artifacts/
|
||||
contracts/coordinator/test/generated-artifacts/
|
||||
contracts/exchange/generated-artifacts/
|
||||
contracts/exchange/test/generated-artifacts/
|
||||
contracts/asset-proxy/generated-artifacts/
|
||||
contracts/asset-proxy/test/generated-artifacts/
|
||||
contracts/multisig/generated-artifacts/
|
||||
contracts/multisig/test/generated-artifacts/
|
||||
contracts/utils/generated-artifacts/
|
||||
contracts/utils/test/generated-artifacts/
|
||||
contracts/exchange-libs/generated-artifacts/
|
||||
contracts/exchange-libs/test/generated-artifacts/
|
||||
contracts/erc20/generated-artifacts/
|
||||
contracts/erc20/test/generated-artifacts/
|
||||
contracts/erc721/generated-artifacts/
|
||||
contracts/erc721/test/generated-artifacts/
|
||||
contracts/erc1155/generated-artifacts/
|
||||
contracts/erc1155/test/generated-artifacts/
|
||||
contracts/extensions/generated-artifacts/
|
||||
contracts/extensions/test/generated-artifacts/
|
||||
contracts/exchange-forwarder/generated-artifacts/
|
||||
contracts/exchange-forwarder/test/generated-artifacts/
|
||||
contracts/dev-utils/generated-artifacts/
|
||||
contracts/dev-utils/test/generated-artifacts/
|
||||
packages/sol-tracing-utils/test/fixtures/artifacts/
|
||||
python-packages/contract_artifacts/src/zero_ex/contract_artifacts/artifacts/
|
||||
|
||||
# generated truffle contract artifacts/
|
||||
contracts/staking/build/
|
||||
contracts/coordinator/build/
|
||||
contracts/exchange/build/
|
||||
contracts/asset-proxy/build/
|
||||
contracts/multisig/build/
|
||||
contracts/utils/build/
|
||||
contracts/exchange-libs/build/
|
||||
contracts/erc20/build/
|
||||
contracts/erc721/build/
|
||||
contracts/erc1155/build/
|
||||
contracts/extensions/build/
|
||||
contracts/exchange-forwarder/build/
|
||||
contracts/dev-utils/build/
|
||||
|
||||
# generated contract wrappers
|
||||
packages/python-contract-wrappers/generated/
|
||||
contracts/integrations/generated-wrappers/
|
||||
contracts/integrations/test/generated-wrappers/
|
||||
contracts/staking/generated-wrappers/
|
||||
contracts/staking/test/generated-wrappers/
|
||||
contracts/coordinator/generated-wrappers/
|
||||
contracts/coordinator/test/generated-wrappers/
|
||||
contracts/exchange/generated-wrappers/
|
||||
contracts/exchange/test/generated-wrappers/
|
||||
contracts/asset-proxy/generated-wrappers/
|
||||
contracts/asset-proxy/test/generated-wrappers/
|
||||
contracts/multisig/generated-wrappers/
|
||||
contracts/multisig/test/generated-wrappers/
|
||||
contracts/utils/generated-wrappers/
|
||||
contracts/utils/test/generated-wrappers/
|
||||
contracts/exchange-libs/generated-wrappers/
|
||||
contracts/exchange-libs/test/generated-wrappers/
|
||||
contracts/erc20/generated-wrappers/
|
||||
contracts/erc20/test/generated-wrappers/
|
||||
contracts/erc721/generated-wrappers/
|
||||
contracts/erc721/test/generated-wrappers/
|
||||
contracts/erc1155/generated-wrappers/
|
||||
contracts/erc1155/test/generated-wrappers/
|
||||
contracts/extensions/generated-wrappers/
|
||||
contracts/extensions/test/generated-wrappers/
|
||||
contracts/exchange-forwarder/generated-wrappers/
|
||||
contracts/exchange-forwarder/test/generated-wrappers/
|
||||
contracts/dev-utils/generated-wrappers/
|
||||
contracts/dev-utils/test/generated-wrappers/
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/dev_utils/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc20_token/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/asset_proxy_owner/__init__.py
|
||||
@@ -164,20 +119,16 @@ python-packages/contract_wrappers/src/zero_ex/contract_wrappers/coordinator_regi
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/dummy_erc20_token/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/dummy_erc721_token/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/dutch_auction/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc1155_mintable/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc1155_proxy/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc20_proxy/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc721_proxy/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc721_token/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/eth_balance_checker/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/forwarder/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/i_asset_proxy/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/i_validator/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/i_wallet/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/multi_asset_proxy/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/order_validator/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/staking/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/staking_proxy/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/static_call_proxy/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/weth9/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/zrx_token/__init__.py
|
||||
|
||||
@@ -194,14 +145,10 @@ __pycache__
|
||||
python-packages/*/src/*.egg-info
|
||||
python-packages/*/.coverage
|
||||
|
||||
# python keeps package-local copies of json schemas and contract addresses
|
||||
# python keeps package-local copies of json schemas
|
||||
python-packages/json_schemas/src/zero_ex/json_schemas/schemas
|
||||
python-packages/contract_addresses/src/zero_ex/contract_addresses/addresses.json
|
||||
|
||||
# Doc README copy
|
||||
packages/*/docs/README.md
|
||||
|
||||
.DS_Store
|
||||
|
||||
# the snapshot that gets built for migrations sure does have a ton of files
|
||||
packages/migrations/0x_ganache_snapshot*
|
||||
|
@@ -1,74 +1,29 @@
|
||||
lib
|
||||
.nyc_output
|
||||
/contracts/integrations/generated-wrappers
|
||||
/contracts/integrations/test/generated-wrappers
|
||||
/contracts/integrations/generated-artifacts
|
||||
/contracts/integrations/test/generated-artifacts
|
||||
/contracts/staking/generated-wrappers
|
||||
/contracts/staking/test/generated-wrappers
|
||||
/contracts/staking/generated-artifacts
|
||||
/contracts/staking/test/generated-artifacts
|
||||
/contracts/coordinator/generated-wrappers
|
||||
/contracts/coordinator/test/generated-wrappers
|
||||
/contracts/coordinator/generated-artifacts
|
||||
/contracts/coordinator/test/generated-artifacts
|
||||
/contracts/exchange/generated-wrappers
|
||||
/contracts/exchange/test/generated-wrappers
|
||||
/contracts/exchange/generated-artifacts
|
||||
/contracts/exchange/test/generated-artifacts
|
||||
/contracts/asset-proxy/generated-wrappers
|
||||
/contracts/asset-proxy/test/generated-wrappers
|
||||
/contracts/asset-proxy/generated-artifacts
|
||||
/contracts/asset-proxy/test/generated-artifacts
|
||||
/contracts/multisig/generated-wrappers
|
||||
/contracts/multisig/test/generated-wrappers
|
||||
/contracts/multisig/generated-artifacts
|
||||
/contracts/multisig/test/generated-artifacts
|
||||
/contracts/utils/generated-wrappers
|
||||
/contracts/utils/test/generated-wrappers
|
||||
/contracts/utils/generated-artifacts
|
||||
/contracts/utils/test/generated-artifacts
|
||||
/contracts/exchange-libs/generated-wrappers
|
||||
/contracts/exchange-libs/test/generated-wrappers
|
||||
/contracts/exchange-libs/generated-artifacts
|
||||
/contracts/exchange-libs/test/generated-artifacts
|
||||
/contracts/erc20/generated-wrappers
|
||||
/contracts/erc20/test/generated-wrappers
|
||||
/contracts/erc20/generated-artifacts
|
||||
/contracts/erc20/test/generated-artifacts
|
||||
/contracts/erc721/generated-wrappers
|
||||
/contracts/erc721/test/generated-wrappers
|
||||
/contracts/erc721/generated-artifacts
|
||||
/contracts/erc721/test/generated-artifacts
|
||||
/contracts/erc1155/generated-wrappers
|
||||
/contracts/erc1155/test/generated-wrappers
|
||||
/contracts/erc1155/generated-artifacts
|
||||
/contracts/erc1155/test/generated-artifacts
|
||||
/contracts/extensions/generated-wrappers
|
||||
/contracts/extensions/test/generated-wrappers
|
||||
/contracts/extensions/generated-artifacts
|
||||
/contracts/extensions/test/generated-artifacts
|
||||
/contracts/exchange-forwarder/generated-wrappers
|
||||
/contracts/exchange-forwarder/test/generated-wrappers
|
||||
/contracts/exchange-forwarder/generated-artifacts
|
||||
/contracts/exchange-forwarder/test/generated-artifacts
|
||||
/contracts/dev-utils/generated-wrappers
|
||||
/contracts/dev-utils/test/generated-wrappers
|
||||
/contracts/dev-utils/generated-artifacts
|
||||
/contracts/dev-utils/test/generated-artifacts
|
||||
/contracts/staking/build/
|
||||
/contracts/coordinator/build/
|
||||
/contracts/exchange/build/
|
||||
/contracts/asset-proxy/build/
|
||||
/contracts/multisig/build/
|
||||
/contracts/utils/build/
|
||||
/contracts/exchange-libs/build/
|
||||
/contracts/erc20/build/
|
||||
/contracts/erc721/build/
|
||||
/contracts/erc1155/build/
|
||||
/contracts/extensions/build/
|
||||
/contracts/exchange-forwarder/build/
|
||||
/contracts/dev-utils/build/
|
||||
/packages/abi-gen/test-cli/output
|
||||
/packages/json-schemas/schemas
|
||||
/python-packages/json_schemas/src/zero_ex/json_schemas/schemas
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"printWidth": 120,
|
||||
"tabWidth": 4,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all"
|
||||
"printWidth": 120,
|
||||
"trailingComma": all,
|
||||
"singleQuote": true
|
||||
}
|
||||
|
@@ -47,7 +47,6 @@ These packages are all under development. See [/contracts/README.md](/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
|
||||
|
||||
@@ -93,12 +92,14 @@ These packages are all under development. See [/contracts/README.md](/contracts/
|
||||
| [`@0x/assert`](/packages/assert) | [](https://www.npmjs.com/package/@0x/assert) | Type and schema assertions used by our packages |
|
||||
| [`@0x/base-contract`](/packages/base-contract) | [](https://www.npmjs.com/package/@0x/base-contract) | BaseContract used by auto-generated `abi-gen` wrapper contracts |
|
||||
| [`@0x/dev-utils`](/packages/dev-utils) | [](https://www.npmjs.com/package/@0x/dev-utils) | Dev utils to be shared across 0x packages |
|
||||
| [`@0x/fill-scenarios`](/packages/fill-scenarios) | [](https://www.npmjs.com/package/@0x/fill-scenarios) | 0x order fill scenario generator |
|
||||
|
||||
#### Private Packages
|
||||
|
||||
| Package | Description |
|
||||
| ---------------------------------- | -------------------------------------------------------------------------------- |
|
||||
| [`@0x/instant`](/packages/instant) | A free and flexible way to offer simple crypto purchasing in any app or website. |
|
||||
| Package | Description |
|
||||
| -------------------------------------------------- | -------------------------------------------------------------------------------- |
|
||||
| [`@0x/instant`](/packages/instant) | A free and flexible way to offer simple crypto purchasing in any app or website. |
|
||||
| [`@0x/testnet-faucets`](/packages/testnet-faucets) | A faucet micro-service that dispenses test ERC20 tokens or Ether |
|
||||
|
||||
## Usage
|
||||
|
||||
|
@@ -1,2 +0,0 @@
|
||||
# solhint can't parse `abi.decode` syntax.
|
||||
contracts/src/ERC1155Proxy.sol
|
@@ -1,62 +1,4 @@
|
||||
[
|
||||
{
|
||||
"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",
|
||||
@@ -181,18 +123,6 @@
|
||||
{
|
||||
"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
|
||||
|
@@ -5,25 +5,6 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## 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
|
||||
@@ -77,9 +58,6 @@ CHANGELOG
|
||||
## 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_
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"artifactsDir": "./test/generated-artifacts",
|
||||
"artifactsDir": "./generated-artifacts",
|
||||
"contractsDir": "./contracts",
|
||||
"useDockerisedSolc": false,
|
||||
"isOfflineMode": false,
|
||||
@@ -22,5 +22,17 @@
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"contracts": [
|
||||
"src/ERC1155Proxy.sol",
|
||||
"src/ERC20Proxy.sol",
|
||||
"src/ERC721Proxy.sol",
|
||||
"src/MixinAuthorizable.sol",
|
||||
"src/MultiAssetProxy.sol",
|
||||
"src/StaticCallProxy.sol",
|
||||
"src/interfaces/IAssetData.sol",
|
||||
"src/interfaces/IAssetProxy.sol",
|
||||
"src/interfaces/IAuthorizable.sol",
|
||||
"test/TestStaticCallTarget.sol"
|
||||
]
|
||||
}
|
||||
|
@@ -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,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -19,18 +19,18 @@
|
||||
pragma solidity ^0.5.9;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
||||
import "@0x/contracts-utils/contracts/src/SafeMath.sol";
|
||||
import "@0x/contracts-erc1155/contracts/src/interfaces/IERC1155.sol";
|
||||
import "../archive/MixinAuthorizable.sol";
|
||||
import "./MixinAuthorizable.sol";
|
||||
import "./interfaces/IAssetProxy.sol";
|
||||
|
||||
|
||||
contract ERC1155Proxy is
|
||||
MixinAuthorizable,
|
||||
SafeMath,
|
||||
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)"));
|
||||
@@ -69,9 +69,9 @@ contract ERC1155Proxy is
|
||||
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
|
||||
// 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);
|
||||
scaledValues[i] = safeMul(values[i], amount);
|
||||
}
|
||||
|
||||
// Execute `safeBatchTransferFrom` call
|
||||
|
@@ -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,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -16,9 +16,9 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "../archive/MixinAuthorizable.sol";
|
||||
import "./MixinAuthorizable.sol";
|
||||
|
||||
|
||||
contract ERC20Proxy is
|
||||
@@ -26,9 +26,9 @@ contract ERC20Proxy is
|
||||
{
|
||||
// Id of this proxy.
|
||||
bytes4 constant internal PROXY_ID = bytes4(keccak256("ERC20Token(address)"));
|
||||
|
||||
|
||||
// solhint-disable-next-line payable-fallback
|
||||
function ()
|
||||
function ()
|
||||
external
|
||||
{
|
||||
assembly {
|
||||
@@ -117,13 +117,13 @@ contract ERC20Proxy is
|
||||
// * 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.
|
||||
@@ -147,7 +147,7 @@ contract ERC20Proxy is
|
||||
// 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.
|
||||
// returned nothing, or returned a non-zero 32 byte value.
|
||||
success := and(success, or(
|
||||
iszero(returndatasize),
|
||||
and(
|
||||
@@ -158,7 +158,7 @@ contract ERC20Proxy is
|
||||
if success {
|
||||
return(0, 0)
|
||||
}
|
||||
|
||||
|
||||
// Revert with `Error("TRANSFER_FAILED")`
|
||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -16,9 +16,9 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "../archive/MixinAuthorizable.sol";
|
||||
import "./MixinAuthorizable.sol";
|
||||
|
||||
|
||||
contract ERC721Proxy is
|
||||
@@ -28,7 +28,7 @@ contract ERC721Proxy is
|
||||
bytes4 constant internal PROXY_ID = bytes4(keccak256("ERC721Token(address,uint256)"));
|
||||
|
||||
// solhint-disable-next-line payable-fallback
|
||||
function ()
|
||||
function ()
|
||||
external
|
||||
{
|
||||
assembly {
|
||||
@@ -93,10 +93,10 @@ contract ERC721Proxy is
|
||||
// | 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 |
|
||||
@@ -121,7 +121,7 @@ contract ERC721Proxy is
|
||||
// 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.
|
||||
@@ -145,7 +145,7 @@ contract ERC721Proxy is
|
||||
if success {
|
||||
return(0, 0)
|
||||
}
|
||||
|
||||
|
||||
// Revert with `Error("TRANSFER_FAILED")`
|
||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -16,16 +16,16 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "../archive/Ownable.sol";
|
||||
import "../src/interfaces/IAssetProxy.sol";
|
||||
import "../src/interfaces/IAssetProxyDispatcher.sol";
|
||||
import "@0x/contracts-utils/contracts/src/Ownable.sol";
|
||||
import "./mixins/MAssetProxyDispatcher.sol";
|
||||
import "./interfaces/IAssetProxy.sol";
|
||||
|
||||
|
||||
contract MixinAssetProxyDispatcher is
|
||||
Ownable,
|
||||
IAssetProxyDispatcher
|
||||
MAssetProxyDispatcher
|
||||
{
|
||||
// Mapping from Asset Proxy Id's to their respective Asset Proxy
|
||||
mapping (bytes4 => address) public assetProxies;
|
||||
@@ -69,7 +69,7 @@ contract MixinAssetProxyDispatcher is
|
||||
/// @param from Address to transfer token from.
|
||||
/// @param to Address to transfer token to.
|
||||
/// @param amount Amount of token to transfer.
|
||||
function _dispatchTransferFrom(
|
||||
function dispatchTransferFrom(
|
||||
bytes memory assetData,
|
||||
address from,
|
||||
address to,
|
||||
@@ -84,7 +84,7 @@ contract MixinAssetProxyDispatcher is
|
||||
assetData.length > 3,
|
||||
"LENGTH_GREATER_THAN_3_REQUIRED"
|
||||
);
|
||||
|
||||
|
||||
// Lookup assetProxy. We do not use `LibBytes.readBytes4` for gas efficiency reasons.
|
||||
bytes4 assetProxyId;
|
||||
assembly {
|
||||
@@ -100,10 +100,10 @@ contract MixinAssetProxyDispatcher is
|
||||
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 |
|
||||
@@ -127,12 +127,12 @@ contract MixinAssetProxyDispatcher is
|
||||
// `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:
|
||||
@@ -142,7 +142,7 @@ contract MixinAssetProxyDispatcher is
|
||||
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)
|
||||
@@ -159,7 +159,7 @@ contract MixinAssetProxyDispatcher is
|
||||
assetProxy, // call address of asset proxy
|
||||
0, // don't send any ETH
|
||||
cdStart, // pointer to start of input
|
||||
sub(cdEnd, cdStart), // length of input
|
||||
sub(cdEnd, cdStart), // length of input
|
||||
cdStart, // write output over input
|
||||
512 // reserve 512 bytes for output
|
||||
)
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -16,15 +16,15 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "../archive/Ownable.sol";
|
||||
import "../src/interfaces/IAuthorizable.sol";
|
||||
import "@0x/contracts-utils/contracts/src/Ownable.sol";
|
||||
import "./mixins/MAuthorizable.sol";
|
||||
|
||||
|
||||
contract MixinAuthorizable is
|
||||
Ownable,
|
||||
IAuthorizable
|
||||
MAuthorizable
|
||||
{
|
||||
/// @dev Only authorized addresses can invoke functions with this modifier.
|
||||
modifier onlyAuthorized {
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -16,10 +16,10 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "../archive/MixinAssetProxyDispatcher.sol";
|
||||
import "../archive/MixinAuthorizable.sol";
|
||||
import "./MixinAssetProxyDispatcher.sol";
|
||||
import "./MixinAuthorizable.sol";
|
||||
|
||||
|
||||
contract MultiAssetProxy is
|
||||
@@ -105,7 +105,7 @@ contract MultiAssetProxy is
|
||||
// | | 36 | | 2. offset to nestedAssetData (*) |
|
||||
// | Data | | | amounts: |
|
||||
// | | 68 | 32 | amounts Length |
|
||||
// | | 100 | a | amounts Contents |
|
||||
// | | 100 | a | amounts Contents |
|
||||
// | | | | nestedAssetData: |
|
||||
// | | 100 + a | 32 | nestedAssetData Length |
|
||||
// | | 132 + a | b | nestedAssetData Contents (offsets) |
|
||||
@@ -149,8 +149,8 @@ contract MultiAssetProxy is
|
||||
// + 32 (amounts offset)
|
||||
let nestedAssetDataOffset := calldataload(add(assetDataOffset, 68))
|
||||
|
||||
// In order to find the start of the `amounts` contents, we must add:
|
||||
// assetDataOffset
|
||||
// In order to find the start of the `amounts` contents, we must add:
|
||||
// assetDataOffset
|
||||
// + 32 (assetData len)
|
||||
// + 4 (assetProxyId)
|
||||
// + amountsOffset
|
||||
@@ -160,8 +160,8 @@ contract MultiAssetProxy is
|
||||
// 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
|
||||
// In order to find the start of the `nestedAssetData` contents, we must add:
|
||||
// assetDataOffset
|
||||
// + 32 (assetData len)
|
||||
// + 4 (assetProxyId)
|
||||
// + nestedAssetDataOffset
|
||||
@@ -190,10 +190,10 @@ contract MultiAssetProxy is
|
||||
|
||||
// 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)
|
||||
|
||||
@@ -208,7 +208,7 @@ contract MultiAssetProxy is
|
||||
let amountsElement := calldataload(add(amountsContentsStart, i))
|
||||
let totalAmount := mul(amountsElement, amount)
|
||||
|
||||
// Revert if `amount` != 0 and multiplication resulted in an overflow
|
||||
// Revert if `amount` != 0 and multiplication resulted in an overflow
|
||||
if iszero(or(
|
||||
iszero(amount),
|
||||
eq(div(totalAmount, amount), amountsElement)
|
||||
@@ -228,7 +228,7 @@ contract MultiAssetProxy is
|
||||
let nestedAssetDataElementOffset := calldataload(add(nestedAssetDataContentsStart, i))
|
||||
|
||||
// In order to find the start of the `nestedAssetData[i]` contents, we must add:
|
||||
// assetDataOffset
|
||||
// assetDataOffset
|
||||
// + 32 (assetData len)
|
||||
// + 4 (assetProxyId)
|
||||
// + nestedAssetDataOffset
|
||||
@@ -274,7 +274,7 @@ contract MultiAssetProxy is
|
||||
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")`
|
||||
@@ -284,7 +284,7 @@ contract MultiAssetProxy is
|
||||
mstore(96, 0)
|
||||
revert(0, 100)
|
||||
}
|
||||
|
||||
|
||||
// Copy `nestedAssetData[i]` from calldata to memory
|
||||
calldatacopy(
|
||||
132, // memory slot after `amounts[i]`
|
||||
@@ -298,7 +298,7 @@ contract MultiAssetProxy is
|
||||
assetProxy, // call address of asset proxy
|
||||
0, // don't send any ETH
|
||||
0, // pointer to start of input
|
||||
add(164, nestedAssetDataElementLen), // length 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
|
||||
)
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@@ -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 "../interfaces/IERC20Bridge.sol";
|
||||
import "../interfaces/IEth2Dai.sol";
|
||||
|
||||
|
||||
// solhint-disable space-after-comma
|
||||
contract Eth2DaiBridge is
|
||||
IERC20Bridge,
|
||||
IWallet
|
||||
{
|
||||
/* Mainnet addresses */
|
||||
address constant public ETH2DAI_ADDRESS = 0x39755357759cE0d7f32dC8dC45414CCa409AE24e;
|
||||
|
||||
/// @dev Callback for `IERC20Bridge`. Tries to buy `amount` of
|
||||
/// `toTokenAddress` tokens by selling the entirety of the opposing asset
|
||||
/// (DAI or WETH) to the Eth2Dai contract, then transfers the bought
|
||||
/// tokens to `to`.
|
||||
/// @param toTokenAddress The token to give to `to` (either DAI or WETH).
|
||||
/// @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 = _getEth2DaiContract();
|
||||
// Grant an allowance to the exchange to spend `fromTokenAddress` token.
|
||||
LibERC20Token.approve(fromTokenAddress, address(exchange), uint256(-1));
|
||||
|
||||
// Try to sell all of this contract's `fromTokenAddress` token balance.
|
||||
uint256 boughtAmount = _getEth2DaiContract().sellAllAmount(
|
||||
address(fromTokenAddress),
|
||||
IERC20Token(fromTokenAddress).balanceOf(address(this)),
|
||||
toTokenAddress,
|
||||
amount
|
||||
);
|
||||
// Transfer the converted `toToken`s to `to`.
|
||||
LibERC20Token.transfer(toTokenAddress, to, boughtAmount);
|
||||
return BRIDGE_SUCCESS;
|
||||
}
|
||||
|
||||
/// @dev `SignatureType.Wallet` callback, so that this bridge can be the maker
|
||||
/// and sign for itself in orders. Always succeeds.
|
||||
/// @return magicValue Magic success bytes, always.
|
||||
function isValidSignature(
|
||||
bytes32,
|
||||
bytes calldata
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (bytes4 magicValue)
|
||||
{
|
||||
return LEGACY_WALLET_MAGIC_VALUE;
|
||||
}
|
||||
|
||||
/// @dev Overridable way to get the eth2dai contract.
|
||||
/// @return exchange The Eth2Dai exchange contract.
|
||||
function _getEth2DaiContract()
|
||||
internal
|
||||
view
|
||||
returns (IEth2Dai exchange)
|
||||
{
|
||||
return IEth2Dai(ETH2DAI_ADDRESS);
|
||||
}
|
||||
}
|
@@ -1,219 +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 "../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
|
||||
{
|
||||
/* Mainnet addresses */
|
||||
address constant private UNISWAP_EXCHANGE_FACTORY_ADDRESS = 0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95;
|
||||
address constant private WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
|
||||
|
||||
// Struct to hold `bridgeTransferFrom()` local variables in memory and to avoid
|
||||
// stack overflows.
|
||||
struct WithdrawToState {
|
||||
IUniswapExchange exchange;
|
||||
uint256 fromTokenBalance;
|
||||
IEtherToken weth;
|
||||
}
|
||||
|
||||
// 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 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.
|
||||
WithdrawToState 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 = getWethContract();
|
||||
|
||||
// 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.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);
|
||||
// Buy as much ETH with `fromTokenAddress` token as possible.
|
||||
uint256 ethBought = 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(ethBought)();
|
||||
// Transfer the WETH to `to`.
|
||||
IEtherToken(toTokenAddress).transfer(to, ethBought);
|
||||
|
||||
// Convert from one token to another.
|
||||
} else {
|
||||
// Grant the exchange an allowance.
|
||||
_grantExchangeAllowance(state.exchange, fromTokenAddress);
|
||||
// Buy as much `toTokenAddress` token with `fromTokenAddress` token
|
||||
// and transfer it to `to`.
|
||||
state.exchange.tokenToTokenTransferInput(
|
||||
// Sell all tokens we hold.
|
||||
state.fromTokenBalance,
|
||||
// Minimum buy amount.
|
||||
amount,
|
||||
// No minimum intermediate ETH buy amount.
|
||||
0,
|
||||
// Expires after this block.
|
||||
block.timestamp,
|
||||
// Recipient is `to`.
|
||||
to,
|
||||
// Convert to `toTokenAddress`.
|
||||
toTokenAddress
|
||||
);
|
||||
}
|
||||
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 Overridable way to get the weth contract.
|
||||
/// @return token The WETH contract.
|
||||
function getWethContract()
|
||||
public
|
||||
view
|
||||
returns (IEtherToken token)
|
||||
{
|
||||
return IEtherToken(WETH_ADDRESS);
|
||||
}
|
||||
|
||||
/// @dev Overridable way to get the uniswap exchange factory contract.
|
||||
/// @return factory The exchange factory contract.
|
||||
function getUniswapExchangeFactoryContract()
|
||||
public
|
||||
view
|
||||
returns (IUniswapExchangeFactory factory)
|
||||
{
|
||||
return IUniswapExchangeFactory(UNISWAP_EXCHANGE_FACTORY_ADDRESS);
|
||||
}
|
||||
|
||||
/// @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.
|
||||
function _grantExchangeAllowance(IUniswapExchange exchange, address tokenAddress)
|
||||
private
|
||||
{
|
||||
LibERC20Token.approve(tokenAddress, address(exchange), uint256(-1));
|
||||
}
|
||||
|
||||
/// @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 == address(getWethContract())) {
|
||||
exchangeTokenAddress = toTokenAddress;
|
||||
}
|
||||
exchange = getUniswapExchangeFactoryContract().getExchange(exchangeTokenAddress);
|
||||
require(address(exchange) != address(0), "NO_UNISWAP_EXCHANGE_FOR_TOKEN");
|
||||
return exchange;
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
|
||||
// solhint-disable
|
||||
pragma solidity ^0.5.9;
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
|
||||
@@ -26,63 +26,33 @@ pragma experimental ABIEncoderV2;
|
||||
// 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,
|
||||
uint256[] calldata tokenValues,
|
||||
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,
|
||||
uint256[] calldata amounts,
|
||||
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,
|
||||
address callTarget,
|
||||
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
|
||||
bytes32 callResultHash
|
||||
)
|
||||
external;
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -16,7 +16,9 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "./IAuthorizable.sol";
|
||||
|
||||
|
||||
contract IAssetProxy {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -16,17 +16,11 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
|
||||
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.
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/interfaces/IOwnable.sol";
|
||||
|
||||
@@ -24,18 +24,6 @@ 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)
|
||||
@@ -54,7 +42,7 @@ contract IAuthorizable is
|
||||
uint256 index
|
||||
)
|
||||
external;
|
||||
|
||||
|
||||
/// @dev Gets all authorized addresses.
|
||||
/// @return Array of authorized addresses.
|
||||
function getAuthorizedAddresses()
|
||||
|
@@ -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 IERC20Bridge {
|
||||
|
||||
// @dev Result of a successful bridge call.
|
||||
bytes4 constant internal BRIDGE_SUCCESS = 0xdc1600f3;
|
||||
|
||||
/// @dev Transfers `amount` of the ERC20 `tokenAddress` from `from` to `to`.
|
||||
/// @param tokenAddress The address of the ERC20 token to transfer.
|
||||
/// @param from Address to transfer asset from.
|
||||
/// @param to Address to transfer asset to.
|
||||
/// @param amount Amount of asset to transfer.
|
||||
/// @param bridgeData Arbitrary asset data needed by the bridge contract.
|
||||
/// @return success The magic bytes `0x37708e9b` if successful.
|
||||
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,77 +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);
|
||||
|
||||
/// @dev Retrieves the token that is associated with this exchange.
|
||||
/// @return tokenAddress The token address.
|
||||
function toTokenAddress()
|
||||
external
|
||||
view
|
||||
returns (address tokenAddress);
|
||||
}
|
@@ -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 (IUniswapExchange);
|
||||
}
|
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
|
||||
contract LibAssetProxyIds {
|
||||
|
||||
// AssetProxy Ids are equiavalent the first 4 bytes of the keccak256 hash of the function signature assigned to each AssetProxy.
|
||||
|
||||
// ERC20Token(address)
|
||||
bytes4 constant public ERC20_PROXY_ID = 0xf47261b0;
|
||||
|
||||
// ERC721Token(address,uint256)
|
||||
bytes4 constant public ERC721_PROXY_ID = 0x02571792;
|
||||
|
||||
// ERC1155Assets(address,uint256[],uint256[],bytes)
|
||||
bytes4 constant public ERC1155_PROXY_ID = 0xa7cb5fb7;
|
||||
|
||||
// MultiAsset(uint256[],bytes[])
|
||||
bytes4 constant public MULTI_ASSET_PROXY_ID = 0x94cfcdd7;
|
||||
|
||||
// StaticCall(address,bytes,bytes32)
|
||||
bytes4 constant public STATIC_CALL_PROXY_ID = 0xc339d10a;
|
||||
}
|
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 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.5;
|
||||
|
||||
import "../interfaces/IAssetProxyDispatcher.sol";
|
||||
|
||||
|
||||
contract MAssetProxyDispatcher is
|
||||
IAssetProxyDispatcher
|
||||
{
|
||||
// Logs registration of new asset proxy
|
||||
event AssetProxyRegistered(
|
||||
bytes4 id, // Id of new registered AssetProxy.
|
||||
address assetProxy // Address of new registered AssetProxy.
|
||||
);
|
||||
|
||||
/// @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;
|
||||
}
|
41
contracts/asset-proxy/contracts/src/mixins/MAuthorizable.sol
Normal file
41
contracts/asset-proxy/contracts/src/mixins/MAuthorizable.sol
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 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.5;
|
||||
|
||||
import "../interfaces/IAuthorizable.sol";
|
||||
|
||||
|
||||
contract MAuthorizable is
|
||||
IAuthorizable
|
||||
{
|
||||
// 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 Only authorized addresses can invoke functions with this modifier.
|
||||
modifier onlyAuthorized { revert(); _; }
|
||||
}
|
@@ -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,202 +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;
|
||||
}
|
||||
|
||||
/// @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 _getEth2DaiContract()
|
||||
internal
|
||||
view
|
||||
returns (IEth2Dai)
|
||||
{
|
||||
return IEth2Dai(address(this));
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@@ -1,432 +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);
|
||||
}
|
||||
|
||||
/// @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 (IUniswapExchange)
|
||||
{
|
||||
return IUniswapExchange(_testExchanges[tokenAddress]);
|
||||
}
|
||||
|
||||
// @dev Use `wethToken`.
|
||||
function getWethContract()
|
||||
public
|
||||
view
|
||||
returns (IEtherToken)
|
||||
{
|
||||
return IEtherToken(address(wethToken));
|
||||
}
|
||||
|
||||
// @dev This contract will double as the Uniswap contract.
|
||||
function getUniswapExchangeFactoryContract()
|
||||
public
|
||||
view
|
||||
returns (IUniswapExchangeFactory)
|
||||
{
|
||||
return IUniswapExchangeFactory(address(this));
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-asset-proxy",
|
||||
"version": "2.3.0-beta.2",
|
||||
"version": "2.2.8",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -12,7 +12,7 @@
|
||||
"scripts": {
|
||||
"build": "yarn pre_build && tsc -b",
|
||||
"build:ci": "yarn build",
|
||||
"pre_build": "run-s compile contracts:gen generate_contract_wrappers contracts:copy",
|
||||
"pre_build": "run-s compile generate_contract_wrappers",
|
||||
"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",
|
||||
@@ -21,23 +21,20 @@
|
||||
"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",
|
||||
"clean": "shx rm -rf lib generated-artifacts generated-wrappers",
|
||||
"generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --output generated-wrappers --backend ethers",
|
||||
"lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||
"fix": "tslint --fix --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./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"
|
||||
"contracts:gen": "contracts-gen",
|
||||
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol"
|
||||
},
|
||||
"config": {
|
||||
"publicInterfaceContracts": "ERC1155Proxy,ERC20Proxy,ERC721Proxy,MultiAssetProxy,StaticCallProxy,ERC20BridgeProxy,Eth2DaiBridge,IAssetData,IAssetProxy,UniswapBridge,TestStaticCallTarget",
|
||||
"abis": "./test/generated-artifacts/@(ERC1155Proxy|ERC20BridgeProxy|ERC20Proxy|ERC721Proxy|Eth2DaiBridge|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|IERC20Bridge|IEth2Dai|IUniswapExchange|IUniswapExchangeFactory|MixinAssetProxyDispatcher|MixinAuthorizable|MultiAssetProxy|Ownable|StaticCallProxy|TestERC20Bridge|TestEth2DaiBridge|TestStaticCallTarget|TestUniswapBridge|UniswapBridge).json",
|
||||
"abis": "./generated-artifacts/@(ERC1155Proxy|ERC20Proxy|ERC721Proxy|IAssetData|IAssetProxy|IAuthorizable|MixinAuthorizable|MultiAssetProxy|StaticCallProxy|TestStaticCallTarget).json",
|
||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
|
||||
},
|
||||
"repository": {
|
||||
@@ -50,14 +47,12 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/protocol/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^4.4.0-beta.2",
|
||||
"@0x/contracts-gen": "^1.1.0-beta.2",
|
||||
"@0x/contracts-test-utils": "^3.2.0-beta.2",
|
||||
"@0x/contracts-utils": "^3.3.0-beta.2",
|
||||
"@0x/dev-utils": "^2.4.0-beta.2",
|
||||
"@0x/sol-compiler": "^3.2.0-beta.2",
|
||||
"@0x/tslint-config": "^3.1.0-beta.2",
|
||||
"@0x/types": "^2.5.0-beta.2",
|
||||
"@0x/abi-gen": "^4.2.1",
|
||||
"@0x/contracts-gen": "^1.0.15",
|
||||
"@0x/contracts-test-utils": "^3.1.16",
|
||||
"@0x/dev-utils": "^2.3.3",
|
||||
"@0x/sol-compiler": "^3.1.15",
|
||||
"@0x/tslint-config": "^3.0.1",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/node": "*",
|
||||
@@ -65,27 +60,27 @@
|
||||
"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",
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^5.5.0-beta.2",
|
||||
"@0x/contracts-dev-utils": "^0.1.0-beta.2",
|
||||
"@0x/contracts-erc1155": "^1.2.0-beta.2",
|
||||
"@0x/contracts-erc20": "^2.3.0-beta.2",
|
||||
"@0x/contracts-erc721": "^2.2.0-beta.2",
|
||||
"@0x/order-utils": "^8.5.0-beta.2",
|
||||
"@0x/typescript-typings": "^4.4.0-beta.2",
|
||||
"@0x/utils": "^4.6.0-beta.2",
|
||||
"@0x/web3-wrapper": "^6.1.0-beta.2",
|
||||
"ethereum-types": "^2.2.0-beta.2",
|
||||
"@0x/base-contract": "^5.4.0",
|
||||
"@0x/contracts-erc1155": "^1.1.15",
|
||||
"@0x/contracts-erc20": "^2.2.14",
|
||||
"@0x/contracts-erc721": "^2.1.15",
|
||||
"@0x/contracts-utils": "^3.2.4",
|
||||
"@0x/order-utils": "^8.4.0",
|
||||
"@0x/types": "^2.4.3",
|
||||
"@0x/typescript-typings": "^4.3.0",
|
||||
"@0x/utils": "^4.5.2",
|
||||
"@0x/web3-wrapper": "^6.0.13",
|
||||
"ethereum-types": "^2.1.6",
|
||||
"ethereumjs-util": "^5.1.1",
|
||||
"lodash": "^4.17.11"
|
||||
},
|
||||
"publishConfig": {
|
||||
|
@@ -6,26 +6,24 @@
|
||||
import { ContractArtifact } from 'ethereum-types';
|
||||
|
||||
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 IAuthorizable from '../generated-artifacts/IAuthorizable.json';
|
||||
import * as MixinAuthorizable from '../generated-artifacts/MixinAuthorizable.json';
|
||||
import * as MultiAssetProxy from '../generated-artifacts/MultiAssetProxy.json';
|
||||
import * as StaticCallProxy from '../generated-artifacts/StaticCallProxy.json';
|
||||
import * as TestStaticCallTarget from '../generated-artifacts/TestStaticCallTarget.json';
|
||||
import * as UniswapBridge from '../generated-artifacts/UniswapBridge.json';
|
||||
export const artifacts = {
|
||||
ERC1155Proxy: ERC1155Proxy as ContractArtifact,
|
||||
ERC20Proxy: ERC20Proxy as ContractArtifact,
|
||||
ERC721Proxy: ERC721Proxy as ContractArtifact,
|
||||
MixinAuthorizable: MixinAuthorizable as ContractArtifact,
|
||||
MultiAssetProxy: MultiAssetProxy as ContractArtifact,
|
||||
StaticCallProxy: StaticCallProxy as ContractArtifact,
|
||||
ERC20BridgeProxy: ERC20BridgeProxy as ContractArtifact,
|
||||
Eth2DaiBridge: Eth2DaiBridge as ContractArtifact,
|
||||
IAssetData: IAssetData as ContractArtifact,
|
||||
IAssetProxy: IAssetProxy as ContractArtifact,
|
||||
UniswapBridge: UniswapBridge as ContractArtifact,
|
||||
IAuthorizable: IAuthorizable as ContractArtifact,
|
||||
TestStaticCallTarget: TestStaticCallTarget as ContractArtifact,
|
||||
};
|
||||
|
@@ -1,6 +1,3 @@
|
||||
export * from './artifacts';
|
||||
export * from './wrappers';
|
||||
|
||||
export { ERC20Wrapper } from './erc20_wrapper';
|
||||
export { ERC721Wrapper } from './erc721_wrapper';
|
||||
export { ERC1155ProxyWrapper } from './erc1155_proxy_wrapper';
|
||||
export * from '../test/utils';
|
||||
|
@@ -4,13 +4,12 @@
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
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_authorizable';
|
||||
export * from '../generated-wrappers/mixin_authorizable';
|
||||
export * from '../generated-wrappers/multi_asset_proxy';
|
||||
export * from '../generated-wrappers/static_call_proxy';
|
||||
export * from '../generated-wrappers/test_static_call_target';
|
||||
export * from '../generated-wrappers/uniswap_bridge';
|
||||
|
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* -----------------------------------------------------------------------------
|
||||
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
import { ContractArtifact } from 'ethereum-types';
|
||||
|
||||
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 IERC20Bridge from '../test/generated-artifacts/IERC20Bridge.json';
|
||||
import * as IEth2Dai from '../test/generated-artifacts/IEth2Dai.json';
|
||||
import * as IUniswapExchange from '../test/generated-artifacts/IUniswapExchange.json';
|
||||
import * as IUniswapExchangeFactory from '../test/generated-artifacts/IUniswapExchangeFactory.json';
|
||||
import * as MixinAssetProxyDispatcher from '../test/generated-artifacts/MixinAssetProxyDispatcher.json';
|
||||
import * as MixinAuthorizable from '../test/generated-artifacts/MixinAuthorizable.json';
|
||||
import * as MultiAssetProxy from '../test/generated-artifacts/MultiAssetProxy.json';
|
||||
import * as Ownable from '../test/generated-artifacts/Ownable.json';
|
||||
import * as StaticCallProxy from '../test/generated-artifacts/StaticCallProxy.json';
|
||||
import * as TestERC20Bridge from '../test/generated-artifacts/TestERC20Bridge.json';
|
||||
import * as TestEth2DaiBridge from '../test/generated-artifacts/TestEth2DaiBridge.json';
|
||||
import * as TestStaticCallTarget from '../test/generated-artifacts/TestStaticCallTarget.json';
|
||||
import * as TestUniswapBridge from '../test/generated-artifacts/TestUniswapBridge.json';
|
||||
import * as UniswapBridge from '../test/generated-artifacts/UniswapBridge.json';
|
||||
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,
|
||||
Eth2DaiBridge: Eth2DaiBridge as ContractArtifact,
|
||||
UniswapBridge: UniswapBridge as ContractArtifact,
|
||||
IAssetData: IAssetData as ContractArtifact,
|
||||
IAssetProxy: IAssetProxy as ContractArtifact,
|
||||
IAssetProxyDispatcher: IAssetProxyDispatcher as ContractArtifact,
|
||||
IAuthorizable: IAuthorizable as ContractArtifact,
|
||||
IERC20Bridge: IERC20Bridge as ContractArtifact,
|
||||
IEth2Dai: IEth2Dai as ContractArtifact,
|
||||
IUniswapExchange: IUniswapExchange as ContractArtifact,
|
||||
IUniswapExchangeFactory: IUniswapExchangeFactory as ContractArtifact,
|
||||
TestERC20Bridge: TestERC20Bridge as ContractArtifact,
|
||||
TestEth2DaiBridge: TestEth2DaiBridge as ContractArtifact,
|
||||
TestStaticCallTarget: TestStaticCallTarget as ContractArtifact,
|
||||
TestUniswapBridge: TestUniswapBridge as ContractArtifact,
|
||||
};
|
@@ -1,13 +1,18 @@
|
||||
import { chaiSetup, expectTransactionFailedAsync, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils';
|
||||
import {
|
||||
chaiSetup,
|
||||
constants,
|
||||
expectTransactionFailedAsync,
|
||||
provider,
|
||||
txDefaults,
|
||||
web3Wrapper,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||
import { RevertReason } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as chai from 'chai';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { artifacts } from './artifacts';
|
||||
|
||||
import { MixinAuthorizableContract } from './wrappers';
|
||||
import { artifacts, MixinAuthorizableContract } from '../src';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
@@ -22,11 +27,9 @@ describe('Authorizable', () => {
|
||||
before(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
|
||||
before(async () => {
|
||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
||||
[owner, address, notOwner] = _.slice(accounts, 0, 3);
|
||||
@@ -37,57 +40,74 @@ describe('Authorizable', () => {
|
||||
artifacts,
|
||||
);
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
|
||||
describe('addAuthorizedAddress', () => {
|
||||
it('should revert if not called by owner', async () => {
|
||||
await expectTransactionFailedAsync(
|
||||
authorizable.addAuthorizedAddress(notOwner).sendTransactionAsync({ from: notOwner }),
|
||||
it('should throw if not called by owner', async () => {
|
||||
return expectTransactionFailedAsync(
|
||||
authorizable.addAuthorizedAddress.sendTransactionAsync(notOwner, { from: notOwner }),
|
||||
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();
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const isAuthorized = await authorizable.authorized.callAsync(address);
|
||||
expect(isAuthorized).to.be.true();
|
||||
});
|
||||
|
||||
it('should revert if owner attempts to authorize a duplicate address', async () => {
|
||||
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
||||
it('should throw if owner attempts to authorize a duplicate address', async () => {
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
return expectTransactionFailedAsync(
|
||||
authorizable.addAuthorizedAddress(address).sendTransactionAsync({ from: owner }),
|
||||
authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }),
|
||||
RevertReason.TargetAlreadyAuthorized,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeAuthorizedAddress', () => {
|
||||
it('should revert if not called by owner', async () => {
|
||||
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
||||
await expectTransactionFailedAsync(
|
||||
authorizable.removeAuthorizedAddress(address).sendTransactionAsync({ from: notOwner }),
|
||||
it('should throw if not called by owner', async () => {
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
return expectTransactionFailedAsync(
|
||||
authorizable.removeAuthorizedAddress.sendTransactionAsync(address, {
|
||||
from: notOwner,
|
||||
}),
|
||||
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();
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await authorizable.removeAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const isAuthorized = await authorizable.authorized.callAsync(address);
|
||||
expect(isAuthorized).to.be.false();
|
||||
});
|
||||
|
||||
it('should revert if owner attempts to remove an address that is not authorized', async () => {
|
||||
it('should throw if owner attempts to remove an address that is not authorized', async () => {
|
||||
return expectTransactionFailedAsync(
|
||||
authorizable.removeAuthorizedAddress(address).sendTransactionAsync({
|
||||
authorizable.removeAuthorizedAddress.sendTransactionAsync(address, {
|
||||
from: owner,
|
||||
}),
|
||||
RevertReason.TargetNotAuthorized,
|
||||
@@ -96,73 +116,100 @@ describe('Authorizable', () => {
|
||||
});
|
||||
|
||||
describe('removeAuthorizedAddressAtIndex', () => {
|
||||
it('should revert if not called by owner', async () => {
|
||||
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
||||
it('should throw if not called by owner', async () => {
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const index = new BigNumber(0);
|
||||
await expectTransactionFailedAsync(
|
||||
authorizable.removeAuthorizedAddressAtIndex(address, index).sendTransactionAsync({
|
||||
return expectTransactionFailedAsync(
|
||||
authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, {
|
||||
from: notOwner,
|
||||
}),
|
||||
RevertReason.OnlyContractOwner,
|
||||
);
|
||||
});
|
||||
|
||||
it('should revert if index is >= authorities.length', async () => {
|
||||
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
||||
it('should throw if index is >= authorities.length', async () => {
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const index = new BigNumber(1);
|
||||
return expectTransactionFailedAsync(
|
||||
authorizable.removeAuthorizedAddressAtIndex(address, index).sendTransactionAsync({
|
||||
authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, {
|
||||
from: owner,
|
||||
}),
|
||||
RevertReason.IndexOutOfBounds,
|
||||
);
|
||||
});
|
||||
|
||||
it('should revert if owner attempts to remove an address that is not authorized', async () => {
|
||||
it('should throw if owner attempts to remove an address that is not authorized', async () => {
|
||||
const index = new BigNumber(0);
|
||||
return expectTransactionFailedAsync(
|
||||
authorizable.removeAuthorizedAddressAtIndex(address, index).sendTransactionAsync({
|
||||
authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, {
|
||||
from: owner,
|
||||
}),
|
||||
RevertReason.TargetNotAuthorized,
|
||||
);
|
||||
});
|
||||
|
||||
it('should revert if address at index does not match target', async () => {
|
||||
it('should throw 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 });
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
address1,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
address2,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const address1Index = new BigNumber(0);
|
||||
return expectTransactionFailedAsync(
|
||||
authorizable.removeAuthorizedAddressAtIndex(address2, address1Index).sendTransactionAsync({
|
||||
authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address2, address1Index, {
|
||||
from: owner,
|
||||
}),
|
||||
RevertReason.AuthorizedAddressMismatch,
|
||||
);
|
||||
});
|
||||
|
||||
it('should allow owner to remove an authorized address', async () => {
|
||||
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const index = new BigNumber(0);
|
||||
await authorizable.removeAuthorizedAddressAtIndex(address, index).awaitTransactionSuccessAsync({
|
||||
from: owner,
|
||||
});
|
||||
const isAuthorized = await authorizable.authorized(address).callAsync();
|
||||
await authorizable.removeAuthorizedAddressAtIndex.awaitTransactionSuccessAsync(
|
||||
address,
|
||||
index,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const isAuthorized = await authorizable.authorized.callAsync(address);
|
||||
expect(isAuthorized).to.be.false();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAuthorizedAddresses', () => {
|
||||
it('should return all authorized addresses', async () => {
|
||||
const initial = await authorizable.getAuthorizedAddresses().callAsync();
|
||||
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();
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
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();
|
||||
await authorizable.removeAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const afterRemove = await authorizable.getAuthorizedAddresses.callAsync();
|
||||
expect(afterRemove).to.have.length(0);
|
||||
});
|
||||
});
|
||||
|
@@ -1,4 +1,3 @@
|
||||
import { DevUtilsContract } from '@0x/contracts-dev-utils';
|
||||
import {
|
||||
artifacts as erc1155Artifacts,
|
||||
DummyERC1155ReceiverBatchTokenReceivedEventArgs,
|
||||
@@ -15,8 +14,8 @@ import {
|
||||
txDefaults,
|
||||
web3Wrapper,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { SafeMathRevertErrors } from '@0x/contracts-utils';
|
||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||
import { assetDataUtils } from '@0x/order-utils';
|
||||
import { AssetProxyId, RevertReason } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as chai from 'chai';
|
||||
@@ -24,10 +23,7 @@ import { LogWithDecodedArgs } from 'ethereum-types';
|
||||
import * as ethUtil from 'ethereumjs-util';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { ERC1155ProxyWrapper } from '../src/erc1155_proxy_wrapper';
|
||||
import { ERC1155ProxyContract, IAssetDataContract } from '../src/wrappers';
|
||||
|
||||
import { artifacts } from './artifacts';
|
||||
import { artifacts, ERC1155ProxyContract, ERC1155ProxyWrapper } from '../src';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
@@ -63,8 +59,6 @@ describe('ERC1155Proxy', () => {
|
||||
// tokens
|
||||
let fungibleTokens: BigNumber[];
|
||||
let nonFungibleTokensOwnedBySpender: BigNumber[];
|
||||
// devUtils for encoding and decoding assetData
|
||||
let devUtils: DevUtilsContract;
|
||||
// tests
|
||||
before(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
@@ -78,8 +72,16 @@ describe('ERC1155Proxy', () => {
|
||||
const usedAddresses = ([owner, notAuthorized, authorized, spender, receiver] = _.slice(accounts, 0, 5));
|
||||
erc1155ProxyWrapper = new ERC1155ProxyWrapper(provider, usedAddresses, owner);
|
||||
erc1155Proxy = await erc1155ProxyWrapper.deployProxyAsync();
|
||||
await erc1155Proxy.addAuthorizedAddress(authorized).awaitTransactionSuccessAsync({ from: owner });
|
||||
await erc1155Proxy.addAuthorizedAddress(erc1155Proxy.address).awaitTransactionSuccessAsync({ from: owner });
|
||||
await erc1155Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
authorized,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await erc1155Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
erc1155Proxy.address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
// deploy & configure ERC1155 tokens and receiver
|
||||
[erc1155Wrapper] = await erc1155ProxyWrapper.deployDummyContractsAsync();
|
||||
erc1155Contract = erc1155Wrapper.getContract();
|
||||
@@ -101,8 +103,6 @@ describe('ERC1155Proxy', () => {
|
||||
tokenBalances.nonFungible[spender][erc1155Contract.address][nonFungibleTokenAsString][0];
|
||||
nonFungibleTokensOwnedBySpender.push(nonFungibleTokenHeldBySpender);
|
||||
});
|
||||
// set up devUtils
|
||||
devUtils = new DevUtilsContract(constants.NULL_ADDRESS, provider, { from: owner });
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
@@ -123,7 +123,7 @@ describe('ERC1155Proxy', () => {
|
||||
);
|
||||
});
|
||||
it('should have an id of 0xa7cb5fb7', async () => {
|
||||
const proxyId = await erc1155Proxy.getProxyId().callAsync();
|
||||
const proxyId = await erc1155Proxy.getProxyId.callAsync();
|
||||
const expectedProxyId = AssetProxyId.ERC1155;
|
||||
expect(proxyId).to.equal(expectedProxyId);
|
||||
});
|
||||
@@ -638,14 +638,12 @@ describe('ERC1155Proxy', () => {
|
||||
return value.times(valueMultiplier);
|
||||
});
|
||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||
const assetData = await devUtils
|
||||
.encodeERC1155AssetData(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
)
|
||||
.callAsync();
|
||||
const assetData = assetDataUtils.encodeERC1155AssetData(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
const extraData = '0102030405060708091001020304050607080910010203040506070809100102';
|
||||
const assetDataWithExtraData = `${assetData}${extraData}`;
|
||||
// check balances before transfer
|
||||
@@ -698,20 +696,25 @@ describe('ERC1155Proxy', () => {
|
||||
const tokenUri = '';
|
||||
for (const tokenToCreate of tokensToCreate) {
|
||||
// create token
|
||||
await erc1155Wrapper
|
||||
.getContract()
|
||||
.createWithType(tokenToCreate, tokenUri)
|
||||
.awaitTransactionSuccessAsync({
|
||||
await erc1155Wrapper.getContract().createWithType.awaitTransactionSuccessAsync(
|
||||
tokenToCreate,
|
||||
tokenUri,
|
||||
{
|
||||
from: owner,
|
||||
});
|
||||
},
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
|
||||
// mint balance for spender
|
||||
await erc1155Wrapper
|
||||
.getContract()
|
||||
.mintFungible(tokenToCreate, [spender], [spenderInitialBalance])
|
||||
.awaitTransactionSuccessAsync({
|
||||
await erc1155Wrapper.getContract().mintFungible.awaitTransactionSuccessAsync(
|
||||
tokenToCreate,
|
||||
[spender],
|
||||
[spenderInitialBalance],
|
||||
{
|
||||
from: owner,
|
||||
});
|
||||
},
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
}
|
||||
///// Step 2/5 /////
|
||||
// Check balances before transfer
|
||||
@@ -744,16 +747,18 @@ describe('ERC1155Proxy', () => {
|
||||
const tokensToTransfer = [new BigNumber(1), new BigNumber(2)];
|
||||
const valuesToTransfer = tokensToTransfer;
|
||||
const valueMultiplier = new BigNumber(2);
|
||||
|
||||
// hand encode optimized assetData because our tooling (based on LibAssetData.sol/encodeERC1155AssetData) does not use optimized encoding
|
||||
const assetDataContract = new IAssetDataContract(constants.NULL_ADDRESS, provider);
|
||||
const selector = assetDataContract.getSelector('ERC1155Assets');
|
||||
const assetDataWithoutContractAddress =
|
||||
const assetData = assetDataUtils.encodeERC1155AssetData(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
// remove the function selector and contract address from check, as these change on each test
|
||||
const offsetToTokenIds = 74;
|
||||
const assetDataWithoutContractAddress = assetData.substr(offsetToTokenIds);
|
||||
const expectedAssetDataWithoutContractAddress =
|
||||
'0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040102030400000000000000000000000000000000000000000000000000000000';
|
||||
const assetData = `${selector}000000000000000000000000${erc1155ContractAddress.substr(
|
||||
2,
|
||||
)}${assetDataWithoutContractAddress}`;
|
||||
|
||||
expect(assetDataWithoutContractAddress).to.be.equal(expectedAssetDataWithoutContractAddress);
|
||||
///// Step 4/5 /////
|
||||
// Transfer token IDs [1, 2] and amounts [1, 2] with a multiplier of 2;
|
||||
// the expected trade will be token IDs [1, 2] and amounts [2, 4]
|
||||
@@ -800,20 +805,25 @@ describe('ERC1155Proxy', () => {
|
||||
const tokenUri = '';
|
||||
for (const tokenToCreate of tokensToCreate) {
|
||||
// create token
|
||||
await erc1155Wrapper
|
||||
.getContract()
|
||||
.createWithType(tokenToCreate, tokenUri)
|
||||
.awaitTransactionSuccessAsync({
|
||||
await erc1155Wrapper.getContract().createWithType.awaitTransactionSuccessAsync(
|
||||
tokenToCreate,
|
||||
tokenUri,
|
||||
{
|
||||
from: owner,
|
||||
});
|
||||
},
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
|
||||
// mint balance for spender
|
||||
await erc1155Wrapper
|
||||
.getContract()
|
||||
.mintFungible(tokenToCreate, [spender], [spenderInitialBalance])
|
||||
.awaitTransactionSuccessAsync({
|
||||
await erc1155Wrapper.getContract().mintFungible.awaitTransactionSuccessAsync(
|
||||
tokenToCreate,
|
||||
[spender],
|
||||
[spenderInitialBalance],
|
||||
{
|
||||
from: owner,
|
||||
});
|
||||
},
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
}
|
||||
///// Step 2/5 /////
|
||||
// Check balances before transfer
|
||||
@@ -857,14 +867,12 @@ describe('ERC1155Proxy', () => {
|
||||
const valuesToTransfer = [new BigNumber(2), new BigNumber(2)];
|
||||
const valueMultiplier = new BigNumber(2);
|
||||
// create callback data that is the encoded version of `valuesToTransfer`
|
||||
const generatedAssetData = await devUtils
|
||||
.encodeERC1155AssetData(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
)
|
||||
.callAsync();
|
||||
const generatedAssetData = assetDataUtils.encodeERC1155AssetData(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
// remove the function selector and contract address from check, as these change on each test
|
||||
const offsetToTokenIds = 74;
|
||||
const assetDataSelectorAndContractAddress = generatedAssetData.substr(0, offsetToTokenIds);
|
||||
@@ -929,20 +937,25 @@ describe('ERC1155Proxy', () => {
|
||||
const tokenUri = '';
|
||||
for (const tokenToCreate of tokensToCreate) {
|
||||
// create token
|
||||
await erc1155Wrapper
|
||||
.getContract()
|
||||
.createWithType(tokenToCreate, tokenUri)
|
||||
.awaitTransactionSuccessAsync({
|
||||
await erc1155Wrapper.getContract().createWithType.awaitTransactionSuccessAsync(
|
||||
tokenToCreate,
|
||||
tokenUri,
|
||||
{
|
||||
from: owner,
|
||||
});
|
||||
},
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
|
||||
// mint balance for spender
|
||||
await erc1155Wrapper
|
||||
.getContract()
|
||||
.mintFungible(tokenToCreate, [spender], [spenderInitialBalance])
|
||||
.awaitTransactionSuccessAsync({
|
||||
await erc1155Wrapper.getContract().mintFungible.awaitTransactionSuccessAsync(
|
||||
tokenToCreate,
|
||||
[spender],
|
||||
[spenderInitialBalance],
|
||||
{
|
||||
from: owner,
|
||||
});
|
||||
},
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
}
|
||||
///// Step 2/5 /////
|
||||
// Check balances before transfer
|
||||
@@ -983,14 +996,12 @@ describe('ERC1155Proxy', () => {
|
||||
const valuesToTransfer = [new BigNumber(1), new BigNumber(2)];
|
||||
const valueMultiplier = new BigNumber(2);
|
||||
// create callback data that is the encoded version of `valuesToTransfer`
|
||||
const generatedAssetData = await devUtils
|
||||
.encodeERC1155AssetData(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
)
|
||||
.callAsync();
|
||||
const generatedAssetData = assetDataUtils.encodeERC1155AssetData(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
// remove the function selector and contract address from check, as these change on each test
|
||||
const offsetToTokenIds = 74;
|
||||
const assetDataSelectorAndContractAddress = generatedAssetData.substr(0, offsetToTokenIds);
|
||||
@@ -1048,14 +1059,12 @@ describe('ERC1155Proxy', () => {
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||
const assetData = await devUtils
|
||||
.encodeERC1155AssetData(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
)
|
||||
.callAsync();
|
||||
const assetData = assetDataUtils.encodeERC1155AssetData(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
// The asset data we just generated will look like this:
|
||||
// a7cb5fb7
|
||||
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
|
||||
@@ -1097,14 +1106,12 @@ describe('ERC1155Proxy', () => {
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||
const assetData = await devUtils
|
||||
.encodeERC1155AssetData(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
)
|
||||
.callAsync();
|
||||
const assetData = assetDataUtils.encodeERC1155AssetData(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
// The asset data we just generated will look like this:
|
||||
// a7cb5fb7
|
||||
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
|
||||
@@ -1150,14 +1157,12 @@ describe('ERC1155Proxy', () => {
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||
const assetData = await devUtils
|
||||
.encodeERC1155AssetData(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
)
|
||||
.callAsync();
|
||||
const assetData = assetDataUtils.encodeERC1155AssetData(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
// The asset data we just generated will look like this:
|
||||
// a7cb5fb7
|
||||
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
|
||||
@@ -1203,14 +1208,12 @@ describe('ERC1155Proxy', () => {
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||
const assetData = await devUtils
|
||||
.encodeERC1155AssetData(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
)
|
||||
.callAsync();
|
||||
const assetData = assetDataUtils.encodeERC1155AssetData(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
// The asset data we just generated will look like this:
|
||||
// a7cb5fb7
|
||||
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
|
||||
@@ -1256,14 +1259,12 @@ describe('ERC1155Proxy', () => {
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||
const assetData = await devUtils
|
||||
.encodeERC1155AssetData(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
)
|
||||
.callAsync();
|
||||
const assetData = assetDataUtils.encodeERC1155AssetData(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
// The asset data we just generated will look like this:
|
||||
// a7cb5fb7
|
||||
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
|
||||
@@ -1310,14 +1311,12 @@ describe('ERC1155Proxy', () => {
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||
const assetData = await devUtils
|
||||
.encodeERC1155AssetData(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
)
|
||||
.callAsync();
|
||||
const assetData = assetDataUtils.encodeERC1155AssetData(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
// The asset data we just generated will look like this:
|
||||
// a7cb5fb7
|
||||
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
|
||||
@@ -1359,14 +1358,12 @@ describe('ERC1155Proxy', () => {
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||
const assetData = await devUtils
|
||||
.encodeERC1155AssetData(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
)
|
||||
.callAsync();
|
||||
const assetData = assetDataUtils.encodeERC1155AssetData(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
// The asset data we just generated will look like this:
|
||||
// a7cb5fb7
|
||||
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
|
||||
@@ -1412,14 +1409,12 @@ describe('ERC1155Proxy', () => {
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||
const assetData = await devUtils
|
||||
.encodeERC1155AssetData(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
)
|
||||
.callAsync();
|
||||
const assetData = assetDataUtils.encodeERC1155AssetData(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
// The asset data we just generated will look like this:
|
||||
// a7cb5fb7
|
||||
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
|
||||
@@ -1461,14 +1456,12 @@ describe('ERC1155Proxy', () => {
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||
const assetData = await devUtils
|
||||
.encodeERC1155AssetData(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
)
|
||||
.callAsync();
|
||||
const assetData = assetDataUtils.encodeERC1155AssetData(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
// The asset data we just generated will look like this:
|
||||
// a7cb5fb7
|
||||
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
|
||||
@@ -1514,15 +1507,13 @@ describe('ERC1155Proxy', () => {
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||
const assetData = await devUtils
|
||||
.encodeERC1155AssetData(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
)
|
||||
.callAsync();
|
||||
const txData = await erc1155ProxyWrapper.getTransferFromAbiEncodedTxDataAsync(
|
||||
const assetData = assetDataUtils.encodeERC1155AssetData(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
const txData = erc1155ProxyWrapper.getTransferFromAbiEncodedTxData(
|
||||
spender,
|
||||
receiverContract,
|
||||
erc1155Contract.address,
|
||||
@@ -1547,15 +1538,13 @@ describe('ERC1155Proxy', () => {
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||
const assetData = await devUtils
|
||||
.encodeERC1155AssetData(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
)
|
||||
.callAsync();
|
||||
const txData = await erc1155ProxyWrapper.getTransferFromAbiEncodedTxDataAsync(
|
||||
const assetData = assetDataUtils.encodeERC1155AssetData(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
const txData = erc1155ProxyWrapper.getTransferFromAbiEncodedTxData(
|
||||
spender,
|
||||
receiverContract,
|
||||
erc1155Contract.address,
|
||||
@@ -1678,9 +1667,13 @@ describe('ERC1155Proxy', () => {
|
||||
it('should propagate revert reason from erc1155 contract failure', async () => {
|
||||
// disable transfers
|
||||
const shouldRejectTransfer = true;
|
||||
await erc1155Receiver.setRejectTransferFlag(shouldRejectTransfer).awaitTransactionSuccessAsync({
|
||||
from: owner,
|
||||
});
|
||||
await erc1155Receiver.setRejectTransferFlag.awaitTransactionSuccessAsync(
|
||||
shouldRejectTransfer,
|
||||
{
|
||||
from: owner,
|
||||
},
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiverContract];
|
||||
const tokensToTransfer = fungibleTokens.slice(0, 1);
|
||||
@@ -1755,14 +1748,9 @@ describe('ERC1155Proxy', () => {
|
||||
nftNotOwnerBalance,
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
|
||||
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
|
||||
maxUintValue,
|
||||
valueMultiplier,
|
||||
);
|
||||
// execute transfer
|
||||
// note - this will overflow because we are trying to transfer `maxUintValue * 2` of the 2nd token
|
||||
await expect(
|
||||
await expectTransactionFailedAsync(
|
||||
erc1155ProxyWrapper.transferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
@@ -1773,7 +1761,8 @@ describe('ERC1155Proxy', () => {
|
||||
receiverCallbackData,
|
||||
authorized,
|
||||
),
|
||||
).to.revertWith(expectedError);
|
||||
RevertReason.Uint256Overflow,
|
||||
);
|
||||
});
|
||||
it('should revert if transferring > 1 instances of a non-fungible token (valueMultiplier field >1)', async () => {
|
||||
// setup test parameters
|
||||
@@ -1843,23 +1832,20 @@ describe('ERC1155Proxy', () => {
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
|
||||
SafeMathRevertErrors.BinOpErrorCodes.SubtractionUnderflow,
|
||||
spenderInitialFungibleBalance,
|
||||
valuesToTransfer[0].times(valueMultiplier),
|
||||
);
|
||||
// execute transfer
|
||||
const tx = erc1155ProxyWrapper.transferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
receiverCallbackData,
|
||||
authorized,
|
||||
await expectTransactionFailedAsync(
|
||||
erc1155ProxyWrapper.transferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
receiverCallbackData,
|
||||
authorized,
|
||||
),
|
||||
RevertReason.Uint256Underflow,
|
||||
);
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
it('should revert if sender allowance is insufficient', async () => {
|
||||
// dremove allowance for ERC1155 proxy
|
||||
|
@@ -1,290 +0,0 @@
|
||||
import {
|
||||
blockchainTests,
|
||||
constants,
|
||||
expect,
|
||||
getRandomInteger,
|
||||
hexLeftPad,
|
||||
hexRightPad,
|
||||
hexSlice,
|
||||
Numberish,
|
||||
randomAddress,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { AuthorizableRevertErrors } from '@0x/contracts-utils';
|
||||
import { AssetProxyId } from '@0x/types';
|
||||
import { AbiEncoder, BigNumber, 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 = hexRightPad(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 = hexSlice(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: hexLeftPad('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: hexRightPad('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,
|
||||
hexLeftPad,
|
||||
hexRandom,
|
||||
Numberish,
|
||||
randomAddress,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { AssetProxyId } from '@0x/types';
|
||||
import { BigNumber, 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(hexRandom(), hexRandom(_.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: hexLeftPad(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.
|
||||
hexLeftPad(_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: hexLeftPad(0) });
|
||||
const tx = withdrawToAsync(opts);
|
||||
return expect(tx).to.revertWith(new RawRevertError(hexLeftPad(0)));
|
||||
});
|
||||
|
||||
it('succeeds if `toTokenAddress.transfer()` returns true', async () => {
|
||||
await withdrawToAsync({ toTokenTransferReturnData: hexLeftPad(1) });
|
||||
});
|
||||
});
|
||||
});
|
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,3 @@
|
||||
import { DevUtilsContract } from '@0x/contracts-dev-utils';
|
||||
import {
|
||||
chaiSetup,
|
||||
constants,
|
||||
@@ -9,14 +8,13 @@ import {
|
||||
web3Wrapper,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||
import { assetDataUtils } from '@0x/order-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 { IAssetProxyContract, StaticCallProxyContract, TestStaticCallTargetContract } from './wrappers';
|
||||
import { artifacts, IAssetProxyContract, StaticCallProxyContract, TestStaticCallTargetContract } from '../src';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
@@ -27,7 +25,6 @@ describe('StaticCallProxy', () => {
|
||||
let fromAddress: string;
|
||||
let toAddress: string;
|
||||
|
||||
let devUtils: DevUtilsContract;
|
||||
let staticCallProxy: IAssetProxyContract;
|
||||
let staticCallTarget: TestStaticCallTargetContract;
|
||||
|
||||
@@ -46,14 +43,7 @@ describe('StaticCallProxy', () => {
|
||||
txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
devUtils = new DevUtilsContract(constants.NULL_ADDRESS, provider);
|
||||
staticCallProxy = new IAssetProxyContract(
|
||||
staticCallProxyWithoutTransferFrom.address,
|
||||
provider,
|
||||
txDefaults,
|
||||
{},
|
||||
StaticCallProxyContract.deployedBytecode,
|
||||
);
|
||||
staticCallProxy = new IAssetProxyContract(staticCallProxyWithoutTransferFrom.address, provider, txDefaults);
|
||||
staticCallTarget = await TestStaticCallTargetContract.deployFrom0xArtifactAsync(
|
||||
artifacts.TestStaticCallTarget,
|
||||
provider,
|
||||
@@ -81,21 +71,26 @@ describe('StaticCallProxy', () => {
|
||||
);
|
||||
});
|
||||
it('should have an id of 0xc339d10a', async () => {
|
||||
const proxyId = await staticCallProxy.getProxyId().callAsync();
|
||||
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 staticCallData = staticCallTarget.noInputFunction.getABIEncodedTransactionData();
|
||||
const expectedResultHash = constants.KECCAK256_NULL;
|
||||
const assetData = await devUtils
|
||||
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||
.callAsync();
|
||||
const txData = staticCallProxy
|
||||
.transferFrom(assetData, fromAddress, toAddress, amount)
|
||||
.getABIEncodedTransactionData();
|
||||
const assetData = assetDataUtils.encodeStaticCallAssetData(
|
||||
staticCallTarget.address,
|
||||
staticCallData,
|
||||
expectedResultHash,
|
||||
);
|
||||
const txData = staticCallProxy.transferFrom.getABIEncodedTransactionData(
|
||||
assetData,
|
||||
fromAddress,
|
||||
toAddress,
|
||||
amount,
|
||||
);
|
||||
const offsetToAssetData = '0000000000000000000000000000000000000000000000000000000000000080';
|
||||
const txDataEndBuffer = ethUtil.toBuffer((txData.length - 2) / 2 - 4);
|
||||
const paddedTxDataEndBuffer = ethUtil.setLengthLeft(txDataEndBuffer, 32);
|
||||
@@ -113,21 +108,23 @@ describe('StaticCallProxy', () => {
|
||||
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 = (await devUtils
|
||||
const assetData = assetDataUtils
|
||||
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||
.callAsync()).slice(0, -128);
|
||||
.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(),
|
||||
staticCallProxy.transferFrom.sendTransactionAsync(assetData, fromAddress, toAddress, amount),
|
||||
);
|
||||
});
|
||||
it('should revert if the offset to `staticCallData` points to outside of assetData', async () => {
|
||||
const staticCallData = staticCallTarget.noInputFunction().getABIEncodedTransactionData();
|
||||
const staticCallData = staticCallTarget.noInputFunction.getABIEncodedTransactionData();
|
||||
const expectedResultHash = constants.KECCAK256_NULL;
|
||||
const assetData = await devUtils
|
||||
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||
.callAsync();
|
||||
const assetData = assetDataUtils.encodeStaticCallAssetData(
|
||||
staticCallTarget.address,
|
||||
staticCallData,
|
||||
expectedResultHash,
|
||||
);
|
||||
const offsetToStaticCallData = '0000000000000000000000000000000000000000000000000000000000000060';
|
||||
const assetDataEndBuffer = ethUtil.toBuffer((assetData.length - 2) / 2 - 4);
|
||||
const paddedAssetDataEndBuffer = ethUtil.setLengthLeft(assetDataEndBuffer, 32);
|
||||
@@ -138,88 +135,90 @@ describe('StaticCallProxy', () => {
|
||||
invalidOffsetToStaticCallData,
|
||||
)}${newStaticCallData}`;
|
||||
await expectTransactionFailedWithoutReasonAsync(
|
||||
staticCallProxy.transferFrom(badAssetData, fromAddress, toAddress, amount).sendTransactionAsync(),
|
||||
staticCallProxy.transferFrom.sendTransactionAsync(badAssetData, fromAddress, toAddress, amount),
|
||||
);
|
||||
});
|
||||
it('should revert if the callTarget attempts to write to state', async () => {
|
||||
const staticCallData = staticCallTarget.updateState().getABIEncodedTransactionData();
|
||||
const staticCallData = staticCallTarget.updateState.getABIEncodedTransactionData();
|
||||
const expectedResultHash = constants.KECCAK256_NULL;
|
||||
const assetData = await devUtils
|
||||
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||
.callAsync();
|
||||
const assetData = assetDataUtils.encodeStaticCallAssetData(
|
||||
staticCallTarget.address,
|
||||
staticCallData,
|
||||
expectedResultHash,
|
||||
);
|
||||
await expectTransactionFailedWithoutReasonAsync(
|
||||
staticCallProxy.transferFrom(assetData, fromAddress, toAddress, amount).sendTransactionAsync(),
|
||||
staticCallProxy.transferFrom.sendTransactionAsync(assetData, fromAddress, toAddress, amount),
|
||||
);
|
||||
});
|
||||
it('should revert with data provided by the callTarget if the staticcall reverts', async () => {
|
||||
const staticCallData = staticCallTarget.assertEvenNumber(new BigNumber(1)).getABIEncodedTransactionData();
|
||||
const staticCallData = staticCallTarget.assertEvenNumber.getABIEncodedTransactionData(new BigNumber(1));
|
||||
const expectedResultHash = constants.KECCAK256_NULL;
|
||||
const assetData = await devUtils
|
||||
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||
.callAsync();
|
||||
const assetData = assetDataUtils.encodeStaticCallAssetData(
|
||||
staticCallTarget.address,
|
||||
staticCallData,
|
||||
expectedResultHash,
|
||||
);
|
||||
await expectTransactionFailedAsync(
|
||||
staticCallProxy.transferFrom(assetData, fromAddress, toAddress, amount).sendTransactionAsync(),
|
||||
staticCallProxy.transferFrom.sendTransactionAsync(assetData, fromAddress, toAddress, amount),
|
||||
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 staticCallData = staticCallTarget.isOddNumber.getABIEncodedTransactionData(new BigNumber(0));
|
||||
const trueAsBuffer = ethUtil.toBuffer('0x0000000000000000000000000000000000000000000000000000000000000001');
|
||||
const expectedResultHash = ethUtil.bufferToHex(ethUtil.sha3(trueAsBuffer));
|
||||
const assetData = await devUtils
|
||||
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||
.callAsync();
|
||||
const assetData = assetDataUtils.encodeStaticCallAssetData(
|
||||
staticCallTarget.address,
|
||||
staticCallData,
|
||||
expectedResultHash,
|
||||
);
|
||||
await expectTransactionFailedAsync(
|
||||
staticCallProxy.transferFrom(assetData, fromAddress, toAddress, amount).sendTransactionAsync(),
|
||||
staticCallProxy.transferFrom.sendTransactionAsync(assetData, fromAddress, toAddress, amount),
|
||||
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 staticCallData = staticCallTarget.noInputFunction.getABIEncodedTransactionData();
|
||||
const expectedResultHash = constants.KECCAK256_NULL;
|
||||
const assetData = await devUtils
|
||||
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||
.callAsync();
|
||||
await staticCallProxy
|
||||
.transferFrom(assetData, fromAddress, toAddress, amount)
|
||||
.awaitTransactionSuccessAsync();
|
||||
const assetData = assetDataUtils.encodeStaticCallAssetData(
|
||||
staticCallTarget.address,
|
||||
staticCallData,
|
||||
expectedResultHash,
|
||||
);
|
||||
await staticCallProxy.transferFrom.awaitTransactionSuccessAsync(assetData, fromAddress, toAddress, amount);
|
||||
});
|
||||
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 = await devUtils
|
||||
.encodeStaticCallAssetData(toAddress, staticCallData, expectedResultHash)
|
||||
.callAsync();
|
||||
await staticCallProxy
|
||||
.transferFrom(assetData, fromAddress, toAddress, amount)
|
||||
.awaitTransactionSuccessAsync();
|
||||
const assetData = assetDataUtils.encodeStaticCallAssetData(toAddress, staticCallData, expectedResultHash);
|
||||
await staticCallProxy.transferFrom.awaitTransactionSuccessAsync(assetData, fromAddress, toAddress, amount);
|
||||
});
|
||||
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 staticCallData = staticCallTarget.isOddNumber.getABIEncodedTransactionData(new BigNumber(1));
|
||||
const trueAsBuffer = ethUtil.toBuffer('0x0000000000000000000000000000000000000000000000000000000000000001');
|
||||
const expectedResultHash = ethUtil.bufferToHex(ethUtil.sha3(trueAsBuffer));
|
||||
const assetData = await devUtils
|
||||
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||
.callAsync();
|
||||
await staticCallProxy
|
||||
.transferFrom(assetData, fromAddress, toAddress, amount)
|
||||
.awaitTransactionSuccessAsync();
|
||||
const assetData = assetDataUtils.encodeStaticCallAssetData(
|
||||
staticCallTarget.address,
|
||||
staticCallData,
|
||||
expectedResultHash,
|
||||
);
|
||||
await staticCallProxy.transferFrom.awaitTransactionSuccessAsync(assetData, fromAddress, toAddress, amount);
|
||||
});
|
||||
it('should be successful if a function with one dynamic input is successful', async () => {
|
||||
const dynamicInput = '0x0102030405060708';
|
||||
const staticCallData = staticCallTarget.dynamicInputFunction(dynamicInput).getABIEncodedTransactionData();
|
||||
const staticCallData = staticCallTarget.dynamicInputFunction.getABIEncodedTransactionData(dynamicInput);
|
||||
const expectedResultHash = constants.KECCAK256_NULL;
|
||||
const assetData = await devUtils
|
||||
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||
.callAsync();
|
||||
await staticCallProxy
|
||||
.transferFrom(assetData, fromAddress, toAddress, amount)
|
||||
.awaitTransactionSuccessAsync();
|
||||
const assetData = assetDataUtils.encodeStaticCallAssetData(
|
||||
staticCallTarget.address,
|
||||
staticCallData,
|
||||
expectedResultHash,
|
||||
);
|
||||
await staticCallProxy.transferFrom.awaitTransactionSuccessAsync(assetData, fromAddress, toAddress, amount);
|
||||
});
|
||||
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 staticCallData = staticCallTarget.returnComplexType.getABIEncodedTransactionData(a, b);
|
||||
const abiEncoder = new AbiEncoder.DynamicBytes({
|
||||
name: '',
|
||||
type: 'bytes',
|
||||
@@ -232,12 +231,12 @@ describe('StaticCallProxy', () => {
|
||||
const expectedResultHash = ethUtil.bufferToHex(
|
||||
ethUtil.sha3(ethUtil.toBuffer(encodedExpectedResultWithOffset)),
|
||||
);
|
||||
const assetData = await devUtils
|
||||
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||
.callAsync();
|
||||
await staticCallProxy
|
||||
.transferFrom(assetData, fromAddress, toAddress, amount)
|
||||
.awaitTransactionSuccessAsync();
|
||||
const assetData = assetDataUtils.encodeStaticCallAssetData(
|
||||
staticCallTarget.address,
|
||||
staticCallData,
|
||||
expectedResultHash,
|
||||
);
|
||||
await staticCallProxy.transferFrom.awaitTransactionSuccessAsync(assetData, fromAddress, toAddress, amount);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -1,370 +0,0 @@
|
||||
import {
|
||||
blockchainTests,
|
||||
constants,
|
||||
expect,
|
||||
filterLogs,
|
||||
filterLogsToArguments,
|
||||
getRandomInteger,
|
||||
hexLeftPad,
|
||||
hexRandom,
|
||||
Numberish,
|
||||
randomAddress,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { AssetProxyId } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import { DecodedLogs } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { artifacts } from './artifacts';
|
||||
|
||||
import {
|
||||
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(hexRandom(), hexRandom(_.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.
|
||||
hexLeftPad(_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(0);
|
||||
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),
|
||||
hexLeftPad(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),
|
||||
hexLeftPad(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),
|
||||
hexLeftPad(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,4 +1,3 @@
|
||||
import { DevUtilsContract } from '@0x/contracts-dev-utils';
|
||||
import { artifacts as erc1155Artifacts, ERC1155MintableContract, Erc1155Wrapper } from '@0x/contracts-erc1155';
|
||||
import {
|
||||
constants,
|
||||
@@ -8,14 +7,13 @@ import {
|
||||
LogDecoder,
|
||||
txDefaults,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { assetDataUtils } from '@0x/order-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, IAssetProxyContract } from './wrappers';
|
||||
import { artifacts, ERC1155ProxyContract, IAssetProxyContract } from '../../src';
|
||||
|
||||
export class ERC1155ProxyWrapper {
|
||||
private readonly _tokenOwnerAddresses: string[];
|
||||
@@ -28,7 +26,6 @@ export class ERC1155ProxyWrapper {
|
||||
private readonly _logDecoder: LogDecoder;
|
||||
private readonly _dummyTokenWrappers: Erc1155Wrapper[];
|
||||
private readonly _assetProxyInterface: IAssetProxyContract;
|
||||
private readonly _devUtils: DevUtilsContract;
|
||||
private _proxyContract?: ERC1155ProxyContract;
|
||||
private _proxyIdIfExists?: string;
|
||||
private _initialTokenIdsByOwner: ERC1155HoldingsByOwner = { fungible: {}, nonFungible: {} };
|
||||
@@ -40,7 +37,6 @@ export class ERC1155ProxyWrapper {
|
||||
this._logDecoder = new LogDecoder(this._web3Wrapper, allArtifacts);
|
||||
this._dummyTokenWrappers = [];
|
||||
this._assetProxyInterface = new IAssetProxyContract(constants.NULL_ADDRESS, provider);
|
||||
this._devUtils = new DevUtilsContract(constants.NULL_ADDRESS, provider);
|
||||
this._tokenOwnerAddresses = tokenOwnerAddresses;
|
||||
this._contractOwnerAddress = contractOwnerAddress;
|
||||
this._fungibleTokenIds = [];
|
||||
@@ -76,7 +72,7 @@ export class ERC1155ProxyWrapper {
|
||||
txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
this._proxyIdIfExists = await this._proxyContract.getProxyId().callAsync();
|
||||
this._proxyIdIfExists = await this._proxyContract.getProxyId.callAsync();
|
||||
return this._proxyContract;
|
||||
}
|
||||
/**
|
||||
@@ -99,7 +95,7 @@ export class ERC1155ProxyWrapper {
|
||||
* @param extraData extra data to append to `transferFrom` transaction. Optional.
|
||||
* @return abi encoded tx data.
|
||||
*/
|
||||
public async getTransferFromAbiEncodedTxDataAsync(
|
||||
public getTransferFromAbiEncodedTxData(
|
||||
from: string,
|
||||
to: string,
|
||||
contractAddress: string,
|
||||
@@ -109,17 +105,23 @@ export class ERC1155ProxyWrapper {
|
||||
receiverCallbackData: string,
|
||||
authorizedSender: string,
|
||||
assetData_?: string,
|
||||
): Promise<string> {
|
||||
): string {
|
||||
this._validateProxyContractExistsOrThrow();
|
||||
const assetData =
|
||||
assetData_ === undefined
|
||||
? await this._devUtils
|
||||
.encodeERC1155AssetData(contractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
|
||||
.callAsync()
|
||||
? assetDataUtils.encodeERC1155AssetData(
|
||||
contractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
)
|
||||
: assetData_;
|
||||
const data = this._assetProxyInterface
|
||||
.transferFrom(assetData, from, to, valueMultiplier)
|
||||
.getABIEncodedTransactionData();
|
||||
const data = this._assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
assetData,
|
||||
from,
|
||||
to,
|
||||
valueMultiplier,
|
||||
);
|
||||
return data;
|
||||
}
|
||||
/**
|
||||
@@ -167,13 +169,19 @@ export class ERC1155ProxyWrapper {
|
||||
this._validateProxyContractExistsOrThrow();
|
||||
const assetData =
|
||||
assetData_ === undefined
|
||||
? await this._devUtils
|
||||
.encodeERC1155AssetData(contractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
|
||||
.callAsync()
|
||||
? assetDataUtils.encodeERC1155AssetData(
|
||||
contractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
)
|
||||
: assetData_;
|
||||
const data = this._assetProxyInterface
|
||||
.transferFrom(assetData, from, to, valueMultiplier)
|
||||
.getABIEncodedTransactionData();
|
||||
const data = this._assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
assetData,
|
||||
from,
|
||||
to,
|
||||
valueMultiplier,
|
||||
);
|
||||
const txHash = await this._web3Wrapper.sendTransactionAsync({
|
||||
to: (this._proxyContract as ERC1155ProxyContract).address,
|
||||
data,
|
||||
@@ -328,22 +336,6 @@ export class ERC1155ProxyWrapper {
|
||||
};
|
||||
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.
|
||||
@@ -354,7 +346,7 @@ export class ERC1155ProxyWrapper {
|
||||
this._validateProxyContractExistsOrThrow();
|
||||
const tokenContract = this._getContractFromAddress(contractAddress);
|
||||
const operator = (this._proxyContract as ERC1155ProxyContract).address;
|
||||
const didApproveAll = await tokenContract.isApprovedForAll(userAddress, operator).callAsync();
|
||||
const didApproveAll = await tokenContract.isApprovedForAll.callAsync(userAddress, operator);
|
||||
return didApproveAll;
|
||||
}
|
||||
public getFungibleTokenIds(): BigNumber[] {
|
@@ -1,20 +1,17 @@
|
||||
import { DevUtilsContract } from '@0x/contracts-dev-utils';
|
||||
import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contracts-erc20';
|
||||
import { constants, ERC20BalancesByOwner, txDefaults } from '@0x/contracts-test-utils';
|
||||
import { assetDataUtils } from '@0x/order-utils';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import { ZeroExProvider } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { artifacts } from './artifacts';
|
||||
|
||||
import { ERC20ProxyContract } from './wrappers';
|
||||
import { artifacts, ERC20ProxyContract } from '../../src';
|
||||
|
||||
export class ERC20Wrapper {
|
||||
private readonly _tokenOwnerAddresses: string[];
|
||||
private readonly _contractOwnerAddress: string;
|
||||
private readonly _provider: ZeroExProvider;
|
||||
private readonly _dummyTokenContracts: DummyERC20TokenContract[];
|
||||
private readonly _devUtils: DevUtilsContract;
|
||||
private _proxyContract?: ERC20ProxyContract;
|
||||
private _proxyIdIfExists?: string;
|
||||
/**
|
||||
@@ -29,7 +26,6 @@ export class ERC20Wrapper {
|
||||
this._provider = provider;
|
||||
this._tokenOwnerAddresses = tokenOwnerAddresses;
|
||||
this._contractOwnerAddress = contractOwnerAddress;
|
||||
this._devUtils = new DevUtilsContract(constants.NULL_ADDRESS, provider);
|
||||
}
|
||||
public async deployDummyTokensAsync(
|
||||
numberToDeploy: number,
|
||||
@@ -58,7 +54,7 @@ export class ERC20Wrapper {
|
||||
txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
this._proxyIdIfExists = await this._proxyContract.getProxyId().callAsync();
|
||||
this._proxyIdIfExists = await this._proxyContract.getProxyId.callAsync();
|
||||
return this._proxyContract;
|
||||
}
|
||||
public getProxyId(): string {
|
||||
@@ -70,39 +66,50 @@ export class ERC20Wrapper {
|
||||
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 });
|
||||
await dummyTokenContract.setBalance.awaitTransactionSuccessAsync(
|
||||
tokenOwnerAddress,
|
||||
constants.INITIAL_ERC20_BALANCE,
|
||||
{ from: this._contractOwnerAddress },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await dummyTokenContract.approve.awaitTransactionSuccessAsync(
|
||||
(this._proxyContract as ERC20ProxyContract).address,
|
||||
constants.INITIAL_ERC20_ALLOWANCE,
|
||||
{ from: tokenOwnerAddress },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
public async getBalanceAsync(userAddress: string, assetData: string): Promise<BigNumber> {
|
||||
const tokenContract = await this._getTokenContractFromAssetDataAsync(assetData);
|
||||
const balance = new BigNumber(await tokenContract.balanceOf(userAddress).callAsync());
|
||||
const tokenContract = this._getTokenContractFromAssetData(assetData);
|
||||
const balance = new BigNumber(await tokenContract.balanceOf.callAsync(userAddress));
|
||||
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 },
|
||||
);
|
||||
const tokenContract = this._getTokenContractFromAssetData(assetData);
|
||||
await tokenContract.setBalance.awaitTransactionSuccessAsync(
|
||||
userAddress,
|
||||
amount,
|
||||
{ from: this._contractOwnerAddress },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
}
|
||||
public async getProxyAllowanceAsync(userAddress: string, assetData: string): Promise<BigNumber> {
|
||||
const tokenContract = await this._getTokenContractFromAssetDataAsync(assetData);
|
||||
const tokenContract = this._getTokenContractFromAssetData(assetData);
|
||||
const proxyAddress = (this._proxyContract as ERC20ProxyContract).address;
|
||||
const allowance = new BigNumber(await tokenContract.allowance(userAddress, proxyAddress).callAsync());
|
||||
const allowance = new BigNumber(await tokenContract.allowance.callAsync(userAddress, proxyAddress));
|
||||
return allowance;
|
||||
}
|
||||
public async setAllowanceAsync(userAddress: string, assetData: string, amount: BigNumber): Promise<void> {
|
||||
const tokenContract = await this._getTokenContractFromAssetDataAsync(assetData);
|
||||
const tokenContract = this._getTokenContractFromAssetData(assetData);
|
||||
const proxyAddress = (this._proxyContract as ERC20ProxyContract).address;
|
||||
await tokenContract.approve(proxyAddress, amount).awaitTransactionSuccessAsync({ from: userAddress });
|
||||
await tokenContract.approve.awaitTransactionSuccessAsync(
|
||||
proxyAddress,
|
||||
amount,
|
||||
{ from: userAddress },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
}
|
||||
public async getBalancesAsync(): Promise<ERC20BalancesByOwner> {
|
||||
this._validateDummyTokenContractsExistOrThrow();
|
||||
@@ -111,7 +118,7 @@ export class ERC20Wrapper {
|
||||
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());
|
||||
balances.push(await dummyTokenContract.balanceOf.callAsync(tokenOwnerAddress));
|
||||
balanceInfo.push({
|
||||
tokenOwnerAddress,
|
||||
tokenAddress: dummyTokenContract.address,
|
||||
@@ -144,8 +151,9 @@ export class ERC20Wrapper {
|
||||
const tokenAddresses = _.map(this._dummyTokenContracts, dummyTokenContract => dummyTokenContract.address);
|
||||
return tokenAddresses;
|
||||
}
|
||||
private async _getTokenContractFromAssetDataAsync(assetData: string): Promise<DummyERC20TokenContract> {
|
||||
const [proxyId, tokenAddress] = await this._devUtils.decodeERC20AssetData(assetData).callAsync(); // tslint:disable-line:no-unused-variable
|
||||
private _getTokenContractFromAssetData(assetData: string): DummyERC20TokenContract {
|
||||
const erc20ProxyData = assetDataUtils.decodeERC20AssetData(assetData);
|
||||
const tokenAddress = erc20ProxyData.tokenAddress;
|
||||
const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === tokenAddress);
|
||||
if (tokenContractIfExists === undefined) {
|
||||
throw new Error(`Token: ${tokenAddress} was not deployed through ERC20Wrapper`);
|
@@ -5,9 +5,7 @@ import { BigNumber } from '@0x/utils';
|
||||
import { ZeroExProvider } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { artifacts } from './artifacts';
|
||||
|
||||
import { ERC721ProxyContract } from './wrappers';
|
||||
import { artifacts, ERC721ProxyContract } from '../../src';
|
||||
|
||||
export class ERC721Wrapper {
|
||||
private readonly _tokenOwnerAddresses: string[];
|
||||
@@ -46,7 +44,7 @@ export class ERC721Wrapper {
|
||||
txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
this._proxyIdIfExists = await this._proxyContract.getProxyId().callAsync();
|
||||
this._proxyIdIfExists = await this._proxyContract.getProxyId.callAsync();
|
||||
return this._proxyContract;
|
||||
}
|
||||
public getProxyId(): string {
|
||||
@@ -73,14 +71,14 @@ export class ERC721Wrapper {
|
||||
}
|
||||
this._initialTokenIdsByOwner[tokenOwnerAddress][dummyTokenContract.address].push(tokenId);
|
||||
|
||||
await this.approveProxyForAllAsync(dummyTokenContract.address, tokenOwnerAddress, true);
|
||||
await this.approveProxyAsync(dummyTokenContract.address, tokenId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public async doesTokenExistAsync(tokenAddress: string, tokenId: BigNumber): Promise<boolean> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||
const owner = await tokenContract.ownerOf(tokenId).callAsync();
|
||||
const owner = await tokenContract.ownerOf.callAsync(tokenId);
|
||||
const doesExist = owner !== constants.NULL_ADDRESS;
|
||||
return doesExist;
|
||||
}
|
||||
@@ -88,21 +86,26 @@ export class ERC721Wrapper {
|
||||
const proxyAddress = (this._proxyContract as ERC721ProxyContract).address;
|
||||
await this.approveAsync(proxyAddress, tokenAddress, tokenId);
|
||||
}
|
||||
public async approveProxyForAllAsync(
|
||||
tokenAddress: string,
|
||||
ownerAddress: string,
|
||||
isApproved: boolean,
|
||||
): Promise<void> {
|
||||
public async approveProxyForAllAsync(tokenAddress: string, tokenId: BigNumber, isApproved: boolean): Promise<void> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||
const tokenOwner = await this.ownerOfAsync(tokenAddress, tokenId);
|
||||
const proxyAddress = (this._proxyContract as ERC721ProxyContract).address;
|
||||
await tokenContract.setApprovalForAll(proxyAddress, isApproved).awaitTransactionSuccessAsync({
|
||||
from: ownerAddress,
|
||||
});
|
||||
await tokenContract.setApprovalForAll.awaitTransactionSuccessAsync(
|
||||
proxyAddress,
|
||||
isApproved,
|
||||
{ from: tokenOwner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
}
|
||||
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 });
|
||||
await tokenContract.approve.awaitTransactionSuccessAsync(
|
||||
to,
|
||||
tokenId,
|
||||
{ from: tokenOwner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
}
|
||||
public async transferFromAsync(
|
||||
tokenAddress: string,
|
||||
@@ -111,28 +114,40 @@ export class ERC721Wrapper {
|
||||
userAddress: string,
|
||||
): Promise<void> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||
await tokenContract.transferFrom(currentOwner, userAddress, tokenId).awaitTransactionSuccessAsync({
|
||||
from: currentOwner,
|
||||
});
|
||||
await tokenContract.transferFrom.awaitTransactionSuccessAsync(
|
||||
currentOwner,
|
||||
userAddress,
|
||||
tokenId,
|
||||
{ from: currentOwner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
}
|
||||
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,
|
||||
});
|
||||
await tokenContract.mint.awaitTransactionSuccessAsync(
|
||||
userAddress,
|
||||
tokenId,
|
||||
{ from: this._contractOwnerAddress },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
}
|
||||
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 });
|
||||
await tokenContract.burn.awaitTransactionSuccessAsync(
|
||||
owner,
|
||||
tokenId,
|
||||
{ from: this._contractOwnerAddress },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
}
|
||||
public async ownerOfAsync(tokenAddress: string, tokenId: BigNumber): Promise<string> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||
const owner = await tokenContract.ownerOf(tokenId).callAsync();
|
||||
const owner = await tokenContract.ownerOf.callAsync(tokenId);
|
||||
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 tokenOwner = await tokenContract.ownerOf.callAsync(tokenId);
|
||||
const isOwner = tokenOwner === userAddress;
|
||||
return isOwner;
|
||||
}
|
||||
@@ -140,13 +155,13 @@ export class ERC721Wrapper {
|
||||
this._validateProxyContractExistsOrThrow();
|
||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||
const operator = (this._proxyContract as ERC721ProxyContract).address;
|
||||
const didApproveAll = await tokenContract.isApprovedForAll(userAddress, operator).callAsync();
|
||||
const didApproveAll = await tokenContract.isApprovedForAll.callAsync(userAddress, operator);
|
||||
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 approvedAddress = await tokenContract.getApproved.callAsync(tokenId);
|
||||
const proxyAddress = (this._proxyContract as ERC721ProxyContract).address;
|
||||
const isProxyAnApprovedOperator = approvedAddress === proxyAddress;
|
||||
return isProxyAnApprovedOperator;
|
||||
@@ -163,7 +178,7 @@ export class ERC721Wrapper {
|
||||
dummyTokenContract.address
|
||||
];
|
||||
for (const tokenId of initialTokenOwnerIds) {
|
||||
tokenOwnerAddresses.push(await dummyTokenContract.ownerOf(tokenId).callAsync());
|
||||
tokenOwnerAddresses.push(await dummyTokenContract.ownerOf.callAsync(tokenId));
|
||||
tokenInfo.push({
|
||||
tokenId,
|
||||
tokenAddress: dummyTokenContract.address,
|
3
contracts/asset-proxy/test/utils/index.ts
Normal file
3
contracts/asset-proxy/test/utils/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './erc20_wrapper';
|
||||
export * from './erc721_wrapper';
|
||||
export * from './erc1155_proxy_wrapper';
|
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* -----------------------------------------------------------------------------
|
||||
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
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_erc20_bridge';
|
||||
export * from '../test/generated-wrappers/i_eth2_dai';
|
||||
export * from '../test/generated-wrappers/i_uniswap_exchange';
|
||||
export * from '../test/generated-wrappers/i_uniswap_exchange_factory';
|
||||
export * from '../test/generated-wrappers/mixin_asset_proxy_dispatcher';
|
||||
export * from '../test/generated-wrappers/mixin_authorizable';
|
||||
export * from '../test/generated-wrappers/multi_asset_proxy';
|
||||
export * from '../test/generated-wrappers/ownable';
|
||||
export * from '../test/generated-wrappers/static_call_proxy';
|
||||
export * from '../test/generated-wrappers/test_erc20_bridge';
|
||||
export * from '../test/generated-wrappers/test_eth2_dai_bridge';
|
||||
export * from '../test/generated-wrappers/test_static_call_target';
|
||||
export * from '../test/generated-wrappers/test_uniswap_bridge';
|
||||
export * from '../test/generated-wrappers/uniswap_bridge';
|
@@ -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: 'constantinople',
|
||||
optimizer: {
|
||||
enabled: true,
|
||||
runs: 1000000,
|
||||
details: { yul: true, deduplicate: true, cse: true, constantOptimizer: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
@@ -4,39 +4,15 @@
|
||||
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
|
||||
"files": [
|
||||
"generated-artifacts/ERC1155Proxy.json",
|
||||
"generated-artifacts/ERC20BridgeProxy.json",
|
||||
"generated-artifacts/ERC20Proxy.json",
|
||||
"generated-artifacts/ERC721Proxy.json",
|
||||
"generated-artifacts/Eth2DaiBridge.json",
|
||||
"generated-artifacts/IAssetData.json",
|
||||
"generated-artifacts/IAssetProxy.json",
|
||||
"generated-artifacts/IAuthorizable.json",
|
||||
"generated-artifacts/MixinAuthorizable.json",
|
||||
"generated-artifacts/MultiAssetProxy.json",
|
||||
"generated-artifacts/StaticCallProxy.json",
|
||||
"generated-artifacts/TestStaticCallTarget.json",
|
||||
"generated-artifacts/UniswapBridge.json",
|
||||
"test/generated-artifacts/ERC1155Proxy.json",
|
||||
"test/generated-artifacts/ERC20BridgeProxy.json",
|
||||
"test/generated-artifacts/ERC20Proxy.json",
|
||||
"test/generated-artifacts/ERC721Proxy.json",
|
||||
"test/generated-artifacts/Eth2DaiBridge.json",
|
||||
"test/generated-artifacts/IAssetData.json",
|
||||
"test/generated-artifacts/IAssetProxy.json",
|
||||
"test/generated-artifacts/IAssetProxyDispatcher.json",
|
||||
"test/generated-artifacts/IAuthorizable.json",
|
||||
"test/generated-artifacts/IERC20Bridge.json",
|
||||
"test/generated-artifacts/IEth2Dai.json",
|
||||
"test/generated-artifacts/IUniswapExchange.json",
|
||||
"test/generated-artifacts/IUniswapExchangeFactory.json",
|
||||
"test/generated-artifacts/MixinAssetProxyDispatcher.json",
|
||||
"test/generated-artifacts/MixinAuthorizable.json",
|
||||
"test/generated-artifacts/MultiAssetProxy.json",
|
||||
"test/generated-artifacts/Ownable.json",
|
||||
"test/generated-artifacts/StaticCallProxy.json",
|
||||
"test/generated-artifacts/TestERC20Bridge.json",
|
||||
"test/generated-artifacts/TestEth2DaiBridge.json",
|
||||
"test/generated-artifacts/TestStaticCallTarget.json",
|
||||
"test/generated-artifacts/TestUniswapBridge.json",
|
||||
"test/generated-artifacts/UniswapBridge.json"
|
||||
"generated-artifacts/TestStaticCallTarget.json"
|
||||
],
|
||||
"exclude": ["./deploy/solc/solc_bin"]
|
||||
}
|
||||
|
@@ -1,10 +0,0 @@
|
||||
# Blacklist all files
|
||||
.*
|
||||
*
|
||||
# Whitelist lib
|
||||
!lib/**/*
|
||||
# Whitelist Solidity contracts
|
||||
!contracts/src/**/*
|
||||
# Blacklist tests in lib
|
||||
/lib/test/*
|
||||
# Package specific ignore
|
@@ -1,2 +0,0 @@
|
||||
# solhint can't parse `abi.decode` syntax.
|
||||
contracts/src/MixinCoordinatorApprovalVerifier.sol
|
@@ -1,77 +1,4 @@
|
||||
[
|
||||
{
|
||||
"version": "2.1.0-beta.2",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils",
|
||||
"pr": 2330
|
||||
},
|
||||
{
|
||||
"note": "Introduced new export CoordinatorRevertErrors",
|
||||
"pr": 2321
|
||||
},
|
||||
{
|
||||
"note": "Added dependency on @0x/contracts-utils",
|
||||
"pr": 2321
|
||||
}
|
||||
],
|
||||
"timestamp": 1574030254
|
||||
},
|
||||
{
|
||||
"version": "2.1.0-beta.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
],
|
||||
"timestamp": 1573159180
|
||||
},
|
||||
{
|
||||
"version": "2.1.0-beta.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add chainId to domain separator",
|
||||
"pr": 1742
|
||||
},
|
||||
{
|
||||
"note": "Inherit Exchange domain constants from `exchange-libs` to reduce code duplication",
|
||||
"pr": 1742
|
||||
},
|
||||
{
|
||||
"note": "Update domain separator",
|
||||
"pr": 1742
|
||||
},
|
||||
{
|
||||
"note": "Refactor contract to use new ITransactions interface",
|
||||
"pr": 1753
|
||||
},
|
||||
{
|
||||
"note": "Add verifyingContractIfExists arg to LibEIP712CoordinatorDomain constructor",
|
||||
"pr": 1753
|
||||
},
|
||||
{
|
||||
"note": "Remove LibZeroExTransaction contract",
|
||||
"pr": 1753
|
||||
},
|
||||
{
|
||||
"note": "Update tests for arbitrary fee tokens (ZEIP-28).",
|
||||
"pr": 1819
|
||||
},
|
||||
{
|
||||
"note": "Update for new `marketXOrders` consolidation.",
|
||||
"pr": 2042
|
||||
},
|
||||
{
|
||||
"note": "Use built in selectors instead of hard coded constants",
|
||||
"pr": 2055
|
||||
},
|
||||
{
|
||||
"note": "Compile and export all contracts, artifacts, and wrappers by default",
|
||||
"pr": 2055
|
||||
}
|
||||
],
|
||||
"timestamp": 1570135330
|
||||
},
|
||||
{
|
||||
"timestamp": 1568744790,
|
||||
"version": "2.0.13",
|
||||
|
@@ -5,29 +5,6 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v2.1.0-beta.2 - _November 17, 2019_
|
||||
|
||||
* Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330)
|
||||
* Introduced new export CoordinatorRevertErrors (#2321)
|
||||
* Added dependency on @0x/contracts-utils (#2321)
|
||||
|
||||
## v2.1.0-beta.1 - _November 7, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.1.0-beta.0 - _October 3, 2019_
|
||||
|
||||
* Add chainId to domain separator (#1742)
|
||||
* Inherit Exchange domain constants from `exchange-libs` to reduce code duplication (#1742)
|
||||
* Update domain separator (#1742)
|
||||
* Refactor contract to use new ITransactions interface (#1753)
|
||||
* Add verifyingContractIfExists arg to LibEIP712CoordinatorDomain constructor (#1753)
|
||||
* Remove LibZeroExTransaction contract (#1753)
|
||||
* Update tests for arbitrary fee tokens (ZEIP-28). (#1819)
|
||||
* Update for new `marketXOrders` consolidation. (#2042)
|
||||
* Use built in selectors instead of hard coded constants (#2055)
|
||||
* Compile and export all contracts, artifacts, and wrappers by default (#2055)
|
||||
|
||||
## v2.0.13 - _September 17, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"artifactsDir": "./test/generated-artifacts",
|
||||
"artifactsDir": "./generated-artifacts",
|
||||
"contractsDir": "./contracts",
|
||||
"useDockerisedSolc": false,
|
||||
"compilerSettings": {
|
||||
@@ -21,5 +21,6 @@
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"contracts": ["src/Coordinator.sol", "src/registry/CoordinatorRegistry.sol"]
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -16,12 +16,10 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental "ABIEncoderV2";
|
||||
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibEIP712ExchangeDomain.sol";
|
||||
import "./libs/LibConstants.sol";
|
||||
import "./libs/LibEIP712CoordinatorDomain.sol";
|
||||
import "./MixinSignatureValidator.sol";
|
||||
import "./MixinCoordinatorApprovalVerifier.sol";
|
||||
import "./MixinCoordinatorCore.sol";
|
||||
@@ -34,12 +32,8 @@ contract Coordinator is
|
||||
MixinCoordinatorApprovalVerifier,
|
||||
MixinCoordinatorCore
|
||||
{
|
||||
/// @param exchange Address of the 0x Exchange contract.
|
||||
/// @param chainId Chain ID of the network this contract is deployed on.
|
||||
constructor (address exchange, uint256 chainId)
|
||||
constructor (address _exchange)
|
||||
public
|
||||
LibConstants(exchange)
|
||||
LibEIP712CoordinatorDomain(chainId, address(0))
|
||||
LibEIP712ExchangeDomain(chainId, exchange)
|
||||
LibConstants(_exchange)
|
||||
{}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -16,28 +16,26 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental "ABIEncoderV2";
|
||||
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibEIP712ExchangeDomain.sol";
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibExchangeSelectors.sol";
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibAddressArray.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
|
||||
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibAddressArray.sol";
|
||||
import "./libs/LibCoordinatorApproval.sol";
|
||||
import "./libs/LibCoordinatorRichErrors.sol";
|
||||
import "./interfaces/ICoordinatorSignatureValidator.sol";
|
||||
import "./interfaces/ICoordinatorApprovalVerifier.sol";
|
||||
import "./libs/LibZeroExTransaction.sol";
|
||||
import "./mixins/MSignatureValidator.sol";
|
||||
import "./mixins/MCoordinatorApprovalVerifier.sol";
|
||||
|
||||
|
||||
// solhint-disable avoid-tx-origin
|
||||
contract MixinCoordinatorApprovalVerifier is
|
||||
LibExchangeSelectors,
|
||||
LibCoordinatorApproval,
|
||||
LibEIP712ExchangeDomain,
|
||||
ICoordinatorSignatureValidator,
|
||||
ICoordinatorApprovalVerifier
|
||||
LibZeroExTransaction,
|
||||
MSignatureValidator,
|
||||
MCoordinatorApprovalVerifier
|
||||
{
|
||||
using LibBytes for bytes;
|
||||
using LibAddressArray for address[];
|
||||
@@ -47,12 +45,13 @@ contract MixinCoordinatorApprovalVerifier is
|
||||
/// @param transaction 0x transaction containing salt, signerAddress, and data.
|
||||
/// @param txOrigin Required signer of Ethereum transaction calling this function.
|
||||
/// @param transactionSignature Proof that the transaction has been signed by the signer.
|
||||
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each
|
||||
/// order in the transaction's Exchange calldata.
|
||||
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
|
||||
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order in the transaction's Exchange calldata.
|
||||
function assertValidCoordinatorApprovals(
|
||||
LibZeroExTransaction.ZeroExTransaction memory transaction,
|
||||
address txOrigin,
|
||||
bytes memory transactionSignature,
|
||||
uint256[] memory approvalExpirationTimeSeconds,
|
||||
bytes[] memory approvalSignatures
|
||||
)
|
||||
public
|
||||
@@ -64,11 +63,12 @@ contract MixinCoordinatorApprovalVerifier is
|
||||
// No approval is required for non-fill methods
|
||||
if (orders.length > 0) {
|
||||
// Revert if approval is invalid for transaction orders
|
||||
_assertValidTransactionOrdersApproval(
|
||||
assertValidTransactionOrdersApproval(
|
||||
transaction,
|
||||
orders,
|
||||
txOrigin,
|
||||
transactionSignature,
|
||||
approvalExpirationTimeSeconds,
|
||||
approvalSignatures
|
||||
);
|
||||
}
|
||||
@@ -76,7 +76,7 @@ contract MixinCoordinatorApprovalVerifier is
|
||||
|
||||
/// @dev Decodes the orders from Exchange calldata representing any fill method.
|
||||
/// @param data Exchange calldata representing a fill method.
|
||||
/// @return orders The orders from the Exchange calldata.
|
||||
/// @return The orders from the Exchange calldata.
|
||||
function decodeOrdersFromFillData(bytes memory data)
|
||||
public
|
||||
pure
|
||||
@@ -84,8 +84,9 @@ contract MixinCoordinatorApprovalVerifier is
|
||||
{
|
||||
bytes4 selector = data.readBytes4(0);
|
||||
if (
|
||||
selector == IExchange(address(0)).fillOrder.selector ||
|
||||
selector == IExchange(address(0)).fillOrKillOrder.selector
|
||||
selector == FILL_ORDER_SELECTOR ||
|
||||
selector == FILL_ORDER_NO_THROW_SELECTOR ||
|
||||
selector == FILL_OR_KILL_ORDER_SELECTOR
|
||||
) {
|
||||
// Decode single order
|
||||
(LibOrder.Order memory order) = abi.decode(
|
||||
@@ -95,13 +96,13 @@ contract MixinCoordinatorApprovalVerifier is
|
||||
orders = new LibOrder.Order[](1);
|
||||
orders[0] = order;
|
||||
} else if (
|
||||
selector == IExchange(address(0)).batchFillOrders.selector ||
|
||||
selector == IExchange(address(0)).batchFillOrdersNoThrow.selector ||
|
||||
selector == IExchange(address(0)).batchFillOrKillOrders.selector ||
|
||||
selector == IExchange(address(0)).marketBuyOrdersNoThrow.selector ||
|
||||
selector == IExchange(address(0)).marketBuyOrdersFillOrKill.selector ||
|
||||
selector == IExchange(address(0)).marketSellOrdersNoThrow.selector ||
|
||||
selector == IExchange(address(0)).marketSellOrdersFillOrKill.selector
|
||||
selector == BATCH_FILL_ORDERS_SELECTOR ||
|
||||
selector == BATCH_FILL_ORDERS_NO_THROW_SELECTOR ||
|
||||
selector == BATCH_FILL_OR_KILL_ORDERS_SELECTOR ||
|
||||
selector == MARKET_BUY_ORDERS_SELECTOR ||
|
||||
selector == MARKET_BUY_ORDERS_NO_THROW_SELECTOR ||
|
||||
selector == MARKET_SELL_ORDERS_SELECTOR ||
|
||||
selector == MARKET_SELL_ORDERS_NO_THROW_SELECTOR
|
||||
) {
|
||||
// Decode all orders
|
||||
// solhint-disable indent
|
||||
@@ -109,10 +110,7 @@ contract MixinCoordinatorApprovalVerifier is
|
||||
data.slice(4, data.length),
|
||||
(LibOrder.Order[])
|
||||
);
|
||||
} else if (
|
||||
selector == IExchange(address(0)).matchOrders.selector ||
|
||||
selector == IExchange(address(0)).matchOrdersWithMaximalFill.selector
|
||||
) {
|
||||
} else if (selector == MATCH_ORDERS_SELECTOR) {
|
||||
// Decode left and right orders
|
||||
(LibOrder.Order memory leftOrder, LibOrder.Order memory rightOrder) = abi.decode(
|
||||
data.slice(4, data.length),
|
||||
@@ -132,24 +130,27 @@ contract MixinCoordinatorApprovalVerifier is
|
||||
/// @param orders Array of order structs containing order specifications.
|
||||
/// @param txOrigin Required signer of Ethereum transaction calling this function.
|
||||
/// @param transactionSignature Proof that the transaction has been signed by the signer.
|
||||
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
|
||||
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order.
|
||||
function _assertValidTransactionOrdersApproval(
|
||||
function assertValidTransactionOrdersApproval(
|
||||
LibZeroExTransaction.ZeroExTransaction memory transaction,
|
||||
LibOrder.Order[] memory orders,
|
||||
address txOrigin,
|
||||
bytes memory transactionSignature,
|
||||
uint256[] memory approvalExpirationTimeSeconds,
|
||||
bytes[] memory approvalSignatures
|
||||
)
|
||||
internal
|
||||
view
|
||||
{
|
||||
// Verify that Ethereum tx signer is the same as the approved txOrigin
|
||||
if (tx.origin != txOrigin) {
|
||||
LibRichErrors.rrevert(LibCoordinatorRichErrors.InvalidOriginError(txOrigin));
|
||||
}
|
||||
require(
|
||||
tx.origin == txOrigin,
|
||||
"INVALID_ORIGIN"
|
||||
);
|
||||
|
||||
// Hash 0x transaction
|
||||
bytes32 transactionHash = LibZeroExTransaction.getTypedDataHash(transaction, EIP712_EXCHANGE_DOMAIN_HASH);
|
||||
bytes32 transactionHash = getTransactionHash(transaction);
|
||||
|
||||
// Create empty list of approval signers
|
||||
address[] memory approvalSignerAddresses = new address[](0);
|
||||
@@ -157,12 +158,21 @@ contract MixinCoordinatorApprovalVerifier is
|
||||
uint256 signaturesLength = approvalSignatures.length;
|
||||
for (uint256 i = 0; i != signaturesLength; i++) {
|
||||
// Create approval message
|
||||
uint256 currentApprovalExpirationTimeSeconds = approvalExpirationTimeSeconds[i];
|
||||
CoordinatorApproval memory approval = CoordinatorApproval({
|
||||
txOrigin: txOrigin,
|
||||
transactionHash: transactionHash,
|
||||
transactionSignature: transactionSignature
|
||||
transactionSignature: transactionSignature,
|
||||
approvalExpirationTimeSeconds: currentApprovalExpirationTimeSeconds
|
||||
});
|
||||
|
||||
// Ensure approval has not expired
|
||||
require(
|
||||
// solhint-disable-next-line not-rely-on-time
|
||||
currentApprovalExpirationTimeSeconds > block.timestamp,
|
||||
"APPROVAL_EXPIRED"
|
||||
);
|
||||
|
||||
// Hash approval message and recover signer address
|
||||
bytes32 approvalHash = getCoordinatorApprovalHash(approval);
|
||||
address approvalSignerAddress = getSignerAddress(approvalHash, approvalSignatures[i]);
|
||||
@@ -184,12 +194,10 @@ contract MixinCoordinatorApprovalVerifier is
|
||||
// Ensure feeRecipient of order has approved this 0x transaction
|
||||
address approverAddress = orders[i].feeRecipientAddress;
|
||||
bool isOrderApproved = approvalSignerAddresses.contains(approverAddress);
|
||||
if (!isOrderApproved) {
|
||||
LibRichErrors.rrevert(LibCoordinatorRichErrors.InvalidApprovalSignatureError(
|
||||
transactionHash,
|
||||
approverAddress
|
||||
));
|
||||
}
|
||||
require(
|
||||
isOrderApproved,
|
||||
"INVALID_APPROVAL_SIGNATURE"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -16,57 +16,50 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental "ABIEncoderV2";
|
||||
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol";
|
||||
import "@0x/contracts-utils/contracts/src/Refundable.sol";
|
||||
import "./libs/LibZeroExTransaction.sol";
|
||||
import "./libs/LibConstants.sol";
|
||||
import "./mixins/MCoordinatorApprovalVerifier.sol";
|
||||
import "./interfaces/ICoordinatorCore.sol";
|
||||
import "./interfaces/ICoordinatorApprovalVerifier.sol";
|
||||
|
||||
|
||||
// solhint-disable no-empty-blocks
|
||||
contract MixinCoordinatorCore is
|
||||
Refundable,
|
||||
LibConstants,
|
||||
ICoordinatorApprovalVerifier,
|
||||
MCoordinatorApprovalVerifier,
|
||||
ICoordinatorCore
|
||||
{
|
||||
|
||||
/// @dev A payable fallback function that makes this contract "payable". This is necessary to allow
|
||||
/// this contract to gracefully handle refunds from the Exchange.
|
||||
function ()
|
||||
external
|
||||
payable
|
||||
{}
|
||||
|
||||
/// @dev Executes a 0x transaction that has been signed by the feeRecipients that correspond to
|
||||
/// each order in the transaction's Exchange calldata.
|
||||
/// @dev Executes a 0x transaction that has been signed by the feeRecipients that correspond to each order in the transaction's Exchange calldata.
|
||||
/// @param transaction 0x transaction containing salt, signerAddress, and data.
|
||||
/// @param txOrigin Required signer of Ethereum transaction calling this function.
|
||||
/// @param transactionSignature Proof that the transaction has been signed by the signer.
|
||||
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each
|
||||
/// order in the transaction's Exchange calldata.
|
||||
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
|
||||
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order in the transaction's Exchange calldata.
|
||||
function executeTransaction(
|
||||
LibZeroExTransaction.ZeroExTransaction memory transaction,
|
||||
address txOrigin,
|
||||
bytes memory transactionSignature,
|
||||
uint256[] memory approvalExpirationTimeSeconds,
|
||||
bytes[] memory approvalSignatures
|
||||
)
|
||||
public
|
||||
payable
|
||||
refundFinalBalance
|
||||
{
|
||||
// Validate that the 0x transaction has been approves by each feeRecipient
|
||||
assertValidCoordinatorApprovals(
|
||||
transaction,
|
||||
txOrigin,
|
||||
transactionSignature,
|
||||
approvalExpirationTimeSeconds,
|
||||
approvalSignatures
|
||||
);
|
||||
|
||||
// Execute the transaction
|
||||
EXCHANGE.executeTransaction.value(msg.value)(transaction, transactionSignature);
|
||||
EXCHANGE.executeTransaction(
|
||||
transaction.salt,
|
||||
transaction.signerAddress,
|
||||
transaction.data,
|
||||
transactionSignature
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -16,48 +16,38 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
|
||||
import "./interfaces/ICoordinatorSignatureValidator.sol";
|
||||
import "./libs/LibCoordinatorRichErrors.sol";
|
||||
import "./mixins/MSignatureValidator.sol";
|
||||
|
||||
|
||||
contract MixinSignatureValidator is
|
||||
ICoordinatorSignatureValidator
|
||||
MSignatureValidator
|
||||
{
|
||||
using LibBytes for bytes;
|
||||
|
||||
/// @dev Recovers the address of a signer given a hash and signature.
|
||||
/// @param hash Any 32 byte hash.
|
||||
/// @param signature Proof that the hash has been signed by signer.
|
||||
/// @return signerAddress Address of the signer.
|
||||
function getSignerAddress(bytes32 hash, bytes memory signature)
|
||||
public
|
||||
pure
|
||||
returns (address signerAddress)
|
||||
{
|
||||
uint256 signatureLength = signature.length;
|
||||
if (signatureLength == 0) {
|
||||
LibRichErrors.rrevert(LibCoordinatorRichErrors.SignatureError(
|
||||
LibCoordinatorRichErrors.SignatureErrorCodes.INVALID_LENGTH,
|
||||
hash,
|
||||
signature
|
||||
));
|
||||
}
|
||||
require(
|
||||
signature.length > 0,
|
||||
"LENGTH_GREATER_THAN_0_REQUIRED"
|
||||
);
|
||||
|
||||
// Pop last byte off of signature byte array.
|
||||
uint8 signatureTypeRaw = uint8(signature[signature.length - 1]);
|
||||
uint8 signatureTypeRaw = uint8(signature.popLastByte());
|
||||
|
||||
// Ensure signature is supported
|
||||
if (signatureTypeRaw >= uint8(SignatureType.NSignatureTypes)) {
|
||||
LibRichErrors.rrevert(LibCoordinatorRichErrors.SignatureError(
|
||||
LibCoordinatorRichErrors.SignatureErrorCodes.UNSUPPORTED,
|
||||
hash,
|
||||
signature
|
||||
));
|
||||
}
|
||||
require(
|
||||
signatureTypeRaw < uint8(SignatureType.NSignatureTypes),
|
||||
"SIGNATURE_UNSUPPORTED"
|
||||
);
|
||||
|
||||
SignatureType signatureType = SignatureType(signatureTypeRaw);
|
||||
|
||||
@@ -67,32 +57,25 @@ contract MixinSignatureValidator is
|
||||
// it an explicit option. This aids testing and analysis. It is
|
||||
// also the initialization value for the enum type.
|
||||
if (signatureType == SignatureType.Illegal) {
|
||||
LibRichErrors.rrevert(LibCoordinatorRichErrors.SignatureError(
|
||||
LibCoordinatorRichErrors.SignatureErrorCodes.ILLEGAL,
|
||||
hash,
|
||||
signature
|
||||
));
|
||||
revert("SIGNATURE_ILLEGAL");
|
||||
|
||||
// Always invalid signature.
|
||||
// Like Illegal, this is always implicitly available and therefore
|
||||
// offered explicitly. It can be implicitly created by providing
|
||||
// a correctly formatted but incorrect signature.
|
||||
} else if (signatureType == SignatureType.Invalid) {
|
||||
LibRichErrors.rrevert(LibCoordinatorRichErrors.SignatureError(
|
||||
LibCoordinatorRichErrors.SignatureErrorCodes.INVALID,
|
||||
hash,
|
||||
signature
|
||||
));
|
||||
require(
|
||||
signature.length == 0,
|
||||
"LENGTH_0_REQUIRED"
|
||||
);
|
||||
revert("SIGNATURE_INVALID");
|
||||
|
||||
// Signature using EIP712
|
||||
} else if (signatureType == SignatureType.EIP712) {
|
||||
if (signatureLength != 66) {
|
||||
LibRichErrors.rrevert(LibCoordinatorRichErrors.SignatureError(
|
||||
LibCoordinatorRichErrors.SignatureErrorCodes.INVALID_LENGTH,
|
||||
hash,
|
||||
signature
|
||||
));
|
||||
}
|
||||
require(
|
||||
signature.length == 65,
|
||||
"LENGTH_65_REQUIRED"
|
||||
);
|
||||
uint8 v = uint8(signature[0]);
|
||||
bytes32 r = signature.readBytes32(1);
|
||||
bytes32 s = signature.readBytes32(33);
|
||||
@@ -106,13 +89,10 @@ contract MixinSignatureValidator is
|
||||
|
||||
// Signed using web3.eth_sign
|
||||
} else if (signatureType == SignatureType.EthSign) {
|
||||
if (signatureLength != 66) {
|
||||
LibRichErrors.rrevert(LibCoordinatorRichErrors.SignatureError(
|
||||
LibCoordinatorRichErrors.SignatureErrorCodes.INVALID_LENGTH,
|
||||
hash,
|
||||
signature
|
||||
));
|
||||
}
|
||||
require(
|
||||
signature.length == 65,
|
||||
"LENGTH_65_REQUIRED"
|
||||
);
|
||||
uint8 v = uint8(signature[0]);
|
||||
bytes32 r = signature.readBytes32(1);
|
||||
bytes32 s = signature.readBytes32(33);
|
||||
@@ -133,10 +113,6 @@ contract MixinSignatureValidator is
|
||||
// that we currently support. In this case returning false
|
||||
// may lead the caller to incorrectly believe that the
|
||||
// signature was invalid.)
|
||||
LibRichErrors.rrevert(LibCoordinatorRichErrors.SignatureError(
|
||||
LibCoordinatorRichErrors.SignatureErrorCodes.UNSUPPORTED,
|
||||
hash,
|
||||
signature
|
||||
));
|
||||
revert("SIGNATURE_UNSUPPORTED");
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -16,11 +16,11 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental "ABIEncoderV2";
|
||||
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol";
|
||||
import "../libs/LibZeroExTransaction.sol";
|
||||
|
||||
|
||||
contract ICoordinatorApprovalVerifier {
|
||||
@@ -30,12 +30,13 @@ contract ICoordinatorApprovalVerifier {
|
||||
/// @param transaction 0x transaction containing salt, signerAddress, and data.
|
||||
/// @param txOrigin Required signer of Ethereum transaction calling this function.
|
||||
/// @param transactionSignature Proof that the transaction has been signed by the signer.
|
||||
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each
|
||||
/// order in the transaction's Exchange calldata.
|
||||
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
|
||||
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order in the transaction's Exchange calldata.
|
||||
function assertValidCoordinatorApprovals(
|
||||
LibZeroExTransaction.ZeroExTransaction memory transaction,
|
||||
address txOrigin,
|
||||
bytes memory transactionSignature,
|
||||
uint256[] memory approvalExpirationTimeSeconds,
|
||||
bytes[] memory approvalSignatures
|
||||
)
|
||||
public
|
||||
@@ -43,7 +44,7 @@ contract ICoordinatorApprovalVerifier {
|
||||
|
||||
/// @dev Decodes the orders from Exchange calldata representing any fill method.
|
||||
/// @param data Exchange calldata representing a fill method.
|
||||
/// @return orders The orders from the Exchange calldata.
|
||||
/// @return The orders from the Exchange calldata.
|
||||
function decodeOrdersFromFillData(bytes memory data)
|
||||
public
|
||||
pure
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -16,27 +16,26 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental "ABIEncoderV2";
|
||||
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol";
|
||||
import "../libs/LibZeroExTransaction.sol";
|
||||
|
||||
|
||||
contract ICoordinatorCore {
|
||||
|
||||
/// @dev Executes a 0x transaction that has been signed by the feeRecipients that correspond to
|
||||
/// each order in the transaction's Exchange calldata.
|
||||
/// @dev Executes a 0x transaction that has been signed by the feeRecipients that correspond to each order in the transaction's Exchange calldata.
|
||||
/// @param transaction 0x transaction containing salt, signerAddress, and data.
|
||||
/// @param txOrigin Required signer of Ethereum transaction calling this function.
|
||||
/// @param transactionSignature Proof that the transaction has been signed by the signer.
|
||||
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each
|
||||
/// order in the transaction's Exchange calldata.
|
||||
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
|
||||
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order in the transaction's Exchange calldata.
|
||||
function executeTransaction(
|
||||
LibZeroExTransaction.ZeroExTransaction memory transaction,
|
||||
address txOrigin,
|
||||
bytes memory transactionSignature,
|
||||
uint256[] memory approvalExpirationTimeSeconds,
|
||||
bytes[] memory approvalSignatures
|
||||
)
|
||||
public
|
||||
payable;
|
||||
public;
|
||||
}
|
||||
|
@@ -1,45 +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 ICoordinatorSignatureValidator {
|
||||
|
||||
// Allowed signature types.
|
||||
enum SignatureType {
|
||||
Illegal, // 0x00, default value
|
||||
Invalid, // 0x01
|
||||
EIP712, // 0x02
|
||||
EthSign, // 0x03
|
||||
Wallet, // 0x04
|
||||
Validator, // 0x05
|
||||
PreSigned, // 0x06
|
||||
EIP1271Wallet, // 0x07
|
||||
NSignatureTypes // 0x08, number of signature types. Always leave at end.
|
||||
}
|
||||
|
||||
/// @dev Recovers the address of a signer given a hash and signature.
|
||||
/// @param hash Any 32 byte hash.
|
||||
/// @param signature Proof that the hash has been signed by signer.
|
||||
/// @return signerAddress Address of the signer.
|
||||
function getSignerAddress(bytes32 hash, bytes memory signature)
|
||||
public
|
||||
pure
|
||||
returns (address signerAddress);
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -18,10 +18,14 @@
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "../src/libs/LibExchangeRichErrorDecoder.sol";
|
||||
|
||||
contract ISignatureValidator {
|
||||
|
||||
// solhint-disable no-empty-blocks
|
||||
contract TestLibExchangeRichErrorDecoder is
|
||||
LibExchangeRichErrorDecoder
|
||||
{}
|
||||
/// @dev Recovers the address of a signer given a hash and signature.
|
||||
/// @param hash Any 32 byte hash.
|
||||
/// @param signature Proof that the hash has been signed by signer.
|
||||
function getSignerAddress(bytes32 hash, bytes memory signature)
|
||||
public
|
||||
pure
|
||||
returns (address signerAddress);
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -15,24 +15,21 @@
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/LibEIP1271.sol";
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
|
||||
contract IEIP1271Wallet is
|
||||
LibEIP1271
|
||||
{
|
||||
/// @dev Verifies that a signature is valid.
|
||||
/// @param data Arbitrary signed data.
|
||||
/// @param signature Proof that data has been signed.
|
||||
/// @return magicValue bytes4(0x20c13b0b) if the signature check succeeds.
|
||||
function isValidSignature(
|
||||
contract ITransactions {
|
||||
|
||||
/// @dev Executes an exchange method call in the context of signer.
|
||||
/// @param salt Arbitrary number to ensure uniqueness of transaction hash.
|
||||
/// @param signerAddress Address of transaction signer.
|
||||
/// @param data AbiV2 encoded calldata.
|
||||
/// @param signature Proof of signer transaction by signer.
|
||||
function executeTransaction(
|
||||
uint256 salt,
|
||||
address signerAddress,
|
||||
bytes calldata data,
|
||||
bytes calldata signature
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (bytes4 magicValue);
|
||||
external;
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -16,21 +16,19 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "@0x/contracts-exchange/contracts/src/interfaces/ITransactions.sol";
|
||||
import "../interfaces/ITransactions.sol";
|
||||
|
||||
|
||||
// solhint-disable var-name-mixedcase
|
||||
contract LibConstants {
|
||||
|
||||
// The 0x Exchange contract.
|
||||
// solhint-disable-next-line var-name-mixedcase
|
||||
ITransactions internal EXCHANGE;
|
||||
|
||||
/// @param exchange Address of the 0x Exchange contract.
|
||||
constructor (address exchange)
|
||||
constructor (address _exchange)
|
||||
public
|
||||
{
|
||||
EXCHANGE = ITransactions(exchange);
|
||||
EXCHANGE = ITransactions(_exchange);
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -16,52 +16,49 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental "ABIEncoderV2";
|
||||
|
||||
import "./LibEIP712CoordinatorDomain.sol";
|
||||
import "./LibEIP712Domain.sol";
|
||||
|
||||
|
||||
contract LibCoordinatorApproval is
|
||||
LibEIP712CoordinatorDomain
|
||||
LibEIP712Domain
|
||||
{
|
||||
// Hash for the EIP712 Coordinator approval message
|
||||
// keccak256(abi.encodePacked(
|
||||
// "CoordinatorApproval(",
|
||||
// "address txOrigin,",
|
||||
// "bytes32 transactionHash,",
|
||||
// "bytes transactionSignature",
|
||||
// "bytes transactionSignature,",
|
||||
// "uint256 approvalExpirationTimeSeconds",
|
||||
// ")"
|
||||
// ));
|
||||
bytes32 constant public EIP712_COORDINATOR_APPROVAL_SCHEMA_HASH =
|
||||
0xa6511c04ca44625d50986f8c36bedc09366207a17b96e347094053a9f8507168;
|
||||
bytes32 constant internal EIP712_COORDINATOR_APPROVAL_SCHEMA_HASH = 0x2fbcdbaa76bc7589916958ae919dfbef04d23f6bbf26de6ff317b32c6cc01e05;
|
||||
|
||||
struct CoordinatorApproval {
|
||||
address txOrigin; // Required signer of Ethereum transaction that is submitting approval.
|
||||
bytes32 transactionHash; // EIP712 hash of the transaction.
|
||||
bytes transactionSignature; // Signature of the 0x transaction.
|
||||
uint256 approvalExpirationTimeSeconds; // Timestamp in seconds for which the approval expires.
|
||||
}
|
||||
|
||||
/// @dev Calculates the EIP712 hash of the Coordinator approval mesasage using the domain
|
||||
/// separator of this contract.
|
||||
/// @param approval Coordinator approval message containing the transaction hash, and transaction
|
||||
/// signature.
|
||||
/// @return approvalHash EIP712 hash of the Coordinator approval message with the domain
|
||||
/// separator of this contract.
|
||||
/// @dev Calculated the EIP712 hash of the Coordinator approval mesasage using the domain separator of this contract.
|
||||
/// @param approval Coordinator approval message containing the transaction hash, transaction signature, and expiration of the approval.
|
||||
/// @return EIP712 hash of the Coordinator approval message with the domain separator of this contract.
|
||||
function getCoordinatorApprovalHash(CoordinatorApproval memory approval)
|
||||
public
|
||||
view
|
||||
returns (bytes32 approvalHash)
|
||||
{
|
||||
approvalHash = _hashEIP712CoordinatorMessage(_hashCoordinatorApproval(approval));
|
||||
approvalHash = hashEIP712CoordinatorMessage(hashCoordinatorApproval(approval));
|
||||
return approvalHash;
|
||||
}
|
||||
|
||||
/// @dev Calculates the EIP712 hash of the Coordinator approval mesasage with no domain separator.
|
||||
/// @param approval Coordinator approval message containing the transaction hash, and transaction
|
||||
// signature.
|
||||
/// @return result EIP712 hash of the Coordinator approval message with no domain separator.
|
||||
function _hashCoordinatorApproval(CoordinatorApproval memory approval)
|
||||
/// @dev Calculated the EIP712 hash of the Coordinator approval mesasage with no domain separator.
|
||||
/// @param approval Coordinator approval message containing the transaction hash, transaction signature, and expiration of the approval.
|
||||
/// @return EIP712 hash of the Coordinator approval message with no domain separator.
|
||||
function hashCoordinatorApproval(CoordinatorApproval memory approval)
|
||||
internal
|
||||
pure
|
||||
returns (bytes32 result)
|
||||
@@ -70,6 +67,7 @@ contract LibCoordinatorApproval is
|
||||
bytes memory transactionSignature = approval.transactionSignature;
|
||||
address txOrigin = approval.txOrigin;
|
||||
bytes32 transactionHash = approval.transactionHash;
|
||||
uint256 approvalExpirationTimeSeconds = approval.approvalExpirationTimeSeconds;
|
||||
|
||||
// Assembly for more efficiently computing:
|
||||
// keccak256(abi.encodePacked(
|
||||
@@ -77,6 +75,7 @@ contract LibCoordinatorApproval is
|
||||
// approval.txOrigin,
|
||||
// approval.transactionHash,
|
||||
// keccak256(approval.transactionSignature)
|
||||
// approval.approvalExpirationTimeSeconds,
|
||||
// ));
|
||||
|
||||
assembly {
|
||||
@@ -90,8 +89,9 @@ contract LibCoordinatorApproval is
|
||||
mstore(add(memPtr, 32), txOrigin) // txOrigin
|
||||
mstore(add(memPtr, 64), transactionHash) // transactionHash
|
||||
mstore(add(memPtr, 96), transactionSignatureHash) // transactionSignatureHash
|
||||
mstore(add(memPtr, 128), approvalExpirationTimeSeconds) // approvalExpirationTimeSeconds
|
||||
// Compute hash
|
||||
result := keccak256(memPtr, 128)
|
||||
result := keccak256(memPtr, 160)
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@@ -1,87 +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;
|
||||
|
||||
|
||||
library LibCoordinatorRichErrors {
|
||||
enum SignatureErrorCodes {
|
||||
INVALID_LENGTH,
|
||||
UNSUPPORTED,
|
||||
ILLEGAL,
|
||||
INVALID
|
||||
}
|
||||
|
||||
// bytes4(keccak256("SignatureError(uint8,bytes32,bytes)"))
|
||||
bytes4 internal constant SIGNATURE_ERROR_SELECTOR =
|
||||
0x779c5223;
|
||||
|
||||
// bytes4(keccak256("InvalidOriginError(address)"))
|
||||
bytes4 internal constant INVALID_ORIGIN_ERROR_SELECTOR =
|
||||
0xa458d7ff;
|
||||
|
||||
// bytes4(keccak256("InvalidApprovalSignatureError(bytes32,address)"))
|
||||
bytes4 internal constant INVALID_APPROVAL_SIGNATURE_ERROR_SELECTOR =
|
||||
0xd789b640;
|
||||
|
||||
// solhint-disable func-name-mixedcase
|
||||
function SignatureError(
|
||||
SignatureErrorCodes errorCode,
|
||||
bytes32 hash,
|
||||
bytes memory signature
|
||||
)
|
||||
internal
|
||||
pure
|
||||
returns (bytes memory)
|
||||
{
|
||||
return abi.encodeWithSelector(
|
||||
SIGNATURE_ERROR_SELECTOR,
|
||||
errorCode,
|
||||
hash,
|
||||
signature
|
||||
);
|
||||
}
|
||||
|
||||
function InvalidOriginError(
|
||||
address expectedOrigin
|
||||
)
|
||||
internal
|
||||
pure
|
||||
returns (bytes memory)
|
||||
{
|
||||
return abi.encodeWithSelector(
|
||||
INVALID_ORIGIN_ERROR_SELECTOR,
|
||||
expectedOrigin
|
||||
);
|
||||
}
|
||||
|
||||
function InvalidApprovalSignatureError(
|
||||
bytes32 transactionHash,
|
||||
address approverAddress
|
||||
)
|
||||
internal
|
||||
pure
|
||||
returns (bytes memory)
|
||||
{
|
||||
return abi.encodeWithSelector(
|
||||
INVALID_APPROVAL_SIGNATURE_ERROR_SELECTOR,
|
||||
transactionHash,
|
||||
approverAddress
|
||||
);
|
||||
}
|
||||
}
|
@@ -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-utils/contracts/src/LibEIP712.sol";
|
||||
|
||||
|
||||
contract LibEIP712CoordinatorDomain {
|
||||
|
||||
// EIP712 Domain Name value for the Coordinator
|
||||
string constant public EIP712_COORDINATOR_DOMAIN_NAME = "0x Protocol Coordinator";
|
||||
|
||||
// EIP712 Domain Version value for the Coordinator
|
||||
string constant public EIP712_COORDINATOR_DOMAIN_VERSION = "3.0.0";
|
||||
|
||||
// Hash of the EIP712 Domain Separator data for the Coordinator
|
||||
// solhint-disable-next-line var-name-mixedcase
|
||||
bytes32 public EIP712_COORDINATOR_DOMAIN_HASH;
|
||||
|
||||
/// @param chainId Chain ID of the network this contract is deployed on.
|
||||
/// @param verifyingContractAddressIfExists Address of the verifying contract (null if the address of this contract)
|
||||
constructor (
|
||||
uint256 chainId,
|
||||
address verifyingContractAddressIfExists
|
||||
)
|
||||
public
|
||||
{
|
||||
address verifyingContractAddress = verifyingContractAddressIfExists == address(0)
|
||||
? address(this)
|
||||
: verifyingContractAddressIfExists;
|
||||
EIP712_COORDINATOR_DOMAIN_HASH = LibEIP712.hashEIP712Domain(
|
||||
EIP712_COORDINATOR_DOMAIN_NAME,
|
||||
EIP712_COORDINATOR_DOMAIN_VERSION,
|
||||
chainId,
|
||||
verifyingContractAddress
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Calculates EIP712 encoding for a hash struct in the EIP712 domain
|
||||
/// of this contract.
|
||||
/// @param hashStruct The EIP712 hash struct.
|
||||
/// @return result EIP712 hash applied to this EIP712 Domain.
|
||||
function _hashEIP712CoordinatorMessage(bytes32 hashStruct)
|
||||
internal
|
||||
view
|
||||
returns (bytes32 result)
|
||||
{
|
||||
return LibEIP712.hashEIP712Message(EIP712_COORDINATOR_DOMAIN_HASH, hashStruct);
|
||||
}
|
||||
}
|
131
contracts/coordinator/contracts/src/libs/LibEIP712Domain.sol
Normal file
131
contracts/coordinator/contracts/src/libs/LibEIP712Domain.sol
Normal file
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 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.5;
|
||||
|
||||
import "./LibConstants.sol";
|
||||
|
||||
|
||||
contract LibEIP712Domain is
|
||||
LibConstants
|
||||
{
|
||||
|
||||
// EIP191 header for EIP712 prefix
|
||||
string constant internal EIP191_HEADER = "\x19\x01";
|
||||
|
||||
// EIP712 Domain Name value for the Coordinator
|
||||
string constant internal EIP712_COORDINATOR_DOMAIN_NAME = "0x Protocol Coordinator";
|
||||
|
||||
// EIP712 Domain Version value for the Coordinator
|
||||
string constant internal EIP712_COORDINATOR_DOMAIN_VERSION = "1.0.0";
|
||||
|
||||
// EIP712 Domain Name value for the Exchange
|
||||
string constant internal EIP712_EXCHANGE_DOMAIN_NAME = "0x Protocol";
|
||||
|
||||
// EIP712 Domain Version value for the Exchange
|
||||
string constant internal EIP712_EXCHANGE_DOMAIN_VERSION = "2";
|
||||
|
||||
// Hash of the EIP712 Domain Separator Schema
|
||||
bytes32 constant internal EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH = keccak256(abi.encodePacked(
|
||||
"EIP712Domain(",
|
||||
"string name,",
|
||||
"string version,",
|
||||
"address verifyingContract",
|
||||
")"
|
||||
));
|
||||
|
||||
// Hash of the EIP712 Domain Separator data for the Coordinator
|
||||
// solhint-disable-next-line var-name-mixedcase
|
||||
bytes32 public EIP712_COORDINATOR_DOMAIN_HASH;
|
||||
|
||||
// Hash of the EIP712 Domain Separator data for the Exchange
|
||||
// solhint-disable-next-line var-name-mixedcase
|
||||
bytes32 public EIP712_EXCHANGE_DOMAIN_HASH;
|
||||
|
||||
constructor ()
|
||||
public
|
||||
{
|
||||
EIP712_COORDINATOR_DOMAIN_HASH = keccak256(abi.encodePacked(
|
||||
EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH,
|
||||
keccak256(bytes(EIP712_COORDINATOR_DOMAIN_NAME)),
|
||||
keccak256(bytes(EIP712_COORDINATOR_DOMAIN_VERSION)),
|
||||
uint256(address(this))
|
||||
));
|
||||
|
||||
EIP712_EXCHANGE_DOMAIN_HASH = keccak256(abi.encodePacked(
|
||||
EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH,
|
||||
keccak256(bytes(EIP712_EXCHANGE_DOMAIN_NAME)),
|
||||
keccak256(bytes(EIP712_EXCHANGE_DOMAIN_VERSION)),
|
||||
uint256(address(EXCHANGE))
|
||||
));
|
||||
}
|
||||
|
||||
/// @dev Calculates EIP712 encoding for a hash struct in the EIP712 domain
|
||||
/// of this contract.
|
||||
/// @param hashStruct The EIP712 hash struct.
|
||||
/// @return EIP712 hash applied to this EIP712 Domain.
|
||||
function hashEIP712CoordinatorMessage(bytes32 hashStruct)
|
||||
internal
|
||||
view
|
||||
returns (bytes32 result)
|
||||
{
|
||||
return hashEIP712Message(EIP712_COORDINATOR_DOMAIN_HASH, hashStruct);
|
||||
}
|
||||
|
||||
/// @dev Calculates EIP712 encoding for a hash struct in the EIP712 domain
|
||||
/// of the Exchange contract.
|
||||
/// @param hashStruct The EIP712 hash struct.
|
||||
/// @return EIP712 hash applied to the Exchange EIP712 Domain.
|
||||
function hashEIP712ExchangeMessage(bytes32 hashStruct)
|
||||
internal
|
||||
view
|
||||
returns (bytes32 result)
|
||||
{
|
||||
return hashEIP712Message(EIP712_EXCHANGE_DOMAIN_HASH, hashStruct);
|
||||
}
|
||||
|
||||
/// @dev Calculates EIP712 encoding for a hash struct with a given domain hash.
|
||||
/// @param eip712DomainHash Hash of the domain domain separator data.
|
||||
/// @param hashStruct The EIP712 hash struct.
|
||||
/// @return EIP712 hash applied to the Exchange EIP712 Domain.
|
||||
function hashEIP712Message(bytes32 eip712DomainHash, bytes32 hashStruct)
|
||||
internal
|
||||
pure
|
||||
returns (bytes32 result)
|
||||
{
|
||||
// Assembly for more efficient computing:
|
||||
// keccak256(abi.encodePacked(
|
||||
// EIP191_HEADER,
|
||||
// EIP712_DOMAIN_HASH,
|
||||
// hashStruct
|
||||
// ));
|
||||
|
||||
assembly {
|
||||
// Load free memory pointer
|
||||
let memPtr := mload(64)
|
||||
|
||||
mstore(memPtr, 0x1901000000000000000000000000000000000000000000000000000000000000) // EIP191 header
|
||||
mstore(add(memPtr, 2), eip712DomainHash) // EIP712 domain hash
|
||||
mstore(add(memPtr, 34), hashStruct) // Hash of struct
|
||||
|
||||
// Compute hash
|
||||
result := keccak256(memPtr, 66)
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 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.5;
|
||||
pragma experimental "ABIEncoderV2";
|
||||
|
||||
import "./LibEIP712Domain.sol";
|
||||
|
||||
|
||||
contract LibZeroExTransaction is
|
||||
LibEIP712Domain
|
||||
{
|
||||
// Hash for the EIP712 0x transaction schema
|
||||
// keccak256(abi.encodePacked(
|
||||
// "ZeroExTransaction(",
|
||||
// "uint256 salt,",
|
||||
// "address signerAddress,",
|
||||
// "bytes data",
|
||||
// ")"
|
||||
// ));
|
||||
bytes32 constant internal EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH = 0x213c6f636f3ea94e701c0adf9b2624aa45a6c694f9a292c094f9a81c24b5df4c;
|
||||
|
||||
struct ZeroExTransaction {
|
||||
uint256 salt; // Arbitrary number to ensure uniqueness of transaction hash.
|
||||
address signerAddress; // Address of transaction signer.
|
||||
bytes data; // AbiV2 encoded calldata.
|
||||
}
|
||||
|
||||
/// @dev Calculates the EIP712 hash of a 0x transaction using the domain separator of the Exchange contract.
|
||||
/// @param transaction 0x transaction containing salt, signerAddress, and data.
|
||||
/// @return EIP712 hash of the transaction with the domain separator of this contract.
|
||||
function getTransactionHash(ZeroExTransaction memory transaction)
|
||||
public
|
||||
view
|
||||
returns (bytes32 transactionHash)
|
||||
{
|
||||
// Hash the transaction with the domain separator of the Exchange contract.
|
||||
transactionHash = hashEIP712ExchangeMessage(hashZeroExTransaction(transaction));
|
||||
return transactionHash;
|
||||
}
|
||||
|
||||
/// @dev Calculates EIP712 hash of the 0x transaction with no domain separator.
|
||||
/// @param transaction 0x transaction containing salt, signerAddress, and data.
|
||||
/// @return EIP712 hash of the transaction with no domain separator.
|
||||
function hashZeroExTransaction(ZeroExTransaction memory transaction)
|
||||
internal
|
||||
pure
|
||||
returns (bytes32 result)
|
||||
{
|
||||
bytes32 schemaHash = EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH;
|
||||
bytes memory data = transaction.data;
|
||||
uint256 salt = transaction.salt;
|
||||
address signerAddress = transaction.signerAddress;
|
||||
|
||||
// Assembly for more efficiently computing:
|
||||
// keccak256(abi.encodePacked(
|
||||
// EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH,
|
||||
// transaction.salt,
|
||||
// uint256(transaction.signerAddress),
|
||||
// keccak256(transaction.data)
|
||||
// ));
|
||||
|
||||
assembly {
|
||||
// Compute hash of data
|
||||
let dataHash := keccak256(add(data, 32), mload(data))
|
||||
|
||||
// Load free memory pointer
|
||||
let memPtr := mload(64)
|
||||
|
||||
mstore(memPtr, schemaHash) // hash of schema
|
||||
mstore(add(memPtr, 32), salt) // salt
|
||||
mstore(add(memPtr, 64), and(signerAddress, 0xffffffffffffffffffffffffffffffffffffffff)) // signerAddress
|
||||
mstore(add(memPtr, 96), dataHash) // hash of data
|
||||
|
||||
// Compute hash
|
||||
result := keccak256(memPtr, 128)
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 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.5;
|
||||
pragma experimental "ABIEncoderV2";
|
||||
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||
import "../interfaces/ICoordinatorApprovalVerifier.sol";
|
||||
|
||||
|
||||
contract MCoordinatorApprovalVerifier is
|
||||
ICoordinatorApprovalVerifier
|
||||
{
|
||||
/// @dev Validates that the feeRecipients of a batch of order have approved a 0x transaction.
|
||||
/// @param transaction 0x transaction containing salt, signerAddress, and data.
|
||||
/// @param orders Array of order structs containing order specifications.
|
||||
/// @param txOrigin Required signer of Ethereum transaction calling this function.
|
||||
/// @param transactionSignature Proof that the transaction has been signed by the signer.
|
||||
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
|
||||
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order.
|
||||
function assertValidTransactionOrdersApproval(
|
||||
LibZeroExTransaction.ZeroExTransaction memory transaction,
|
||||
LibOrder.Order[] memory orders,
|
||||
address txOrigin,
|
||||
bytes memory transactionSignature,
|
||||
uint256[] memory approvalExpirationTimeSeconds,
|
||||
bytes[] memory approvalSignatures
|
||||
)
|
||||
internal
|
||||
view;
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -16,27 +16,20 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "../src/Staking.sol";
|
||||
import "../interfaces/ISignatureValidator.sol";
|
||||
|
||||
|
||||
contract TestExchangeManager is
|
||||
Staking
|
||||
contract MSignatureValidator is
|
||||
ISignatureValidator
|
||||
{
|
||||
function setValidExchange(address exchange)
|
||||
external
|
||||
{
|
||||
validExchanges[exchange] = true;
|
||||
}
|
||||
|
||||
function onlyExchangeFunction()
|
||||
external
|
||||
view
|
||||
onlyExchange
|
||||
returns (bool)
|
||||
{
|
||||
return true;
|
||||
// Allowed signature types.
|
||||
enum SignatureType {
|
||||
Illegal, // 0x00, default value
|
||||
Invalid, // 0x01
|
||||
EIP712, // 0x02
|
||||
EthSign, // 0x03
|
||||
NSignatureTypes // 0x04, number of signature types. Always leave at end.
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "./MixinCoordinatorRegistryCore.sol";
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "./interfaces/ICoordinatorRegistryCore.sol";
|
||||
|
||||
@@ -29,7 +29,7 @@ contract MixinCoordinatorRegistryCore is
|
||||
mapping (address => string) internal coordinatorEndpoints;
|
||||
|
||||
/// @dev Called by a Coordinator operator to set the endpoint of their Coordinator.
|
||||
/// @param coordinatorEndpoint Endpoint of the Coordinator as a string.
|
||||
/// @param coordinatorEndpoint endpoint of the Coordinator.
|
||||
function setCoordinatorEndpoint(string calldata coordinatorEndpoint) external {
|
||||
address coordinatorOperator = msg.sender;
|
||||
coordinatorEndpoints[coordinatorOperator] = coordinatorEndpoint;
|
||||
@@ -37,8 +37,7 @@ contract MixinCoordinatorRegistryCore is
|
||||
}
|
||||
|
||||
/// @dev Gets the endpoint for a Coordinator.
|
||||
/// @param coordinatorOperator Operator of the Coordinator endpoint.
|
||||
/// @return coordinatorEndpoint Endpoint of the Coordinator as a string.
|
||||
/// @param coordinatorOperator operator of the Coordinator endpoint.
|
||||
function getCoordinatorEndpoint(address coordinatorOperator)
|
||||
external
|
||||
view
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
|
||||
// solhint-disable no-empty-blocks
|
||||
@@ -29,12 +29,11 @@ contract ICoordinatorRegistryCore
|
||||
);
|
||||
|
||||
/// @dev Called by a Coordinator operator to set the endpoint of their Coordinator.
|
||||
/// @param coordinatorEndpoint Endpoint of the Coordinator as a string.
|
||||
/// @param coordinatorEndpoint endpoint of the Coordinator.
|
||||
function setCoordinatorEndpoint(string calldata coordinatorEndpoint) external;
|
||||
|
||||
/// @dev Gets the endpoint for a Coordinator.
|
||||
/// @param coordinatorOperator Operator of the Coordinator endpoint.
|
||||
/// @return coordinatorEndpoint Endpoint of the Coordinator as a string.
|
||||
/// @param coordinatorOperator operator of the Coordinator endpoint.
|
||||
function getCoordinatorEndpoint(address coordinatorOperator)
|
||||
external
|
||||
view
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-coordinator",
|
||||
"version": "2.1.0-beta.2",
|
||||
"version": "2.0.13",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -12,7 +12,7 @@
|
||||
"scripts": {
|
||||
"build": "yarn pre_build && tsc -b",
|
||||
"build:ci": "yarn build",
|
||||
"pre_build": "run-s compile contracts:gen generate_contract_wrappers contracts:copy",
|
||||
"pre_build": "run-s compile generate_contract_wrappers",
|
||||
"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",
|
||||
@@ -21,23 +21,20 @@
|
||||
"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",
|
||||
"clean": "shx rm -rf lib generated-artifacts generated-wrappers",
|
||||
"generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --output generated-wrappers --backend ethers",
|
||||
"lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||
"fix": "tslint --fix --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./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"
|
||||
"contracts:gen": "contracts-gen",
|
||||
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol"
|
||||
},
|
||||
"config": {
|
||||
"publicInterfaceContracts": "Coordinator,CoordinatorRegistry,LibCoordinatorApproval,LibCoordinatorRichErrors,LibEIP712CoordinatorDomain,LibConstants",
|
||||
"abis": "./test/generated-artifacts/@(Coordinator|CoordinatorRegistry|ICoordinatorApprovalVerifier|ICoordinatorCore|ICoordinatorRegistryCore|ICoordinatorSignatureValidator|LibConstants|LibCoordinatorApproval|LibCoordinatorRichErrors|LibEIP712CoordinatorDomain|MixinCoordinatorApprovalVerifier|MixinCoordinatorCore|MixinCoordinatorRegistryCore|MixinSignatureValidator).json",
|
||||
"abis": "./generated-artifacts/@(Coordinator|CoordinatorRegistry).json",
|
||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
|
||||
},
|
||||
"repository": {
|
||||
@@ -50,18 +47,12 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^4.4.0-beta.2",
|
||||
"@0x/contracts-asset-proxy": "^2.3.0-beta.2",
|
||||
"@0x/contracts-dev-utils": "^0.1.0-beta.2",
|
||||
"@0x/contracts-erc20": "^2.3.0-beta.2",
|
||||
"@0x/contracts-exchange": "^2.2.0-beta.2",
|
||||
"@0x/contracts-gen": "^1.1.0-beta.2",
|
||||
"@0x/contracts-test-utils": "^3.2.0-beta.2",
|
||||
"@0x/dev-utils": "^2.4.0-beta.2",
|
||||
"@0x/order-utils": "^8.5.0-beta.2",
|
||||
"@0x/sol-compiler": "^3.2.0-beta.2",
|
||||
"@0x/tslint-config": "^3.1.0-beta.2",
|
||||
"@0x/web3-wrapper": "^6.1.0-beta.2",
|
||||
"@0x/abi-gen": "^4.2.1",
|
||||
"@0x/contracts-gen": "^1.0.15",
|
||||
"@0x/contracts-test-utils": "^3.1.16",
|
||||
"@0x/dev-utils": "^2.3.3",
|
||||
"@0x/sol-compiler": "^3.1.15",
|
||||
"@0x/tslint-config": "^3.0.1",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/node": "*",
|
||||
@@ -69,23 +60,29 @@
|
||||
"chai-as-promised": "^7.1.0",
|
||||
"chai-bignumber": "^3.0.0",
|
||||
"dirty-chai": "^2.0.1",
|
||||
"lodash": "^4.17.11",
|
||||
"make-promises-safe": "^1.1.0",
|
||||
"mocha": "^6.2.0",
|
||||
"npm-run-all": "^4.1.2",
|
||||
"shx": "^0.2.2",
|
||||
"solhint": "^1.4.1",
|
||||
"truffle": "^5.0.32",
|
||||
"tslint": "5.11.0",
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^5.5.0-beta.2",
|
||||
"@0x/contracts-utils": "^3.3.0-beta.2",
|
||||
"@0x/types": "^2.5.0-beta.2",
|
||||
"@0x/typescript-typings": "^4.4.0-beta.2",
|
||||
"@0x/utils": "^4.6.0-beta.2",
|
||||
"ethereum-types": "^2.2.0-beta.2"
|
||||
"@0x/base-contract": "^5.4.0",
|
||||
"@0x/contracts-asset-proxy": "^2.2.8",
|
||||
"@0x/contracts-erc20": "^2.2.14",
|
||||
"@0x/contracts-exchange": "^2.1.14",
|
||||
"@0x/contracts-exchange-libs": "^3.0.8",
|
||||
"@0x/contracts-utils": "^3.2.4",
|
||||
"@0x/order-utils": "^8.4.0",
|
||||
"@0x/types": "^2.4.3",
|
||||
"@0x/typescript-typings": "^4.3.0",
|
||||
"@0x/utils": "^4.5.2",
|
||||
"@0x/web3-wrapper": "^6.0.13",
|
||||
"ethereum-types": "^2.1.6",
|
||||
"ethereumjs-util": "^5.1.1",
|
||||
"lodash": "^4.17.11"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
@@ -7,15 +7,7 @@ import { ContractArtifact } from 'ethereum-types';
|
||||
|
||||
import * as Coordinator from '../generated-artifacts/Coordinator.json';
|
||||
import * as CoordinatorRegistry from '../generated-artifacts/CoordinatorRegistry.json';
|
||||
import * as LibConstants from '../generated-artifacts/LibConstants.json';
|
||||
import * as LibCoordinatorApproval from '../generated-artifacts/LibCoordinatorApproval.json';
|
||||
import * as LibCoordinatorRichErrors from '../generated-artifacts/LibCoordinatorRichErrors.json';
|
||||
import * as LibEIP712CoordinatorDomain from '../generated-artifacts/LibEIP712CoordinatorDomain.json';
|
||||
export const artifacts = {
|
||||
Coordinator: Coordinator as ContractArtifact,
|
||||
CoordinatorRegistry: CoordinatorRegistry as ContractArtifact,
|
||||
LibCoordinatorApproval: LibCoordinatorApproval as ContractArtifact,
|
||||
LibCoordinatorRichErrors: LibCoordinatorRichErrors as ContractArtifact,
|
||||
LibEIP712CoordinatorDomain: LibEIP712CoordinatorDomain as ContractArtifact,
|
||||
LibConstants: LibConstants as ContractArtifact,
|
||||
};
|
||||
|
@@ -1,28 +0,0 @@
|
||||
import { hexConcat } from '@0x/contracts-test-utils';
|
||||
import { eip712Utils } from '@0x/order-utils';
|
||||
import { SignedZeroExTransaction } from '@0x/types';
|
||||
import { signTypedDataUtils } from '@0x/utils';
|
||||
|
||||
export const hashUtils = {
|
||||
async getApprovalHashBufferAsync(
|
||||
transaction: SignedZeroExTransaction,
|
||||
verifyingContract: string,
|
||||
txOrigin: string,
|
||||
): Promise<Buffer> {
|
||||
const typedData = await eip712Utils.createCoordinatorApprovalTypedDataAsync(
|
||||
transaction,
|
||||
verifyingContract,
|
||||
txOrigin,
|
||||
);
|
||||
const hashBuffer = signTypedDataUtils.generateTypedDataHash(typedData);
|
||||
return hashBuffer;
|
||||
},
|
||||
async getApprovalHashHexAsync(
|
||||
transaction: SignedZeroExTransaction,
|
||||
verifyingContract: string,
|
||||
txOrigin: string,
|
||||
): Promise<string> {
|
||||
const hashHex = hexConcat(await hashUtils.getApprovalHashBufferAsync(transaction, verifyingContract, txOrigin));
|
||||
return hashHex;
|
||||
},
|
||||
};
|
@@ -1,5 +1,3 @@
|
||||
export * from './artifacts';
|
||||
export * from './wrappers';
|
||||
export import CoordinatorRevertErrors = require('./revert_errors');
|
||||
export { ApprovalFactory } from './approval_factory';
|
||||
export { SignedCoordinatorApproval } from './types';
|
||||
export * from '../test/utils';
|
||||
|
@@ -1,52 +0,0 @@
|
||||
import { BigNumber, RevertError } from '@0x/utils';
|
||||
|
||||
// tslint:disable:max-classes-per-file
|
||||
|
||||
export enum SignatureErrorCodes {
|
||||
InvalidLength,
|
||||
Unsupported,
|
||||
Illegal,
|
||||
Invalid,
|
||||
}
|
||||
|
||||
export class SignatureError extends RevertError {
|
||||
constructor(errorCode?: SignatureErrorCodes, hash?: string, signature?: string) {
|
||||
super('SignatureError', 'SignatureError(uint8 errorCode, bytes32 hash, bytes signature)', {
|
||||
errorCode,
|
||||
hash,
|
||||
signature,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class InvalidOriginError extends RevertError {
|
||||
constructor(expectedOrigin?: string) {
|
||||
super('InvalidOriginError', 'InvalidOriginError(address expectedOrigin)', { expectedOrigin });
|
||||
}
|
||||
}
|
||||
|
||||
export class ApprovalExpiredError extends RevertError {
|
||||
constructor(transactionHash?: string, approvalExpirationTime?: BigNumber | number | string) {
|
||||
super('ApprovalExpiredError', 'ApprovalExpiredError(bytes32 transactionHash, uint256 approvalExpirationTime)', {
|
||||
transactionHash,
|
||||
approvalExpirationTime,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class InvalidApprovalSignatureError extends RevertError {
|
||||
constructor(transactionHash?: string, approverAddress?: string) {
|
||||
super(
|
||||
'InvalidApprovalSignatureError',
|
||||
'InvalidApprovalSignatureError(bytes32 transactionHash, address approverAddress)',
|
||||
{ transactionHash, approverAddress },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const types = [SignatureError, InvalidOriginError, ApprovalExpiredError, InvalidApprovalSignatureError];
|
||||
|
||||
// Register the types we've defined.
|
||||
for (const type of types) {
|
||||
RevertError.registerType(type);
|
||||
}
|
@@ -5,7 +5,3 @@
|
||||
*/
|
||||
export * from '../generated-wrappers/coordinator';
|
||||
export * from '../generated-wrappers/coordinator_registry';
|
||||
export * from '../generated-wrappers/lib_constants';
|
||||
export * from '../generated-wrappers/lib_coordinator_approval';
|
||||
export * from '../generated-wrappers/lib_coordinator_rich_errors';
|
||||
export * from '../generated-wrappers/lib_e_i_p712_coordinator_domain';
|
||||
|
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* -----------------------------------------------------------------------------
|
||||
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
import { ContractArtifact } from 'ethereum-types';
|
||||
|
||||
import * as Coordinator from '../test/generated-artifacts/Coordinator.json';
|
||||
import * as CoordinatorRegistry from '../test/generated-artifacts/CoordinatorRegistry.json';
|
||||
import * as ICoordinatorApprovalVerifier from '../test/generated-artifacts/ICoordinatorApprovalVerifier.json';
|
||||
import * as ICoordinatorCore from '../test/generated-artifacts/ICoordinatorCore.json';
|
||||
import * as ICoordinatorRegistryCore from '../test/generated-artifacts/ICoordinatorRegistryCore.json';
|
||||
import * as ICoordinatorSignatureValidator from '../test/generated-artifacts/ICoordinatorSignatureValidator.json';
|
||||
import * as LibConstants from '../test/generated-artifacts/LibConstants.json';
|
||||
import * as LibCoordinatorApproval from '../test/generated-artifacts/LibCoordinatorApproval.json';
|
||||
import * as LibCoordinatorRichErrors from '../test/generated-artifacts/LibCoordinatorRichErrors.json';
|
||||
import * as LibEIP712CoordinatorDomain from '../test/generated-artifacts/LibEIP712CoordinatorDomain.json';
|
||||
import * as MixinCoordinatorApprovalVerifier from '../test/generated-artifacts/MixinCoordinatorApprovalVerifier.json';
|
||||
import * as MixinCoordinatorCore from '../test/generated-artifacts/MixinCoordinatorCore.json';
|
||||
import * as MixinCoordinatorRegistryCore from '../test/generated-artifacts/MixinCoordinatorRegistryCore.json';
|
||||
import * as MixinSignatureValidator from '../test/generated-artifacts/MixinSignatureValidator.json';
|
||||
export const artifacts = {
|
||||
Coordinator: Coordinator as ContractArtifact,
|
||||
MixinCoordinatorApprovalVerifier: MixinCoordinatorApprovalVerifier as ContractArtifact,
|
||||
MixinCoordinatorCore: MixinCoordinatorCore as ContractArtifact,
|
||||
MixinSignatureValidator: MixinSignatureValidator as ContractArtifact,
|
||||
ICoordinatorApprovalVerifier: ICoordinatorApprovalVerifier as ContractArtifact,
|
||||
ICoordinatorCore: ICoordinatorCore as ContractArtifact,
|
||||
ICoordinatorSignatureValidator: ICoordinatorSignatureValidator as ContractArtifact,
|
||||
LibConstants: LibConstants as ContractArtifact,
|
||||
LibCoordinatorApproval: LibCoordinatorApproval as ContractArtifact,
|
||||
LibCoordinatorRichErrors: LibCoordinatorRichErrors as ContractArtifact,
|
||||
LibEIP712CoordinatorDomain: LibEIP712CoordinatorDomain as ContractArtifact,
|
||||
CoordinatorRegistry: CoordinatorRegistry as ContractArtifact,
|
||||
MixinCoordinatorRegistryCore: MixinCoordinatorRegistryCore as ContractArtifact,
|
||||
ICoordinatorRegistryCore: ICoordinatorRegistryCore as ContractArtifact,
|
||||
};
|
523
contracts/coordinator/test/coordinator.ts
Normal file
523
contracts/coordinator/test/coordinator.ts
Normal file
@@ -0,0 +1,523 @@
|
||||
import { ERC20ProxyContract, ERC20Wrapper } from '@0x/contracts-asset-proxy';
|
||||
import { DummyERC20TokenContract } from '@0x/contracts-erc20';
|
||||
import {
|
||||
artifacts as exchangeArtifacts,
|
||||
ExchangeCancelEventArgs,
|
||||
ExchangeCancelUpToEventArgs,
|
||||
ExchangeContract,
|
||||
ExchangeFillEventArgs,
|
||||
} from '@0x/contracts-exchange';
|
||||
import {
|
||||
chaiSetup,
|
||||
constants as devConstants,
|
||||
expectTransactionFailedAsync,
|
||||
getLatestBlockTimestampAsync,
|
||||
OrderFactory,
|
||||
provider,
|
||||
TransactionFactory,
|
||||
txDefaults,
|
||||
web3Wrapper,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||
import { assetDataUtils, orderHashUtils } from '@0x/order-utils';
|
||||
import { RevertReason, SignedOrder } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as chai from 'chai';
|
||||
import { LogWithDecodedArgs } from 'ethereum-types';
|
||||
|
||||
import { ApprovalFactory, artifacts, constants, CoordinatorContract, exchangeDataEncoder } from '../src';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
web3Wrapper.abiDecoder.addABI(exchangeArtifacts.Exchange.compilerOutput.abi);
|
||||
// tslint:disable:no-unnecessary-type-assertion
|
||||
describe('Coordinator tests', () => {
|
||||
let makerAddress: string;
|
||||
let owner: string;
|
||||
let takerAddress: string;
|
||||
let feeRecipientAddress: string;
|
||||
|
||||
let erc20Proxy: ERC20ProxyContract;
|
||||
let erc20TokenA: DummyERC20TokenContract;
|
||||
let erc20TokenB: DummyERC20TokenContract;
|
||||
let zrxToken: DummyERC20TokenContract;
|
||||
let coordinatorContract: CoordinatorContract;
|
||||
let exchange: ExchangeContract;
|
||||
|
||||
let erc20Wrapper: ERC20Wrapper;
|
||||
let orderFactory: OrderFactory;
|
||||
let takerTransactionFactory: TransactionFactory;
|
||||
let makerTransactionFactory: TransactionFactory;
|
||||
let approvalFactory: ApprovalFactory;
|
||||
|
||||
before(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
after(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
before(async () => {
|
||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
||||
const usedAddresses = ([owner, makerAddress, takerAddress, feeRecipientAddress] = accounts.slice(0, 4));
|
||||
|
||||
erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner);
|
||||
erc20Proxy = await erc20Wrapper.deployProxyAsync();
|
||||
const numDummyErc20ToDeploy = 3;
|
||||
[erc20TokenA, erc20TokenB, zrxToken] = await erc20Wrapper.deployDummyTokensAsync(
|
||||
numDummyErc20ToDeploy,
|
||||
devConstants.DUMMY_TOKEN_DECIMALS,
|
||||
);
|
||||
await erc20Wrapper.setBalancesAndAllowancesAsync();
|
||||
|
||||
exchange = await ExchangeContract.deployFrom0xArtifactAsync(
|
||||
exchangeArtifacts.Exchange,
|
||||
provider,
|
||||
txDefaults,
|
||||
artifacts,
|
||||
assetDataUtils.encodeERC20AssetData(zrxToken.address),
|
||||
);
|
||||
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { from: owner }),
|
||||
devConstants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await exchange.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, { from: owner }),
|
||||
devConstants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
|
||||
coordinatorContract = await CoordinatorContract.deployFrom0xArtifactAsync(
|
||||
artifacts.Coordinator,
|
||||
provider,
|
||||
txDefaults,
|
||||
artifacts,
|
||||
exchange.address,
|
||||
);
|
||||
|
||||
// Configure order defaults
|
||||
const defaultOrderParams = {
|
||||
...devConstants.STATIC_ORDER_PARAMS,
|
||||
exchangeAddress: exchange.address,
|
||||
senderAddress: coordinatorContract.address,
|
||||
makerAddress,
|
||||
feeRecipientAddress,
|
||||
makerAssetData: assetDataUtils.encodeERC20AssetData(erc20TokenA.address),
|
||||
takerAssetData: assetDataUtils.encodeERC20AssetData(erc20TokenB.address),
|
||||
};
|
||||
const makerPrivateKey = devConstants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)];
|
||||
const takerPrivateKey = devConstants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(takerAddress)];
|
||||
const feeRecipientPrivateKey = devConstants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(feeRecipientAddress)];
|
||||
orderFactory = new OrderFactory(makerPrivateKey, defaultOrderParams);
|
||||
makerTransactionFactory = new TransactionFactory(makerPrivateKey, exchange.address);
|
||||
takerTransactionFactory = new TransactionFactory(takerPrivateKey, exchange.address);
|
||||
approvalFactory = new ApprovalFactory(feeRecipientPrivateKey, coordinatorContract.address);
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
afterEach(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
|
||||
describe('single order fills', () => {
|
||||
for (const fnName of constants.SINGLE_FILL_FN_NAMES) {
|
||||
it(`${fnName} should fill the order with a signed approval`, async () => {
|
||||
const orders = [await orderFactory.newSignedOrderAsync()];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory.newSignedApproval(
|
||||
transaction,
|
||||
takerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: takerAddress },
|
||||
),
|
||||
devConstants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const fillLogs = transactionReceipt.logs.filter(
|
||||
log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill',
|
||||
);
|
||||
expect(fillLogs.length).to.eq(1);
|
||||
const fillLogArgs = (fillLogs[0] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
|
||||
expect(fillLogArgs.makerAddress).to.eq(makerAddress);
|
||||
expect(fillLogArgs.takerAddress).to.eq(takerAddress);
|
||||
expect(fillLogArgs.senderAddress).to.eq(coordinatorContract.address);
|
||||
expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
|
||||
expect(fillLogArgs.makerAssetData).to.eq(orders[0].makerAssetData);
|
||||
expect(fillLogArgs.takerAssetData).to.eq(orders[0].takerAssetData);
|
||||
expect(fillLogArgs.makerAssetFilledAmount).to.bignumber.eq(orders[0].makerAssetAmount);
|
||||
expect(fillLogArgs.takerAssetFilledAmount).to.bignumber.eq(orders[0].takerAssetAmount);
|
||||
expect(fillLogArgs.makerFeePaid).to.bignumber.eq(orders[0].makerFee);
|
||||
expect(fillLogArgs.takerFeePaid).to.bignumber.eq(orders[0].takerFee);
|
||||
expect(fillLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(orders[0]));
|
||||
});
|
||||
it(`${fnName} should fill the order if called by approver`, async () => {
|
||||
const orders = [await orderFactory.newSignedOrderAsync()];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
feeRecipientAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{ from: feeRecipientAddress },
|
||||
),
|
||||
devConstants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const fillLogs = transactionReceipt.logs.filter(
|
||||
log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill',
|
||||
);
|
||||
expect(fillLogs.length).to.eq(1);
|
||||
const fillLogArgs = (fillLogs[0] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
|
||||
expect(fillLogArgs.makerAddress).to.eq(makerAddress);
|
||||
expect(fillLogArgs.takerAddress).to.eq(takerAddress);
|
||||
expect(fillLogArgs.senderAddress).to.eq(coordinatorContract.address);
|
||||
expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
|
||||
expect(fillLogArgs.makerAssetData).to.eq(orders[0].makerAssetData);
|
||||
expect(fillLogArgs.takerAssetData).to.eq(orders[0].takerAssetData);
|
||||
expect(fillLogArgs.makerAssetFilledAmount).to.bignumber.eq(orders[0].makerAssetAmount);
|
||||
expect(fillLogArgs.takerAssetFilledAmount).to.bignumber.eq(orders[0].takerAssetAmount);
|
||||
expect(fillLogArgs.makerFeePaid).to.bignumber.eq(orders[0].makerFee);
|
||||
expect(fillLogArgs.takerFeePaid).to.bignumber.eq(orders[0].takerFee);
|
||||
expect(fillLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(orders[0]));
|
||||
});
|
||||
it(`${fnName} should revert with no approval signature`, async () => {
|
||||
const orders = [await orderFactory.newSignedOrderAsync()];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
await expectTransactionFailedAsync(
|
||||
coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{
|
||||
from: takerAddress,
|
||||
gas: devConstants.MAX_EXECUTE_TRANSACTION_GAS,
|
||||
},
|
||||
),
|
||||
RevertReason.InvalidApprovalSignature,
|
||||
);
|
||||
});
|
||||
it(`${fnName} should revert with an invalid approval signature`, async () => {
|
||||
const orders = [await orderFactory.newSignedOrderAsync()];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory.newSignedApproval(
|
||||
transaction,
|
||||
takerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`;
|
||||
await expectTransactionFailedAsync(
|
||||
coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[signature],
|
||||
{ from: takerAddress },
|
||||
),
|
||||
RevertReason.InvalidApprovalSignature,
|
||||
);
|
||||
});
|
||||
it(`${fnName} should revert with an expired approval`, async () => {
|
||||
const orders = [await orderFactory.newSignedOrderAsync()];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory.newSignedApproval(
|
||||
transaction,
|
||||
takerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await expectTransactionFailedAsync(
|
||||
coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: takerAddress },
|
||||
),
|
||||
RevertReason.ApprovalExpired,
|
||||
);
|
||||
});
|
||||
it(`${fnName} should revert if not called by tx signer or approver`, async () => {
|
||||
const orders = [await orderFactory.newSignedOrderAsync()];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory.newSignedApproval(
|
||||
transaction,
|
||||
takerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await expectTransactionFailedAsync(
|
||||
coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: owner },
|
||||
),
|
||||
RevertReason.InvalidOrigin,
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
describe('batch order fills', () => {
|
||||
for (const fnName of [...constants.MARKET_FILL_FN_NAMES, ...constants.BATCH_FILL_FN_NAMES]) {
|
||||
it(`${fnName} should fill the orders with a signed approval`, async () => {
|
||||
const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory.newSignedApproval(
|
||||
transaction,
|
||||
takerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: takerAddress, gas: devConstants.MAX_EXECUTE_TRANSACTION_GAS },
|
||||
),
|
||||
devConstants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const fillLogs = transactionReceipt.logs.filter(
|
||||
log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill',
|
||||
);
|
||||
expect(fillLogs.length).to.eq(orders.length);
|
||||
orders.forEach((order, index) => {
|
||||
const fillLogArgs = (fillLogs[index] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
|
||||
expect(fillLogArgs.makerAddress).to.eq(makerAddress);
|
||||
expect(fillLogArgs.takerAddress).to.eq(takerAddress);
|
||||
expect(fillLogArgs.senderAddress).to.eq(coordinatorContract.address);
|
||||
expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
|
||||
expect(fillLogArgs.makerAssetData).to.eq(order.makerAssetData);
|
||||
expect(fillLogArgs.takerAssetData).to.eq(order.takerAssetData);
|
||||
expect(fillLogArgs.makerAssetFilledAmount).to.bignumber.eq(order.makerAssetAmount);
|
||||
expect(fillLogArgs.takerAssetFilledAmount).to.bignumber.eq(order.takerAssetAmount);
|
||||
expect(fillLogArgs.makerFeePaid).to.bignumber.eq(order.makerFee);
|
||||
expect(fillLogArgs.takerFeePaid).to.bignumber.eq(order.takerFee);
|
||||
expect(fillLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(order));
|
||||
});
|
||||
});
|
||||
it(`${fnName} should fill the orders if called by approver`, async () => {
|
||||
const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
feeRecipientAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{ from: feeRecipientAddress, gas: devConstants.MAX_EXECUTE_TRANSACTION_GAS },
|
||||
),
|
||||
devConstants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const fillLogs = transactionReceipt.logs.filter(
|
||||
log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill',
|
||||
);
|
||||
expect(fillLogs.length).to.eq(orders.length);
|
||||
orders.forEach((order, index) => {
|
||||
const fillLogArgs = (fillLogs[index] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
|
||||
expect(fillLogArgs.makerAddress).to.eq(makerAddress);
|
||||
expect(fillLogArgs.takerAddress).to.eq(takerAddress);
|
||||
expect(fillLogArgs.senderAddress).to.eq(coordinatorContract.address);
|
||||
expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
|
||||
expect(fillLogArgs.makerAssetData).to.eq(order.makerAssetData);
|
||||
expect(fillLogArgs.takerAssetData).to.eq(order.takerAssetData);
|
||||
expect(fillLogArgs.makerAssetFilledAmount).to.bignumber.eq(order.makerAssetAmount);
|
||||
expect(fillLogArgs.takerAssetFilledAmount).to.bignumber.eq(order.takerAssetAmount);
|
||||
expect(fillLogArgs.makerFeePaid).to.bignumber.eq(order.makerFee);
|
||||
expect(fillLogArgs.takerFeePaid).to.bignumber.eq(order.takerFee);
|
||||
expect(fillLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(order));
|
||||
});
|
||||
});
|
||||
it(`${fnName} should revert with an invalid approval signature`, async () => {
|
||||
const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory.newSignedApproval(
|
||||
transaction,
|
||||
takerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`;
|
||||
await expectTransactionFailedAsync(
|
||||
coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[signature],
|
||||
{ from: takerAddress },
|
||||
),
|
||||
RevertReason.InvalidApprovalSignature,
|
||||
);
|
||||
});
|
||||
it(`${fnName} should revert with an expired approval`, async () => {
|
||||
const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory.newSignedApproval(
|
||||
transaction,
|
||||
takerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await expectTransactionFailedAsync(
|
||||
coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: takerAddress },
|
||||
),
|
||||
RevertReason.ApprovalExpired,
|
||||
);
|
||||
});
|
||||
it(`${fnName} should revert if not called by tx signer or approver`, async () => {
|
||||
const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory.newSignedApproval(
|
||||
transaction,
|
||||
takerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await expectTransactionFailedAsync(
|
||||
coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: owner },
|
||||
),
|
||||
RevertReason.InvalidOrigin,
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
describe('cancels', () => {
|
||||
it('cancelOrder call should be successful without an approval', async () => {
|
||||
const orders = [await orderFactory.newSignedOrderAsync()];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(constants.CANCEL_ORDER, orders);
|
||||
const transaction = makerTransactionFactory.newSignedTransaction(data);
|
||||
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
makerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{
|
||||
from: makerAddress,
|
||||
},
|
||||
),
|
||||
);
|
||||
const cancelLogs = transactionReceipt.logs.filter(
|
||||
log => (log as LogWithDecodedArgs<ExchangeCancelEventArgs>).event === 'Cancel',
|
||||
);
|
||||
expect(cancelLogs.length).to.eq(1);
|
||||
const cancelLogArgs = (cancelLogs[0] as LogWithDecodedArgs<ExchangeCancelEventArgs>).args;
|
||||
expect(cancelLogArgs.makerAddress).to.eq(makerAddress);
|
||||
expect(cancelLogArgs.senderAddress).to.eq(coordinatorContract.address);
|
||||
expect(cancelLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
|
||||
expect(cancelLogArgs.makerAssetData).to.eq(orders[0].makerAssetData);
|
||||
expect(cancelLogArgs.takerAssetData).to.eq(orders[0].takerAssetData);
|
||||
expect(cancelLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(orders[0]));
|
||||
});
|
||||
it('batchCancelOrders call should be successful without an approval', async () => {
|
||||
const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(constants.BATCH_CANCEL_ORDERS, orders);
|
||||
const transaction = makerTransactionFactory.newSignedTransaction(data);
|
||||
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
makerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{
|
||||
from: makerAddress,
|
||||
},
|
||||
),
|
||||
);
|
||||
const cancelLogs = transactionReceipt.logs.filter(
|
||||
log => (log as LogWithDecodedArgs<ExchangeCancelEventArgs>).event === 'Cancel',
|
||||
);
|
||||
expect(cancelLogs.length).to.eq(orders.length);
|
||||
orders.forEach((order, index) => {
|
||||
const cancelLogArgs = (cancelLogs[index] as LogWithDecodedArgs<ExchangeCancelEventArgs>).args;
|
||||
expect(cancelLogArgs.makerAddress).to.eq(makerAddress);
|
||||
expect(cancelLogArgs.senderAddress).to.eq(coordinatorContract.address);
|
||||
expect(cancelLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
|
||||
expect(cancelLogArgs.makerAssetData).to.eq(order.makerAssetData);
|
||||
expect(cancelLogArgs.takerAssetData).to.eq(order.takerAssetData);
|
||||
expect(cancelLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(order));
|
||||
});
|
||||
});
|
||||
it('cancelOrdersUpTo call should be successful without an approval', async () => {
|
||||
const orders: SignedOrder[] = [];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(constants.CANCEL_ORDERS_UP_TO, orders);
|
||||
const transaction = makerTransactionFactory.newSignedTransaction(data);
|
||||
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
makerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{
|
||||
from: makerAddress,
|
||||
},
|
||||
),
|
||||
);
|
||||
const cancelLogs = transactionReceipt.logs.filter(
|
||||
log => (log as LogWithDecodedArgs<ExchangeCancelUpToEventArgs>).event === 'CancelUpTo',
|
||||
);
|
||||
expect(cancelLogs.length).to.eq(1);
|
||||
const cancelLogArgs = (cancelLogs[0] as LogWithDecodedArgs<ExchangeCancelUpToEventArgs>).args;
|
||||
expect(cancelLogArgs.makerAddress).to.eq(makerAddress);
|
||||
expect(cancelLogArgs.senderAddress).to.eq(coordinatorContract.address);
|
||||
expect(cancelLogArgs.orderEpoch).to.bignumber.eq(new BigNumber(1));
|
||||
});
|
||||
});
|
||||
});
|
||||
// tslint:disable:max-file-line-count
|
@@ -1,73 +1,81 @@
|
||||
import { blockchainTests, expect, verifyEvents } from '@0x/contracts-test-utils';
|
||||
import { artifacts as exchangeArtifacts } from '@0x/contracts-exchange';
|
||||
import { chaiSetup, provider, web3Wrapper } from '@0x/contracts-test-utils';
|
||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||
import * as chai from 'chai';
|
||||
import { LogWithDecodedArgs } from 'ethereum-types';
|
||||
|
||||
import { artifacts } from './artifacts';
|
||||
import { CoordinatorRegistryCoordinatorEndpointSetEventArgs } from '../src';
|
||||
|
||||
import { CoordinatorRegistryContract, CoordinatorRegistryCoordinatorEndpointSetEventArgs } from './wrappers';
|
||||
import { CoordinatorRegistryWrapper } from './utils/coordinator_registry_wrapper';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
web3Wrapper.abiDecoder.addABI(exchangeArtifacts.Exchange.compilerOutput.abi);
|
||||
// tslint:disable:no-unnecessary-type-assertion
|
||||
blockchainTests.resets('Coordinator Registry tests', env => {
|
||||
let coordinatorRegistry: CoordinatorRegistryContract;
|
||||
describe('Coordinator Registry tests', () => {
|
||||
let coordinatorOperator: string;
|
||||
const coordinatorEndpoint = 'http://sometec.0x.org';
|
||||
const nilCoordinatorEndpoint = '';
|
||||
let coordinatorRegistryWrapper: CoordinatorRegistryWrapper;
|
||||
// tests
|
||||
before(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
after(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
before(async () => {
|
||||
// setup accounts (skip owner)
|
||||
const accounts = await env.getAccountAddressesAsync();
|
||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
||||
[, coordinatorOperator] = accounts;
|
||||
// deploy coordinator registry
|
||||
coordinatorRegistry = await CoordinatorRegistryContract.deployFrom0xArtifactAsync(
|
||||
artifacts.CoordinatorRegistry,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
coordinatorRegistryWrapper = new CoordinatorRegistryWrapper(provider);
|
||||
await coordinatorRegistryWrapper.deployCoordinatorRegistryAsync();
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
afterEach(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
describe('core', () => {
|
||||
it('Should successfully set a Coordinator endpoint', async () => {
|
||||
await coordinatorRegistry.setCoordinatorEndpoint(coordinatorEndpoint).awaitTransactionSuccessAsync({
|
||||
from: coordinatorOperator,
|
||||
});
|
||||
const recordedCoordinatorEndpoint = await coordinatorRegistry
|
||||
.getCoordinatorEndpoint(coordinatorOperator)
|
||||
.callAsync();
|
||||
await coordinatorRegistryWrapper.setCoordinatorEndpointAsync(coordinatorOperator, coordinatorEndpoint);
|
||||
const recordedCoordinatorEndpoint = await coordinatorRegistryWrapper.getCoordinatorEndpointAsync(
|
||||
coordinatorOperator,
|
||||
);
|
||||
expect(recordedCoordinatorEndpoint).to.be.equal(coordinatorEndpoint);
|
||||
});
|
||||
it('Should successfully unset a Coordinator endpoint', async () => {
|
||||
// set Coordinator endpoint
|
||||
await coordinatorRegistry.setCoordinatorEndpoint(coordinatorEndpoint).awaitTransactionSuccessAsync({
|
||||
from: coordinatorOperator,
|
||||
});
|
||||
let recordedCoordinatorEndpoint = await coordinatorRegistry
|
||||
.getCoordinatorEndpoint(coordinatorOperator)
|
||||
.callAsync();
|
||||
await coordinatorRegistryWrapper.setCoordinatorEndpointAsync(coordinatorOperator, coordinatorEndpoint);
|
||||
let recordedCoordinatorEndpoint = await coordinatorRegistryWrapper.getCoordinatorEndpointAsync(
|
||||
coordinatorOperator,
|
||||
);
|
||||
expect(recordedCoordinatorEndpoint).to.be.equal(coordinatorEndpoint);
|
||||
// unset Coordinator endpoint
|
||||
await coordinatorRegistry.setCoordinatorEndpoint(nilCoordinatorEndpoint).awaitTransactionSuccessAsync({
|
||||
from: coordinatorOperator,
|
||||
});
|
||||
recordedCoordinatorEndpoint = await coordinatorRegistry
|
||||
.getCoordinatorEndpoint(coordinatorOperator)
|
||||
.callAsync();
|
||||
await coordinatorRegistryWrapper.setCoordinatorEndpointAsync(coordinatorOperator, nilCoordinatorEndpoint);
|
||||
recordedCoordinatorEndpoint = await coordinatorRegistryWrapper.getCoordinatorEndpointAsync(
|
||||
coordinatorOperator,
|
||||
);
|
||||
expect(recordedCoordinatorEndpoint).to.be.equal(nilCoordinatorEndpoint);
|
||||
});
|
||||
it('Should emit an event when setting Coordinator endpoint', async () => {
|
||||
// set Coordinator endpoint
|
||||
const txReceipt = await coordinatorRegistry
|
||||
.setCoordinatorEndpoint(coordinatorEndpoint)
|
||||
.awaitTransactionSuccessAsync({
|
||||
from: coordinatorOperator,
|
||||
});
|
||||
const recordedCoordinatorEndpoint = await coordinatorRegistry
|
||||
.getCoordinatorEndpoint(coordinatorOperator)
|
||||
.callAsync();
|
||||
expect(recordedCoordinatorEndpoint).to.be.equal(coordinatorEndpoint);
|
||||
// validate event
|
||||
const expectedEvent: CoordinatorRegistryCoordinatorEndpointSetEventArgs = {
|
||||
const txReceipt = await coordinatorRegistryWrapper.setCoordinatorEndpointAsync(
|
||||
coordinatorOperator,
|
||||
coordinatorEndpoint,
|
||||
};
|
||||
verifyEvents(txReceipt, [expectedEvent], 'CoordinatorEndpointSet');
|
||||
);
|
||||
const recordedCoordinatorEndpoint = await coordinatorRegistryWrapper.getCoordinatorEndpointAsync(
|
||||
coordinatorOperator,
|
||||
);
|
||||
expect(recordedCoordinatorEndpoint).to.be.equal(coordinatorEndpoint);
|
||||
// validate event
|
||||
expect(txReceipt.logs.length).to.be.equal(1);
|
||||
const log = txReceipt.logs[0] as LogWithDecodedArgs<CoordinatorRegistryCoordinatorEndpointSetEventArgs>;
|
||||
expect(log.args.coordinatorOperator).to.be.equal(coordinatorOperator);
|
||||
expect(log.args.coordinatorEndpoint).to.be.equal(coordinatorEndpoint);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -1,55 +1,79 @@
|
||||
import { blockchainTests, constants, expect, randomAddress, transactionHashUtils } from '@0x/contracts-test-utils';
|
||||
import { addressUtils, chaiSetup, constants, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils';
|
||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||
import { transactionHashUtils } from '@0x/order-utils';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as chai from 'chai';
|
||||
|
||||
import { hashUtils } from '../src/hash_utils';
|
||||
import { artifacts, CoordinatorContract, hashUtils } from '../src';
|
||||
|
||||
import { artifacts } from './artifacts';
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
|
||||
import { CoordinatorContract } from './wrappers';
|
||||
|
||||
blockchainTests.resets('Libs tests', env => {
|
||||
describe('Libs tests', () => {
|
||||
let coordinatorContract: CoordinatorContract;
|
||||
let chainId: number;
|
||||
const exchangeAddress = randomAddress();
|
||||
const exchangeAddress = addressUtils.generatePseudoRandomAddress();
|
||||
|
||||
before(async () => {
|
||||
chainId = await env.getChainIdAsync();
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
after(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
before(async () => {
|
||||
coordinatorContract = await CoordinatorContract.deployFrom0xArtifactAsync(
|
||||
artifacts.Coordinator,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
provider,
|
||||
txDefaults,
|
||||
artifacts,
|
||||
exchangeAddress,
|
||||
new BigNumber(chainId),
|
||||
);
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
afterEach(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
|
||||
describe('getTransactionHash', () => {
|
||||
it('should return the correct transaction hash', async () => {
|
||||
const tx = {
|
||||
verifyingContractAddress: exchangeAddress,
|
||||
salt: new BigNumber(0),
|
||||
signerAddress: constants.NULL_ADDRESS,
|
||||
data: '0x1234',
|
||||
};
|
||||
const expectedTxHash = transactionHashUtils.getTransactionHashHex(tx);
|
||||
const txHash = await coordinatorContract.getTransactionHash.callAsync(tx);
|
||||
expect(expectedTxHash).to.eq(txHash);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getApprovalHash', () => {
|
||||
it('should return the correct approval hash', async () => {
|
||||
const signedTx = {
|
||||
salt: constants.ZERO_AMOUNT,
|
||||
gasPrice: constants.ZERO_AMOUNT,
|
||||
expirationTimeSeconds: constants.ZERO_AMOUNT,
|
||||
verifyingContractAddress: exchangeAddress,
|
||||
salt: new BigNumber(0),
|
||||
signerAddress: constants.NULL_ADDRESS,
|
||||
data: '0x1234',
|
||||
signature: '0x5678',
|
||||
domain: {
|
||||
verifyingContract: exchangeAddress,
|
||||
chainId,
|
||||
},
|
||||
};
|
||||
const approvalExpirationTimeSeconds = new BigNumber(0);
|
||||
const txOrigin = constants.NULL_ADDRESS;
|
||||
const approval = {
|
||||
txOrigin,
|
||||
transactionHash: transactionHashUtils.getTransactionHashHex(signedTx),
|
||||
transactionSignature: signedTx.signature,
|
||||
approvalExpirationTimeSeconds,
|
||||
};
|
||||
const expectedApprovalHash = await hashUtils.getApprovalHashHexAsync(
|
||||
const expectedApprovalHash = hashUtils.getApprovalHashHex(
|
||||
signedTx,
|
||||
coordinatorContract.address,
|
||||
txOrigin,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const approvalHash = await coordinatorContract.getCoordinatorApprovalHash(approval).callAsync();
|
||||
const approvalHash = await coordinatorContract.getCoordinatorApprovalHash.callAsync(approval);
|
||||
expect(expectedApprovalHash).to.eq(approvalHash);
|
||||
});
|
||||
});
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,33 +1,37 @@
|
||||
import { hexConcat, signingUtils } from '@0x/contracts-test-utils';
|
||||
import { signingUtils } from '@0x/contracts-test-utils';
|
||||
import { SignatureType, SignedZeroExTransaction } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as ethUtil from 'ethereumjs-util';
|
||||
|
||||
import { hashUtils } from './hash_utils';
|
||||
import { SignedCoordinatorApproval } from './types';
|
||||
import { hashUtils, SignedCoordinatorApproval } from './index';
|
||||
|
||||
export class ApprovalFactory {
|
||||
private readonly _privateKey: Buffer;
|
||||
private readonly _verifyingContractAddress: string;
|
||||
|
||||
constructor(privateKey: Buffer, verifyingContract: string) {
|
||||
constructor(privateKey: Buffer, verifyingContractAddress: string) {
|
||||
this._privateKey = privateKey;
|
||||
this._verifyingContractAddress = verifyingContract;
|
||||
this._verifyingContractAddress = verifyingContractAddress;
|
||||
}
|
||||
|
||||
public async newSignedApprovalAsync(
|
||||
public newSignedApproval(
|
||||
transaction: SignedZeroExTransaction,
|
||||
txOrigin: string,
|
||||
approvalExpirationTimeSeconds: BigNumber,
|
||||
signatureType: SignatureType = SignatureType.EthSign,
|
||||
): Promise<SignedCoordinatorApproval> {
|
||||
const approvalHashBuff = await hashUtils.getApprovalHashBufferAsync(
|
||||
): SignedCoordinatorApproval {
|
||||
const approvalHashBuff = hashUtils.getApprovalHashBuffer(
|
||||
transaction,
|
||||
this._verifyingContractAddress,
|
||||
txOrigin,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const signatureBuff = signingUtils.signMessage(approvalHashBuff, this._privateKey, signatureType);
|
||||
const signedApproval = {
|
||||
txOrigin,
|
||||
transaction,
|
||||
signature: hexConcat(signatureBuff),
|
||||
approvalExpirationTimeSeconds,
|
||||
signature: ethUtil.addHexPrefix(signatureBuff.toString('hex')),
|
||||
};
|
||||
return signedApproval;
|
||||
}
|
12
contracts/coordinator/test/utils/constants.ts
Normal file
12
contracts/coordinator/test/utils/constants.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { BigNumber } from '@0x/utils';
|
||||
|
||||
export const constants = {
|
||||
SINGLE_FILL_FN_NAMES: ['fillOrder', 'fillOrKillOrder', 'fillOrderNoThrow'],
|
||||
BATCH_FILL_FN_NAMES: ['batchFillOrders', 'batchFillOrKillOrders', 'batchFillOrdersNoThrow'],
|
||||
MARKET_FILL_FN_NAMES: ['marketBuyOrders', 'marketBuyOrdersNoThrow', 'marketSellOrders', 'marketSellOrdersNoThrow'],
|
||||
MATCH_ORDERS: 'matchOrders',
|
||||
CANCEL_ORDER: 'cancelOrder',
|
||||
BATCH_CANCEL_ORDERS: 'batchCancelOrders',
|
||||
CANCEL_ORDERS_UP_TO: 'cancelOrdersUpTo',
|
||||
TIME_BUFFER: new BigNumber(1000),
|
||||
};
|
@@ -0,0 +1,65 @@
|
||||
import { LogDecoder, txDefaults } from '@0x/contracts-test-utils';
|
||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import { TransactionReceiptWithDecodedLogs, ZeroExProvider } from 'ethereum-types';
|
||||
|
||||
import { artifacts, CoordinatorRegistryContract } from '../../src';
|
||||
|
||||
export class CoordinatorRegistryWrapper {
|
||||
private readonly _web3Wrapper: Web3Wrapper;
|
||||
private readonly _provider: ZeroExProvider;
|
||||
private readonly _logDecoder: LogDecoder;
|
||||
private _coordinatorRegistryContract?: CoordinatorRegistryContract;
|
||||
/**
|
||||
* Instanitates an CoordinatorRegistryWrapper
|
||||
* @param provider Web3 provider to use for all JSON RPC requests
|
||||
* Instance of CoordinatorRegistryWrapper
|
||||
*/
|
||||
constructor(provider: ZeroExProvider) {
|
||||
this._web3Wrapper = new Web3Wrapper(provider);
|
||||
this._provider = provider;
|
||||
this._logDecoder = new LogDecoder(this._web3Wrapper, artifacts);
|
||||
}
|
||||
public async deployCoordinatorRegistryAsync(): Promise<CoordinatorRegistryContract> {
|
||||
this._coordinatorRegistryContract = await CoordinatorRegistryContract.deployFrom0xArtifactAsync(
|
||||
artifacts.CoordinatorRegistry,
|
||||
this._provider,
|
||||
txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
if (this._coordinatorRegistryContract === undefined) {
|
||||
throw new Error(`Failed to deploy Coordinator Registry contract.`);
|
||||
}
|
||||
return this._coordinatorRegistryContract;
|
||||
}
|
||||
public async setCoordinatorEndpointAsync(
|
||||
coordinatorOperator: string,
|
||||
coordinatorEndpoint: string,
|
||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||
this._assertCoordinatorRegistryDeployed();
|
||||
const txReceipt = await this._logDecoder.getTxWithDecodedLogsAsync(
|
||||
await (this
|
||||
._coordinatorRegistryContract as CoordinatorRegistryContract).setCoordinatorEndpoint.sendTransactionAsync(
|
||||
coordinatorEndpoint,
|
||||
{
|
||||
from: coordinatorOperator,
|
||||
},
|
||||
),
|
||||
);
|
||||
return txReceipt;
|
||||
}
|
||||
public async getCoordinatorEndpointAsync(coordinatorOperator: string): Promise<string> {
|
||||
this._assertCoordinatorRegistryDeployed();
|
||||
const coordinatorEndpoint = await (this
|
||||
._coordinatorRegistryContract as CoordinatorRegistryContract).getCoordinatorEndpoint.callAsync(
|
||||
coordinatorOperator,
|
||||
);
|
||||
return coordinatorEndpoint;
|
||||
}
|
||||
private _assertCoordinatorRegistryDeployed(): void {
|
||||
if (this._coordinatorRegistryContract === undefined) {
|
||||
throw new Error(
|
||||
'The Coordinator Registry contract was not deployed through the CoordinatorRegistryWrapper. Call `deployCoordinatorRegistryAsync` to deploy.',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
47
contracts/coordinator/test/utils/exchange_data_encoder.ts
Normal file
47
contracts/coordinator/test/utils/exchange_data_encoder.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { IExchangeContract } from '@0x/contracts-exchange';
|
||||
import { constants as devConstants, provider } from '@0x/contracts-test-utils';
|
||||
import { SignedOrder } from '@0x/types';
|
||||
|
||||
import { constants } from './index';
|
||||
|
||||
export const exchangeDataEncoder = {
|
||||
encodeOrdersToExchangeData(fnName: string, orders: SignedOrder[]): string {
|
||||
const exchangeInstance = new IExchangeContract(devConstants.NULL_ADDRESS, provider);
|
||||
let data;
|
||||
if (constants.SINGLE_FILL_FN_NAMES.indexOf(fnName) !== -1) {
|
||||
data = (exchangeInstance as any)[fnName].getABIEncodedTransactionData(
|
||||
orders[0],
|
||||
orders[0].takerAssetAmount,
|
||||
orders[0].signature,
|
||||
);
|
||||
} else if (constants.BATCH_FILL_FN_NAMES.indexOf(fnName) !== -1) {
|
||||
data = (exchangeInstance as any)[fnName].getABIEncodedTransactionData(
|
||||
orders,
|
||||
orders.map(order => order.takerAssetAmount),
|
||||
orders.map(order => order.signature),
|
||||
);
|
||||
} else if (constants.MARKET_FILL_FN_NAMES.indexOf(fnName) !== -1) {
|
||||
data = (exchangeInstance as any)[fnName].getABIEncodedTransactionData(
|
||||
orders,
|
||||
orders.map(order => order.takerAssetAmount).reduce((prev, curr) => prev.plus(curr)),
|
||||
orders.map(order => order.signature),
|
||||
);
|
||||
} else if (fnName === constants.MATCH_ORDERS) {
|
||||
data = exchangeInstance.matchOrders.getABIEncodedTransactionData(
|
||||
orders[0],
|
||||
orders[1],
|
||||
orders[0].signature,
|
||||
orders[1].signature,
|
||||
);
|
||||
} else if (fnName === constants.CANCEL_ORDER) {
|
||||
data = exchangeInstance.cancelOrder.getABIEncodedTransactionData(orders[0]);
|
||||
} else if (fnName === constants.BATCH_CANCEL_ORDERS) {
|
||||
data = exchangeInstance.batchCancelOrders.getABIEncodedTransactionData(orders);
|
||||
} else if (fnName === constants.CANCEL_ORDERS_UP_TO) {
|
||||
data = exchangeInstance.cancelOrdersUpTo.getABIEncodedTransactionData(devConstants.ZERO_AMOUNT);
|
||||
} else {
|
||||
throw new Error(`Error: ${fnName} not a supported function`);
|
||||
}
|
||||
return data;
|
||||
},
|
||||
};
|
33
contracts/coordinator/test/utils/hash_utils.ts
Normal file
33
contracts/coordinator/test/utils/hash_utils.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { eip712Utils } from '@0x/order-utils';
|
||||
import { SignedZeroExTransaction } from '@0x/types';
|
||||
import { BigNumber, signTypedDataUtils } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
export const hashUtils = {
|
||||
getApprovalHashBuffer(
|
||||
transaction: SignedZeroExTransaction,
|
||||
verifyingContractAddress: string,
|
||||
txOrigin: string,
|
||||
approvalExpirationTimeSeconds: BigNumber,
|
||||
): Buffer {
|
||||
const typedData = eip712Utils.createCoordinatorApprovalTypedData(
|
||||
transaction,
|
||||
verifyingContractAddress,
|
||||
txOrigin,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const hashBuffer = signTypedDataUtils.generateTypedDataHash(typedData);
|
||||
return hashBuffer;
|
||||
},
|
||||
getApprovalHashHex(
|
||||
transaction: SignedZeroExTransaction,
|
||||
verifyingContractAddress: string,
|
||||
txOrigin: string,
|
||||
approvalExpirationTimeSeconds: BigNumber,
|
||||
): string {
|
||||
const hashHex = `0x${hashUtils
|
||||
.getApprovalHashBuffer(transaction, verifyingContractAddress, txOrigin, approvalExpirationTimeSeconds)
|
||||
.toString('hex')}`;
|
||||
return hashHex;
|
||||
},
|
||||
};
|
5
contracts/coordinator/test/utils/index.ts
Normal file
5
contracts/coordinator/test/utils/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export { hashUtils } from './hash_utils';
|
||||
export { ApprovalFactory } from './approval_factory';
|
||||
export { constants } from './constants';
|
||||
export { exchangeDataEncoder } from './exchange_data_encoder';
|
||||
export * from './types';
|
@@ -1,8 +1,10 @@
|
||||
import { SignedZeroExTransaction } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
|
||||
export interface CoordinatorApproval {
|
||||
transaction: SignedZeroExTransaction;
|
||||
txOrigin: string;
|
||||
approvalExpirationTimeSeconds: BigNumber;
|
||||
}
|
||||
|
||||
export interface SignedCoordinatorApproval extends CoordinatorApproval {
|
@@ -1,19 +0,0 @@
|
||||
/*
|
||||
* -----------------------------------------------------------------------------
|
||||
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
export * from '../test/generated-wrappers/coordinator';
|
||||
export * from '../test/generated-wrappers/coordinator_registry';
|
||||
export * from '../test/generated-wrappers/i_coordinator_approval_verifier';
|
||||
export * from '../test/generated-wrappers/i_coordinator_core';
|
||||
export * from '../test/generated-wrappers/i_coordinator_registry_core';
|
||||
export * from '../test/generated-wrappers/i_coordinator_signature_validator';
|
||||
export * from '../test/generated-wrappers/lib_constants';
|
||||
export * from '../test/generated-wrappers/lib_coordinator_approval';
|
||||
export * from '../test/generated-wrappers/lib_coordinator_rich_errors';
|
||||
export * from '../test/generated-wrappers/lib_e_i_p712_coordinator_domain';
|
||||
export * from '../test/generated-wrappers/mixin_coordinator_approval_verifier';
|
||||
export * from '../test/generated-wrappers/mixin_coordinator_core';
|
||||
export * from '../test/generated-wrappers/mixin_coordinator_registry_core';
|
||||
export * from '../test/generated-wrappers/mixin_signature_validator';
|
@@ -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: 'constantinople',
|
||||
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