Compare commits
2820 Commits
@0x/react-
...
monorepo@c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c60d1e50c5 | ||
|
|
501f5ad3de | ||
|
|
9fe6c196ad | ||
|
|
6d462b0598 | ||
|
|
3832c66ad0 | ||
|
|
57028069c0 | ||
|
|
8d84ac9cf8 | ||
|
|
17219d22c3 | ||
|
|
bf4005b0ee | ||
|
|
d8605ed91d | ||
|
|
f18e2a09e6 | ||
|
|
d5c0f5aa47 | ||
|
|
2825a201cd | ||
|
|
2d69afec86 | ||
|
|
851e4bfbea | ||
|
|
c63745c2ce | ||
|
|
95f11f2d0e | ||
|
|
30417fbf24 | ||
|
|
06f095ca23 | ||
|
|
30141ca6cf | ||
|
|
c4b69efdd1 | ||
|
|
d810aff337 | ||
|
|
83532fdae7 | ||
|
|
043534dd56 | ||
|
|
a90c808fb4 | ||
|
|
a06c39df9f | ||
|
|
24c3aefb6f | ||
|
|
9544d317a0 | ||
|
|
506d27816e | ||
|
|
f5a0c87fdc | ||
|
|
90541f0436 | ||
|
|
76cac53692 | ||
|
|
18366bd58d | ||
|
|
27c039d51c | ||
|
|
8465be2a03 | ||
|
|
7d6f49172d | ||
|
|
d15f4de4ae | ||
|
|
4098238019 | ||
|
|
1356237ec9 | ||
|
|
35daecd5ae | ||
|
|
bb87c8e7b5 | ||
|
|
48f7a24505 | ||
|
|
98c59091ab | ||
|
|
4ca08adcfa | ||
|
|
29bcc1b5b7 | ||
|
|
6e4186adbe | ||
|
|
38e540ad9f | ||
|
|
7762b7b665 | ||
|
|
82ac8e29e3 | ||
|
|
e074335285 | ||
|
|
3adbe843da | ||
|
|
d5c8c076dc | ||
|
|
1d68c4105a | ||
|
|
8077123e9f | ||
|
|
0ff8b12770 | ||
|
|
39a8c0f4e6 | ||
|
|
8c5e12d389 | ||
|
|
71660850af | ||
|
|
b83043648e | ||
|
|
b01de802cb | ||
|
|
e954e9ca20 | ||
|
|
3f8639bd9c | ||
|
|
56c956df44 | ||
|
|
c4b621e44d | ||
|
|
9a658bf932 | ||
|
|
d6d506d1c5 | ||
|
|
2c8f7fac0e | ||
|
|
072ff65bf9 | ||
|
|
aa198ad15f | ||
|
|
745da8e363 | ||
|
|
be173a9970 | ||
|
|
8b2b500414 | ||
|
|
98d1f5405a | ||
|
|
03595dd1dd | ||
|
|
6da48be1a4 | ||
|
|
9b91d574f8 | ||
|
|
6ca8edbf19 | ||
|
|
579dba1473 | ||
|
|
1b5fa15c8c | ||
|
|
737ad586a5 | ||
|
|
c051e11a49 | ||
|
|
a5692690dc | ||
|
|
98106ca8a2 | ||
|
|
891d685951 | ||
|
|
991cbc9f4e | ||
|
|
8006e4fe3b | ||
|
|
f17c6b0a83 | ||
|
|
57ca601be1 | ||
|
|
b50e26dc2a | ||
|
|
b728d13d8c | ||
|
|
1959d149f8 | ||
|
|
3e2e05caf2 | ||
|
|
d6a4d67a14 | ||
|
|
cf8d424b9b | ||
|
|
a9f1237208 | ||
|
|
319b4dfd75 | ||
|
|
d39fcd3475 | ||
|
|
c7b7f57ab2 | ||
|
|
a36a5366d3 | ||
|
|
87615025fe | ||
|
|
5b0fc813c4 | ||
|
|
d21f978def | ||
|
|
a9ecf7f1f3 | ||
|
|
f5ad65bb8a | ||
|
|
88dcf6350d | ||
|
|
6cfc17de35 | ||
|
|
a2d105593e | ||
|
|
efd5eff22c | ||
|
|
9282684d93 | ||
|
|
c9e03f7d42 | ||
|
|
690b31c9be | ||
|
|
cb20f03a92 | ||
|
|
97eabc6c03 | ||
|
|
f0e0f08e0c | ||
|
|
470643ee07 | ||
|
|
8ba38b9b29 | ||
|
|
9870f55d24 | ||
|
|
23b724dde4 | ||
|
|
3fa922dbab | ||
|
|
aa7f082d56 | ||
|
|
b178d025b5 | ||
|
|
e333ab18c7 | ||
|
|
b60db6ac72 | ||
|
|
96c8da9fdd | ||
|
|
fca883a4aa | ||
|
|
e60d43110d | ||
|
|
b9b135cfa2 | ||
|
|
f925c35344 | ||
|
|
ca2e9bed27 | ||
|
|
5b77e2c8ac | ||
|
|
9e3331d018 | ||
|
|
4440075425 | ||
|
|
626948774b | ||
|
|
25cb1c1138 | ||
|
|
4a173deb27 | ||
|
|
245c87f026 | ||
|
|
22a6de48ae | ||
|
|
a347c1e848 | ||
|
|
e4ab832ced | ||
|
|
d07005dcbe | ||
|
|
dc06497cae | ||
|
|
c1871b5bca | ||
|
|
410b9c50d3 | ||
|
|
1c42d0ab3c | ||
|
|
98698f702f | ||
|
|
4784131dca | ||
|
|
6eb28b792a | ||
|
|
57f5b12e24 | ||
|
|
e5aaf68277 | ||
|
|
b62486923f | ||
|
|
322a0a5967 | ||
|
|
f30c2c4a23 | ||
|
|
44e5fa5b25 | ||
|
|
18a86ef234 | ||
|
|
a6af3744e2 | ||
|
|
9b40164787 | ||
|
|
52e8cf1366 | ||
|
|
1b159f5ccc | ||
|
|
893ae35296 | ||
|
|
85e56706bf | ||
|
|
b8b21cefe3 | ||
|
|
6d7bf12ade | ||
|
|
5266816dd6 | ||
|
|
0c5f0271c7 | ||
|
|
b65fd06e95 | ||
|
|
7f51822bfc | ||
|
|
1acd8d9577 | ||
|
|
8aa302f6fc | ||
|
|
f8af5879af | ||
|
|
a7aa7feff4 | ||
|
|
b6a96cea23 | ||
|
|
50b02a4a55 | ||
|
|
8a2b178e6f | ||
|
|
7f1afb57b0 | ||
|
|
0bc4b50818 | ||
|
|
91de2194eb | ||
|
|
fef06908ec | ||
|
|
a012eb0cae | ||
|
|
08c805a489 | ||
|
|
5b595dd080 | ||
|
|
c29a22187c | ||
|
|
44f6d21e9b | ||
|
|
ef04248191 | ||
|
|
1b47c473b4 | ||
|
|
fe1fea9a2d | ||
|
|
970f77beb0 | ||
|
|
2587cd380f | ||
|
|
04eab19f15 | ||
|
|
abb2b46ed3 | ||
|
|
db241e8f90 | ||
|
|
5bbd57d236 | ||
|
|
e9f0f4af86 | ||
|
|
ae75aed55e | ||
|
|
b359738037 | ||
|
|
5ce988957f | ||
|
|
2d77fce99d | ||
|
|
62663ed6d2 | ||
|
|
3965d8f8c6 | ||
|
|
fd35249de8 | ||
|
|
156560ae22 | ||
|
|
ee687a7dc4 | ||
|
|
1710f13242 | ||
|
|
ef645e601c | ||
|
|
7de23c6af2 | ||
|
|
639026ea66 | ||
|
|
8ddcf88c01 | ||
|
|
3883297991 | ||
|
|
196cc4313f | ||
|
|
9dd8c61a2f | ||
|
|
6d20f0e987 | ||
|
|
eac4520406 | ||
|
|
e4126189df | ||
|
|
aa4ee2c166 | ||
|
|
c72a15b488 | ||
|
|
6a29654d7d | ||
|
|
3ad7728a0e | ||
|
|
14c4491b8c | ||
|
|
2eff213840 | ||
|
|
2bb9b9a8f7 | ||
|
|
d064543108 | ||
|
|
0270777cfc | ||
|
|
86106713dd | ||
|
|
d5bbbe802b | ||
|
|
b43fa88606 | ||
|
|
54ac1c284b | ||
|
|
d33080cf08 | ||
|
|
b4b6d4d969 | ||
|
|
993f05d5ac | ||
|
|
ac7f6aef9e | ||
|
|
7fb5ed0b42 | ||
|
|
52b0ba5b05 | ||
|
|
a43b494302 | ||
|
|
527ec28915 | ||
|
|
e267a0e855 | ||
|
|
0196ce18f3 | ||
|
|
7ef3c12722 | ||
|
|
38b94ec5f8 | ||
|
|
fa65452e2b | ||
|
|
da0f6b5e8f | ||
|
|
b4929df1e6 | ||
|
|
03c59fdaf7 | ||
|
|
f5ab1e6f86 | ||
|
|
ada1de429c | ||
|
|
a1aad2e55e | ||
|
|
d548ddac0d | ||
|
|
58a5ab4550 | ||
|
|
6a8242a6ca | ||
|
|
46b8bfe338 | ||
|
|
102ca6b854 | ||
|
|
06b4d241af | ||
|
|
712b2569e6 | ||
|
|
294be37afc | ||
|
|
b57c0a2ebb | ||
|
|
94909f1a0f | ||
|
|
9c47d22ff4 | ||
|
|
e4b9d14f45 | ||
|
|
73f1aca4a1 | ||
|
|
bbae6b3de2 | ||
|
|
e5133a2dd9 | ||
|
|
ef0096b7d9 | ||
|
|
29f4d6918a | ||
|
|
45c7653850 | ||
|
|
8bdd1d7680 | ||
|
|
a1ed7183ea | ||
|
|
7b81af2cb4 | ||
|
|
a70a3c9600 | ||
|
|
a14ddbfac2 | ||
|
|
e1c57cf0af | ||
|
|
522994262d | ||
|
|
f681357eeb | ||
|
|
f9b593da59 | ||
|
|
a340c817c9 | ||
|
|
cc67f732e1 | ||
|
|
e2ee7e6837 | ||
|
|
49d223f344 | ||
|
|
314d1b9873 | ||
|
|
2a391bf947 | ||
|
|
7a33f68138 | ||
|
|
48d0b46e43 | ||
|
|
5fe9edce8c | ||
|
|
1c038e1f3c | ||
|
|
a57dd427ca | ||
|
|
2253f214a6 | ||
|
|
ad11dc2421 | ||
|
|
bd3b200b30 | ||
|
|
0d259d13b9 | ||
|
|
6fd55b2f49 | ||
|
|
057aee8ad2 | ||
|
|
f47feabb4a | ||
|
|
eb784a4a7c | ||
|
|
5d30c957cb | ||
|
|
585adef75d | ||
|
|
2f07fcc81c | ||
|
|
1d55d12c8f | ||
|
|
bca8c5eccc | ||
|
|
f7462c9f2b | ||
|
|
549697dc47 | ||
|
|
2869dd3bac | ||
|
|
5a225795e1 | ||
|
|
877abeda63 | ||
|
|
768387fea9 | ||
|
|
db97fe8164 | ||
|
|
be1a70c461 | ||
|
|
b631fc610b | ||
|
|
9b2672841d | ||
|
|
336e8bafb4 | ||
|
|
2ea354f748 | ||
|
|
2d125cdc20 | ||
|
|
0d441a829f | ||
|
|
ee5cb6909c | ||
|
|
5258053dc8 | ||
|
|
bb46f184ed | ||
|
|
7cc1304eca | ||
|
|
94738444de | ||
|
|
4705b15188 | ||
|
|
de567da846 | ||
|
|
6641af2a58 | ||
|
|
de9527ce2f | ||
|
|
16ebdfad9a | ||
|
|
b70db37b4f | ||
|
|
3843c64c40 | ||
|
|
1d1dd4b6a2 | ||
|
|
d3a9ace5fd | ||
|
|
778c57320c | ||
|
|
f98f3660f9 | ||
|
|
fd4141e1f3 | ||
|
|
97c107be3e | ||
|
|
2eada9db62 | ||
|
|
e9362439c3 | ||
|
|
a2419ab31d | ||
|
|
e589f10e23 | ||
|
|
5d84d40a2c | ||
|
|
43d1d0b217 | ||
|
|
0f802d5a1b | ||
|
|
56efde6e34 | ||
|
|
e224e6cde5 | ||
|
|
e1d51bae73 | ||
|
|
f9163ccc01 | ||
|
|
6353bf545d | ||
|
|
12f0797ace | ||
|
|
c9de423fb8 | ||
|
|
2cd0990c65 | ||
|
|
6fd9308e1b | ||
|
|
1fc57baac1 | ||
|
|
2ed63970d4 | ||
|
|
7ba6c601e5 | ||
|
|
cc43c5b28c | ||
|
|
b7f25ee3b6 | ||
|
|
82afdda256 | ||
|
|
be83789bee | ||
|
|
036c8fe920 | ||
|
|
b7b125f623 | ||
|
|
060edf33bd | ||
|
|
656120cd1f | ||
|
|
34acd71835 | ||
|
|
7eb4bebac3 | ||
|
|
f45ee486e9 | ||
|
|
3b28c9d5a7 | ||
|
|
2ed39cd18d | ||
|
|
d8d791e4f0 | ||
|
|
89bd42de04 | ||
|
|
51b460d432 | ||
|
|
6410366f8b | ||
|
|
1c2f4906e6 | ||
|
|
bed90fa8ec | ||
|
|
25787ea806 | ||
|
|
2a21d87193 | ||
|
|
7cee17887a | ||
|
|
0cc94bcf19 | ||
|
|
a554ae904f | ||
|
|
9dcda6113d | ||
|
|
7d50117903 | ||
|
|
76c5517739 | ||
|
|
b9d243e70e | ||
|
|
d37679c129 | ||
|
|
d01cfee455 | ||
|
|
182d360302 | ||
|
|
0d9069ecfe | ||
|
|
6488f91e6e | ||
|
|
70db4d8847 | ||
|
|
faa0d83013 | ||
|
|
6f1f226ed0 | ||
|
|
1d6406bbd6 | ||
|
|
91cee9c648 | ||
|
|
6d83b2676e | ||
|
|
c21bce9641 | ||
|
|
1d5c175316 | ||
|
|
2fdd4e9760 | ||
|
|
abd479dc68 | ||
|
|
67ef17f929 | ||
|
|
35bf179b70 | ||
|
|
10c62c10aa | ||
|
|
8317628c61 | ||
|
|
30fee43928 | ||
|
|
494dc475c1 | ||
|
|
3a503c61b3 | ||
|
|
86a28f0d19 | ||
|
|
24af39d4a8 | ||
|
|
97773e3f64 | ||
|
|
1dc1218bfc | ||
|
|
b458026358 | ||
|
|
b30a33eef5 | ||
|
|
0e96a1c8ba | ||
|
|
f477c0fcc2 | ||
|
|
2ad6dd1ee8 | ||
|
|
87cfe1a8c6 | ||
|
|
d0c6d9cf2d | ||
|
|
fc7f2e7fc6 | ||
|
|
49baafadc1 | ||
|
|
24e62feadf | ||
|
|
67079d96af | ||
|
|
5ac4c72f1a | ||
|
|
deceed37f3 | ||
|
|
4bc84cd526 | ||
|
|
293c428186 | ||
|
|
d1b004ffc1 | ||
|
|
da83f75a13 | ||
|
|
b5be162fa2 | ||
|
|
7e5e2241cb | ||
|
|
c0cb78bb3f | ||
|
|
eb6ad7d29d | ||
|
|
88e56356c4 | ||
|
|
356660ad4f | ||
|
|
7c3567f5e7 | ||
|
|
e9eb3badd9 | ||
|
|
9bbbaadcf8 | ||
|
|
19f44fac1e | ||
|
|
0be2c250ef | ||
|
|
b07fc95c81 | ||
|
|
20ba23fe5f | ||
|
|
b78705120e | ||
|
|
f601329a47 | ||
|
|
2b3e7e7ab7 | ||
|
|
8d5e28f099 | ||
|
|
cb1dc92594 | ||
|
|
495bf08498 | ||
|
|
a1a5bdce78 | ||
|
|
f724212fd7 | ||
|
|
5ccbe167a1 | ||
|
|
2e357ffeab | ||
|
|
af10f52acf | ||
|
|
ed8a6bb97b | ||
|
|
c774b98002 | ||
|
|
9a63bea763 | ||
|
|
0c6a6743ab | ||
|
|
1c37334b18 | ||
|
|
7f40665a0e | ||
|
|
b10036444d | ||
|
|
0542c70d22 | ||
|
|
f71484c9f0 | ||
|
|
2cf74a7a96 | ||
|
|
018bcf273f | ||
|
|
1a3da4b363 | ||
|
|
0999805b3a | ||
|
|
7b5e3dab17 | ||
|
|
a09cd03ce6 | ||
|
|
c1fc454d19 | ||
|
|
93c8284a96 | ||
|
|
e5dcf9063d | ||
|
|
cf35a8032d | ||
|
|
88736aa82b | ||
|
|
90ac5ec577 | ||
|
|
d1eb414749 | ||
|
|
f792d403e5 | ||
|
|
e5706606a0 | ||
|
|
49725c8c33 | ||
|
|
dfcc0c6d09 | ||
|
|
d806701d28 | ||
|
|
3935e661fe | ||
|
|
cb8cf1f107 | ||
|
|
ea8669439f | ||
|
|
75a8b1c081 | ||
|
|
73144fa4d5 | ||
|
|
a6b60f3230 | ||
|
|
48dfb3317a | ||
|
|
b3b0496c49 | ||
|
|
e880447714 | ||
|
|
fd4d10e7a4 | ||
|
|
9974e10069 | ||
|
|
18b65a61ff | ||
|
|
02a1e17f50 | ||
|
|
9a3a302754 | ||
|
|
d131c39e46 | ||
|
|
8231e7703e | ||
|
|
406a78a11a | ||
|
|
fe01a150f0 | ||
|
|
dd499591e9 | ||
|
|
c0f1e5f17f | ||
|
|
b888e48a30 | ||
|
|
8410ee9d2f | ||
|
|
b7238c702b | ||
|
|
a5996b37b2 | ||
|
|
b20503c5a2 | ||
|
|
d0869a8840 | ||
|
|
c156bfc534 | ||
|
|
5f8e092c96 | ||
|
|
87c9f9af71 | ||
|
|
65e5ecf49d | ||
|
|
63f051a9d2 | ||
|
|
2c1393fb09 | ||
|
|
dba0d8469d | ||
|
|
75e6c45285 | ||
|
|
e64e0d7421 | ||
|
|
13d5a5e2ec | ||
|
|
3432083343 | ||
|
|
df4282fb34 | ||
|
|
861aebb2e3 | ||
|
|
cc7b8359b4 | ||
|
|
dd0d848530 | ||
|
|
7f17033ce3 | ||
|
|
3a4e72bb08 | ||
|
|
bf3751fd9e | ||
|
|
05eb646848 | ||
|
|
5fe231b689 | ||
|
|
5ee7c2f9dc | ||
|
|
2c970a0466 | ||
|
|
c688b11c86 | ||
|
|
749c0354b3 | ||
|
|
415af90ae7 | ||
|
|
365cb161cf | ||
|
|
a9857fa298 | ||
|
|
59ae8d4b86 | ||
|
|
b81f6ba685 | ||
|
|
16c9d00494 | ||
|
|
1a833d9dfb | ||
|
|
bde6278781 | ||
|
|
e7c4d2171f | ||
|
|
3a096ff0b4 | ||
|
|
9feac6708a | ||
|
|
aabca97b2d | ||
|
|
9fb933fd06 | ||
|
|
df039f05c2 | ||
|
|
d1bed5729d | ||
|
|
c926a586d2 | ||
|
|
b7397bbb8f | ||
|
|
9b957524a5 | ||
|
|
078b1af04e | ||
|
|
6827ebfb78 | ||
|
|
1d807abe8b | ||
|
|
2c15b3f9bd | ||
|
|
0d5e037081 | ||
|
|
dbda3a04b2 | ||
|
|
98e5b26eb7 | ||
|
|
cd1fc6a1f0 | ||
|
|
52ef745f7c | ||
|
|
961b09977f | ||
|
|
df8419cd9e | ||
|
|
71acf2bfa7 | ||
|
|
1400ceb4e8 | ||
|
|
793e338dd3 | ||
|
|
020e7609c3 | ||
|
|
b1c2f66126 | ||
|
|
8ef0a59b98 | ||
|
|
798fb183a5 | ||
|
|
6bb3992c2f | ||
|
|
830d6f726e | ||
|
|
8f8c16bd0e | ||
|
|
76c0708cf2 | ||
|
|
5e51233b49 | ||
|
|
890bfd18fa | ||
|
|
37cc948741 | ||
|
|
edb923b8bb | ||
|
|
44753bb168 | ||
|
|
7b96fa8d76 | ||
|
|
ca35eed955 | ||
|
|
eb6637afd5 | ||
|
|
a114bbb30e | ||
|
|
9d38bf731f | ||
|
|
f32732db1c | ||
|
|
f41a29ce55 | ||
|
|
2b1e0be4fc | ||
|
|
23dd711396 | ||
|
|
59369cea2a | ||
|
|
e6b81a824d | ||
|
|
1dd216b566 | ||
|
|
27e2a76110 | ||
|
|
47da97137f | ||
|
|
2134537bc3 | ||
|
|
67f91269ee | ||
|
|
57338059e1 | ||
|
|
c1ed836fda | ||
|
|
cd147dbc41 | ||
|
|
0253bba83b | ||
|
|
d845b318b9 | ||
|
|
4b970905cf | ||
|
|
907771f084 | ||
|
|
1724ecd4c3 | ||
|
|
74b9ad5536 | ||
|
|
4f1525fe27 | ||
|
|
4dc7956b56 | ||
|
|
453bf4d195 | ||
|
|
249948e787 | ||
|
|
c0acc8dfdf | ||
|
|
00e87864b1 | ||
|
|
697e5df52d | ||
|
|
ba3cd454ba | ||
|
|
de26925c13 | ||
|
|
47e00ff1a7 | ||
|
|
d106051ee3 | ||
|
|
c939fe2287 | ||
|
|
41b372ffe6 | ||
|
|
5826825d11 | ||
|
|
a765e47dca | ||
|
|
de8b032df9 | ||
|
|
f1b1eb3b58 | ||
|
|
1f334d29ae | ||
|
|
3279d2a803 | ||
|
|
cd14d1ef0f | ||
|
|
9eb676fb46 | ||
|
|
6c82ebe956 | ||
|
|
3922d02910 | ||
|
|
6da70cfa0d | ||
|
|
15c8e06129 | ||
|
|
93506a4e27 | ||
|
|
508e927d63 | ||
|
|
ee969261b4 | ||
|
|
2a5742c12d | ||
|
|
598d70c6dc | ||
|
|
a49e47f34b | ||
|
|
ad4d869137 | ||
|
|
4ef86e6128 | ||
|
|
470036f6cb | ||
|
|
835ab6ddd9 | ||
|
|
b0b387013c | ||
|
|
3e6cae0ca0 | ||
|
|
93844343de | ||
|
|
42430290d5 | ||
|
|
ce15b4c678 | ||
|
|
35f4e2fb4f | ||
|
|
501f6cbab9 | ||
|
|
a724dd98a9 | ||
|
|
d5249425af | ||
|
|
44c44a2b9c | ||
|
|
eb1c48674a | ||
|
|
b44ab72557 | ||
|
|
e6a33dea0e | ||
|
|
217811d0af | ||
|
|
ab3246cc71 | ||
|
|
829533d501 | ||
|
|
8881118a15 | ||
|
|
3f2be5b2da | ||
|
|
9f1904ad3d | ||
|
|
09843c3cf1 | ||
|
|
303279a766 | ||
|
|
f560e7fa96 | ||
|
|
c97c6d1fc2 | ||
|
|
b53bf051ac | ||
|
|
d5189e6143 | ||
|
|
a934c71ccd | ||
|
|
5147b6e699 | ||
|
|
9294bf40a7 | ||
|
|
a02f96c913 | ||
|
|
b756e723ea | ||
|
|
8bc1d5fe3e | ||
|
|
180417b581 | ||
|
|
786655843b | ||
|
|
5e3eeed10f | ||
|
|
8a2df9cd1f | ||
|
|
74d9891e06 | ||
|
|
e0ff859e0d | ||
|
|
2c7efd0b97 | ||
|
|
99a0835ecc | ||
|
|
0bb227a79b | ||
|
|
84e7357960 | ||
|
|
9d0b94305a | ||
|
|
8343105b54 | ||
|
|
38cad56bf9 | ||
|
|
8f291c19c3 | ||
|
|
9566188d6b | ||
|
|
cae8c2013d | ||
|
|
9bc5efe958 | ||
|
|
b36003896f | ||
|
|
4d2ba9f1e6 | ||
|
|
56184c6f4f | ||
|
|
1f64f9eae6 | ||
|
|
7aeeaae015 | ||
|
|
03142d82bc | ||
|
|
78805b1c39 | ||
|
|
e1a5ba9864 | ||
|
|
bc0140ef3a | ||
|
|
36b76550e0 | ||
|
|
1f2e94b585 | ||
|
|
e2a2f932f1 | ||
|
|
6ca2f7e3ac | ||
|
|
f3309d3651 | ||
|
|
0b1d955a9f | ||
|
|
fb75fa4e9a | ||
|
|
0ba8690120 | ||
|
|
cafa3c827c | ||
|
|
bbacce2986 | ||
|
|
ead2d26025 | ||
|
|
7d89449f2d | ||
|
|
b3d1b6c499 | ||
|
|
7d85e61cc5 | ||
|
|
362a8c8fc5 | ||
|
|
6041fb0445 | ||
|
|
b2a7e0536e | ||
|
|
2d39454ce1 | ||
|
|
16de8bf26c | ||
|
|
bb7cecd7c1 | ||
|
|
8e41cc7651 | ||
|
|
55238b9669 | ||
|
|
316ef69074 | ||
|
|
8c839b5c22 | ||
|
|
f98e1d75f4 | ||
|
|
c57d17dc58 | ||
|
|
938f4d2d9d | ||
|
|
307c38bd16 | ||
|
|
202dcfb4c5 | ||
|
|
8dd74bcf82 | ||
|
|
07acdc26ff | ||
|
|
de307bf25a | ||
|
|
b6c4f533d2 | ||
|
|
cbf41e6ade | ||
|
|
b1d98a4183 | ||
|
|
30db88d27b | ||
|
|
fc9d5dee5e | ||
|
|
bb0ada3f59 | ||
|
|
804256075e | ||
|
|
79f28f121b | ||
|
|
717a19a08e | ||
|
|
9c8716da09 | ||
|
|
8293784629 | ||
|
|
a17f123608 | ||
|
|
561fe9c3ea | ||
|
|
e645aa1ee5 | ||
|
|
7f86d2c5fa | ||
|
|
fae14a755f | ||
|
|
1c14948f8a | ||
|
|
6a902eff56 | ||
|
|
c6192ea953 | ||
|
|
36cf4ad304 | ||
|
|
bcfabf18bc | ||
|
|
f15693af1d | ||
|
|
64e42d18e2 | ||
|
|
e2a76c621b | ||
|
|
19f6a8dcfe | ||
|
|
3c4cfe8aee | ||
|
|
43173c1aac | ||
|
|
95b284d648 | ||
|
|
9787cf8296 | ||
|
|
93b57445b6 | ||
|
|
c4ca72cf22 | ||
|
|
13d2cca2bc | ||
|
|
7fe8eac511 | ||
|
|
c062458188 | ||
|
|
3517dd2741 | ||
|
|
7407890deb | ||
|
|
e9a4b0758b | ||
|
|
abf076fc05 | ||
|
|
c344625d0d | ||
|
|
14630465dd | ||
|
|
a497ddfad2 | ||
|
|
34f6facdee | ||
|
|
c3bff31cc4 | ||
|
|
e4475c08e8 | ||
|
|
75a4d129f7 | ||
|
|
c659477358 | ||
|
|
43f38d02ad | ||
|
|
6b8bc55c74 | ||
|
|
0c53e2fe46 | ||
|
|
edef3bc30e | ||
|
|
0c7f09b832 | ||
|
|
34d075ce8c | ||
|
|
8c06d660ea | ||
|
|
748566b4fb | ||
|
|
fb38867e78 | ||
|
|
a2613625c6 | ||
|
|
89f1d54ebc | ||
|
|
740913fa20 | ||
|
|
fd2a240c9f | ||
|
|
96bef08ac2 | ||
|
|
1698519a6a | ||
|
|
d8372f73bc | ||
|
|
fb6e8a4608 | ||
|
|
a3f6160898 | ||
|
|
66f175b659 | ||
|
|
755ef35955 | ||
|
|
b5d6156ffa | ||
|
|
8dd8cf8673 | ||
|
|
9e46099ced | ||
|
|
9f4fe259f9 | ||
|
|
3c169388e2 | ||
|
|
b9e75769a3 | ||
|
|
17a9edd8c3 | ||
|
|
090b83a237 | ||
|
|
7796c88be3 | ||
|
|
e5ee794895 | ||
|
|
2b572cc28f | ||
|
|
88544ae0ef | ||
|
|
146d56be84 | ||
|
|
1934dddcbe | ||
|
|
f1c51bd0db | ||
|
|
d91fc59a28 | ||
|
|
f8025feda2 | ||
|
|
c9f0c46017 | ||
|
|
5879aeac52 | ||
|
|
c73ae579d3 | ||
|
|
3da55ad836 | ||
|
|
26bfcccedc | ||
|
|
af6243afb0 | ||
|
|
673a341626 | ||
|
|
a1aee7111a | ||
|
|
688209e272 | ||
|
|
116945047b | ||
|
|
1b8a9e16e2 | ||
|
|
7ff7e9d2e7 | ||
|
|
15c0d622c9 | ||
|
|
25087f3c92 | ||
|
|
34be9830af | ||
|
|
0fad6a6ec1 | ||
|
|
02599c0df8 | ||
|
|
430d068d78 | ||
|
|
f09cadb7b3 | ||
|
|
c366a4bd83 | ||
|
|
22c8a25a26 | ||
|
|
0ae2d8bab5 | ||
|
|
3e0bd1f02d | ||
|
|
4c78b7d4bb | ||
|
|
35fcfb978d | ||
|
|
c68083cf03 | ||
|
|
8402d211bf | ||
|
|
cc3be9448a | ||
|
|
cfb5119efc | ||
|
|
7ac30c5153 | ||
|
|
5d53fa5635 | ||
|
|
38d2b70ba6 | ||
|
|
29d5db16c2 | ||
|
|
64bc99101c | ||
|
|
09b5018e65 | ||
|
|
a8128c5772 | ||
|
|
1dae1d244c | ||
|
|
2da996f493 | ||
|
|
c5d4559300 | ||
|
|
434d027133 | ||
|
|
f66212ce23 | ||
|
|
6b4e632101 | ||
|
|
53a08d00fd | ||
|
|
fccec66463 | ||
|
|
bc26e807cd | ||
|
|
46dc37fb20 | ||
|
|
ccbb8400ee | ||
|
|
5d3d8a5332 | ||
|
|
0134b2874b | ||
|
|
e2308aabed | ||
|
|
36fac3532c | ||
|
|
327c6e8ac2 | ||
|
|
e9d49d96a6 | ||
|
|
929bb86a54 | ||
|
|
f58e28d1be | ||
|
|
cdabe21e7a | ||
|
|
a7520eeaa8 | ||
|
|
070147db52 | ||
|
|
55436510b6 | ||
|
|
1aec5e455d | ||
|
|
dc31294440 | ||
|
|
d3b8070fd6 | ||
|
|
26e4d66163 | ||
|
|
7cfceebeb8 | ||
|
|
0e2616f16b | ||
|
|
ccce7e001e | ||
|
|
e91ba07f14 | ||
|
|
fb7b51d91b | ||
|
|
88d055c3db | ||
|
|
3afce213c0 | ||
|
|
45f229c531 | ||
|
|
d6772b4a0a | ||
|
|
5016d50c2b | ||
|
|
24eaf93db8 | ||
|
|
2e519b534d | ||
|
|
31c2b36039 | ||
|
|
245956c658 | ||
|
|
0df360c5e8 | ||
|
|
9b786df828 | ||
|
|
ae859fa01e | ||
|
|
7eb64eb3dc | ||
|
|
f45014f75b | ||
|
|
74a5c8c23c | ||
|
|
28e781db15 | ||
|
|
6ca9d4ee78 | ||
|
|
52dcd998c4 | ||
|
|
a8cd168345 | ||
|
|
b05a2a90d0 | ||
|
|
242715240b | ||
|
|
65f17fd76e | ||
|
|
58ee4447a1 | ||
|
|
d153ac0951 | ||
|
|
2e97cfa5e5 | ||
|
|
7d5276ad11 | ||
|
|
2251e5e418 | ||
|
|
e4257fb6c7 | ||
|
|
f22b03fdb5 | ||
|
|
e045f5f74b | ||
|
|
e5c07ff0c5 | ||
|
|
dd4541c825 | ||
|
|
cdf0aa27e4 | ||
|
|
88998e6bb7 | ||
|
|
b885dfa606 | ||
|
|
407495c278 | ||
|
|
ea50a94355 | ||
|
|
cb3318972e | ||
|
|
c057ad7977 | ||
|
|
eb21718462 | ||
|
|
98f77394ed | ||
|
|
fc18db10be | ||
|
|
9382e2e8c7 | ||
|
|
a3b2dbf8e2 | ||
|
|
6df190edbb | ||
|
|
5b0b8a9717 | ||
|
|
a8ddbe4127 | ||
|
|
6e1fdda182 | ||
|
|
7836e10d8a | ||
|
|
946a31821d | ||
|
|
92a915f477 | ||
|
|
60b458dbfb | ||
|
|
8b13efc89a | ||
|
|
ca33090793 | ||
|
|
e34b390c18 | ||
|
|
370df0d495 | ||
|
|
d4e300d0a4 | ||
|
|
c83864af9c | ||
|
|
14fe3045dc | ||
|
|
5d54e6c951 | ||
|
|
921e78c9e2 | ||
|
|
b1778825cb | ||
|
|
cc8d5ac93a | ||
|
|
1d0dce7366 | ||
|
|
ae454b0892 | ||
|
|
a2234b745c | ||
|
|
3dd8dac146 | ||
|
|
6752fc9fe5 | ||
|
|
0d05411cd2 | ||
|
|
7ce65e3cfe | ||
|
|
ea1501abd1 | ||
|
|
ca28b8f93e | ||
|
|
566e74310a | ||
|
|
c18e8ba242 | ||
|
|
1f3f0dce11 | ||
|
|
b73008d83d | ||
|
|
10a8291391 | ||
|
|
ab094ab174 | ||
|
|
29a82f8471 | ||
|
|
8adfa52ae3 | ||
|
|
18485dd456 | ||
|
|
c318b849fe | ||
|
|
88b625fa15 | ||
|
|
2cf9c9b7df | ||
|
|
d179d6a1a2 | ||
|
|
08502c1eb6 | ||
|
|
384cd47416 | ||
|
|
250c46d6a4 | ||
|
|
f394d7dba9 | ||
|
|
ca595cd8cf | ||
|
|
9ce71739f5 | ||
|
|
d69da38f7d | ||
|
|
50f69f734f | ||
|
|
fc9c6c5434 | ||
|
|
6e941be1e9 | ||
|
|
9f677150a4 | ||
|
|
11e273337f | ||
|
|
ec807120c3 | ||
|
|
a5654debeb | ||
|
|
0869c0d8b0 | ||
|
|
b456c3f953 | ||
|
|
8297d68166 | ||
|
|
18ce6797e0 | ||
|
|
c1009d440e | ||
|
|
89ee6fe6db | ||
|
|
e3aa76cd09 | ||
|
|
de897d2ebf | ||
|
|
08118ec36f | ||
|
|
38a12475bc | ||
|
|
9a800264a2 | ||
|
|
8ce390be3c | ||
|
|
cc93532f4f | ||
|
|
37cc40521c | ||
|
|
5ac7ff7084 | ||
|
|
e682b82ca8 | ||
|
|
f757a9de52 | ||
|
|
7cd1fd0aaa | ||
|
|
6d502b6898 | ||
|
|
0eff19f0ff | ||
|
|
b6dfc791d4 | ||
|
|
7002dc63bd | ||
|
|
7eedfc201a | ||
|
|
ac38390241 | ||
|
|
3156f602dd | ||
|
|
ad25942731 | ||
|
|
fddbfc2d32 | ||
|
|
8c9bdadf66 | ||
|
|
6345faa4a9 | ||
|
|
4711ce5532 | ||
|
|
293510c087 | ||
|
|
a179a6892c | ||
|
|
afb310e90a | ||
|
|
51391b7f0e | ||
|
|
264b1d69d9 | ||
|
|
884b1add8e | ||
|
|
8c05a92a1e | ||
|
|
f791cd3a37 | ||
|
|
4600a656d1 | ||
|
|
d03f13a729 | ||
|
|
5a088690b2 | ||
|
|
8d26f58dfa | ||
|
|
a3cdb63ae1 | ||
|
|
9d5b23acd3 | ||
|
|
41e04c0178 | ||
|
|
abaa0cf3d0 | ||
|
|
8670fbe2ae | ||
|
|
898213bb85 | ||
|
|
c30d59d5d3 | ||
|
|
c54d69e5ae | ||
|
|
38a1f08413 | ||
|
|
e2bd80253b | ||
|
|
039cc6e28b | ||
|
|
1030c96eec | ||
|
|
0851c5ac8e | ||
|
|
92d112083e | ||
|
|
1e462f5cc0 | ||
|
|
d974ee169a | ||
|
|
7fb87d4039 | ||
|
|
fc5963fa3d | ||
|
|
cfa362321d | ||
|
|
72c7157138 | ||
|
|
2f91a12f19 | ||
|
|
abe72b7745 | ||
|
|
8cd99c5a40 | ||
|
|
ad83312009 | ||
|
|
9e3b1fe333 | ||
|
|
c3f42995f9 | ||
|
|
3d3a02c892 | ||
|
|
06bec227ad | ||
|
|
5d7803323c | ||
|
|
4303f9a025 | ||
|
|
fe4c5434fa | ||
|
|
9325bb70c5 | ||
|
|
b164557165 | ||
|
|
424f984ea8 | ||
|
|
e7df9d1754 | ||
|
|
d3ab2b077a | ||
|
|
467a11f4b4 | ||
|
|
6cb8c1df42 | ||
|
|
3915c7e8f2 | ||
|
|
5ba0e0dc54 | ||
|
|
78c704e3d1 | ||
|
|
9a35e2db77 | ||
|
|
e204a6d1d0 | ||
|
|
47ab2a1b1d | ||
|
|
6fc38292f2 | ||
|
|
89d8df3385 | ||
|
|
9c7df2b41e | ||
|
|
46f6816511 | ||
|
|
858ccfa934 | ||
|
|
b4a3218b13 | ||
|
|
92a4556956 | ||
|
|
bd42c33daa | ||
|
|
50f1a8fbd8 | ||
|
|
0f90b7b5da | ||
|
|
bec4384a5d | ||
|
|
f361efae5a | ||
|
|
76ca2116ab | ||
|
|
33d8646dc5 | ||
|
|
3cb5190bc6 | ||
|
|
e1796a9f0f | ||
|
|
8c5c81fe70 | ||
|
|
bf8fae2025 | ||
|
|
7f2b715ceb | ||
|
|
5bba06bef8 | ||
|
|
dd3f672a35 | ||
|
|
25ef3b8445 | ||
|
|
7eb2d290d8 | ||
|
|
678762910b | ||
|
|
da28a542c7 | ||
|
|
92602d33ad | ||
|
|
03f04f4bb3 | ||
|
|
0d4dd5ff0d | ||
|
|
884864cc58 | ||
|
|
7c199d83be | ||
|
|
5b4c29c4bb | ||
|
|
f97ee80955 | ||
|
|
547322ae63 | ||
|
|
db74db622e | ||
|
|
57318c0041 | ||
|
|
974189045a | ||
|
|
4eb0767834 | ||
|
|
92bb7808ce | ||
|
|
abfe7d1613 | ||
|
|
6c72239365 | ||
|
|
8001daad8c | ||
|
|
73a38ab4f4 | ||
|
|
281c207921 | ||
|
|
ae531eef5f | ||
|
|
d8b11238e6 | ||
|
|
77feaec444 | ||
|
|
d36eb04ae8 | ||
|
|
b97ba35279 | ||
|
|
46efe92a72 | ||
|
|
9cc27c7d1b | ||
|
|
7d3396f9c5 | ||
|
|
918ef13714 | ||
|
|
e5b6921de9 | ||
|
|
3ca3a2820d | ||
|
|
9f4933e33d | ||
|
|
3cf48a831b | ||
|
|
930b742663 | ||
|
|
d3870fed1c | ||
|
|
99e242affd | ||
|
|
9792246970 | ||
|
|
020b953166 | ||
|
|
52aa8e914a | ||
|
|
e01eadaecd | ||
|
|
61fc32b7c0 | ||
|
|
a9c8207bb0 | ||
|
|
eac4f172fe | ||
|
|
d6271426fd | ||
|
|
7e59110049 | ||
|
|
9d12462893 | ||
|
|
8cf4fb9adc | ||
|
|
f044f364cb | ||
|
|
4d39892a11 | ||
|
|
bf1ebe8e53 | ||
|
|
77b4f32274 | ||
|
|
b2ada13a21 | ||
|
|
03fced81f5 | ||
|
|
f9292a8fb8 | ||
|
|
065f46a020 | ||
|
|
6efb7027b5 | ||
|
|
f4f922acb5 | ||
|
|
4a4d2e7079 | ||
|
|
ed44e16a95 | ||
|
|
1634c90179 | ||
|
|
dadab94644 | ||
|
|
9b5ba6806f | ||
|
|
4afca6ca8d | ||
|
|
7ec232a470 | ||
|
|
dc3569392c | ||
|
|
11999cd407 | ||
|
|
f786f8a7f6 | ||
|
|
aab39e6ae0 | ||
|
|
31d3968649 | ||
|
|
2cd0b01019 | ||
|
|
a6bc0db896 | ||
|
|
7cba95b523 | ||
|
|
1b976130ce | ||
|
|
e9babc5a94 | ||
|
|
c8d0ff846c | ||
|
|
db1e9769d0 | ||
|
|
4b038b07ed | ||
|
|
4c17c142f9 | ||
|
|
65b2fa13ac | ||
|
|
c375199daa | ||
|
|
805131cf1e | ||
|
|
6f64115561 | ||
|
|
0aadb789a5 | ||
|
|
26ee4d626c | ||
|
|
a8939d3eda | ||
|
|
048e48b03a | ||
|
|
c2f34baee0 | ||
|
|
8961b476ef | ||
|
|
58e08335b5 | ||
|
|
ea8fc1d93f | ||
|
|
2968dfb2ae | ||
|
|
24783107ba | ||
|
|
365c056b0f | ||
|
|
88a7d9cca8 | ||
|
|
d8cf9d54aa | ||
|
|
df746c5ff4 | ||
|
|
aa29526ae4 | ||
|
|
f0b5616aba | ||
|
|
2eca95df00 | ||
|
|
f560c2e66a | ||
|
|
5e19496e32 | ||
|
|
aeadaba005 | ||
|
|
8dfda9ffdd | ||
|
|
91992bb034 | ||
|
|
34a93857a0 | ||
|
|
fbda096aa9 | ||
|
|
24a26aef70 | ||
|
|
ec7f9d8a63 | ||
|
|
cc7dec7a99 | ||
|
|
46384ce80d | ||
|
|
5aeb626045 | ||
|
|
d2a27f1a48 | ||
|
|
f07c67202f | ||
|
|
3a3658708a | ||
|
|
f3c5d19246 | ||
|
|
c66d8f202a | ||
|
|
ec641c171d | ||
|
|
773d624365 | ||
|
|
25bd97a014 | ||
|
|
5dd9e28f72 | ||
|
|
cc1ef6f268 | ||
|
|
967e361da3 | ||
|
|
398097900c | ||
|
|
b86473f3c7 | ||
|
|
3d361c6b4a | ||
|
|
3346024ea7 | ||
|
|
2fecf6c80b | ||
|
|
183b4fb7ee | ||
|
|
0e2afc5dcb | ||
|
|
91aa716c07 | ||
|
|
9977626de0 | ||
|
|
8bb3fb5bb3 | ||
|
|
6d7adb277e | ||
|
|
fef1bd13b5 | ||
|
|
910bba9976 | ||
|
|
cd2d756717 | ||
|
|
0a47d89963 | ||
|
|
90d1decb87 | ||
|
|
65e5b09cd1 | ||
|
|
6f3cee1a1e | ||
|
|
c43d4bbf71 | ||
|
|
b7337410aa | ||
|
|
1d8cb1b107 | ||
|
|
65e3d9873d | ||
|
|
50b22c673e | ||
|
|
4ef8b7f733 | ||
|
|
e0ec26255b | ||
|
|
ffa32f7610 | ||
|
|
1c1d257bd9 | ||
|
|
5611cb91a0 | ||
|
|
e0cff4b74e | ||
|
|
c1985e6986 | ||
|
|
416b1aee98 | ||
|
|
7bb9d8b03a | ||
|
|
0473c82029 | ||
|
|
63bd1a4a22 | ||
|
|
5a64759c83 | ||
|
|
0df68a6e06 | ||
|
|
29eff3b515 | ||
|
|
073976de10 | ||
|
|
1fe159f432 | ||
|
|
adad7f4e3f | ||
|
|
378710533e | ||
|
|
d966848ef8 | ||
|
|
d6d613ca37 | ||
|
|
1a385de367 | ||
|
|
73eb56c072 | ||
|
|
9651941cce | ||
|
|
29be79814f | ||
|
|
1ea220f44f | ||
|
|
6cf11554de | ||
|
|
f289b3112b | ||
|
|
c61df50167 | ||
|
|
df5ec33330 | ||
|
|
6384518ee1 | ||
|
|
87bf940f89 | ||
|
|
53db047a4e | ||
|
|
dabef47ce7 | ||
|
|
4de1d69282 | ||
|
|
015c35f2b2 | ||
|
|
5a491b2624 | ||
|
|
3d95817dbe | ||
|
|
96ab74dea4 | ||
|
|
f937a0b038 | ||
|
|
da38285046 | ||
|
|
d3db2dcfbb | ||
|
|
c788db785b | ||
|
|
6df41d2562 | ||
|
|
74fb43998e | ||
|
|
c1f8eabd12 | ||
|
|
2f5a1eebe0 | ||
|
|
4791c120fe | ||
|
|
f6d445b553 | ||
|
|
db3dd4ae5a | ||
|
|
d6ba03916a | ||
|
|
4734acbe61 | ||
|
|
527256b416 | ||
|
|
7a0dc7a364 | ||
|
|
7f88e8ad6e | ||
|
|
c7324121ed | ||
|
|
588ca3a315 | ||
|
|
eb9b2f355e | ||
|
|
cf6144599d | ||
|
|
c23bb5e589 | ||
|
|
0eb5c825a5 | ||
|
|
5dfb65b084 | ||
|
|
ddbe2acbf5 | ||
|
|
2ff3735adc | ||
|
|
c2752d5931 | ||
|
|
309dd7f300 | ||
|
|
33df11b755 | ||
|
|
bd5babf65d | ||
|
|
3c07cbde47 | ||
|
|
dee5ff852d | ||
|
|
073930004d | ||
|
|
01574c5a87 | ||
|
|
3a49369e68 | ||
|
|
42f7b7cc19 | ||
|
|
5f8ebc3601 | ||
|
|
2e5645108b | ||
|
|
d73f7beb2f | ||
|
|
4f6f126952 | ||
|
|
0099cdd6ad | ||
|
|
2b7114b704 | ||
|
|
2041d0d000 | ||
|
|
698f313b73 | ||
|
|
1940458306 | ||
|
|
983def2bbd | ||
|
|
9213bf47ae | ||
|
|
f01743e27c | ||
|
|
9be58972a0 | ||
|
|
a1a6c3e40c | ||
|
|
a2b19a1b9d | ||
|
|
4d1a942e79 | ||
|
|
eb4afa8f2c | ||
|
|
468bbea44d | ||
|
|
b75aa02b0d | ||
|
|
a39f93bcff | ||
|
|
e229d2d59f | ||
|
|
3d58dc2a50 | ||
|
|
0395188aed | ||
|
|
4e6dd1b213 | ||
|
|
a46b13967a | ||
|
|
e916daf5fd | ||
|
|
fad9dae9bb | ||
|
|
83bd5f5561 | ||
|
|
0ddb9f8923 | ||
|
|
03ea97734c | ||
|
|
b7adf59ed5 | ||
|
|
666b992c51 | ||
|
|
56d5e9c889 | ||
|
|
aa36ebf4f2 | ||
|
|
de60123ec7 | ||
|
|
cf3790c2f8 | ||
|
|
b7bac3abf6 | ||
|
|
f4551dd1e5 | ||
|
|
b3da4bb5b7 | ||
|
|
6e4b6929d2 | ||
|
|
a0602c8863 | ||
|
|
55246c5d87 | ||
|
|
81ee577407 | ||
|
|
55e1045000 | ||
|
|
fc96df63fd | ||
|
|
9b787a6bc2 | ||
|
|
201dc7c28d | ||
|
|
5189fa6483 | ||
|
|
1992478cee | ||
|
|
5ad6b48289 | ||
|
|
bbcf83b99d | ||
|
|
9ef55023f2 | ||
|
|
f2f81b0f7b | ||
|
|
c22ed861d1 | ||
|
|
39bed4d306 | ||
|
|
949946589f | ||
|
|
09d970f056 | ||
|
|
5813bb9ca8 | ||
|
|
c48ad5c90f | ||
|
|
4fdc1b3e19 | ||
|
|
dc372a43fe | ||
|
|
589b791cd7 | ||
|
|
564dbea126 | ||
|
|
1bc4bc613e | ||
|
|
11a25cb1ae | ||
|
|
d6c1bf691c | ||
|
|
e6443a2612 | ||
|
|
d79714a3cf | ||
|
|
4bb1312cf1 | ||
|
|
4e2abcaefa | ||
|
|
d463d4c46b | ||
|
|
435c81da98 | ||
|
|
4f818d55fa | ||
|
|
c96a81e319 | ||
|
|
7cff9ba86a | ||
|
|
6ddaa6f52a | ||
|
|
238877b627 | ||
|
|
95e7999e45 | ||
|
|
2f464ee8f0 | ||
|
|
8fe9883b62 | ||
|
|
de896f9159 | ||
|
|
a2468e8129 | ||
|
|
ee89f74afd | ||
|
|
cd08c3e8fa | ||
|
|
9cc8933eec | ||
|
|
91c27ff972 | ||
|
|
07e3ba014c | ||
|
|
57ac0ca6e8 | ||
|
|
816368b1fe | ||
|
|
85ea291745 | ||
|
|
741fdfa52e | ||
|
|
348ec5bc3c | ||
|
|
df9698ab1f | ||
|
|
c1aead970a | ||
|
|
518f351235 | ||
|
|
d79a07e2c4 | ||
|
|
b636057251 | ||
|
|
c692c8f055 | ||
|
|
930736060c | ||
|
|
1c8468d248 | ||
|
|
575842eab4 | ||
|
|
f7f55cad43 | ||
|
|
acd857a8c0 | ||
|
|
53ff248176 | ||
|
|
3de13967bc | ||
|
|
aebb923c2d | ||
|
|
76d577a08d | ||
|
|
3fb34a2a83 | ||
|
|
cdb938ea28 | ||
|
|
882dd4597e | ||
|
|
409efb8c67 | ||
|
|
eaa4373a18 | ||
|
|
d480f8d82a | ||
|
|
339fc9ff14 | ||
|
|
14167412e0 | ||
|
|
7c0f075d1f | ||
|
|
f4aea76c6e | ||
|
|
d263382365 | ||
|
|
2109ed8464 | ||
|
|
d11444b983 | ||
|
|
0508a45681 | ||
|
|
a5c0b95f8b | ||
|
|
f1af12b3b9 | ||
|
|
9d257497bd | ||
|
|
79f40b4ce0 | ||
|
|
1ab62b7a80 | ||
|
|
d2f10d5834 | ||
|
|
c326ec9d1e | ||
|
|
26317d16ff | ||
|
|
79b8f85cdf | ||
|
|
c84a0b8415 | ||
|
|
be5336d074 | ||
|
|
aecb2efad9 | ||
|
|
3654005c3d | ||
|
|
d8fd61955c | ||
|
|
3381ab5093 | ||
|
|
8c2fa64c47 | ||
|
|
12ae9fced5 | ||
|
|
e3bca7e7e7 | ||
|
|
bca199e118 | ||
|
|
938ae5f27c | ||
|
|
8aa3b535f0 | ||
|
|
f4bf4ee0f2 | ||
|
|
1c1ab3cd87 | ||
|
|
f5c215fe65 | ||
|
|
9952de615a | ||
|
|
e1c547be98 | ||
|
|
7cb6795d40 | ||
|
|
a5f4478e20 | ||
|
|
94d0db2dba | ||
|
|
64c596c922 | ||
|
|
e30b8999d4 | ||
|
|
2f0ee84b71 | ||
|
|
c570478aaa | ||
|
|
051997acb0 | ||
|
|
51ef0e1e6d | ||
|
|
ed0fcf2829 | ||
|
|
2ce94b73ad | ||
|
|
ef96bff6ec | ||
|
|
afc888f2ef | ||
|
|
e823c2af9d | ||
|
|
18979f3f30 | ||
|
|
8bd29596c4 | ||
|
|
0cfcb6aa37 | ||
|
|
6b40812e6d | ||
|
|
8af253e9ab | ||
|
|
6763bce627 | ||
|
|
3217c1e11f | ||
|
|
6ce3e18831 | ||
|
|
3e461ac2e5 | ||
|
|
25e2baaea7 | ||
|
|
6696a714f0 | ||
|
|
46af2ffcea | ||
|
|
e5b3a82112 | ||
|
|
9af51aaca1 | ||
|
|
3bacf09710 | ||
|
|
61bdbd2d74 | ||
|
|
a1293f160f | ||
|
|
847503bff1 | ||
|
|
576242551f | ||
|
|
fdb6bee65f | ||
|
|
a2846faa61 | ||
|
|
a32544b53a | ||
|
|
9b093dab0a | ||
|
|
84d433fa06 | ||
|
|
e24b8947e0 | ||
|
|
7835c6e20c | ||
|
|
d942c47f08 | ||
|
|
fdaee1375c | ||
|
|
d25a510291 | ||
|
|
0cb8586f68 | ||
|
|
e42a0979bc | ||
|
|
f1a78682aa | ||
|
|
dc4bfde76d | ||
|
|
e717625f86 | ||
|
|
41444e7ede | ||
|
|
ac9247195b | ||
|
|
fc2a59ceaa | ||
|
|
72f4b216c1 | ||
|
|
d373f5488a | ||
|
|
24906138c7 | ||
|
|
f4b3b69b2f | ||
|
|
e80abad19a | ||
|
|
c9c228ffdd | ||
|
|
6fc30d31bf | ||
|
|
0bcd47b394 | ||
|
|
c24bb139dd | ||
|
|
eb00ff05a8 | ||
|
|
a7fe47f295 | ||
|
|
2a6f02c764 | ||
|
|
1b2ff1f9ae | ||
|
|
7de9a36d01 | ||
|
|
cfa8796b18 | ||
|
|
3c88ede02c | ||
|
|
a0223835b8 | ||
|
|
ac18359410 | ||
|
|
8194e3d3c5 | ||
|
|
440c4fe9b9 | ||
|
|
e54f5d563f | ||
|
|
5781ab5436 | ||
|
|
f8009dbb27 | ||
|
|
15efe3ae31 | ||
|
|
991348bbbe | ||
|
|
233336ea16 | ||
|
|
20d8c1b51a | ||
|
|
06499f2155 | ||
|
|
5b1d9396d7 | ||
|
|
633c5d5938 | ||
|
|
2e846159a8 | ||
|
|
4408604c2d | ||
|
|
15db5c8059 | ||
|
|
53121b1dd4 | ||
|
|
c36dff6354 | ||
|
|
94a91ed5c8 | ||
|
|
162a812189 | ||
|
|
f06f0785f1 | ||
|
|
1aae68c614 | ||
|
|
703a0fde3c | ||
|
|
abb71cd074 | ||
|
|
79cf9156eb | ||
|
|
3a6664282c | ||
|
|
e00ac37cb2 | ||
|
|
6583ac9ba1 | ||
|
|
42963ea77d | ||
|
|
6231724f49 | ||
|
|
5b25eb4fbd | ||
|
|
565cc7b3c8 | ||
|
|
71dc2690aa | ||
|
|
3accd48ea8 | ||
|
|
ed78bde359 | ||
|
|
ff1a3ab307 | ||
|
|
26643a489b | ||
|
|
5955a541a3 | ||
|
|
995669cccd | ||
|
|
371dc347cc | ||
|
|
9a162e5d5c | ||
|
|
9319f362bb | ||
|
|
8305168bc4 | ||
|
|
e8ecbe32ca | ||
|
|
bc5c5050fb | ||
|
|
8a9b9c55ce | ||
|
|
d40f343d2a | ||
|
|
4954d0a018 | ||
|
|
7232bef07b | ||
|
|
7277fb3d93 | ||
|
|
34e0345b29 | ||
|
|
f094e9118c | ||
|
|
63e93d9253 | ||
|
|
2ec0d421f7 | ||
|
|
8ddc890e10 | ||
|
|
095c899913 | ||
|
|
e0d6a3fd1d | ||
|
|
308ff15adc | ||
|
|
c195629a77 | ||
|
|
6af5a67a17 | ||
|
|
0b38289703 | ||
|
|
041bf9b54e | ||
|
|
5a6cf2b690 | ||
|
|
2e44bb6085 | ||
|
|
b8f056b82f | ||
|
|
259b463b73 | ||
|
|
4aae7348d1 | ||
|
|
665942a8c9 | ||
|
|
dc9a26ae8a | ||
|
|
55f3322576 | ||
|
|
376f068719 | ||
|
|
3688956ee5 | ||
|
|
53b1037a33 | ||
|
|
2b5cd02bd9 | ||
|
|
e5fed57b8b | ||
|
|
a0b1f3efa2 | ||
|
|
f7fb1225d2 | ||
|
|
b380952ff9 | ||
|
|
2524e7eea3 | ||
|
|
32460f00f8 | ||
|
|
d58d7f457d | ||
|
|
232a43f34f | ||
|
|
94f5a039d2 | ||
|
|
7a4ae74727 | ||
|
|
1295de4c78 | ||
|
|
77ed54f64d | ||
|
|
e51b425200 | ||
|
|
e6aff19a0c | ||
|
|
bd06c7b343 | ||
|
|
c096eae644 | ||
|
|
f77823ee24 | ||
|
|
35f568e346 | ||
|
|
2d28fde24d | ||
|
|
92fe720ac3 | ||
|
|
74a9a13564 | ||
|
|
7aaef5d807 | ||
|
|
64b4158bad | ||
|
|
4bbaa6b41c | ||
|
|
964d8171dd | ||
|
|
5b1cbbf157 | ||
|
|
db4c29a73c | ||
|
|
4f73008d95 | ||
|
|
1b73cb28f1 | ||
|
|
367c981642 | ||
|
|
3d30eb0748 | ||
|
|
34ffdad521 | ||
|
|
4f82c0c289 | ||
|
|
fd68746dd7 | ||
|
|
bec7d1265b | ||
|
|
620eb2a3be | ||
|
|
e6971c45c8 | ||
|
|
961cd0825c | ||
|
|
a1cb702ecb | ||
|
|
befc14c980 | ||
|
|
ff0ad53c11 | ||
|
|
cf0e57d7ce | ||
|
|
9883e3ed2e | ||
|
|
10b7d7da3f | ||
|
|
97a8c6e5af | ||
|
|
e69d2bb54a | ||
|
|
ead8099109 | ||
|
|
9d455b2bca | ||
|
|
1e6e74878f | ||
|
|
cbcede3b63 | ||
|
|
98fd731485 | ||
|
|
9ca319b4ea | ||
|
|
ff8fabf49e | ||
|
|
1e00f68941 | ||
|
|
6c79edd3b2 | ||
|
|
b79bc6bab9 | ||
|
|
c59d886662 | ||
|
|
e39dce6159 | ||
|
|
620c66fb4c | ||
|
|
3af91d54cb | ||
|
|
1fe1bcff98 | ||
|
|
c58c12c5b3 | ||
|
|
1aeea39eb3 | ||
|
|
d3fbf020de | ||
|
|
8ce8bee76f | ||
|
|
dcf4eb2aaf | ||
|
|
88ff38eca6 | ||
|
|
bf0d90d079 | ||
|
|
c4d9ef9f83 | ||
|
|
37bce53683 | ||
|
|
05d50b62c9 | ||
|
|
eb2fb7f790 | ||
|
|
d280ccb3c4 | ||
|
|
a569815840 | ||
|
|
9e41c3093b | ||
|
|
9dbc9a8ad9 | ||
|
|
c940157814 | ||
|
|
4f19875a58 | ||
|
|
dcbadb2386 | ||
|
|
405a7b2037 | ||
|
|
e69ad24737 | ||
|
|
a31056a4ec | ||
|
|
d41dddddcd | ||
|
|
251ae50d3e | ||
|
|
4ccd2d4955 | ||
|
|
dfb79e0998 | ||
|
|
590055e2ba | ||
|
|
f388751a97 | ||
|
|
53136caaa4 | ||
|
|
dd20d8d6de | ||
|
|
a977957946 | ||
|
|
8974fcabe3 | ||
|
|
aff8e1e025 | ||
|
|
10d767c5ab | ||
|
|
77484dc69e | ||
|
|
185e2342d9 | ||
|
|
54f4727adc | ||
|
|
6e0f982163 | ||
|
|
43072ef80d | ||
|
|
7618e63f49 | ||
|
|
542255332d | ||
|
|
6d6e7e1468 | ||
|
|
0ff88d5c21 | ||
|
|
092e35bae3 | ||
|
|
ac82b2622c | ||
|
|
7197cb57cd | ||
|
|
030d66cb63 | ||
|
|
d414e6a7c4 | ||
|
|
30f9c94620 | ||
|
|
b6b618e5ce | ||
|
|
0bb8887027 | ||
|
|
fabfdd0aa2 | ||
|
|
8f8336b344 | ||
|
|
d10659f986 | ||
|
|
e853555165 | ||
|
|
2ce09d73ac | ||
|
|
14f48a5f4f | ||
|
|
8da4e4a830 | ||
|
|
88ae8311c8 | ||
|
|
eabf6a466a | ||
|
|
989f691d06 | ||
|
|
52a3dae7cb | ||
|
|
8a6dfacf71 | ||
|
|
19ca6c13ad | ||
|
|
9196f122dd | ||
|
|
55e3d81c58 | ||
|
|
80caa16718 | ||
|
|
ddc5aaacdb | ||
|
|
38825865cc | ||
|
|
157b2efa1d | ||
|
|
212a2d229a | ||
|
|
ccb89fb26a | ||
|
|
3a0b0c0973 | ||
|
|
c159ed9ebb | ||
|
|
e37dbcc273 | ||
|
|
841e660b1f | ||
|
|
391aba5f37 | ||
|
|
f5d30f4a07 | ||
|
|
230cf7dfb7 | ||
|
|
8734b70f83 | ||
|
|
ff9a2b2d9a | ||
|
|
a41dab2922 | ||
|
|
f4504106a1 | ||
|
|
1a5b8041c7 | ||
|
|
1e2170f8bb | ||
|
|
0a4a973a7f | ||
|
|
67137cadac | ||
|
|
ead4afa06b | ||
|
|
e39ef95191 | ||
|
|
3d7585671f | ||
|
|
44806106db | ||
|
|
6a8197a4e8 | ||
|
|
4d4b4e0f2b | ||
|
|
82da33d742 | ||
|
|
1c6130a492 | ||
|
|
dabe5e939f | ||
|
|
b4ac6d3439 | ||
|
|
d0ea74e180 | ||
|
|
a5f77f3964 | ||
|
|
9401bb53e8 | ||
|
|
347c6d02cf | ||
|
|
64a0080616 | ||
|
|
288a7d4cea | ||
|
|
1207b68f57 | ||
|
|
2b82187fe0 | ||
|
|
3e2dbfc83c | ||
|
|
6691f490bc | ||
|
|
a7db900e51 | ||
|
|
cfa2a90dae | ||
|
|
feebb45e9d | ||
|
|
13a2f3a330 | ||
|
|
1da8801084 | ||
|
|
7a7b17c4f3 | ||
|
|
2afb06de13 | ||
|
|
ac771e2865 | ||
|
|
4b09204936 | ||
|
|
e4a5518a7c | ||
|
|
e83507ba98 | ||
|
|
6e5b77edb2 | ||
|
|
98167da8fa | ||
|
|
11d5fec59b | ||
|
|
ed02f4ca88 | ||
|
|
e7c612971d | ||
|
|
242a2e21b5 | ||
|
|
92a1e5413b | ||
|
|
e1ab9aa690 | ||
|
|
222f7e6fd4 | ||
|
|
59001f827b | ||
|
|
549bfe98f1 | ||
|
|
64e3b6f5ee | ||
|
|
2fdc0426ff | ||
|
|
e6c70fda66 | ||
|
|
50da1354f3 | ||
|
|
e503cacf57 | ||
|
|
6a1e0edc78 | ||
|
|
048f5c2771 | ||
|
|
d9378e9a8f | ||
|
|
f77aaaf2e0 | ||
|
|
380835b151 | ||
|
|
857d91df85 | ||
|
|
cd9a6b0de7 | ||
|
|
86333f4928 | ||
|
|
d1975bd5dd | ||
|
|
08b9f27eac | ||
|
|
5b2cf8a776 | ||
|
|
7394106880 | ||
|
|
9134a7ae1e | ||
|
|
c617e9f483 | ||
|
|
545fcef716 | ||
|
|
e20fa3a1bf | ||
|
|
9d01b47d5b | ||
|
|
fa4c34df41 | ||
|
|
9f8ab4d626 | ||
|
|
1971c9cecd | ||
|
|
82f730831d | ||
|
|
035167e5b7 | ||
|
|
323fb0a965 | ||
|
|
45aacf122f | ||
|
|
5dcf7919fc | ||
|
|
f15560bd89 | ||
|
|
bf3ae730d6 | ||
|
|
92ce258bb4 | ||
|
|
5810e7df82 | ||
|
|
f2cbf4a561 | ||
|
|
0c8bb2e675 | ||
|
|
f51c4f9617 | ||
|
|
5345f7c983 | ||
|
|
7d9e43b2e1 | ||
|
|
570c1e1809 | ||
|
|
720d335b09 | ||
|
|
f0ecda1a48 | ||
|
|
4ed111a7d4 | ||
|
|
fc257523c7 | ||
|
|
9775f8d83c | ||
|
|
01a1b19556 | ||
|
|
bd228034fd | ||
|
|
2672a5c59f | ||
|
|
5f383430eb | ||
|
|
66ec3e9f4d | ||
|
|
c269c427a6 | ||
|
|
212855ebd8 | ||
|
|
8f78945a73 | ||
|
|
86d50cf597 | ||
|
|
8986d506a5 | ||
|
|
3d2c945c35 | ||
|
|
8206a3969f | ||
|
|
43afe67593 | ||
|
|
ecf939f8c8 | ||
|
|
f0c4ccfa1e | ||
|
|
718d48b7d5 | ||
|
|
d5e88677ae | ||
|
|
b1cfdc7a6a | ||
|
|
5c5f815b20 | ||
|
|
d9edb9675f | ||
|
|
be6fce5a89 | ||
|
|
12afeb30ae | ||
|
|
23df406ff0 | ||
|
|
c9ecef4fc3 | ||
|
|
62f0a867a8 | ||
|
|
f68b8d82e0 | ||
|
|
6b7cb13e9a | ||
|
|
613af6013a | ||
|
|
0db56a781e | ||
|
|
fe7674b184 | ||
|
|
44a6fe7310 | ||
|
|
46690f0c35 | ||
|
|
cfc4e345cc | ||
|
|
c68278d824 | ||
|
|
09600a71cd | ||
|
|
d66ba70f5e | ||
|
|
e7c4120d24 | ||
|
|
1212e534a8 | ||
|
|
899d0f1e42 | ||
|
|
dde4ed3754 | ||
|
|
b596e02752 | ||
|
|
d5afe696da | ||
|
|
976be66dee | ||
|
|
98c8a6387a | ||
|
|
be1b636e30 | ||
|
|
8bae0b81aa | ||
|
|
7ee19e1306 | ||
|
|
626d0dfa93 | ||
|
|
8453c616a5 | ||
|
|
56bc2944d0 | ||
|
|
b2cf701e30 | ||
|
|
9a3b29acb7 | ||
|
|
85b217f167 | ||
|
|
a7700d6c22 | ||
|
|
1e274518dd | ||
|
|
f4f8927c79 | ||
|
|
60b7890f16 | ||
|
|
4d46290ef6 | ||
|
|
00f7e2cfc3 | ||
|
|
e742708261 | ||
|
|
8c9de31c5c | ||
|
|
2bf992f0ac | ||
|
|
52a01bcc11 | ||
|
|
f250e03c40 | ||
|
|
75acbfb042 | ||
|
|
740fd6f5d9 | ||
|
|
c58306cd49 | ||
|
|
985696631c | ||
|
|
d8a3d7f920 | ||
|
|
b6b96f0eee | ||
|
|
3f037ef3cc | ||
|
|
5c2d3fb3da | ||
|
|
84d38ea878 | ||
|
|
6226aa0b23 | ||
|
|
72af35834b | ||
|
|
3019e3817f | ||
|
|
da9e90faf0 | ||
|
|
e737395c2b | ||
|
|
5a33167594 | ||
|
|
dfb7e50948 | ||
|
|
b43c9f075c | ||
|
|
f42ce6adb5 | ||
|
|
bf533bbdbb | ||
|
|
9328729c4c | ||
|
|
2e23a044ca | ||
|
|
691a3a1e72 | ||
|
|
fc40f9634b | ||
|
|
4969441f21 | ||
|
|
d04d50794e | ||
|
|
5e1fbe34a9 | ||
|
|
2b75829a0c | ||
|
|
3b3fd0a3a2 | ||
|
|
1e4b61008a | ||
|
|
ffb71e9fd5 | ||
|
|
c1aeb4bde6 | ||
|
|
66bca5cd81 | ||
|
|
b68df3a067 | ||
|
|
066f3bb646 | ||
|
|
661c334928 | ||
|
|
a1895d4157 | ||
|
|
1e7efe026a | ||
|
|
02e7da979a | ||
|
|
bd2839110b | ||
|
|
1bcb2697e7 | ||
|
|
fdb9a664f5 | ||
|
|
8099c334c7 | ||
|
|
344d28ab78 | ||
|
|
5e41168305 | ||
|
|
dd97669cdd | ||
|
|
af75581659 | ||
|
|
3c08f5b86a | ||
|
|
7809cad6cb | ||
|
|
3e59029966 | ||
|
|
24249bcb4d | ||
|
|
543011c3de | ||
|
|
4d9f2586d9 | ||
|
|
7f94ebe362 | ||
|
|
c9bf1eda54 | ||
|
|
2cd4d9004c | ||
|
|
35cd0319d4 | ||
|
|
899030e966 | ||
|
|
15e3944d23 | ||
|
|
f1fbaedb0f | ||
|
|
cf04062a19 | ||
|
|
2e922bf7db | ||
|
|
741a731ecb | ||
|
|
91bff1976c | ||
|
|
fad500042a | ||
|
|
bdb2a01385 | ||
|
|
f372cd2d13 | ||
|
|
a435da910f | ||
|
|
f1d96d9673 | ||
|
|
45226ee1f6 | ||
|
|
03b8d29740 | ||
|
|
80a4af249f | ||
|
|
900d444946 | ||
|
|
7ca9393d2d | ||
|
|
b22d7bb310 | ||
|
|
c742cdfe5c | ||
|
|
2d3fe02cd7 | ||
|
|
7d61cb6bac | ||
|
|
a804433a7a | ||
|
|
11082320c2 | ||
|
|
9f9797b123 | ||
|
|
05d34616b7 | ||
|
|
c7f474ada1 | ||
|
|
c7328a63d0 | ||
|
|
db818794d5 | ||
|
|
b4b34e4890 | ||
|
|
eec016380d | ||
|
|
dcab272be0 | ||
|
|
418d033a5c | ||
|
|
e7a7713b80 | ||
|
|
52a580d845 | ||
|
|
ea42a0b5f5 | ||
|
|
a0b63da9a6 | ||
|
|
8d9932fc42 | ||
|
|
54be45bedc | ||
|
|
3c9d2a562b | ||
|
|
654bd5175c | ||
|
|
9db660cd3f | ||
|
|
9aeadff9bd | ||
|
|
cd631b789b | ||
|
|
4d98408aaf | ||
|
|
490aafba4e | ||
|
|
3cd31d49bb | ||
|
|
6dd78b8d35 | ||
|
|
bd51c34098 | ||
|
|
0e758fadee | ||
|
|
a67659ff8e | ||
|
|
d1203f90da | ||
|
|
c5f6ebee18 | ||
|
|
41cc5234c4 | ||
|
|
c2d3e5f052 | ||
|
|
f944b95e32 | ||
|
|
0a5b919cd9 | ||
|
|
b5f85c11fe | ||
|
|
5d44b5e913 | ||
|
|
dd1961d86c | ||
|
|
f3539bf448 | ||
|
|
d831e559f0 | ||
|
|
cc079660f3 | ||
|
|
4460dac697 | ||
|
|
31190f921c | ||
|
|
22515d8dce | ||
|
|
a099d970a4 | ||
|
|
95bb2c5504 | ||
|
|
0b1262cc4d | ||
|
|
f9244f6f7e | ||
|
|
9dd4ea1584 | ||
|
|
9d085cdb61 | ||
|
|
f00a2dbc59 | ||
|
|
20862d47ab | ||
|
|
cfa1bf36de | ||
|
|
c362b33b5a | ||
|
|
222a151eff | ||
|
|
d6115cff25 | ||
|
|
6459522617 | ||
|
|
16e1f0eea1 | ||
|
|
0564ac1530 | ||
|
|
b896f82282 | ||
|
|
d737d419d9 | ||
|
|
0de2b6983b | ||
|
|
25628c34ee | ||
|
|
54fdccd397 | ||
|
|
27af4a988d | ||
|
|
fddad131a2 | ||
|
|
4d493eeebd | ||
|
|
14c1495a1a | ||
|
|
49c029ddb3 | ||
|
|
ddbe142c4c | ||
|
|
4710b40e45 | ||
|
|
be0b296769 | ||
|
|
0a0ee67740 | ||
|
|
943d648225 | ||
|
|
6d835f5cc1 | ||
|
|
a59cd67acf | ||
|
|
bbc06be091 | ||
|
|
d303e9f347 | ||
|
|
85ee923d89 | ||
|
|
fe6ba20ff5 | ||
|
|
18acf50b12 | ||
|
|
509fabb61c | ||
|
|
cc3378b4cd | ||
|
|
8d3ccf333d | ||
|
|
520919b165 | ||
|
|
600b86dd31 | ||
|
|
9c3fdd2584 | ||
|
|
73974cd90f | ||
|
|
8a60b3b402 | ||
|
|
59ed5ae6b7 | ||
|
|
c687385974 | ||
|
|
2f72e15ea7 | ||
|
|
7ea99baeb2 | ||
|
|
14f3d20772 | ||
|
|
72f51df25f | ||
|
|
a94b58e304 | ||
|
|
d327fabf9c | ||
|
|
9e02888c74 | ||
|
|
98c9a847f3 | ||
|
|
a159f4c9d6 | ||
|
|
b197731ed2 | ||
|
|
028f54fdf0 | ||
|
|
de62a0f8ed | ||
|
|
a0fcc50a5f | ||
|
|
4f186e843c | ||
|
|
df64c20587 | ||
|
|
c78a602990 | ||
|
|
0acec57ba9 | ||
|
|
7423028fea | ||
|
|
49d951b7be | ||
|
|
a554c9518d | ||
|
|
f3c7ba445e | ||
|
|
3abff41385 | ||
|
|
6c36832f0e | ||
|
|
310c18707b | ||
|
|
422a4a5578 | ||
|
|
a995b2e1ae | ||
|
|
90ad681a9e | ||
|
|
2ff5c39712 | ||
|
|
ded849fd6d | ||
|
|
02f7064953 | ||
|
|
1ddadfce1e | ||
|
|
7720e5007c | ||
|
|
ccfd021796 | ||
|
|
fdcad84cee | ||
|
|
ad3e3b8421 | ||
|
|
f67b4f8902 | ||
|
|
3c0d7319ba | ||
|
|
7f0aab6ec6 | ||
|
|
669578a926 | ||
|
|
fa1db64a8e | ||
|
|
7cf60fa927 | ||
|
|
75a0d3e494 | ||
|
|
a98218ae22 | ||
|
|
10952e9dc4 | ||
|
|
b36e23471b | ||
|
|
f45bf1c95b | ||
|
|
d4db2587aa | ||
|
|
62e6336a7d | ||
|
|
2d8acd4711 | ||
|
|
d7d95be042 | ||
|
|
e3c97f0681 | ||
|
|
1a4fa015b9 | ||
|
|
63dacfaac5 | ||
|
|
f7263ac2c6 | ||
|
|
c7d7e1f0e3 | ||
|
|
9c54b615f5 | ||
|
|
69fe1aa981 | ||
|
|
bfb3d19e3b | ||
|
|
0b4e62a63e | ||
|
|
5cdbc03e71 | ||
|
|
16cd6dd25d | ||
|
|
fc71e7f99f | ||
|
|
e740380731 | ||
|
|
f9921d2c91 | ||
|
|
d8bfc92cc5 | ||
|
|
1f2214f891 | ||
|
|
50835e317f | ||
|
|
6c8d4dcc1e | ||
|
|
85a7efbd61 | ||
|
|
6b5ef10467 | ||
|
|
0bf46bfcb5 | ||
|
|
8b8dc7ac78 | ||
|
|
a017122c44 | ||
|
|
86a9a892d2 | ||
|
|
f31a141d78 | ||
|
|
fa67997424 | ||
|
|
a5f06c577d | ||
|
|
e575672877 | ||
|
|
a34d5b29e8 | ||
|
|
91ec65da1b | ||
|
|
ee8d40a66e | ||
|
|
38ac2e80ed | ||
|
|
8b70762e34 | ||
|
|
18c613a611 | ||
|
|
957f8c56a1 | ||
|
|
b16446877e | ||
|
|
9164d58dc7 | ||
|
|
0beb2f9d3c | ||
|
|
ccdd66052a | ||
|
|
518fd814b6 | ||
|
|
f49bb78dba | ||
|
|
a54aa77d28 | ||
|
|
f85e443c9c | ||
|
|
399e004e7f | ||
|
|
3099ba71eb | ||
|
|
951fcf384c | ||
|
|
c750368a3e | ||
|
|
3149d86855 | ||
|
|
3aaf21e34e | ||
|
|
519c375a42 | ||
|
|
3ed2c732bd | ||
|
|
0aa5550d0f | ||
|
|
1bc8dd83d3 | ||
|
|
c642cd6fed | ||
|
|
f72918362d | ||
|
|
28c4ca73ab | ||
|
|
a256494ec8 | ||
|
|
5fd359a64f | ||
|
|
e043735362 | ||
|
|
7010b1adb9 | ||
|
|
17ff262729 | ||
|
|
1c18838cd8 | ||
|
|
55b87ae78d | ||
|
|
e446e902f3 | ||
|
|
095b52e0b2 | ||
|
|
4f25ff6a50 | ||
|
|
6242e0aeec | ||
|
|
fde9fc9dd4 | ||
|
|
c481e42673 | ||
|
|
5e228d7232 | ||
|
|
6b8e40fdc9 | ||
|
|
bca44bf9e3 | ||
|
|
08e49dcf2e | ||
|
|
0f45409b4d | ||
|
|
5469d3ec13 | ||
|
|
d12f8410b9 | ||
|
|
704c52d229 | ||
|
|
7dcdda14f5 | ||
|
|
0f59256ca7 | ||
|
|
ddee04e98c | ||
|
|
50f5002b71 | ||
|
|
e866add4b0 | ||
|
|
39b93b88c5 | ||
|
|
deb7e95567 | ||
|
|
91500501ce | ||
|
|
6a1caeb9a1 | ||
|
|
cee7803c37 | ||
|
|
4e8d0ac7cb | ||
|
|
47bbcb9935 | ||
|
|
c0288c5f26 | ||
|
|
18c2013625 | ||
|
|
548089888d | ||
|
|
180c65cfeb | ||
|
|
6a28e41bc8 | ||
|
|
f9ef942a98 | ||
|
|
8272c7a74e | ||
|
|
4e745489db | ||
|
|
fc2625a0c0 | ||
|
|
d0e43ebaf1 | ||
|
|
879805e316 | ||
|
|
6845d2e0ef | ||
|
|
542d1c1c41 | ||
|
|
31568e7abb | ||
|
|
4b2488b124 | ||
|
|
d4c37ecfa3 | ||
|
|
3f9fd7c060 | ||
|
|
6ed1412bdd | ||
|
|
78b9a45158 | ||
|
|
b8925baa88 | ||
|
|
87fd3f2a82 | ||
|
|
0490ef5900 | ||
|
|
8af164dbd2 | ||
|
|
c994afbf3c | ||
|
|
fa36c91bd6 | ||
|
|
843caf86fb | ||
|
|
5b8f294aaf | ||
|
|
44eef5b0e0 | ||
|
|
aad75840d4 | ||
|
|
31faef7030 | ||
|
|
941877a05a | ||
|
|
4fd4c1e8e1 | ||
|
|
7cd27cd9c8 | ||
|
|
54dbef2b4f | ||
|
|
05424c9f33 | ||
|
|
346c6fc590 | ||
|
|
7ed3afe9f0 | ||
|
|
d29ed6cd49 | ||
|
|
8c3abf3473 | ||
|
|
384114d3c7 | ||
|
|
e8be5a0a8f | ||
|
|
98c6fa10e6 | ||
|
|
30d0bdec07 | ||
|
|
7024f29865 | ||
|
|
29ebed9514 | ||
|
|
c0260bc44a | ||
|
|
6992bff0e0 | ||
|
|
6957e6e8f7 | ||
|
|
31dbca7efc | ||
|
|
82b6dad1ba | ||
|
|
5063c17e6b | ||
|
|
3fff3d9c60 | ||
|
|
2fbe0aed32 | ||
|
|
3bdc1802cb | ||
|
|
2675833b0d | ||
|
|
939a5b477a | ||
|
|
dc57e7a5b3 | ||
|
|
a401b8f475 | ||
|
|
b008fabdac | ||
|
|
caf286b8cb | ||
|
|
aea278c022 | ||
|
|
4e20fe1602 | ||
|
|
849ca58228 | ||
|
|
9991fca2e5 | ||
|
|
9c7bdcfeef | ||
|
|
b5463d522b | ||
|
|
af0d830103 | ||
|
|
d4187dffa3 | ||
|
|
a7f06f2be5 | ||
|
|
7ff7d1a185 | ||
|
|
d3ab612a89 | ||
|
|
e3bc80e027 | ||
|
|
a8720806f1 | ||
|
|
5aac4c2e5d | ||
|
|
6fa645aab9 | ||
|
|
842ea4645b | ||
|
|
e1cfbcd4f6 | ||
|
|
6f17ff55fa | ||
|
|
d88af4dfa6 | ||
|
|
bb0ba21e92 | ||
|
|
f0753c8e58 | ||
|
|
4c55004b08 | ||
|
|
ae24119c09 | ||
|
|
b8753d8f20 | ||
|
|
bab368b956 | ||
|
|
22bc1fb21e | ||
|
|
63ba764de8 | ||
|
|
6a7530d741 | ||
|
|
eb9bf7c4f9 | ||
|
|
b6571d0ca3 | ||
|
|
9207d1c680 | ||
|
|
f783c9bb25 | ||
|
|
df5786deda | ||
|
|
de971e6c46 | ||
|
|
2a6ed0c96e | ||
|
|
9ec380777a | ||
|
|
8916d0d367 | ||
|
|
1e5648111e | ||
|
|
ae51cfe8b9 | ||
|
|
05ef250ab4 | ||
|
|
38e4871f32 | ||
|
|
a86ba7af2e | ||
|
|
5704afc54c | ||
|
|
55c4fc9aca | ||
|
|
888c17353b | ||
|
|
054c0e91a3 | ||
|
|
4dfb610eba | ||
|
|
008eb8dd8b | ||
|
|
2882c4bb89 | ||
|
|
4473851f5b | ||
|
|
15d9e2d3d5 | ||
|
|
d5d9df383e | ||
|
|
243a04b756 | ||
|
|
b77dcbd39b | ||
|
|
ad5d4bdfc5 | ||
|
|
54c17b0068 | ||
|
|
616907eff8 | ||
|
|
afaabb3673 | ||
|
|
1c1f625352 | ||
|
|
d3f45d2148 | ||
|
|
7326dbd108 | ||
|
|
4bf311a282 | ||
|
|
7f5a3f12ca | ||
|
|
6a9b71466d | ||
|
|
70e550a25f | ||
|
|
1328882ab6 | ||
|
|
b378a0608d | ||
|
|
e28c6d6f9c | ||
|
|
f57f29e426 | ||
|
|
f2857452e3 | ||
|
|
af04c294b9 | ||
|
|
65c8630534 | ||
|
|
095882d016 | ||
|
|
1693506f80 | ||
|
|
667c22169f | ||
|
|
c53edf6bd9 | ||
|
|
b601220845 | ||
|
|
e77a608f45 | ||
|
|
2822e77716 | ||
|
|
98227928af | ||
|
|
430afbdc80 | ||
|
|
dfdb48ce7d | ||
|
|
257d1b2b52 | ||
|
|
07200437b6 | ||
|
|
ebfa00d555 | ||
|
|
421f555d57 | ||
|
|
4b60d941cc | ||
|
|
60d24ada62 | ||
|
|
a1c121e2fe | ||
|
|
b3775a3ca5 | ||
|
|
5d36c97a46 | ||
|
|
4bdd412c15 | ||
|
|
46e3bcd6b9 | ||
|
|
68a85ddf90 | ||
|
|
6eba9273cb | ||
|
|
9de151cc14 | ||
|
|
b8c9a5dd1f | ||
|
|
4dfb3507c4 | ||
|
|
0cac2d407b | ||
|
|
b3106cd932 | ||
|
|
c4a467fa96 | ||
|
|
cf7afbe7f6 | ||
|
|
a8d58aeac4 | ||
|
|
28209e9413 | ||
|
|
68ed56f2d9 | ||
|
|
0c03747807 | ||
|
|
dfe7ecbb5b | ||
|
|
3039d8edfa | ||
|
|
7c850cc082 | ||
|
|
0e07ee3d81 | ||
|
|
14b820f2c1 | ||
|
|
9366fa3b45 | ||
|
|
d356e9e65f | ||
|
|
230ebffd0e | ||
|
|
5eedc1edca | ||
|
|
fa6db9411b | ||
|
|
b88e42a52d | ||
|
|
8d1b27d130 | ||
|
|
1148d37102 | ||
|
|
88704ce417 | ||
|
|
9f69d2cb76 | ||
|
|
644f54bfba | ||
|
|
755c4da8cc | ||
|
|
483c77fba8 | ||
|
|
55fd71c5e1 | ||
|
|
1991bd437f | ||
|
|
22af796302 | ||
|
|
a70931ffbf | ||
|
|
2f7dd177c1 | ||
|
|
d35a053efd | ||
|
|
87cc1f9415 | ||
|
|
e7bb524362 | ||
|
|
9e03e1c742 | ||
|
|
6fbfcef1fa | ||
|
|
9471510086 | ||
|
|
56320468fd | ||
|
|
53a70bbffb | ||
|
|
4f2e547bd6 | ||
|
|
af82fa7b62 | ||
|
|
6bd1a5ab80 | ||
|
|
93568f6fd0 | ||
|
|
1a3bf81c19 | ||
|
|
2eeabe3998 | ||
|
|
b1d86a7a2d | ||
|
|
6cfc9ba47b | ||
|
|
350474540b | ||
|
|
1136e58de7 | ||
|
|
558ce4713c | ||
|
|
69c6f50dfb | ||
|
|
babe01321c | ||
|
|
ff61dc4391 | ||
|
|
4da6ede9d0 | ||
|
|
0b1bab873e | ||
|
|
6c60341ce8 | ||
|
|
e1c1878130 | ||
|
|
fea5a39740 | ||
|
|
9f2221a885 | ||
|
|
977cd2505e | ||
|
|
e3f85a7c0f | ||
|
|
d81339722e | ||
|
|
f5c09e02de | ||
|
|
5e102526ec | ||
|
|
3c2efd4b67 | ||
|
|
7bb93a7c32 | ||
|
|
ac8f4ae2f9 | ||
|
|
1ff49188b1 | ||
|
|
2c329023c2 | ||
|
|
4457a300bf | ||
|
|
397496aff0 | ||
|
|
7458fe0d81 | ||
|
|
453c81f634 | ||
|
|
eebce4b54d | ||
|
|
f7976e18f1 | ||
|
|
d951fe9988 | ||
|
|
fd4da78075 | ||
|
|
a025ae3f54 | ||
|
|
a75ba0d903 | ||
|
|
99318ae2ba | ||
|
|
8369bcb605 | ||
|
|
338cc69034 | ||
|
|
4526c52fe8 | ||
|
|
81f9bda502 | ||
|
|
46bc5463ca | ||
|
|
bb346537ba | ||
|
|
3b5f0d5c30 | ||
|
|
808ce969d9 | ||
|
|
16f8339f3c | ||
|
|
25d68c3904 | ||
|
|
77ad8e1a80 | ||
|
|
9e8c18075a | ||
|
|
4278cdfd29 | ||
|
|
142c2bd0f0 | ||
|
|
13ee8686bb | ||
|
|
5c06df2635 | ||
|
|
d69bf76341 | ||
|
|
d0d1b295b4 | ||
|
|
e450191548 | ||
|
|
3aee83f3d8 | ||
|
|
3610a2bc8d | ||
|
|
ab559d4620 | ||
|
|
15d308d4c5 | ||
|
|
e7ea66afb5 | ||
|
|
d9a1d8bde6 | ||
|
|
9ac4486403 | ||
|
|
428afabaa3 | ||
|
|
9162189fa6 | ||
|
|
154ca9b760 | ||
|
|
9fcead3973 | ||
|
|
2fd9d0359c | ||
|
|
4cf9e030a2 | ||
|
|
5570d14179 | ||
|
|
fd3c546994 | ||
|
|
e81ae05df9 | ||
|
|
cb394f3a1c | ||
|
|
03ed057ff6 | ||
|
|
807290ff38 | ||
|
|
40a4b4fa7a | ||
|
|
b7d2ad3651 | ||
|
|
faac286f70 | ||
|
|
c6c7f6f907 | ||
|
|
ccd0da58cb | ||
|
|
500e5f1b5d | ||
|
|
ebb6177271 | ||
|
|
951c256980 | ||
|
|
a134ef03dd | ||
|
|
f21a9d16d0 | ||
|
|
42c3bb00ec | ||
|
|
0d0fcfe49a | ||
|
|
3f0db92be6 | ||
|
|
ec24c79da1 | ||
|
|
f5fffbea04 | ||
|
|
fb0a2ef248 | ||
|
|
240f482e8e | ||
|
|
885031d3ce | ||
|
|
eb212de70e | ||
|
|
68323d6def | ||
|
|
7c492071f1 | ||
|
|
8fbdef2a1d | ||
|
|
8f64784781 | ||
|
|
1bd6095c60 | ||
|
|
e133a5f0f3 | ||
|
|
b47886416e | ||
|
|
c522d611d1 | ||
|
|
f6d9b6b7aa | ||
|
|
1de6bca12d | ||
|
|
2de9b862d8 | ||
|
|
3c649df3df | ||
|
|
9bf38d9e4d | ||
|
|
dee40f038d | ||
|
|
d0aa907418 | ||
|
|
8dbdffc9b4 | ||
|
|
f409780455 | ||
|
|
bebcd99b3b | ||
|
|
99aeaddf42 | ||
|
|
793398216f | ||
|
|
b353ed3157 | ||
|
|
8637212a17 | ||
|
|
c42ce38e1c | ||
|
|
76d228a603 | ||
|
|
5cb52faa10 | ||
|
|
e67e822845 | ||
|
|
a52686ca3b | ||
|
|
7a1e6cccfd | ||
|
|
1166b6c2fb | ||
|
|
7a6693694c | ||
|
|
6ed423d1af | ||
|
|
8b69444602 | ||
|
|
7de5e8d9c8 | ||
|
|
844e3d1934 | ||
|
|
9eafbbc0ae | ||
|
|
74677e3d54 | ||
|
|
57f4638742 | ||
|
|
c7a32f2d56 | ||
|
|
d2dc64aa2d | ||
|
|
edb2a34c51 | ||
|
|
dbd9b1c5c4 | ||
|
|
c3b758845d | ||
|
|
4d770549fc | ||
|
|
a67674bae1 | ||
|
|
462a61da73 | ||
|
|
ae8a7f6320 | ||
|
|
a23c6a0996 | ||
|
|
e6594cecce | ||
|
|
85ed5d27f5 | ||
|
|
6ac9e11245 | ||
|
|
9fbd809344 | ||
|
|
63a098d757 | ||
|
|
b68d9ed672 | ||
|
|
5934eb0559 | ||
|
|
b998d719a6 | ||
|
|
33832244cf | ||
|
|
df8ea3b65c | ||
|
|
7067d4a499 | ||
|
|
3e764eafd9 | ||
|
|
4169a41eda | ||
|
|
379eab19ad | ||
|
|
6cba1f3b61 | ||
|
|
5dad2404d3 | ||
|
|
a1b20eea2c | ||
|
|
af075e7b8b | ||
|
|
ef0a699919 | ||
|
|
8279ef0e05 | ||
|
|
c1cee8e951 | ||
|
|
4c17373311 | ||
|
|
c20972de3f | ||
|
|
64401f1031 | ||
|
|
1f895d0f27 | ||
|
|
5aa8ea10c3 | ||
|
|
cf65d4a909 | ||
|
|
079f627b34 | ||
|
|
f6c6cbc343 | ||
|
|
c6cdea77b6 | ||
|
|
b1fd3429ae | ||
|
|
dc161ef08d | ||
|
|
9502e789b3 | ||
|
|
a71aed0233 | ||
|
|
eda8dc278b | ||
|
|
23a7ffb167 | ||
|
|
ab11aaa694 | ||
|
|
83b5a2709d | ||
|
|
f67f94fd6b | ||
|
|
4e8ae037bb | ||
|
|
04d8f46ff3 | ||
|
|
3191de68b8 | ||
|
|
047de370d6 | ||
|
|
e21dc5f026 | ||
|
|
f3da56773e | ||
|
|
5197758579 | ||
|
|
d48af7c4c2 | ||
|
|
bbafe0fc46 | ||
|
|
9a91f917e0 | ||
|
|
1f681f02ae | ||
|
|
778a86e7eb | ||
|
|
78da033441 | ||
|
|
6eb923d22f | ||
|
|
e602afcd5f | ||
|
|
bf1115d417 | ||
|
|
b51038b9e7 | ||
|
|
44e0b58c66 | ||
|
|
3f7d5274a2 | ||
|
|
c8f5f6045e | ||
|
|
6bcc2064a2 | ||
|
|
3f9872d8b3 | ||
|
|
5814cd5fae | ||
|
|
44f8c7c548 | ||
|
|
c2549308fe | ||
|
|
cf1a3bf4e7 | ||
|
|
f5d668af31 | ||
|
|
b7bd4e5c51 | ||
|
|
62373f969c | ||
|
|
ceb651fdd1 | ||
|
|
8fa0f4a232 | ||
|
|
30f2e3b606 | ||
|
|
7d3d997083 | ||
|
|
3955e2c84a | ||
|
|
6a6c41df26 | ||
|
|
1dfd2aec50 | ||
|
|
8885f543ae | ||
|
|
8804e6c2ca | ||
|
|
88b7c214f7 | ||
|
|
297f3da5db | ||
|
|
ec4d634677 | ||
|
|
0ddec99704 | ||
|
|
64cec1de3b | ||
|
|
fdaf5d1fc8 | ||
|
|
6baf71dca5 | ||
|
|
9d75a72a02 | ||
|
|
4bf66d6a63 | ||
|
|
8d9a550107 | ||
|
|
a076278026 | ||
|
|
998dac44ee | ||
|
|
871e81bd71 | ||
|
|
d892d16b51 | ||
|
|
4cbf2781e5 | ||
|
|
69653b1c7c | ||
|
|
39390865da | ||
|
|
4952cd5afb | ||
|
|
e86df96bfe | ||
|
|
ef5195db90 | ||
|
|
bc7e62db9f | ||
|
|
9a7ccc20e8 | ||
|
|
154e7a6a8a | ||
|
|
ba56019f2d | ||
|
|
31d289c504 | ||
|
|
a42b22966a | ||
|
|
17db62d463 | ||
|
|
d6d40ea839 | ||
|
|
eca8410dbb | ||
|
|
eb3a530e6c | ||
|
|
26e17074c2 | ||
|
|
6b775628d5 | ||
|
|
13eb9103a7 | ||
|
|
1fa82c1077 | ||
|
|
426190181b | ||
|
|
1232a9a03d | ||
|
|
e643c13292 | ||
|
|
522736e367 | ||
|
|
12fd9c29f0 | ||
|
|
0c871b67ca | ||
|
|
379e828fc1 | ||
|
|
fe1e8575ea | ||
|
|
8f14099adc | ||
|
|
852c1396ae | ||
|
|
b0f9195b57 | ||
|
|
98236736e0 | ||
|
|
8b93a2816c | ||
|
|
76d8a991c0 | ||
|
|
9186af1433 | ||
|
|
6a473f4c57 | ||
|
|
42224ab534 | ||
|
|
20bcf09459 | ||
|
|
5d1dd536e9 | ||
|
|
169b8807f7 | ||
|
|
5acdab849e | ||
|
|
2417084145 | ||
|
|
c16e62b5bf | ||
|
|
6fa7d90f1f | ||
|
|
1a3fd0227d | ||
|
|
2259b7dfcf | ||
|
|
33fd70d0e1 | ||
|
|
fc906bcaae | ||
|
|
184e111e39 | ||
|
|
ab62f50f5c | ||
|
|
3eac854356 | ||
|
|
f7892cd86b | ||
|
|
6e0260385c | ||
|
|
44300dabb0 | ||
|
|
580c3038c8 | ||
|
|
2a9175e964 | ||
|
|
5db20bbbe8 | ||
|
|
ae144dff39 | ||
|
|
9f533efc92 | ||
|
|
76e15c4166 | ||
|
|
62bb2daf16 | ||
|
|
59e292030a | ||
|
|
533fc7e444 | ||
|
|
e5c5338eb6 | ||
|
|
9463acb131 | ||
|
|
e69749350b | ||
|
|
42749187ff | ||
|
|
d5c2038d58 | ||
|
|
5843a61c9f | ||
|
|
ecde2c9d7f | ||
|
|
abd597c7a6 | ||
|
|
b35e1d350b | ||
|
|
918399458d | ||
|
|
72d9b7028b | ||
|
|
ed9790c840 | ||
|
|
fd04bc4820 | ||
|
|
58a213b7f9 | ||
|
|
7f8bbfb4bd | ||
|
|
8a3e64b3cd | ||
|
|
572de86d06 | ||
|
|
88c33fa4d4 | ||
|
|
ee1c6a1ce1 | ||
|
|
09e90bea8b | ||
|
|
bf758412c1 | ||
|
|
0bbbf5b431 | ||
|
|
87b2236cfb | ||
|
|
855d2bb87f | ||
|
|
13b54a3b4d | ||
|
|
5d24ef4849 | ||
|
|
b2da451e8d | ||
|
|
139b66fa76 | ||
|
|
390ed5d7f1 | ||
|
|
16b5fb6b56 | ||
|
|
6205d6c885 | ||
|
|
e5ea65da82 | ||
|
|
9caa5feff5 | ||
|
|
7f616356d2 | ||
|
|
155b5dd42f | ||
|
|
41aaf428bf | ||
|
|
a17cdf33ea | ||
|
|
3d5e879d5f | ||
|
|
1a73ad3a32 | ||
|
|
abb5e7cd39 | ||
|
|
cdae6de52e | ||
|
|
910fe77ca3 | ||
|
|
b9a93c82d7 | ||
|
|
1c040bd616 | ||
|
|
166c537938 | ||
|
|
79c8de5fd0 | ||
|
|
7e6d2102a5 | ||
|
|
4f7d6132fc | ||
|
|
b7fcc70cd9 | ||
|
|
43ad27931c | ||
|
|
68dc3f75eb | ||
|
|
e67eb6d5b4 | ||
|
|
2e060bc85f | ||
|
|
22f80e4b19 | ||
|
|
1ef434f95e | ||
|
|
5676702da6 | ||
|
|
2fe0539600 | ||
|
|
13df5adecd | ||
|
|
f33f808dc7 | ||
|
|
82ed9ef25e | ||
|
|
0aa26d791d | ||
|
|
255f278f5e | ||
|
|
1c76f9a0ff | ||
|
|
9a308e25a3 | ||
|
|
4b52f8c823 | ||
|
|
8f7145f80a | ||
|
|
eb17f053f7 | ||
|
|
304e714331 | ||
|
|
525685f942 | ||
|
|
e2477b66c6 | ||
|
|
58d51e039c | ||
|
|
cb5b5167fc | ||
|
|
7d68378c3c | ||
|
|
87da6a947d | ||
|
|
0e644da6ac | ||
|
|
d61a7c360e | ||
|
|
ab286d851d | ||
|
|
e8b60ab292 | ||
|
|
1cb7e70b42 | ||
|
|
25b9c22845 | ||
|
|
47166104fa | ||
|
|
d0e0622058 | ||
|
|
87c75a1e43 | ||
|
|
d1cc08f1d9 | ||
|
|
6619626486 | ||
|
|
1681ba090c | ||
|
|
c36e1ad056 | ||
|
|
d79c7f70be | ||
|
|
b7eb2e4746 | ||
|
|
4db9b8b0e3 | ||
|
|
f3a537d5c2 | ||
|
|
6a704b8a77 | ||
|
|
df896877f8 | ||
|
|
3cb2d3dfff | ||
|
|
d5f5e79661 | ||
|
|
82dffe9d0e | ||
|
|
d7825dd7db | ||
|
|
c0a2f429b6 | ||
|
|
8c8cab9309 | ||
|
|
7ad4cb0078 | ||
|
|
1bff790628 | ||
|
|
0010ca3e03 | ||
|
|
9edb5dae88 | ||
|
|
85b49096dc | ||
|
|
a7eaa10220 | ||
|
|
5a46ce55b6 | ||
|
|
dbebbecab1 | ||
|
|
c7593e66bf | ||
|
|
d52dc69279 | ||
|
|
1c65fa212d | ||
|
|
be88eb00f8 | ||
|
|
65c60f5386 | ||
|
|
83a043e639 | ||
|
|
8175192f60 | ||
|
|
7d2c975d73 | ||
|
|
e5153737d8 | ||
|
|
88766a02c7 | ||
|
|
8162394797 | ||
|
|
fd001186dd | ||
|
|
128fef8838 | ||
|
|
1e25a0654a | ||
|
|
82b0ff6008 | ||
|
|
ca7c3630f9 | ||
|
|
260bb8218f | ||
|
|
a691de7d55 | ||
|
|
5674c484e2 | ||
|
|
9286dc284c | ||
|
|
624b71bd39 | ||
|
|
b7ea605a3b | ||
|
|
489a787de8 | ||
|
|
84150036ed | ||
|
|
619123720c | ||
|
|
cc98ff9fe6 | ||
|
|
39d159f38c | ||
|
|
ca8eb90cec | ||
|
|
f8e3e28d85 | ||
|
|
4d868489bd | ||
|
|
b06f09f6af | ||
|
|
bf00f5f945 | ||
|
|
6ceb6cc301 | ||
|
|
00a6afaa8e |
@@ -4,110 +4,101 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
resource_class: medium+
|
resource_class: medium+
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:9-browsers
|
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||||
environment:
|
environment:
|
||||||
CONTRACTS_COMMIT_HASH: '9ed05f5'
|
CONTRACTS_COMMIT_HASH: '9ed05f5'
|
||||||
working_directory: ~/repo
|
working_directory: ~/repo
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- run: echo 'export PATH=$HOME/CIRCLE_PROJECT_REPONAME/node_modules/.bin:$PATH' >> $BASH_ENV
|
- run: echo 'export PATH=$HOME/CIRCLE_PROJECT_REPONAME/node_modules/.bin:$PATH' >> $BASH_ENV
|
||||||
|
# HACK(feuGeneA): commented out this hack as we're changing
|
||||||
|
# from a circleci-maintained container to a different
|
||||||
|
# container, and this hack may not apply anymore, as
|
||||||
|
# suggested by the non-existance of `/home/circleci/.bashrc`
|
||||||
|
# when running the command below.
|
||||||
|
# - run:
|
||||||
|
# # HACK(albrow): Without this, yarn commands will sometimes
|
||||||
|
# # fail with a "permission denied" error.
|
||||||
|
# name: Set npm path
|
||||||
|
# command: npm set prefix=/home/circleci/npm && echo 'export PATH=$HOME/circleci/npm/bin:$PATH' >> /home/circleci/.bashrc
|
||||||
- run:
|
- run:
|
||||||
name: install-yarn
|
name: install-yarn
|
||||||
command: sudo npm install --global yarn@1.9.4
|
command: npm install --global yarn@1.17.0
|
||||||
- run:
|
- run:
|
||||||
name: yarn
|
name: yarn
|
||||||
command: yarn --frozen-lockfile --ignore-engines install || yarn --frozen-lockfile --ignore-engines install
|
command: yarn --frozen-lockfile --ignore-engines install || yarn --frozen-lockfile --ignore-engines install
|
||||||
- setup_remote_docker
|
- setup_remote_docker
|
||||||
- run: yarn build:ci:no_website
|
- run: yarn build:ci
|
||||||
- run: yarn build:ts
|
- run: yarn build:ts
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: repo-{{ .Environment.CIRCLE_SHA1 }}
|
key: repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
paths:
|
paths:
|
||||||
- ~/repo
|
- ~/repo
|
||||||
build-website:
|
- store_artifacts:
|
||||||
|
path: ~/repo/packages/abi-gen/test-cli/output
|
||||||
|
- store_artifacts:
|
||||||
|
path: ~/repo/packages/abi-gen-wrappers/generated_docs
|
||||||
|
test-contracts-ganache:
|
||||||
resource_class: medium+
|
resource_class: medium+
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:9-browsers
|
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||||
working_directory: ~/repo
|
working_directory: ~/repo
|
||||||
steps:
|
steps:
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
- run: cd packages/website && yarn build:prod
|
- 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 @0x/contracts-staking
|
||||||
test-contracts-ganache:
|
test-exchange-ganache-3.0:
|
||||||
|
resource_class: medium+
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:9-browsers
|
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||||
working_directory: ~/repo
|
working_directory: ~/repo
|
||||||
steps:
|
steps:
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
- run: yarn wsrun test:circleci @0x/contracts-multisig
|
|
||||||
- run: yarn wsrun test:circleci @0x/contracts-utils
|
|
||||||
- run: yarn wsrun test:circleci @0x/contracts-exchange-libs
|
|
||||||
- run: yarn wsrun test:circleci @0x/contracts-erc20
|
|
||||||
- run: yarn wsrun test:circleci @0x/contracts-erc721
|
|
||||||
- run: yarn wsrun test:circleci @0x/contracts-extensions
|
|
||||||
- run: yarn wsrun test:circleci @0x/contracts-asset-proxy
|
|
||||||
- run: yarn wsrun test:circleci @0x/contracts-exchange
|
- run: yarn wsrun test:circleci @0x/contracts-exchange
|
||||||
- run: yarn wsrun test:circleci @0x/contracts-exchange-forwarder
|
test-contracts-rest-ganache-3.0:
|
||||||
test-contracts-geth:
|
resource_class: medium+
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:9-browsers
|
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||||
- image: 0xorg/devnet
|
|
||||||
working_directory: ~/repo
|
working_directory: ~/repo
|
||||||
steps:
|
steps:
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
# HACK(albrow): we need to sleep 10 seconds to ensure the devnet is
|
- 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-dev-utils @0x/contracts-staking
|
||||||
# initialized
|
# TODO(dorothy-zbornak): Re-enable after updating this package for 3.0.
|
||||||
- run: sleep 10 && TEST_PROVIDER=geth yarn wsrun test @0x/contracts-multisig
|
# - run: yarn wsrun test:circleci @0x/contracts-extensions
|
||||||
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-utils
|
# TODO(abandeali): Re-enable after this package is complete.
|
||||||
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-exchange-libs
|
# - run: yarn wsrun test:circleci @0x/contracts-coordinator
|
||||||
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-erc20
|
|
||||||
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-erc721
|
|
||||||
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-extensions
|
|
||||||
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-asset-proxy
|
|
||||||
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-exchange
|
|
||||||
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-exchange-forwarder
|
|
||||||
test-publish:
|
test-publish:
|
||||||
resource_class: medium+
|
resource_class: medium+
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:9-browsers
|
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||||
- image: 0xorg/verdaccio
|
- image: 0xorg/verdaccio
|
||||||
working_directory: ~/repo
|
working_directory: ~/repo
|
||||||
steps:
|
steps:
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
- run: yarn test:publish:circleci
|
- run:
|
||||||
|
command: yarn test:publish:circleci
|
||||||
|
no_output_timeout: 1800
|
||||||
test-doc-generation:
|
test-doc-generation:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:9-browsers
|
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||||
working_directory: ~/repo
|
working_directory: ~/repo
|
||||||
steps:
|
steps:
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
- run: yarn test:generate_docs:circleci
|
- run:
|
||||||
test-pipeline:
|
command: yarn test:generate_docs:circleci
|
||||||
docker:
|
no_output_timeout: 1200
|
||||||
- image: circleci/node:9
|
|
||||||
- image: postgres:11-alpine
|
|
||||||
working_directory: ~/repo
|
|
||||||
steps:
|
|
||||||
- restore_cache:
|
|
||||||
keys:
|
|
||||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
|
||||||
- run: ZEROEX_DATA_PIPELINE_TEST_DB_URL='postgresql://postgres@localhost/postgres' yarn wsrun test:circleci @0x/pipeline
|
|
||||||
- save_cache:
|
|
||||||
key: coverage-pipeline-{{ .Environment.CIRCLE_SHA1 }}
|
|
||||||
paths:
|
|
||||||
- ~/repo/packages/pipeline/coverage/lcov.info
|
|
||||||
test-rest:
|
test-rest:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:9-browsers
|
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||||
working_directory: ~/repo
|
working_directory: ~/repo
|
||||||
steps:
|
steps:
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
@@ -115,15 +106,17 @@ jobs:
|
|||||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
- run: yarn wsrun test:circleci @0x/contracts-test-utils
|
- run: yarn wsrun test:circleci @0x/contracts-test-utils
|
||||||
- run: yarn wsrun test:circleci @0x/abi-gen
|
- run: yarn wsrun test:circleci @0x/abi-gen
|
||||||
|
# TODO (xianny): Needs to be updated for 3.0
|
||||||
|
# - run: yarn wsrun test:circleci @0x/asset-buyer
|
||||||
|
- run: yarn wsrun test:circleci @0x/contract-artifacts
|
||||||
- run: yarn wsrun test:circleci @0x/assert
|
- run: yarn wsrun test:circleci @0x/assert
|
||||||
- run: yarn wsrun test:circleci @0x/base-contract
|
- run: yarn wsrun test:circleci @0x/base-contract
|
||||||
- run: yarn wsrun test:circleci @0x/connect
|
# TODO (xianny): Needs to be updated for 3.0
|
||||||
|
# - run: yarn wsrun test:circleci @0x/connect
|
||||||
- run: yarn wsrun test:circleci @0x/contract-wrappers
|
- run: yarn wsrun test:circleci @0x/contract-wrappers
|
||||||
- run: yarn wsrun test:circleci @0x/dev-utils
|
- run: yarn wsrun test:circleci @0x/dev-utils
|
||||||
- run: yarn wsrun test:circleci @0x/json-schemas
|
- run: yarn wsrun test:circleci @0x/json-schemas
|
||||||
- run: yarn wsrun test:circleci @0x/metacoin
|
|
||||||
- run: yarn wsrun test:circleci @0x/order-utils
|
- run: yarn wsrun test:circleci @0x/order-utils
|
||||||
- run: yarn wsrun test:circleci @0x/order-watcher
|
|
||||||
- run: yarn wsrun test:circleci @0x/sol-compiler
|
- run: yarn wsrun test:circleci @0x/sol-compiler
|
||||||
- run: yarn wsrun test:circleci @0x/sol-tracing-utils
|
- run: yarn wsrun test:circleci @0x/sol-tracing-utils
|
||||||
- run: yarn wsrun test:circleci @0x/sol-doc
|
- run: yarn wsrun test:circleci @0x/sol-doc
|
||||||
@@ -139,6 +132,10 @@ jobs:
|
|||||||
key: coverage-assert-{{ .Environment.CIRCLE_SHA1 }}
|
key: coverage-assert-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
paths:
|
paths:
|
||||||
- ~/repo/packages/assert/coverage/lcov.info
|
- ~/repo/packages/assert/coverage/lcov.info
|
||||||
|
- save_cache:
|
||||||
|
key: coverage-asset-buyer-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
|
paths:
|
||||||
|
- ~/repo/packages/asset-buyer/coverage/lcov.info
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: coverage-base-contract-{{ .Environment.CIRCLE_SHA1 }}
|
key: coverage-base-contract-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
paths:
|
paths:
|
||||||
@@ -159,18 +156,10 @@ jobs:
|
|||||||
key: coverage-json-schemas-{{ .Environment.CIRCLE_SHA1 }}
|
key: coverage-json-schemas-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
paths:
|
paths:
|
||||||
- ~/repo/packages/json-schemas/coverage/lcov.info
|
- ~/repo/packages/json-schemas/coverage/lcov.info
|
||||||
- save_cache:
|
|
||||||
key: coverage-metacoin-{{ .Environment.CIRCLE_SHA1 }}
|
|
||||||
paths:
|
|
||||||
- ~/repo/packages/metacoin/coverage/lcov.info
|
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: coverage-order-utils-{{ .Environment.CIRCLE_SHA1 }}
|
key: coverage-order-utils-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
paths:
|
paths:
|
||||||
- ~/repo/packages/order-utils/coverage/lcov.info
|
- ~/repo/packages/order-utils/coverage/lcov.info
|
||||||
- save_cache:
|
|
||||||
key: coverage-order-watcher-{{ .Environment.CIRCLE_SHA1 }}
|
|
||||||
paths:
|
|
||||||
- ~/repo/packages/order-watcher/coverage/lcov.info
|
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: coverage-sol-compiler-{{ .Environment.CIRCLE_SHA1 }}
|
key: coverage-sol-compiler-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
paths:
|
paths:
|
||||||
@@ -194,31 +183,38 @@ jobs:
|
|||||||
test-python:
|
test-python:
|
||||||
working_directory: ~/repo
|
working_directory: ~/repo
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/python
|
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||||
- image: 0xorg/ganache-cli:2.2.2
|
- image: 0xorg/ganache-cli:2.2.2
|
||||||
- image: 0xorg/launch-kit-ci
|
- image: 0xorg/launch-kit-backend:74bcc39
|
||||||
|
environment:
|
||||||
|
RPC_URL: http://localhost:8545
|
||||||
|
NETWORK_ID: 50
|
||||||
|
WHITELIST_ALL_TOKENS: True
|
||||||
command: |
|
command: |
|
||||||
yarn start:ts -p 3000:3000
|
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:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- run: sudo chown -R circleci:circleci /usr/local/bin
|
|
||||||
- run: sudo chown -R circleci:circleci /usr/local/lib/python3.7
|
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
key: deps9-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
|
key: installed-py-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
|
- restore_cache:
|
||||||
|
keys:
|
||||||
|
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
- run:
|
- run:
|
||||||
command: |
|
command: |
|
||||||
cd python-packages
|
cd python-packages
|
||||||
python -m ensurepip
|
python -m ensurepip
|
||||||
|
./pre_install
|
||||||
./install
|
./install
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: deps9-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
|
key: installed-py-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
paths:
|
paths:
|
||||||
- '/usr/local/bin'
|
- '/usr/local/bin'
|
||||||
- '/usr/local/lib/python3.7/site-packages'
|
- '/usr/local/lib/python3.7/site-packages'
|
||||||
- run:
|
- run:
|
||||||
command: |
|
command: |
|
||||||
cd python-packages
|
cd python-packages
|
||||||
./cmd_pkgs_in_dep_order.py coverage run setup.py test
|
./parallel_without_sra_client coverage run setup.py test
|
||||||
|
./build_docs
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: coverage-python-contract-addresses-{{ .Environment.CIRCLE_SHA1 }}
|
key: coverage-python-contract-addresses-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
paths:
|
paths:
|
||||||
@@ -243,23 +239,37 @@ jobs:
|
|||||||
key: coverage-python-sra-client-{{ .Environment.CIRCLE_SHA1 }}
|
key: coverage-python-sra-client-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
paths:
|
paths:
|
||||||
- ~/repo/python-packages/sra_client/.coverage
|
- ~/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:
|
||||||
|
path: ~/repo/python-packages/contract_artifacts/build
|
||||||
|
- store_artifacts:
|
||||||
|
path: ~/repo/python-packages/contract_wrappers/build
|
||||||
|
- store_artifacts:
|
||||||
|
path: ~/repo/python-packages/json_schemas/build
|
||||||
|
- store_artifacts:
|
||||||
|
path: ~/repo/python-packages/middlewares/build
|
||||||
|
- store_artifacts:
|
||||||
|
path: ~/repo/python-packages/order_utils/build
|
||||||
|
- store_artifacts:
|
||||||
|
path: ~/repo/python-packages/sra_client/build
|
||||||
test-rest-python:
|
test-rest-python:
|
||||||
working_directory: ~/repo
|
working_directory: ~/repo
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/python
|
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- run: sudo chown -R circleci:circleci /usr/local/bin
|
|
||||||
- run: sudo chown -R circleci:circleci /usr/local/lib/python3.7
|
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
key: deps9-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
|
key: installed-py-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
- run:
|
- run:
|
||||||
command: |
|
command: |
|
||||||
cd python-packages/order_utils
|
cd python-packages/order_utils
|
||||||
python -m ensurepip
|
python -m ensurepip
|
||||||
python -m pip install .
|
python -m pip install .
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: deps9-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
|
key: installed-py-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
paths:
|
paths:
|
||||||
- '/usr/local/bin'
|
- '/usr/local/bin'
|
||||||
- '/usr/local/lib/python3.7/site-packages'
|
- '/usr/local/lib/python3.7/site-packages'
|
||||||
@@ -274,23 +284,26 @@ jobs:
|
|||||||
static-tests-python:
|
static-tests-python:
|
||||||
working_directory: ~/repo
|
working_directory: ~/repo
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/python
|
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- run: sudo chown -R circleci:circleci /usr/local/bin
|
|
||||||
- run: sudo chown -R circleci:circleci /usr/local/lib/python3.7
|
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
key: deps9-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
|
key: installed-py-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
|
- restore_cache:
|
||||||
|
keys:
|
||||||
|
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
- run:
|
- run:
|
||||||
command: |
|
command: |
|
||||||
python -m ensurepip
|
python -m ensurepip
|
||||||
cd python-packages
|
cd python-packages
|
||||||
|
./pre_install
|
||||||
./install
|
./install
|
||||||
./lint
|
./lint
|
||||||
static-tests:
|
static-tests:
|
||||||
|
resource_class: large
|
||||||
working_directory: ~/repo
|
working_directory: ~/repo
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:9-browsers
|
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||||
steps:
|
steps:
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
@@ -298,11 +311,12 @@ jobs:
|
|||||||
- run: yarn lerna run lint
|
- run: yarn lerna run lint
|
||||||
- run: yarn prettier:ci
|
- run: yarn prettier:ci
|
||||||
- run: yarn deps_versions:ci
|
- run: yarn deps_versions:ci
|
||||||
|
- run: yarn diff_md_docs:ci
|
||||||
- run: cd packages/0x.js && yarn build:umd:prod
|
- run: cd packages/0x.js && yarn build:umd:prod
|
||||||
- run: yarn bundlewatch
|
- run: yarn bundlewatch
|
||||||
submit-coverage:
|
submit-coverage:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:9-browsers
|
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||||
working_directory: ~/repo
|
working_directory: ~/repo
|
||||||
steps:
|
steps:
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
@@ -314,6 +328,9 @@ jobs:
|
|||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- coverage-assert-{{ .Environment.CIRCLE_SHA1 }}
|
- coverage-assert-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
|
- restore_cache:
|
||||||
|
keys:
|
||||||
|
- coverage-asset-buyer-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- coverage-base-contract-{{ .Environment.CIRCLE_SHA1 }}
|
- coverage-base-contract-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
@@ -329,15 +346,9 @@ jobs:
|
|||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- coverage-json-schemas-{{ .Environment.CIRCLE_SHA1 }}
|
- coverage-json-schemas-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
- restore_cache:
|
|
||||||
keys:
|
|
||||||
- coverage-metacoin-{{ .Environment.CIRCLE_SHA1 }}
|
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- coverage-order-utils-{{ .Environment.CIRCLE_SHA1 }}
|
- coverage-order-utils-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
- restore_cache:
|
|
||||||
keys:
|
|
||||||
- coverage-order-watcher-{{ .Environment.CIRCLE_SHA1 }}
|
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- coverage-sol-compiler-{{ .Environment.CIRCLE_SHA1 }}
|
- coverage-sol-compiler-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
@@ -380,16 +391,10 @@ workflows:
|
|||||||
main:
|
main:
|
||||||
jobs:
|
jobs:
|
||||||
- build
|
- build
|
||||||
- build-website:
|
- test-exchange-ganache-3.0:
|
||||||
requires:
|
requires:
|
||||||
- build
|
- build
|
||||||
- test-contracts-ganache:
|
- test-contracts-rest-ganache-3.0:
|
||||||
requires:
|
|
||||||
- build
|
|
||||||
- test-contracts-geth:
|
|
||||||
requires:
|
|
||||||
- build
|
|
||||||
- test-pipeline:
|
|
||||||
requires:
|
requires:
|
||||||
- build
|
- build
|
||||||
- test-rest:
|
- test-rest:
|
||||||
@@ -406,11 +411,16 @@ workflows:
|
|||||||
- build
|
- build
|
||||||
- submit-coverage:
|
- submit-coverage:
|
||||||
requires:
|
requires:
|
||||||
|
- test-contracts-rest-ganache-3.0
|
||||||
|
- test-exchange-ganache-3.0
|
||||||
- test-rest
|
- test-rest
|
||||||
- test-python
|
- static-tests
|
||||||
- static-tests-python:
|
# - test-python:
|
||||||
requires:
|
# requires:
|
||||||
- test-python
|
# - build
|
||||||
- test-python
|
# - test-rest
|
||||||
|
# - static-tests-python:
|
||||||
|
# requires:
|
||||||
|
# - test-python
|
||||||
# skip python tox run for now, as we don't yet have multiple test environments to support.
|
# skip python tox run for now, as we don't yet have multiple test environments to support.
|
||||||
#- test-rest-python
|
# - test-rest-python
|
||||||
|
|||||||
7
.gitattributes
vendored
7
.gitattributes
vendored
@@ -1,7 +1,8 @@
|
|||||||
*.sol linguist-language=Solidity
|
*.sol linguist-language=Solidity
|
||||||
|
|
||||||
# Automatically collapse generated files in GitHub.
|
# Automatically collapse generated files in GitHub.
|
||||||
*.svg linguist-generated
|
*.svg linguist-generated=true
|
||||||
packages/contract-artifacts/artifacts/*json linguist-generated
|
packages/contract-artifacts/artifacts/*json linguist-generated=true
|
||||||
packages/abi-gen-wrappers/wrappers/*.ts liguist-generated
|
packages/abi-gen-wrappers/src/generated-wrappers/*.ts linguist-generated=true
|
||||||
|
packages/contract-wrappers/src/generated-wrappers/*.ts linguist-generated=true
|
||||||
|
|
||||||
|
|||||||
73
.github/autolabeler.yml
vendored
73
.github/autolabeler.yml
vendored
@@ -1,43 +1,36 @@
|
|||||||
python: ['python-packages']
|
python: ['python-packages']
|
||||||
contracts: ['contracts']
|
contracts: ['contracts']
|
||||||
sol-doc: ['packages/sol-doc']
|
@0x/sol-doc: ['packages/sol-doc']
|
||||||
sol-resolver: ['packages/sol-resolver']
|
@0x/sol-resolver: ['packages/sol-resolver']
|
||||||
contracts-gen: ['packages/contracts-gen']
|
@0x/contracts-gen: ['packages/contracts-gen']
|
||||||
sra-spec: ['packages/sra-spec']
|
@0x/sra-spec: ['packages/sra-spec']
|
||||||
subproviders: ['packages/subproviders']
|
@0x/subproviders: ['packages/subproviders']
|
||||||
contract-addresses: ['packages/contract-addresses']
|
@0x/contract-addresses: ['packages/contract-addresses']
|
||||||
migrations: ['packages/migrations']
|
@0x/migrations: ['packages/migrations']
|
||||||
web3-wrapper: ['packages/web3-wrapper']
|
@0x/web3-wrapper: ['packages/web3-wrapper']
|
||||||
sol-compiler: ['packages/sol-compiler']
|
@0x/sol-compiler: ['packages/sol-compiler']
|
||||||
types: ['packages/types']
|
@0x/types: ['packages/types']
|
||||||
instant: ['packages/instant']
|
@0x/instant: ['packages/instant']
|
||||||
abi-gen-templates: ['packages/abi-gen-templates']
|
@0x/abi-gen-templates: ['packages/abi-gen-templates']
|
||||||
abi-gen: ['packages/abi-gen']
|
@0x/abi-gen: ['packages/abi-gen']
|
||||||
website: ['packages/website']
|
@0x/sol-coverage: ['packages/sol-coverage']
|
||||||
sol-coverage: ['packages/sol-coverage']
|
@0x/sol-profiler: ['packages/sol-profiler']
|
||||||
sol-profiler: ['packages/sol-profiler']
|
@0x/sol-trace: ['packages/sol-trace']
|
||||||
sol-trace: ['packages/sol-trace']
|
@0x/sol-tracing-utils: ['packages/sol-tracing-utils']
|
||||||
sol-tracing-utils: ['packages/sol-tracing-utils']
|
@0x/utils: ['packages/utils']
|
||||||
utils: ['packages/utils']
|
@0x/tslint-config: ['packages/tslint-config']
|
||||||
tslint-config: ['packages/tslint-config']
|
@0x/asset-buyer: ['packages/asset-buyer']
|
||||||
asset-buyer: ['packages/asset-buyer']
|
@0x/order-utils: ['packages/order-utils']
|
||||||
order-watcher: ['packages/order-watcher']
|
@0x/assert: ['packages/assert']
|
||||||
react-docs: ['packages/react-docs']
|
@0x/base-contract: ['packages/base-contract']
|
||||||
order-utils: ['packages/order-utils']
|
@0x/typescript-typings: ['packages/typescript-typings']
|
||||||
react-shared: ['packages/react-shared']
|
|
||||||
assert: ['packages/assert']
|
|
||||||
base-contract: ['packages/base-contract']
|
|
||||||
typescript-typings: ['packages/typescript-typings']
|
|
||||||
0x.js: ['packages/0x.js']
|
0x.js: ['packages/0x.js']
|
||||||
abi-gen-wrappers: ['packages/abi-gen-wrappers']
|
@0x/abi-gen-wrappers: ['packages/abi-gen-wrappers']
|
||||||
metacoin: ['packages/metacoin']
|
@0x/contract-artifacts: ['packages/contract-artifacts']
|
||||||
contract-artifacts: ['packages/contract-artifacts']
|
@0x/dev-utils: ['packages/dev-utils']
|
||||||
dev-utils: ['packages/dev-utils']
|
@0x/contract-wrappers: ['packages/contract-wrappers']
|
||||||
contract-wrappers: ['packages/contract-wrappers']
|
@0x/json-schemas: ['packages/json-schemas']
|
||||||
json-schemas: ['packages/json-schemas']
|
@0x/ethereum-types: ['ethereum-types']
|
||||||
ethereum-types: ['ethereum-types']
|
@0x/connect: ['packages/connect']
|
||||||
connect: ['packages/connect']
|
@0x/testnet-faucets: ['packages/testnet-faucets']
|
||||||
fill-scenarios: ['packages/fill-scenarios']
|
@0x/monorepo-scripts: ['packages/monorepo-scripts']
|
||||||
dev-tools-pages: ['packages/dev-tools-pages']
|
|
||||||
testnet-faucets: ['packages/testnet-faucets']
|
|
||||||
monorepo-scripts: ['packages/monorepo-scripts']
|
|
||||||
|
|||||||
68
.gitignore
vendored
68
.gitignore
vendored
@@ -40,9 +40,12 @@ build/Release
|
|||||||
node_modules/
|
node_modules/
|
||||||
jspm_packages/
|
jspm_packages/
|
||||||
|
|
||||||
# Typescript v1 declaration files
|
# TypeScript v1 declaration files
|
||||||
typings/
|
typings/
|
||||||
|
|
||||||
|
# NVM config
|
||||||
|
.nvmrc
|
||||||
|
|
||||||
# Optional npm cache directory
|
# Optional npm cache directory
|
||||||
.npm
|
.npm
|
||||||
.npmrc
|
.npmrc
|
||||||
@@ -75,14 +78,12 @@ TODO.md
|
|||||||
# VSCode file
|
# VSCode file
|
||||||
.vscode
|
.vscode
|
||||||
|
|
||||||
packages/website/public/bundle*
|
|
||||||
packages/dev-tools-pages/public/bundle*
|
|
||||||
packages/react-docs/example/public/bundle*
|
|
||||||
|
|
||||||
# server cli
|
# server cli
|
||||||
packages/testnet-faucets/server/
|
packages/testnet-faucets/server/
|
||||||
|
|
||||||
# generated contract artifacts/
|
# generated contract artifacts/
|
||||||
|
contracts/staking/generated-artifacts/
|
||||||
|
contracts/coordinator/generated-artifacts/
|
||||||
contracts/exchange/generated-artifacts/
|
contracts/exchange/generated-artifacts/
|
||||||
contracts/asset-proxy/generated-artifacts/
|
contracts/asset-proxy/generated-artifacts/
|
||||||
contracts/multisig/generated-artifacts/
|
contracts/multisig/generated-artifacts/
|
||||||
@@ -90,13 +91,32 @@ contracts/utils/generated-artifacts/
|
|||||||
contracts/exchange-libs/generated-artifacts/
|
contracts/exchange-libs/generated-artifacts/
|
||||||
contracts/erc20/generated-artifacts/
|
contracts/erc20/generated-artifacts/
|
||||||
contracts/erc721/generated-artifacts/
|
contracts/erc721/generated-artifacts/
|
||||||
|
contracts/erc1155/generated-artifacts/
|
||||||
contracts/extensions/generated-artifacts/
|
contracts/extensions/generated-artifacts/
|
||||||
contracts/exchange-forwarder/generated-artifacts/
|
contracts/exchange-forwarder/generated-artifacts/
|
||||||
|
contracts/dev-utils/generated-artifacts/
|
||||||
packages/sol-tracing-utils/test/fixtures/artifacts/
|
packages/sol-tracing-utils/test/fixtures/artifacts/
|
||||||
packages/metacoin/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
|
# generated contract wrappers
|
||||||
packages/abi-gen-wrappers/wrappers
|
packages/python-contract-wrappers/generated/
|
||||||
|
contracts/staking/generated-wrappers/
|
||||||
|
contracts/coordinator/generated-wrappers/
|
||||||
contracts/exchange/generated-wrappers/
|
contracts/exchange/generated-wrappers/
|
||||||
contracts/asset-proxy/generated-wrappers/
|
contracts/asset-proxy/generated-wrappers/
|
||||||
contracts/multisig/generated-wrappers/
|
contracts/multisig/generated-wrappers/
|
||||||
@@ -104,16 +124,34 @@ contracts/utils/generated-wrappers/
|
|||||||
contracts/exchange-libs/generated-wrappers/
|
contracts/exchange-libs/generated-wrappers/
|
||||||
contracts/erc20/generated-wrappers/
|
contracts/erc20/generated-wrappers/
|
||||||
contracts/erc721/generated-wrappers/
|
contracts/erc721/generated-wrappers/
|
||||||
|
contracts/erc1155/generated-wrappers/
|
||||||
contracts/extensions/generated-wrappers/
|
contracts/extensions/generated-wrappers/
|
||||||
contracts/exchange-forwarder/generated-wrappers/
|
contracts/exchange-forwarder/generated-wrappers/
|
||||||
packages/metacoin/src/contract_wrappers
|
contracts/dev-utils/generated-wrappers/
|
||||||
|
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
|
||||||
|
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/coordinator/__init__.py
|
||||||
|
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/coordinator_registry/__init__.py
|
||||||
|
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/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/weth9/__init__.py
|
||||||
|
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/zrx_token/__init__.py
|
||||||
|
|
||||||
# solc-bin in sol-compiler
|
# solc-bin in sol-compiler
|
||||||
packages/sol-compiler/solc_bin/
|
packages/sol-compiler/solc_bin/
|
||||||
|
|
||||||
# Monorepo scripts
|
|
||||||
packages/*/scripts/
|
|
||||||
|
|
||||||
# python stuff
|
# python stuff
|
||||||
.eggs
|
.eggs
|
||||||
.mypy_cache
|
.mypy_cache
|
||||||
@@ -123,3 +161,11 @@ python-packages/*/dist
|
|||||||
__pycache__
|
__pycache__
|
||||||
python-packages/*/src/*.egg-info
|
python-packages/*/src/*.egg-info
|
||||||
python-packages/*/.coverage
|
python-packages/*/.coverage
|
||||||
|
|
||||||
|
# python keeps package-local copies of json schemas
|
||||||
|
python-packages/json_schemas/src/zero_ex/json_schemas/schemas
|
||||||
|
|
||||||
|
# Doc README copy
|
||||||
|
packages/*/docs/README.md
|
||||||
|
|
||||||
|
.DS_Store
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
lib
|
lib
|
||||||
.nyc_output
|
.nyc_output
|
||||||
|
/contracts/staking/generated-wrappers
|
||||||
|
/contracts/staking/generated-artifacts
|
||||||
|
/contracts/coordinator/generated-wrappers
|
||||||
|
/contracts/coordinator/generated-artifacts
|
||||||
/contracts/exchange/generated-wrappers
|
/contracts/exchange/generated-wrappers
|
||||||
/contracts/exchange/generated-artifacts
|
/contracts/exchange/generated-artifacts
|
||||||
/contracts/asset-proxy/generated-wrappers
|
/contracts/asset-proxy/generated-wrappers
|
||||||
@@ -14,22 +18,38 @@ lib
|
|||||||
/contracts/erc20/generated-artifacts
|
/contracts/erc20/generated-artifacts
|
||||||
/contracts/erc721/generated-wrappers
|
/contracts/erc721/generated-wrappers
|
||||||
/contracts/erc721/generated-artifacts
|
/contracts/erc721/generated-artifacts
|
||||||
|
/contracts/erc1155/generated-wrappers
|
||||||
|
/contracts/erc1155/generated-artifacts
|
||||||
/contracts/extensions/generated-wrappers
|
/contracts/extensions/generated-wrappers
|
||||||
/contracts/extensions/generated-artifacts
|
/contracts/extensions/generated-artifacts
|
||||||
/contracts/exchange-forwarder/generated-wrappers
|
/contracts/exchange-forwarder/generated-wrappers
|
||||||
/contracts/exchange-forwarder/generated-artifacts
|
/contracts/exchange-forwarder/generated-artifacts
|
||||||
/packages/abi-gen-wrappers/src/generated-wrappers
|
/contracts/dev-utils/generated-wrappers
|
||||||
/packages/contract-artifacts/artifacts
|
/contracts/dev-utils/generated-artifacts
|
||||||
/python-packages/contract_artifacts/src/zero_ex/contract_artifacts/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
|
/packages/json-schemas/schemas
|
||||||
/python-packages/json_schemas/src/zero_ex/json_schemas/schemas
|
/python-packages/json_schemas/src/zero_ex/json_schemas/schemas
|
||||||
/packages/metacoin/src/contract_wrappers
|
|
||||||
/packages/metacoin/artifacts
|
|
||||||
/packages/sra-spec/public/
|
/packages/sra-spec/public/
|
||||||
/packages/dev-tools-pages/ts/**/data.json
|
|
||||||
package.json
|
package.json
|
||||||
scripts/postpublish_utils.js
|
scripts/postpublish_utils.js
|
||||||
packages/sol-coverage/test/fixtures/artifacts
|
packages/sol-coverage/test/fixtures/artifacts
|
||||||
.pytest_cache
|
.pytest_cache
|
||||||
.mypy_cache
|
.mypy_cache
|
||||||
.tox
|
.tox
|
||||||
|
packages/abi-gen/test-cli/fixtures/artifacts/AbiGenDummy.json
|
||||||
|
packages/abi-gen/test-cli/fixtures/artifacts/LibDummy.json
|
||||||
|
packages/abi-gen/test-cli/fixtures/artifacts/TestLibDummy.json
|
||||||
|
packages/*/docs
|
||||||
|
|||||||
12
CODEOWNERS
12
CODEOWNERS
@@ -7,22 +7,21 @@
|
|||||||
# Website
|
# Website
|
||||||
packages/asset-buyer/ @BMillman19 @fragosti @steveklebanoff
|
packages/asset-buyer/ @BMillman19 @fragosti @steveklebanoff
|
||||||
packages/instant/ @BMillman19 @fragosti @steveklebanoff
|
packages/instant/ @BMillman19 @fragosti @steveklebanoff
|
||||||
packages/website/ @BMillman19 @fragosti @fabioberger @steveklebanoff
|
|
||||||
|
|
||||||
# Dev tools & setup
|
# Dev tools & setup
|
||||||
.circleci/ @LogvinovLeon
|
.circleci/ @LogvinovLeon
|
||||||
packages/abi-gen/ @LogvinovLeon
|
packages/abi-gen/ @feuGeneA
|
||||||
packages/base-contract/ @LogvinovLeon
|
packages/base-contract/ @xianny
|
||||||
packages/connect/ @fragosti
|
packages/connect/ @fragosti
|
||||||
packages/abi-gen-templates/ @LogvinovLeon
|
packages/abi-gen-templates/ @feuGeneA @xianny
|
||||||
packages/contract-addresses/ @albrow
|
packages/contract-addresses/ @albrow
|
||||||
packages/contract-artifacts/ @albrow
|
packages/contract-artifacts/ @albrow
|
||||||
packages/dev-utils/ @LogvinovLeon @fabioberger
|
packages/dev-utils/ @LogvinovLeon @fabioberger
|
||||||
packages/devnet/ @albrow
|
packages/devnet/ @albrow
|
||||||
packages/ethereum-types/ @LogvinovLeon
|
packages/ethereum-types/ @LogvinovLeon
|
||||||
packages/metacoin/ @LogvinovLeon
|
|
||||||
packages/monorepo-scripts/ @fabioberger
|
packages/monorepo-scripts/ @fabioberger
|
||||||
packages/order-utils/ @fabioberger @LogvinovLeon
|
packages/order-utils/ @fabioberger @LogvinovLeon
|
||||||
|
packages/python-contract-wrappers/ @feuGeneA
|
||||||
packages/sol-compiler/ @LogvinovLeon
|
packages/sol-compiler/ @LogvinovLeon
|
||||||
packages/sol-coverage/ @LogvinovLeon
|
packages/sol-coverage/ @LogvinovLeon
|
||||||
packages/sol-profiler/ @LogvinovLeon
|
packages/sol-profiler/ @LogvinovLeon
|
||||||
@@ -33,6 +32,7 @@ packages/subproviders/ @fabioberger @dekz
|
|||||||
packages/verdaccio/ @albrow
|
packages/verdaccio/ @albrow
|
||||||
packages/web3-wrapper/ @LogvinovLeon @fabioberger
|
packages/web3-wrapper/ @LogvinovLeon @fabioberger
|
||||||
python-packages/ @feuGeneA
|
python-packages/ @feuGeneA
|
||||||
|
packages/utils/ @hysz
|
||||||
|
|
||||||
# Protocol/smart contracts
|
# Protocol/smart contracts
|
||||||
contracts/core/test/ @albrow
|
contracts/ @abandeali1 @hysz
|
||||||
|
|||||||
@@ -29,9 +29,9 @@ ALL PRs should be opened against `development`.
|
|||||||
|
|
||||||
Branch names should be prefixed with `fix`, `feature` or `refactor`.
|
Branch names should be prefixed with `fix`, `feature` or `refactor`.
|
||||||
|
|
||||||
- e.g `fix/broken-wiki-link`
|
- e.g `fix/missing-import`
|
||||||
- If the PR only edits a single package, add it's name too
|
- If the PR only edits a single package, add it's name too
|
||||||
- e.g `fix/website/broken-wiki-link`
|
- e.g `fix/subproviders/missing-import`
|
||||||
|
|
||||||
### CHANGELOGs
|
### CHANGELOGs
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ If an entry without a `timestamp` already exists, this means other changes have
|
|||||||
|
|
||||||
### Development Tooling
|
### Development Tooling
|
||||||
|
|
||||||
We strongly recommend you use the [VSCode](https://code.visualstudio.com/) text editor since most of our code is written in Typescript and it offers amazing support for the language.
|
We strongly recommend you use the [VSCode](https://code.visualstudio.com/) text editor since most of our code is written in TypeScript and it offers amazing support for the language.
|
||||||
|
|
||||||
#### Linter
|
#### Linter
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ A few of our coding conventions are not yet enforced by the linter/auto-formatte
|
|||||||
1. Do not import from a project's `index.ts` (e.g import { Token } from '../src';). Always import from the source file itself.
|
1. Do not import from a project's `index.ts` (e.g import { Token } from '../src';). Always import from the source file itself.
|
||||||
1. Generic error variables should be named `err` instead of `e` or `error`.
|
1. Generic error variables should be named `err` instead of `e` or `error`.
|
||||||
1. If you _must_ cast a variable to any - try to type it back as fast as possible. (e.g., `const cw = ((zeroEx as any)._contractWrappers as ContractWrappers);`). This ensures subsequent code is type-safe.
|
1. If you _must_ cast a variable to any - try to type it back as fast as possible. (e.g., `const cw = ((zeroEx as any)._contractWrappers as ContractWrappers);`). This ensures subsequent code is type-safe.
|
||||||
1. Our enum conventions coincide with the recommended Typescript conventions, using capitalized keys, and all-caps snake-case values. Eg `GetStats = 'GET_STATS'`
|
1. Our enum conventions coincide with the recommended TypeScript conventions, using capitalized keys, and all-caps snake-case values. Eg `GetStats = 'GET_STATS'`
|
||||||
1. All public, exported methods/functions/classes must have associated Javadoc-style comments.
|
1. All public, exported methods/functions/classes must have associated Javadoc-style comments.
|
||||||
|
|
||||||
### Fix `submit-coverage` CI failure
|
### Fix `submit-coverage` CI failure
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<!--- Before submitting please check to see if this issue was already reported -->
|
<!--- Before submitting please check to see if this issue was already reported -->
|
||||||
|
|
||||||
<!--- Provide a general summary of the issue in the Title above -->
|
<!--- Prefix your issue title with the package name it relates to (e.g., `0x.js: ` or `general:`) -->
|
||||||
|
|
||||||
## Expected Behavior
|
## Expected Behavior
|
||||||
|
|
||||||
|
|||||||
56
README.md
56
README.md
@@ -6,21 +6,16 @@
|
|||||||
|
|
||||||
This repository is a monorepo including the 0x protocol smart contracts and numerous developer tools. Each public sub-package is independently published to NPM.
|
This repository is a monorepo including the 0x protocol smart contracts and numerous developer tools. Each public sub-package is independently published to NPM.
|
||||||
|
|
||||||
If you're developing on 0x now or are interested in using 0x infrastructure in the future, please join our [developer mailing list][dev-mailing-list-url] for updates.
|
[website-url]: https://0x.org
|
||||||
|
|
||||||
[website-url]: https://0xproject.com
|
|
||||||
[whitepaper-url]: https://0xproject.com/pdfs/0x_white_paper.pdf
|
|
||||||
[dev-mailing-list-url]: http://eepurl.com/dx4cPf
|
|
||||||
|
|
||||||
[](https://circleci.com/gh/0xProject/0x-monorepo)
|
[](https://circleci.com/gh/0xProject/0x-monorepo)
|
||||||
[](https://coveralls.io/github/0xProject/0x-monorepo?branch=development)
|
[](https://coveralls.io/github/0xProject/0x-monorepo?branch=development)
|
||||||
[](https://chat.0xproject.com)
|
[](https://discordapp.com/invite/d3FTX3M)
|
||||||
[](https://gitter.im/0xProject/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
|
||||||
[](https://opensource.org/licenses/Apache-2.0)
|
[](https://opensource.org/licenses/Apache-2.0)
|
||||||
|
|
||||||
## Packages
|
## Packages
|
||||||
|
|
||||||
Visit our [developer portal](https://0xproject.com/docs/order-utils) for a comprehensive list of core & community maintained packages. All packages maintained with this monorepo are listed below.
|
Visit our [developer portal](https://0x.org/docs/tools/order-utils) for a comprehensive list of core & community maintained packages. All packages maintained with this monorepo are listed below.
|
||||||
|
|
||||||
### Python Packages
|
### Python Packages
|
||||||
|
|
||||||
@@ -28,26 +23,33 @@ Visit our [developer portal](https://0xproject.com/docs/order-utils) for a compr
|
|||||||
| -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
|
| -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
|
||||||
| [`0x-contract-addresses`](/python-packages/contract_addresses) | [](https://pypi.org/project/0x-contract-addresses/) | A tiny utility library for getting known deployed contract addresses for a particular network |
|
| [`0x-contract-addresses`](/python-packages/contract_addresses) | [](https://pypi.org/project/0x-contract-addresses/) | A tiny utility library for getting known deployed contract addresses for a particular network |
|
||||||
| [`0x-contract-artifacts`](/python-packages/contract_artifacts) | [](https://pypi.org/project/0x-contract-artifacts/) | 0x smart contract compilation artifacts |
|
| [`0x-contract-artifacts`](/python-packages/contract_artifacts) | [](https://pypi.org/project/0x-contract-artifacts/) | 0x smart contract compilation artifacts |
|
||||||
|
| [`0x-contract-wrappers`](/python-packages/contract_wrappers) | [](https://pypi.org/project/0x-contract-wrappers/) | 0x smart contract wrappers |
|
||||||
| [`0x-json-schemas`](/python-packages/json_schemas) | [](https://pypi.org/project/0x-json-schemas/) | 0x-related JSON schemas |
|
| [`0x-json-schemas`](/python-packages/json_schemas) | [](https://pypi.org/project/0x-json-schemas/) | 0x-related JSON schemas |
|
||||||
| [`0x-order-utils`](/python-packages/order_utils) | [](https://pypi.org/project/0x-order-utils/) | A set of utilities for generating, parsing, signing and validating 0x orders |
|
| [`0x-order-utils`](/python-packages/order_utils) | [](https://pypi.org/project/0x-order-utils/) | A set of utilities for generating, parsing, signing and validating 0x orders |
|
||||||
| [`0x-sra-client`](/python-packages/sra_client) | [](https://pypi.org/project/0x-sra-client/) | A Python client for interacting with servers conforming to the Standard Relayer API specification |
|
| [`0x-sra-client`](/python-packages/sra_client) | [](https://pypi.org/project/0x-sra-client/) | A Python client for interacting with servers conforming to the Standard Relayer API specification |
|
||||||
|
|
||||||
### Solidity Packages
|
### Solidity Packages
|
||||||
|
|
||||||
|
These packages are all under development. See [/contracts/README.md](/contracts/README.md) for a list of deployed packages.
|
||||||
|
|
||||||
| Package | Version | Description |
|
| Package | Version | Description |
|
||||||
| ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| [`@0x/contracts-asset-proxy`](/contracts/asset-proxy) | [](https://www.npmjs.com/package/@0x/contracts-asset-proxy) | [`AssetProxy`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#assetproxy) contracts used within the protocol |
|
| [`@0x/contracts-asset-proxy`](/contracts/asset-proxy) | [](https://www.npmjs.com/package/@0x/contracts-asset-proxy) | [`AssetProxy`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#assetproxy) contracts used within the protocol |
|
||||||
| [`@0x/contracts-erc20`](/contracts/erc20) | [](https://www.npmjs.com/package/@0x/contracts-erc20) | Implementations of various ERC20 tokens |
|
| [`@0x/contracts-erc20`](/contracts/erc20) | [](https://www.npmjs.com/package/@0x/contracts-erc20) | Implementations of various ERC20 tokens |
|
||||||
| [`@0x/contracts-erc721`](/contracts/erc721) | [](https://www.npmjs.com/package/@0x/contracts-erc721) | Implementations of various ERC721 tokens |
|
| [`@0x/contracts-erc721`](/contracts/erc721) | [](https://www.npmjs.com/package/@0x/contracts-erc721) | Implementations of various ERC721 tokens |
|
||||||
|
| [`@0x/contracts-erc1155`](/contracts/erc1155) | [](https://www.npmjs.com/package/@0x/contracts-erc1155) | Implementations of various ERC1155 tokens |
|
||||||
| [`@0x/contracts-exchange`](/contracts/exchange) | [](https://www.npmjs.com/package/@0x/contracts-exchange) | The [`Exchange`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#exchange) contract used for settling trades within the protocol |
|
| [`@0x/contracts-exchange`](/contracts/exchange) | [](https://www.npmjs.com/package/@0x/contracts-exchange) | The [`Exchange`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#exchange) contract used for settling trades within the protocol |
|
||||||
| [`@0x/contracts-exchange-forwarder`](/contracts/exchange-forwarder) | [](https://www.npmjs.com/package/@0x/contracts-exchange-forwarder) | A [`Forwarder`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/forwarder-specification.md) contract used to simplify UX for interacting with the protocol |
|
| [`@0x/contracts-exchange-forwarder`](/contracts/exchange-forwarder) | [](https://www.npmjs.com/package/@0x/contracts-exchange-forwarder) | A [`Forwarder`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/forwarder-specification.md) contract used to simplify UX for interacting with the protocol |
|
||||||
| [`@0x/contracts-exchange-libs`](/contracts/exchange-libs) | [](https://www.npmjs.com/package/@0x/contracts-exchange-libs) | Protocol specific Llbraries used within the [`Exchange`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#exchange) contract |
|
| [`@0x/contracts-exchange-libs`](/contracts/exchange-libs) | [](https://www.npmjs.com/package/@0x/contracts-exchange-libs) | Protocol specific libraries used within the [`Exchange`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#exchange) contract |
|
||||||
| [`@0x/contracts-extensions`](/contracts/extensions) | [](https://www.npmjs.com/package/@0x/contracts-extensions) | Contracts that interact with and extend the functionality of the core protocol |
|
| [`@0x/contracts-extensions`](/contracts/extensions) | [](https://www.npmjs.com/package/@0x/contracts-extensions) | Contracts that interact with and extend the functionality of the core protocol |
|
||||||
| [`@0x/contracts-multisig`](/contracts/multisig) | [](https://www.npmjs.com/package/@0x/contracts-multisig) | Various implementations of multisignature wallets, including the [`AssetProxyOwner`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#assetproxyowner) contract that has permissions to upgrade the protocol |
|
| [`@0x/contracts-multisig`](/contracts/multisig) | [](https://www.npmjs.com/package/@0x/contracts-multisig) | Various implementations of multisignature wallets, including the [`AssetProxyOwner`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#assetproxyowner) contract that has permissions to upgrade the protocol |
|
||||||
| [`@0x/contracts-test-utils`](/contracts/test-utils) | [](https://www.npmjs.com/package/@0x/contracts-test-utils) | Typescript/Javascript shared utilities used for testing contracts |
|
| [`@0x/contracts-test-utils`](/contracts/test-utils) | [](https://www.npmjs.com/package/@0x/contracts-test-utils) | TypeScript/Javascript shared utilities used for testing contracts |
|
||||||
| [`@0x/contracts-utils`](/contracts/utils) | [](https://www.npmjs.com/package/@0x/contracts-utils) | Generic libraries and utilities used throughout all of the contracts |
|
| [`@0x/contracts-utils`](/contracts/utils) | [](https://www.npmjs.com/package/@0x/contracts-utils) | Generic libraries and utilities used throughout all of the contracts |
|
||||||
|
| [`@0x/contracts-coordinator`](/contracts/coordinator) | [](https://www.npmjs.com/package/@0x/contracts-coordinator) | A contract that allows users to execute 0x transactions with permission from a Coordinator |
|
||||||
|
| [`@0x/contracts-dev-utils`](/contracts/dev-utils) | [](https://www.npmjs.com/package/@0x/contracts-dev-utils) | A contract contains utility functions for developers (such as validating many orders using a single eth_call) |
|
||||||
|
| [`@0x/contracts-staking`](/contracts/staking) | [](https://www.npmjs.com/package/@0x/contracts-staking) | Implements the stake-based liquidity incentives defined by [`ZEIP-31`](https://github.com/0xProject/ZEIPs/issues/31) |
|
||||||
|
|
||||||
### Typescript/Javascript Packages
|
### TypeScript/Javascript Packages
|
||||||
|
|
||||||
#### 0x-specific packages
|
#### 0x-specific packages
|
||||||
|
|
||||||
@@ -57,14 +59,14 @@ Visit our [developer portal](https://0xproject.com/docs/order-utils) for a compr
|
|||||||
| [`@0x/contract-addresses`](/packages/contract-addresses) | [](https://www.npmjs.com/package/@0x/contract-addresses) | A tiny utility library for getting known deployed contract addresses for a particular network. |
|
| [`@0x/contract-addresses`](/packages/contract-addresses) | [](https://www.npmjs.com/package/@0x/contract-addresses) | A tiny utility library for getting known deployed contract addresses for a particular network. |
|
||||||
| [`@0x/contract-wrappers`](/packages/contract-wrappers) | [](https://www.npmjs.com/package/@0x/contract-wrappers) | JS/TS wrappers for interacting with the 0x smart contracts |
|
| [`@0x/contract-wrappers`](/packages/contract-wrappers) | [](https://www.npmjs.com/package/@0x/contract-wrappers) | JS/TS wrappers for interacting with the 0x smart contracts |
|
||||||
| [`@0x/order-utils`](/packages/order-utils) | [](https://www.npmjs.com/package/@0x/order-utils) | A set of utilities for generating, parsing, signing and validating 0x orders |
|
| [`@0x/order-utils`](/packages/order-utils) | [](https://www.npmjs.com/package/@0x/order-utils) | A set of utilities for generating, parsing, signing and validating 0x orders |
|
||||||
| [`@0x/json-schemas`](/packages/json-schemas) | [](https://www.npmjs.com/package/@0x/json-schemas) | 0x-related JSON schemas |
|
| [`@0x/json-schemas`](/packages/json-schemas) | [](https://www.npmjs.com/package/@0x/json-schemas) | 0x-related JSON schemas | |
|
||||||
| [`@0x/order-watcher`](/packages/order-watcher) | [](https://www.npmjs.com/package/@0x/order-watcher) | An order watcher daemon that watches for order validity |
|
|
||||||
| [`@0x/migrations`](/packages/migrations) | [](https://www.npmjs.com/package/@0x/migrations) | Migration tool for deploying 0x smart contracts on private testnets |
|
| [`@0x/migrations`](/packages/migrations) | [](https://www.npmjs.com/package/@0x/migrations) | Migration tool for deploying 0x smart contracts on private testnets |
|
||||||
| [`@0x/contract-artifacts`](/packages/contract-artifacts) | [](https://www.npmjs.com/package/@0x/contract-artifacts) | 0x smart contract compilation artifacts |
|
| [`@0x/contract-artifacts`](/packages/contract-artifacts) | [](https://www.npmjs.com/package/@0x/contract-artifacts) | 0x smart contract compilation artifacts |
|
||||||
| [`@0x/abi-gen-wrappers`](/packages/abi-gen-wrappers) | [](https://www.npmjs.com/package/@0x/abi-gen-wrappers) | Low-level 0x smart contract wrappers generated using `@0x/abi-gen` |
|
| [`@0x/abi-gen-wrappers`](/packages/abi-gen-wrappers) | [](https://www.npmjs.com/package/@0x/abi-gen-wrappers) | Low-level 0x smart contract wrappers generated using `@0x/abi-gen` |
|
||||||
| [`@0x/sra-spec`](/packages/sra-spec) | [](https://www.npmjs.com/package/@0x/sra-spec) | OpenAPI specification for the Standard Relayer API |
|
| [`@0x/sra-spec`](/packages/sra-spec) | [](https://www.npmjs.com/package/@0x/sra-spec) | OpenAPI specification for the Standard Relayer API |
|
||||||
| [`@0x/connect`](/packages/connect) | [](https://www.npmjs.com/package/@0x/connect) | An HTTP/WS client for interacting with the Standard Relayer API |
|
| [`@0x/connect`](/packages/connect) | [](https://www.npmjs.com/package/@0x/connect) | An HTTP/WS client for interacting with the Standard Relayer API |
|
||||||
| [`@0x/asset-buyer`](/packages/asset-buyer) | [](https://www.npmjs.com/package/@0x/asset-buyer) | Convenience package for discovering and buying assets with Ether |
|
| [`@0x/asset-buyer`](/packages/asset-buyer) | [](https://www.npmjs.com/package/@0x/asset-buyer) | Convenience package for discovering and buying assets with Ether |
|
||||||
|
| [`@0x/asset-swapper`](/packages/asset-swapper) | [](https://www.npmjs.com/package/@0x/asset-swapper) | Convenience package for discovering and performing swaps for any ERC20 Assets |
|
||||||
|
|
||||||
#### Ethereum tooling
|
#### Ethereum tooling
|
||||||
|
|
||||||
@@ -81,19 +83,16 @@ Visit our [developer portal](https://0xproject.com/docs/order-utils) for a compr
|
|||||||
|
|
||||||
#### Utilities
|
#### Utilities
|
||||||
|
|
||||||
| Package | Version | Description |
|
| Package | Version | Description |
|
||||||
| -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
|
| -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- |
|
||||||
| [`@0x/abi-gen`](/packages/abi-gen) | [](https://www.npmjs.com/package/@0x/abi-gen) | Tool to generate TS wrappers from smart contract ABIs |
|
| [`@0x/abi-gen`](/packages/abi-gen) | [](https://www.npmjs.com/package/@0x/abi-gen) | Tool to generate TS wrappers from smart contract ABIs |
|
||||||
| [`@0x/tslint-config`](/packages/tslint-config) | [](https://www.npmjs.com/package/@0x/tslint-config) | Custom TSLint rules used by the 0x core team |
|
| [`@0x/tslint-config`](/packages/tslint-config) | [](https://www.npmjs.com/package/@0x/tslint-config) | Custom TSLint rules used by the 0x core team |
|
||||||
| [`@0x/types`](/packages/types) | [](https://www.npmjs.com/package/@0x/types) | Shared type declarations |
|
| [`@0x/types`](/packages/types) | [](https://www.npmjs.com/package/@0x/types) | Shared type declarations |
|
||||||
| [`@0x/typescript-typings`](/packages/typescript-typings) | [](https://www.npmjs.com/package/@0x/typescript-typings) | Repository of types for external packages |
|
| [`@0x/typescript-typings`](/packages/typescript-typings) | [](https://www.npmjs.com/package/@0x/typescript-typings) | Repository of types for external packages |
|
||||||
| [`@0x/utils`](/packages/utils) | [](https://www.npmjs.com/package/@0x/utils) | Shared utilities |
|
| [`@0x/utils`](/packages/utils) | [](https://www.npmjs.com/package/@0x/utils) | Shared utilities |
|
||||||
| [`@0x/react-docs`](/packages/react-docs) | [](https://www.npmjs.com/package/@0x/react-docs) | React documentation component for rendering TypeDoc & sol-doc generated JSON |
|
| [`@0x/assert`](/packages/assert) | [](https://www.npmjs.com/package/@0x/assert) | Type and schema assertions used by our packages |
|
||||||
| [`@0x/react-shared`](/packages/react-shared) | [](https://www.npmjs.com/package/@0x/react-shared) | 0x shared react components |
|
| [`@0x/base-contract`](/packages/base-contract) | [](https://www.npmjs.com/package/@0x/base-contract) | BaseContract used by auto-generated `abi-gen` wrapper contracts |
|
||||||
| [`@0x/assert`](/packages/assert) | [](https://www.npmjs.com/package/@0x/assert) | Type and schema assertions used by our packages |
|
| [`@0x/dev-utils`](/packages/dev-utils) | [](https://www.npmjs.com/package/@0x/dev-utils) | Dev utils to be shared across 0x 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
|
#### Private Packages
|
||||||
|
|
||||||
@@ -101,11 +100,10 @@ Visit our [developer portal](https://0xproject.com/docs/order-utils) for a compr
|
|||||||
| -------------------------------------------------- | -------------------------------------------------------------------------------- |
|
| -------------------------------------------------- | -------------------------------------------------------------------------------- |
|
||||||
| [`@0x/instant`](/packages/instant) | A free and flexible way to offer simple crypto purchasing in any app or website. |
|
| [`@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 |
|
| [`@0x/testnet-faucets`](/packages/testnet-faucets) | A faucet micro-service that dispenses test ERC20 tokens or Ether |
|
||||||
| [`@0x/website`](/packages/website) | 0x website |
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Node version >= 6.12 is required.
|
Node version 6.x or 8.x is required.
|
||||||
|
|
||||||
Most of the packages require additional typings for external dependencies.
|
Most of the packages require additional typings for external dependencies.
|
||||||
You can include those by prepending the `@0x/typescript-typings` package to your [`typeRoots`](http://www.typescriptlang.org/docs/handbook/tsconfig-json.html) config.
|
You can include those by prepending the `@0x/typescript-typings` package to your [`typeRoots`](http://www.typescriptlang.org/docs/handbook/tsconfig-json.html) config.
|
||||||
@@ -134,6 +132,8 @@ Then install dependencies
|
|||||||
yarn install
|
yarn install
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You will also need to have Python 3 installed, in order to build and run the tests of `abi-gen`'s command-line interface, which is integrated with the yarn build, yarn test, and yarn lint commands described below. More specifically, your local pip should resolve to the Python 3 version of pip, not a Python 2.x version.
|
||||||
|
|
||||||
### Build
|
### Build
|
||||||
|
|
||||||
To build all packages:
|
To build all packages:
|
||||||
|
|||||||
16
contracts/README.md
Normal file
16
contracts/README.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#### Deployed Contract Packages
|
||||||
|
|
||||||
|
| Contract | Package | Version | Git Tag |
|
||||||
|
| --------------- | ------------------------------------------------------------------- | -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| AssetProxyOwner | [`@0x/contracts-multisig`](/contracts/multisig) | [v1.0.2](https://www.npmjs.com/package/@0x/contracts-multisig/v/1.0.2) | [@0x/contracts-multisig@1.0.2](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-multisig@1.0.2) |
|
||||||
|
| ERC20Proxy | [`@0x/contracts-asset-proxy`](/contracts/asset-proxy) | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-asset-proxy/v/1.0.1) | [@0x/contracts-asset-proxy@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-asset-proxy@1.0.1) |
|
||||||
|
| ERC721Proxy | [`@0x/contracts-asset-proxy`](/contracts/asset-proxy) | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-asset-proxy/v/1.0.1) | [@0x/contracts-asset-proxy@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-asset-proxy@1.0.1) |
|
||||||
|
| Exchange | [`@0x/contracts-exchange`](/contracts/exchange) | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-exchange/v/1.0.1) | [@0x/contracts-exchange@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-exchange@1.0.1) |
|
||||||
|
| DutchAuction | [`@0x/contracts-extensions`](/contracts/extensions) | [v1.0.2](https://www.npmjs.com/package/@0x/contracts-extensions/v/1.0.2) | [@0x/contracts-extensions@1.0.2](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-extensions@1.0.2) |
|
||||||
|
| Forwarder | [`@0x/contracts-exchange-forwarder`](/contracts/exchange-forwarder) | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-exchange-forwarder/v/1.0.1) | [@0x/contracts-exchange-forwarder@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-exchange-forwarder@1.0.1) |
|
||||||
|
| MultiAssetProxy | [`@0x/contracts-asset-proxy`](/contracts/asset-proxy) | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-asset-proxy/v/1.0.1) | [@0x/contracts-asset-proxy@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-asset-proxy@1.0.1) |
|
||||||
|
| ZRXToken | [`@0x/contracts-erc20`](/contracts/erc20) | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-erc20/v/1.0.1) | [@0x/contracts-erc20@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-erc20@1.0.1) |
|
||||||
|
|
||||||
|
#### Development
|
||||||
|
|
||||||
|
Building solidity files will update the contract artifact in `{package-name}/generated-artifacts/{contract}.json`, but does not automatically update the `abi-gen-wrappers` package, which are generated from the artifact JSON. To ensure consistency, clean and rebuild `abi-gen-wrappers` after any changes to the artifact JSON.
|
||||||
2
contracts/asset-proxy/.solhintignore
Normal file
2
contracts/asset-proxy/.solhintignore
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# solhint can't parse `abi.decode` syntax.
|
||||||
|
contracts/src/ERC1155Proxy.sol
|
||||||
@@ -1,4 +1,251 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1570135330
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1568744790,
|
||||||
|
"version": "2.2.8",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1567521715,
|
||||||
|
"version": "2.2.7",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1566446343,
|
||||||
|
"version": "2.2.6",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1565296576,
|
||||||
|
"version": "2.2.5",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "2.2.4",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Updated calls to <contract wrapper>.deployFrom0xArtifactAsync to include artifact dependencies.",
|
||||||
|
"pr": 1995
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1564607468
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1563957393,
|
||||||
|
"version": "2.2.3",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1563193019,
|
||||||
|
"version": "2.2.2",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1563047529,
|
||||||
|
"version": "2.2.1",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "2.2.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Add `LibAssetProxyIds` contract",
|
||||||
|
"pr": 1835
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Updated ERC1155 Asset Proxy. Less optimization. More explicit handling of edge cases.",
|
||||||
|
"pr": 1852
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Implement StaticCallProxy",
|
||||||
|
"pr": 1863
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1563006338
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1558712885,
|
||||||
|
"version": "2.1.5",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1557961111,
|
||||||
|
"version": "2.1.4",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1557799313,
|
||||||
|
"version": "2.1.3",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "2.1.2",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Update tests to use contract-built-in `awaitTransactionSuccessAsync`",
|
||||||
|
"pr": 1797
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Make `ERC721Wrapper.setApprovalForAll()` take an owner address instead of a token ID",
|
||||||
|
"pr": 1819
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Automatically set unlimited proxy allowances in `ERC721.setBalancesAndAllowancesAsync()`",
|
||||||
|
"pr": 1819
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Add `setProxyAllowanceForAllAsync()` to `ERC1155ProxyWrapper`.",
|
||||||
|
"pr": 1819
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1557507213
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "2.1.1",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1554997931
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "2.1.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Run Web3ProviderEngine without excess block polling",
|
||||||
|
"pr": 1695
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1553183790
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Do not reexport external dependencies",
|
||||||
|
"pr": 1682
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Add ERC1155Proxy",
|
||||||
|
"pr": 1661
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Bumped solidity version to ^0.5.5",
|
||||||
|
"pr": 1701
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Integration testing for ERC1155Proxy",
|
||||||
|
"pr": 1673
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1553091633
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1551479279,
|
||||||
|
"version": "1.0.9",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1551299797,
|
||||||
|
"version": "1.0.8",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1551220833,
|
||||||
|
"version": "1.0.7",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1551130135,
|
||||||
|
"version": "1.0.6",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"timestamp": 1549733923,
|
"timestamp": 1549733923,
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
|
|||||||
@@ -5,6 +5,103 @@ Edit the package's CHANGELOG.json file only.
|
|||||||
|
|
||||||
CHANGELOG
|
CHANGELOG
|
||||||
|
|
||||||
|
## 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)
|
||||||
|
|
||||||
|
## v2.2.8 - _September 17, 2019_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v2.2.7 - _September 3, 2019_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v2.2.6 - _August 22, 2019_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v2.2.5 - _August 8, 2019_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v2.2.4 - _July 31, 2019_
|
||||||
|
|
||||||
|
* Updated calls to <contract wrapper>.deployFrom0xArtifactAsync to include artifact dependencies. (#1995)
|
||||||
|
|
||||||
|
## v2.2.3 - _July 24, 2019_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v2.2.2 - _July 15, 2019_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v2.2.1 - _July 13, 2019_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v2.2.0 - _July 13, 2019_
|
||||||
|
|
||||||
|
* Add `LibAssetProxyIds` contract (#1835)
|
||||||
|
* Updated ERC1155 Asset Proxy. Less optimization. More explicit handling of edge cases. (#1852)
|
||||||
|
* Implement StaticCallProxy (#1863)
|
||||||
|
|
||||||
|
## v2.1.5 - _May 24, 2019_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v2.1.4 - _May 15, 2019_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v2.1.3 - _May 14, 2019_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v2.1.2 - _May 10, 2019_
|
||||||
|
|
||||||
|
* Update tests to use contract-built-in `awaitTransactionSuccessAsync` (#1797)
|
||||||
|
* Make `ERC721Wrapper.setApprovalForAll()` take an owner address instead of a token ID (#1819)
|
||||||
|
* Automatically set unlimited proxy allowances in `ERC721.setBalancesAndAllowancesAsync()` (#1819)
|
||||||
|
* Add `setProxyAllowanceForAllAsync()` to `ERC1155ProxyWrapper`. (#1819)
|
||||||
|
|
||||||
|
## v2.1.1 - _April 11, 2019_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v2.1.0 - _March 21, 2019_
|
||||||
|
|
||||||
|
* Run Web3ProviderEngine without excess block polling (#1695)
|
||||||
|
|
||||||
|
## v2.0.0 - _March 20, 2019_
|
||||||
|
|
||||||
|
* Do not reexport external dependencies (#1682)
|
||||||
|
* Add ERC1155Proxy (#1661)
|
||||||
|
* Bumped solidity version to ^0.5.5 (#1701)
|
||||||
|
* Integration testing for ERC1155Proxy (#1673)
|
||||||
|
|
||||||
|
## v1.0.9 - _March 1, 2019_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v1.0.8 - _February 27, 2019_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v1.0.7 - _February 26, 2019_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v1.0.6 - _February 25, 2019_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
## v1.0.5 - _February 9, 2019_
|
## v1.0.5 - _February 9, 2019_
|
||||||
|
|
||||||
* Dependencies updated
|
* Dependencies updated
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
## AssetProxy
|
## AssetProxy
|
||||||
|
|
||||||
This package contains the implementations of all of the [`AssetProxy`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#assetproxy) contracts available within the 0x protocol. These contracts are responsible for decoding the `assetData` sent to them and performing the actual transfer of assets. Addresses of the deployed contracts can be found in the 0x [wiki](https://0xproject.com/wiki#Deployed-Addresses) or the [DEPLOYS](./DEPLOYS.json) file within this package.
|
This package contains the implementations of all of the [`AssetProxy`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#assetproxy) contracts available within the 0x protocol. These contracts are responsible for decoding the `assetData` sent to them and performing the actual transfer of assets. Addresses of the deployed contracts can be found in this 0x [guide](https://0x.org/docs/guides/0x-cheat-sheet) or the [DEPLOYS](./DEPLOYS.json) file within this package.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ npm install @0x/contracts-asset-proxy --save
|
|||||||
|
|
||||||
## Bug bounty
|
## Bug bounty
|
||||||
|
|
||||||
A bug bounty for the 2.0.0 contracts is ongoing! Instructions can be found [here](https://0xproject.com/wiki#Bug-Bounty).
|
A bug bounty for the 2.0.0 contracts is ongoing! Instructions can be found [here](https://0x.org/docs/guides/bug-bounty-program).
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,20 @@
|
|||||||
{
|
{
|
||||||
"artifactsDir": "./generated-artifacts",
|
"artifactsDir": "./generated-artifacts",
|
||||||
"contractsDir": "./contracts",
|
"contractsDir": "./contracts",
|
||||||
"useDockerisedSolc": true,
|
"useDockerisedSolc": false,
|
||||||
|
"isOfflineMode": false,
|
||||||
"compilerSettings": {
|
"compilerSettings": {
|
||||||
"optimizer": { "enabled": true, "runs": 1000000 },
|
"evmVersion": "constantinople",
|
||||||
|
"optimizer": {
|
||||||
|
"enabled": true,
|
||||||
|
"runs": 1000000,
|
||||||
|
"details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
|
||||||
|
},
|
||||||
"outputSelection": {
|
"outputSelection": {
|
||||||
"*": {
|
"*": {
|
||||||
"*": [
|
"*": [
|
||||||
"abi",
|
"abi",
|
||||||
|
"devdoc",
|
||||||
"evm.bytecode.object",
|
"evm.bytecode.object",
|
||||||
"evm.bytecode.sourceMap",
|
"evm.bytecode.sourceMap",
|
||||||
"evm.deployedBytecode.object",
|
"evm.deployedBytecode.object",
|
||||||
@@ -15,19 +22,5 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"contracts": [
|
|
||||||
"@0x/contracts-erc20/contracts/test/DummyERC20Token.sol",
|
|
||||||
"@0x/contracts-erc20/contracts/test/DummyMultipleReturnERC20Token.sol",
|
|
||||||
"@0x/contracts-erc20/contracts/test/DummyNoReturnERC20Token.sol",
|
|
||||||
"@0x/contracts-erc721/contracts/test/DummyERC721Receiver.sol",
|
|
||||||
"@0x/contracts-erc721/contracts/test/DummyERC721Token.sol",
|
|
||||||
"src/ERC20Proxy.sol",
|
|
||||||
"src/ERC721Proxy.sol",
|
|
||||||
"src/MixinAuthorizable.sol",
|
|
||||||
"src/MultiAssetProxy.sol",
|
|
||||||
"src/interfaces/IAssetData.sol",
|
|
||||||
"src/interfaces/IAssetProxy.sol",
|
|
||||||
"src/interfaces/IAuthorizable.sol"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
|
|
||||||
Copyright 2018 ZeroEx Intl.
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -16,19 +16,19 @@
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pragma solidity ^0.4.24;
|
pragma solidity ^0.5.9;
|
||||||
|
|
||||||
import "@0x/contracts-utils/contracts/src/Ownable.sol";
|
import "../archive/Ownable.sol";
|
||||||
import "./mixins/MAssetProxyDispatcher.sol";
|
import "../src/interfaces/IAssetProxy.sol";
|
||||||
import "./interfaces/IAssetProxy.sol";
|
import "../src/interfaces/IAssetProxyDispatcher.sol";
|
||||||
|
|
||||||
|
|
||||||
contract MixinAssetProxyDispatcher is
|
contract MixinAssetProxyDispatcher is
|
||||||
Ownable,
|
Ownable,
|
||||||
MAssetProxyDispatcher
|
IAssetProxyDispatcher
|
||||||
{
|
{
|
||||||
// Mapping from Asset Proxy Id's to their respective Asset Proxy
|
// Mapping from Asset Proxy Id's to their respective Asset Proxy
|
||||||
mapping (bytes4 => IAssetProxy) public assetProxies;
|
mapping (bytes4 => address) public assetProxies;
|
||||||
|
|
||||||
/// @dev Registers an asset proxy to its asset proxy id.
|
/// @dev Registers an asset proxy to its asset proxy id.
|
||||||
/// Once an asset proxy is registered, it cannot be unregistered.
|
/// Once an asset proxy is registered, it cannot be unregistered.
|
||||||
@@ -37,10 +37,8 @@ contract MixinAssetProxyDispatcher is
|
|||||||
external
|
external
|
||||||
onlyOwner
|
onlyOwner
|
||||||
{
|
{
|
||||||
IAssetProxy assetProxyContract = IAssetProxy(assetProxy);
|
|
||||||
|
|
||||||
// Ensure that no asset proxy exists with current id.
|
// Ensure that no asset proxy exists with current id.
|
||||||
bytes4 assetProxyId = assetProxyContract.getProxyId();
|
bytes4 assetProxyId = IAssetProxy(assetProxy).getProxyId();
|
||||||
address currentAssetProxy = assetProxies[assetProxyId];
|
address currentAssetProxy = assetProxies[assetProxyId];
|
||||||
require(
|
require(
|
||||||
currentAssetProxy == address(0),
|
currentAssetProxy == address(0),
|
||||||
@@ -48,7 +46,7 @@ contract MixinAssetProxyDispatcher is
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Add asset proxy and log registration.
|
// Add asset proxy and log registration.
|
||||||
assetProxies[assetProxyId] = assetProxyContract;
|
assetProxies[assetProxyId] = assetProxy;
|
||||||
emit AssetProxyRegistered(
|
emit AssetProxyRegistered(
|
||||||
assetProxyId,
|
assetProxyId,
|
||||||
assetProxy
|
assetProxy
|
||||||
@@ -71,7 +69,7 @@ contract MixinAssetProxyDispatcher is
|
|||||||
/// @param from Address to transfer token from.
|
/// @param from Address to transfer token from.
|
||||||
/// @param to Address to transfer token to.
|
/// @param to Address to transfer token to.
|
||||||
/// @param amount Amount of token to transfer.
|
/// @param amount Amount of token to transfer.
|
||||||
function dispatchTransferFrom(
|
function _dispatchTransferFrom(
|
||||||
bytes memory assetData,
|
bytes memory assetData,
|
||||||
address from,
|
address from,
|
||||||
address to,
|
address to,
|
||||||
@@ -86,7 +84,7 @@ contract MixinAssetProxyDispatcher is
|
|||||||
assetData.length > 3,
|
assetData.length > 3,
|
||||||
"LENGTH_GREATER_THAN_3_REQUIRED"
|
"LENGTH_GREATER_THAN_3_REQUIRED"
|
||||||
);
|
);
|
||||||
|
|
||||||
// Lookup assetProxy. We do not use `LibBytes.readBytes4` for gas efficiency reasons.
|
// Lookup assetProxy. We do not use `LibBytes.readBytes4` for gas efficiency reasons.
|
||||||
bytes4 assetProxyId;
|
bytes4 assetProxyId;
|
||||||
assembly {
|
assembly {
|
||||||
@@ -102,10 +100,10 @@ contract MixinAssetProxyDispatcher is
|
|||||||
assetProxy != address(0),
|
assetProxy != address(0),
|
||||||
"ASSET_PROXY_DOES_NOT_EXIST"
|
"ASSET_PROXY_DOES_NOT_EXIST"
|
||||||
);
|
);
|
||||||
|
|
||||||
// We construct calldata for the `assetProxy.transferFrom` ABI.
|
// We construct calldata for the `assetProxy.transferFrom` ABI.
|
||||||
// The layout of this calldata is in the table below.
|
// The layout of this calldata is in the table below.
|
||||||
//
|
//
|
||||||
// | Area | Offset | Length | Contents |
|
// | Area | Offset | Length | Contents |
|
||||||
// | -------- |--------|---------|-------------------------------------------- |
|
// | -------- |--------|---------|-------------------------------------------- |
|
||||||
// | Header | 0 | 4 | function selector |
|
// | Header | 0 | 4 | function selector |
|
||||||
@@ -129,12 +127,12 @@ contract MixinAssetProxyDispatcher is
|
|||||||
// `cdEnd` is the end of the calldata for `assetProxy.transferFrom`.
|
// `cdEnd` is the end of the calldata for `assetProxy.transferFrom`.
|
||||||
let cdEnd := add(cdStart, add(132, dataAreaLength))
|
let cdEnd := add(cdStart, add(132, dataAreaLength))
|
||||||
|
|
||||||
|
|
||||||
/////// Setup Header Area ///////
|
/////// Setup Header Area ///////
|
||||||
// This area holds the 4-byte `transferFromSelector`.
|
// This area holds the 4-byte `transferFromSelector`.
|
||||||
// bytes4(keccak256("transferFrom(bytes,address,address,uint256)")) = 0xa85e59e4
|
// bytes4(keccak256("transferFrom(bytes,address,address,uint256)")) = 0xa85e59e4
|
||||||
mstore(cdStart, 0xa85e59e400000000000000000000000000000000000000000000000000000000)
|
mstore(cdStart, 0xa85e59e400000000000000000000000000000000000000000000000000000000)
|
||||||
|
|
||||||
/////// Setup Params Area ///////
|
/////// Setup Params Area ///////
|
||||||
// Each parameter is padded to 32-bytes. The entire Params Area is 128 bytes.
|
// Each parameter is padded to 32-bytes. The entire Params Area is 128 bytes.
|
||||||
// Notes:
|
// Notes:
|
||||||
@@ -144,7 +142,7 @@ contract MixinAssetProxyDispatcher is
|
|||||||
mstore(add(cdStart, 36), and(from, 0xffffffffffffffffffffffffffffffffffffffff))
|
mstore(add(cdStart, 36), and(from, 0xffffffffffffffffffffffffffffffffffffffff))
|
||||||
mstore(add(cdStart, 68), and(to, 0xffffffffffffffffffffffffffffffffffffffff))
|
mstore(add(cdStart, 68), and(to, 0xffffffffffffffffffffffffffffffffffffffff))
|
||||||
mstore(add(cdStart, 100), amount)
|
mstore(add(cdStart, 100), amount)
|
||||||
|
|
||||||
/////// Setup Data Area ///////
|
/////// Setup Data Area ///////
|
||||||
// This area holds `assetData`.
|
// This area holds `assetData`.
|
||||||
let dataArea := add(cdStart, 132)
|
let dataArea := add(cdStart, 132)
|
||||||
@@ -161,7 +159,7 @@ contract MixinAssetProxyDispatcher is
|
|||||||
assetProxy, // call address of asset proxy
|
assetProxy, // call address of asset proxy
|
||||||
0, // don't send any ETH
|
0, // don't send any ETH
|
||||||
cdStart, // pointer to start of input
|
cdStart, // pointer to start of input
|
||||||
sub(cdEnd, cdStart), // length of input
|
sub(cdEnd, cdStart), // length of input
|
||||||
cdStart, // write output over input
|
cdStart, // write output over input
|
||||||
512 // reserve 512 bytes for output
|
512 // reserve 512 bytes for output
|
||||||
)
|
)
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
|
|
||||||
Copyright 2018 ZeroEx Intl.
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -16,15 +16,15 @@
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pragma solidity ^0.4.24;
|
pragma solidity ^0.5.9;
|
||||||
|
|
||||||
import "@0x/contracts-utils/contracts/src/Ownable.sol";
|
import "../archive/Ownable.sol";
|
||||||
import "./mixins/MAuthorizable.sol";
|
import "../src/interfaces/IAuthorizable.sol";
|
||||||
|
|
||||||
|
|
||||||
contract MixinAuthorizable is
|
contract MixinAuthorizable is
|
||||||
Ownable,
|
Ownable,
|
||||||
MAuthorizable
|
IAuthorizable
|
||||||
{
|
{
|
||||||
/// @dev Only authorized addresses can invoke functions with this modifier.
|
/// @dev Only authorized addresses can invoke functions with this modifier.
|
||||||
modifier onlyAuthorized {
|
modifier onlyAuthorized {
|
||||||
33
contracts/asset-proxy/contracts/archive/Ownable.sol
Normal file
33
contracts/asset-proxy/contracts/archive/Ownable.sol
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
97
contracts/asset-proxy/contracts/src/ERC1155Proxy.sol
Normal file
97
contracts/asset-proxy/contracts/src/ERC1155Proxy.sol
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.5.9;
|
||||||
|
|
||||||
|
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||||
|
import "@0x/contracts-utils/contracts/src/SafeMath.sol";
|
||||||
|
import "@0x/contracts-erc1155/contracts/src/interfaces/IERC1155.sol";
|
||||||
|
import "../archive/MixinAuthorizable.sol";
|
||||||
|
import "./interfaces/IAssetProxy.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract ERC1155Proxy is
|
||||||
|
MixinAuthorizable,
|
||||||
|
SafeMath,
|
||||||
|
IAssetProxy
|
||||||
|
{
|
||||||
|
using LibBytes for bytes;
|
||||||
|
|
||||||
|
// Id of this proxy.
|
||||||
|
bytes4 constant internal PROXY_ID = bytes4(keccak256("ERC1155Assets(address,uint256[],uint256[],bytes)"));
|
||||||
|
|
||||||
|
/// @dev Transfers batch of ERC1155 assets. Either succeeds or throws.
|
||||||
|
/// @param assetData Byte array encoded with ERC1155 token address, array of ids, array of values, and callback data.
|
||||||
|
/// @param from Address to transfer assets from.
|
||||||
|
/// @param to Address to transfer assets to.
|
||||||
|
/// @param amount Amount that will be multiplied with each element of `assetData.values` to scale the
|
||||||
|
/// values that will be transferred.
|
||||||
|
function transferFrom(
|
||||||
|
bytes calldata assetData,
|
||||||
|
address from,
|
||||||
|
address to,
|
||||||
|
uint256 amount
|
||||||
|
)
|
||||||
|
external
|
||||||
|
onlyAuthorized
|
||||||
|
{
|
||||||
|
// Decode params from `assetData`
|
||||||
|
// solhint-disable indent
|
||||||
|
(
|
||||||
|
address erc1155TokenAddress,
|
||||||
|
uint256[] memory ids,
|
||||||
|
uint256[] memory values,
|
||||||
|
bytes memory data
|
||||||
|
) = abi.decode(
|
||||||
|
assetData.sliceDestructive(4, assetData.length),
|
||||||
|
(address, uint256[], uint256[], bytes)
|
||||||
|
);
|
||||||
|
// solhint-enable indent
|
||||||
|
|
||||||
|
// Scale values up by `amount`
|
||||||
|
uint256 length = values.length;
|
||||||
|
uint256[] memory scaledValues = new uint256[](length);
|
||||||
|
for (uint256 i = 0; i != length; i++) {
|
||||||
|
// We write the scaled values to an unused location in memory in order
|
||||||
|
// to avoid copying over `ids` or `data`. This is possible if they are
|
||||||
|
// identical to `values` and the offsets for each are pointing to the
|
||||||
|
// same location in the ABI encoded calldata.
|
||||||
|
scaledValues[i] = _safeMul(values[i], amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute `safeBatchTransferFrom` call
|
||||||
|
// Either succeeds or throws
|
||||||
|
IERC1155(erc1155TokenAddress).safeBatchTransferFrom(
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
ids,
|
||||||
|
scaledValues,
|
||||||
|
data
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Gets the proxy id associated with the proxy address.
|
||||||
|
/// @return Proxy id.
|
||||||
|
function getProxyId()
|
||||||
|
external
|
||||||
|
pure
|
||||||
|
returns (bytes4)
|
||||||
|
{
|
||||||
|
return PROXY_ID;
|
||||||
|
}
|
||||||
|
}
|
||||||
126
contracts/asset-proxy/contracts/src/ERC20BridgeProxy.sol
Normal file
126
contracts/asset-proxy/contracts/src/ERC20BridgeProxy.sol
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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).withdrawTo(
|
||||||
|
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 2018 ZeroEx Intl.
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -16,9 +16,9 @@
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pragma solidity 0.4.24;
|
pragma solidity ^0.5.9;
|
||||||
|
|
||||||
import "./MixinAuthorizable.sol";
|
import "../archive/MixinAuthorizable.sol";
|
||||||
|
|
||||||
|
|
||||||
contract ERC20Proxy is
|
contract ERC20Proxy is
|
||||||
@@ -26,9 +26,9 @@ contract ERC20Proxy is
|
|||||||
{
|
{
|
||||||
// Id of this proxy.
|
// Id of this proxy.
|
||||||
bytes4 constant internal PROXY_ID = bytes4(keccak256("ERC20Token(address)"));
|
bytes4 constant internal PROXY_ID = bytes4(keccak256("ERC20Token(address)"));
|
||||||
|
|
||||||
// solhint-disable-next-line payable-fallback
|
// solhint-disable-next-line payable-fallback
|
||||||
function ()
|
function ()
|
||||||
external
|
external
|
||||||
{
|
{
|
||||||
assembly {
|
assembly {
|
||||||
@@ -117,13 +117,13 @@ contract ERC20Proxy is
|
|||||||
// * The "token address" is offset 32+4=36 bytes into "assetData" (tables 1 & 2).
|
// * The "token address" is offset 32+4=36 bytes into "assetData" (tables 1 & 2).
|
||||||
// [tokenOffset = assetDataOffsetFromHeader + 36 = calldataload(4) + 4 + 36]
|
// [tokenOffset = assetDataOffsetFromHeader + 36 = calldataload(4) + 4 + 36]
|
||||||
let token := calldataload(add(calldataload(4), 40))
|
let token := calldataload(add(calldataload(4), 40))
|
||||||
|
|
||||||
/////// Setup Header Area ///////
|
/////// Setup Header Area ///////
|
||||||
// This area holds the 4-byte `transferFrom` selector.
|
// This area holds the 4-byte `transferFrom` selector.
|
||||||
// Any trailing data in transferFromSelector will be
|
// Any trailing data in transferFromSelector will be
|
||||||
// overwritten in the next `mstore` call.
|
// overwritten in the next `mstore` call.
|
||||||
mstore(0, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
|
mstore(0, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
|
||||||
|
|
||||||
/////// Setup Params Area ///////
|
/////// Setup Params Area ///////
|
||||||
// We copy the fields `from`, `to` and `amount` in bulk
|
// We copy the fields `from`, `to` and `amount` in bulk
|
||||||
// from our own calldata to the new calldata.
|
// 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
|
// If the token does return data, we require that it is a single
|
||||||
// nonzero 32 bytes value.
|
// nonzero 32 bytes value.
|
||||||
// So the transfer succeeded if the call succeeded and either
|
// 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(
|
success := and(success, or(
|
||||||
iszero(returndatasize),
|
iszero(returndatasize),
|
||||||
and(
|
and(
|
||||||
@@ -158,7 +158,7 @@ contract ERC20Proxy is
|
|||||||
if success {
|
if success {
|
||||||
return(0, 0)
|
return(0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Revert with `Error("TRANSFER_FAILED")`
|
// Revert with `Error("TRANSFER_FAILED")`
|
||||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
||||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
|
|
||||||
Copyright 2018 ZeroEx Intl.
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -16,9 +16,9 @@
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pragma solidity 0.4.24;
|
pragma solidity ^0.5.9;
|
||||||
|
|
||||||
import "./MixinAuthorizable.sol";
|
import "../archive/MixinAuthorizable.sol";
|
||||||
|
|
||||||
|
|
||||||
contract ERC721Proxy is
|
contract ERC721Proxy is
|
||||||
@@ -28,7 +28,7 @@ contract ERC721Proxy is
|
|||||||
bytes4 constant internal PROXY_ID = bytes4(keccak256("ERC721Token(address,uint256)"));
|
bytes4 constant internal PROXY_ID = bytes4(keccak256("ERC721Token(address,uint256)"));
|
||||||
|
|
||||||
// solhint-disable-next-line payable-fallback
|
// solhint-disable-next-line payable-fallback
|
||||||
function ()
|
function ()
|
||||||
external
|
external
|
||||||
{
|
{
|
||||||
assembly {
|
assembly {
|
||||||
@@ -93,10 +93,10 @@ contract ERC721Proxy is
|
|||||||
// | Params | | 2 * 32 | function parameters: |
|
// | Params | | 2 * 32 | function parameters: |
|
||||||
// | | 4 | 12 + 20 | 1. token address |
|
// | | 4 | 12 + 20 | 1. token address |
|
||||||
// | | 36 | | 2. tokenId |
|
// | | 36 | | 2. tokenId |
|
||||||
|
|
||||||
// We construct calldata for the `token.transferFrom` ABI.
|
// We construct calldata for the `token.transferFrom` ABI.
|
||||||
// The layout of this calldata is in the table below.
|
// The layout of this calldata is in the table below.
|
||||||
//
|
//
|
||||||
// | Area | Offset | Length | Contents |
|
// | Area | Offset | Length | Contents |
|
||||||
// |----------|--------|---------|-------------------------------------|
|
// |----------|--------|---------|-------------------------------------|
|
||||||
// | Header | 0 | 4 | function selector |
|
// | Header | 0 | 4 | function selector |
|
||||||
@@ -121,7 +121,7 @@ contract ERC721Proxy is
|
|||||||
// Any trailing data in transferFromSelector will be
|
// Any trailing data in transferFromSelector will be
|
||||||
// overwritten in the next `mstore` call.
|
// overwritten in the next `mstore` call.
|
||||||
mstore(0, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
|
mstore(0, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
|
||||||
|
|
||||||
/////// Setup Params Area ///////
|
/////// Setup Params Area ///////
|
||||||
// We copy the fields `from` and `to` in bulk
|
// We copy the fields `from` and `to` in bulk
|
||||||
// from our own calldata to the new calldata.
|
// from our own calldata to the new calldata.
|
||||||
@@ -145,7 +145,7 @@ contract ERC721Proxy is
|
|||||||
if success {
|
if success {
|
||||||
return(0, 0)
|
return(0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Revert with `Error("TRANSFER_FAILED")`
|
// Revert with `Error("TRANSFER_FAILED")`
|
||||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
||||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
|
|
||||||
Copyright 2018 ZeroEx Intl.
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -16,10 +16,10 @@
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pragma solidity 0.4.24;
|
pragma solidity ^0.5.9;
|
||||||
|
|
||||||
import "./MixinAssetProxyDispatcher.sol";
|
import "../archive/MixinAssetProxyDispatcher.sol";
|
||||||
import "./MixinAuthorizable.sol";
|
import "../archive/MixinAuthorizable.sol";
|
||||||
|
|
||||||
|
|
||||||
contract MultiAssetProxy is
|
contract MultiAssetProxy is
|
||||||
@@ -90,7 +90,10 @@ contract MultiAssetProxy is
|
|||||||
// offset to assetData.
|
// offset to assetData.
|
||||||
|
|
||||||
// Load offset to `assetData`
|
// Load offset to `assetData`
|
||||||
let assetDataOffset := calldataload(4)
|
let assetDataOffset := add(calldataload(4), 4)
|
||||||
|
|
||||||
|
// Load length in bytes of `assetData`
|
||||||
|
let assetDataLength := calldataload(assetDataOffset)
|
||||||
|
|
||||||
// Asset data itself is encoded as follows:
|
// Asset data itself is encoded as follows:
|
||||||
//
|
//
|
||||||
@@ -102,47 +105,68 @@ contract MultiAssetProxy is
|
|||||||
// | | 36 | | 2. offset to nestedAssetData (*) |
|
// | | 36 | | 2. offset to nestedAssetData (*) |
|
||||||
// | Data | | | amounts: |
|
// | Data | | | amounts: |
|
||||||
// | | 68 | 32 | amounts Length |
|
// | | 68 | 32 | amounts Length |
|
||||||
// | | 100 | a | amounts Contents |
|
// | | 100 | a | amounts Contents |
|
||||||
// | | | | nestedAssetData: |
|
// | | | | nestedAssetData: |
|
||||||
// | | 100 + a | 32 | nestedAssetData Length |
|
// | | 100 + a | 32 | nestedAssetData Length |
|
||||||
// | | 132 + a | b | nestedAssetData Contents (offsets) |
|
// | | 132 + a | b | nestedAssetData Contents (offsets) |
|
||||||
// | | 132 + a + b | | nestedAssetData[0, ..., len] |
|
// | | 132 + a + b | | nestedAssetData[0, ..., len] |
|
||||||
|
|
||||||
|
// Assert that the length of asset data:
|
||||||
|
// 1. Must be at least 68 bytes (see table above)
|
||||||
|
// 2. Must be a multiple of 32 (excluding the 4-byte selector)
|
||||||
|
if or(lt(assetDataLength, 68), mod(sub(assetDataLength, 4), 32)) {
|
||||||
|
// Revert with `Error("INVALID_ASSET_DATA_LENGTH")`
|
||||||
|
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
||||||
|
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
||||||
|
mstore(64, 0x00000019494e56414c49445f41535345545f444154415f4c454e475448000000)
|
||||||
|
mstore(96, 0)
|
||||||
|
revert(0, 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
// End of asset data in calldata
|
||||||
|
// assetDataOffset
|
||||||
|
// + 32 (assetData len)
|
||||||
|
let assetDataEnd := add(assetDataOffset, add(assetDataLength, 32))
|
||||||
|
if gt(assetDataEnd, calldatasize()) {
|
||||||
|
// Revert with `Error("INVALID_ASSET_DATA_END")`
|
||||||
|
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
||||||
|
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
||||||
|
mstore(64, 0x00000016494e56414c49445f41535345545f444154415f454e44000000000000)
|
||||||
|
mstore(96, 0)
|
||||||
|
revert(0, 100)
|
||||||
|
}
|
||||||
|
|
||||||
// In order to find the offset to `amounts`, we must add:
|
// In order to find the offset to `amounts`, we must add:
|
||||||
// 4 (function selector)
|
// assetDataOffset
|
||||||
// + assetDataOffset
|
|
||||||
// + 32 (assetData len)
|
// + 32 (assetData len)
|
||||||
// + 4 (assetProxyId)
|
// + 4 (assetProxyId)
|
||||||
let amountsOffset := calldataload(add(assetDataOffset, 40))
|
let amountsOffset := calldataload(add(assetDataOffset, 36))
|
||||||
|
|
||||||
// In order to find the offset to `nestedAssetData`, we must add:
|
// In order to find the offset to `nestedAssetData`, we must add:
|
||||||
// 4 (function selector)
|
// assetDataOffset
|
||||||
// + assetDataOffset
|
|
||||||
// + 32 (assetData len)
|
// + 32 (assetData len)
|
||||||
// + 4 (assetProxyId)
|
// + 4 (assetProxyId)
|
||||||
// + 32 (amounts offset)
|
// + 32 (amounts offset)
|
||||||
let nestedAssetDataOffset := calldataload(add(assetDataOffset, 72))
|
let nestedAssetDataOffset := calldataload(add(assetDataOffset, 68))
|
||||||
|
|
||||||
// In order to find the start of the `amounts` contents, we must add:
|
// In order to find the start of the `amounts` contents, we must add:
|
||||||
// 4 (function selector)
|
// assetDataOffset
|
||||||
// + assetDataOffset
|
|
||||||
// + 32 (assetData len)
|
// + 32 (assetData len)
|
||||||
// + 4 (assetProxyId)
|
// + 4 (assetProxyId)
|
||||||
// + amountsOffset
|
// + amountsOffset
|
||||||
// + 32 (amounts len)
|
// + 32 (amounts len)
|
||||||
let amountsContentsStart := add(assetDataOffset, add(amountsOffset, 72))
|
let amountsContentsStart := add(assetDataOffset, add(amountsOffset, 68))
|
||||||
|
|
||||||
// Load number of elements in `amounts`
|
// Load number of elements in `amounts`
|
||||||
let amountsLen := calldataload(sub(amountsContentsStart, 32))
|
let amountsLen := calldataload(sub(amountsContentsStart, 32))
|
||||||
|
|
||||||
// In order to find the start of the `nestedAssetData` contents, we must add:
|
// In order to find the start of the `nestedAssetData` contents, we must add:
|
||||||
// 4 (function selector)
|
// assetDataOffset
|
||||||
// + assetDataOffset
|
|
||||||
// + 32 (assetData len)
|
// + 32 (assetData len)
|
||||||
// + 4 (assetProxyId)
|
// + 4 (assetProxyId)
|
||||||
// + nestedAssetDataOffset
|
// + nestedAssetDataOffset
|
||||||
// + 32 (nestedAssetData len)
|
// + 32 (nestedAssetData len)
|
||||||
let nestedAssetDataContentsStart := add(assetDataOffset, add(nestedAssetDataOffset, 72))
|
let nestedAssetDataContentsStart := add(assetDataOffset, add(nestedAssetDataOffset, 68))
|
||||||
|
|
||||||
// Load number of elements in `nestedAssetData`
|
// Load number of elements in `nestedAssetData`
|
||||||
let nestedAssetDataLen := calldataload(sub(nestedAssetDataContentsStart, 32))
|
let nestedAssetDataLen := calldataload(sub(nestedAssetDataContentsStart, 32))
|
||||||
@@ -166,10 +190,10 @@ contract MultiAssetProxy is
|
|||||||
|
|
||||||
// Overwrite existing offset to `assetData` with our own
|
// Overwrite existing offset to `assetData` with our own
|
||||||
mstore(4, 128)
|
mstore(4, 128)
|
||||||
|
|
||||||
// Load `amount`
|
// Load `amount`
|
||||||
let amount := calldataload(100)
|
let amount := calldataload(100)
|
||||||
|
|
||||||
// Calculate number of bytes in `amounts` contents
|
// Calculate number of bytes in `amounts` contents
|
||||||
let amountsByteLen := mul(amountsLen, 32)
|
let amountsByteLen := mul(amountsLen, 32)
|
||||||
|
|
||||||
@@ -184,7 +208,7 @@ contract MultiAssetProxy is
|
|||||||
let amountsElement := calldataload(add(amountsContentsStart, i))
|
let amountsElement := calldataload(add(amountsContentsStart, i))
|
||||||
let totalAmount := mul(amountsElement, amount)
|
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(
|
if iszero(or(
|
||||||
iszero(amount),
|
iszero(amount),
|
||||||
eq(div(totalAmount, amount), amountsElement)
|
eq(div(totalAmount, amount), amountsElement)
|
||||||
@@ -204,15 +228,20 @@ contract MultiAssetProxy is
|
|||||||
let nestedAssetDataElementOffset := calldataload(add(nestedAssetDataContentsStart, i))
|
let nestedAssetDataElementOffset := calldataload(add(nestedAssetDataContentsStart, i))
|
||||||
|
|
||||||
// In order to find the start of the `nestedAssetData[i]` contents, we must add:
|
// In order to find the start of the `nestedAssetData[i]` contents, we must add:
|
||||||
// 4 (function selector)
|
// assetDataOffset
|
||||||
// + assetDataOffset
|
|
||||||
// + 32 (assetData len)
|
// + 32 (assetData len)
|
||||||
// + 4 (assetProxyId)
|
// + 4 (assetProxyId)
|
||||||
// + nestedAssetDataOffset
|
// + nestedAssetDataOffset
|
||||||
// + 32 (nestedAssetData len)
|
// + 32 (nestedAssetData len)
|
||||||
// + nestedAssetDataElementOffset
|
// + nestedAssetDataElementOffset
|
||||||
// + 32 (nestedAssetDataElement len)
|
// + 32 (nestedAssetDataElement len)
|
||||||
let nestedAssetDataElementContentsStart := add(assetDataOffset, add(nestedAssetDataOffset, add(nestedAssetDataElementOffset, 104)))
|
let nestedAssetDataElementContentsStart := add(
|
||||||
|
assetDataOffset,
|
||||||
|
add(
|
||||||
|
nestedAssetDataOffset,
|
||||||
|
add(nestedAssetDataElementOffset, 100)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
// Load length of `nestedAssetData[i]`
|
// Load length of `nestedAssetData[i]`
|
||||||
let nestedAssetDataElementLenStart := sub(nestedAssetDataElementContentsStart, 32)
|
let nestedAssetDataElementLenStart := sub(nestedAssetDataElementContentsStart, 32)
|
||||||
@@ -245,7 +274,7 @@ contract MultiAssetProxy is
|
|||||||
mstore(164, assetProxies_slot)
|
mstore(164, assetProxies_slot)
|
||||||
assetProxy := sload(keccak256(132, 64))
|
assetProxy := sload(keccak256(132, 64))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Revert if AssetProxy with given id does not exist
|
// Revert if AssetProxy with given id does not exist
|
||||||
if iszero(assetProxy) {
|
if iszero(assetProxy) {
|
||||||
// Revert with `Error("ASSET_PROXY_DOES_NOT_EXIST")`
|
// Revert with `Error("ASSET_PROXY_DOES_NOT_EXIST")`
|
||||||
@@ -255,7 +284,7 @@ contract MultiAssetProxy is
|
|||||||
mstore(96, 0)
|
mstore(96, 0)
|
||||||
revert(0, 100)
|
revert(0, 100)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy `nestedAssetData[i]` from calldata to memory
|
// Copy `nestedAssetData[i]` from calldata to memory
|
||||||
calldatacopy(
|
calldatacopy(
|
||||||
132, // memory slot after `amounts[i]`
|
132, // memory slot after `amounts[i]`
|
||||||
@@ -269,7 +298,7 @@ contract MultiAssetProxy is
|
|||||||
assetProxy, // call address of asset proxy
|
assetProxy, // call address of asset proxy
|
||||||
0, // don't send any ETH
|
0, // don't send any ETH
|
||||||
0, // pointer to start of input
|
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, // write output over memory that won't be reused
|
||||||
0 // don't copy output to memory
|
0 // don't copy output to memory
|
||||||
)
|
)
|
||||||
|
|||||||
83
contracts/asset-proxy/contracts/src/StaticCallProxy.sol
Normal file
83
contracts/asset-proxy/contracts/src/StaticCallProxy.sol
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.5.9;
|
||||||
|
|
||||||
|
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||||
|
|
||||||
|
|
||||||
|
// solhint-disable no-unused-vars
|
||||||
|
contract StaticCallProxy {
|
||||||
|
|
||||||
|
using LibBytes for bytes;
|
||||||
|
|
||||||
|
// Id of this proxy.
|
||||||
|
bytes4 constant internal PROXY_ID = bytes4(keccak256("StaticCall(address,bytes,bytes32)"));
|
||||||
|
|
||||||
|
/// @dev Makes a staticcall to a target address and verifies that the data returned matches the expected return data.
|
||||||
|
/// @param assetData Byte array encoded with staticCallTarget, staticCallData, and expectedCallResultHash
|
||||||
|
/// @param from This value is ignored.
|
||||||
|
/// @param to This value is ignored.
|
||||||
|
/// @param amount This value is ignored.
|
||||||
|
function transferFrom(
|
||||||
|
bytes calldata assetData,
|
||||||
|
address from,
|
||||||
|
address to,
|
||||||
|
uint256 amount
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
{
|
||||||
|
// Decode params from `assetData`
|
||||||
|
(
|
||||||
|
address staticCallTarget,
|
||||||
|
bytes memory staticCallData,
|
||||||
|
bytes32 expectedReturnDataHash
|
||||||
|
) = abi.decode(
|
||||||
|
assetData.sliceDestructive(4, assetData.length),
|
||||||
|
(address, bytes, bytes32)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Execute staticcall
|
||||||
|
(bool success, bytes memory returnData) = staticCallTarget.staticcall(staticCallData);
|
||||||
|
|
||||||
|
// Revert with returned data if staticcall is unsuccessful
|
||||||
|
if (!success) {
|
||||||
|
assembly {
|
||||||
|
revert(add(returnData, 32), mload(returnData))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Revert if hash of return data is not as expected
|
||||||
|
bytes32 returnDataHash = keccak256(returnData);
|
||||||
|
require(
|
||||||
|
expectedReturnDataHash == returnDataHash,
|
||||||
|
"UNEXPECTED_STATIC_CALL_RESULT"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Gets the proxy id associated with the proxy address.
|
||||||
|
/// @return Proxy id.
|
||||||
|
function getProxyId()
|
||||||
|
external
|
||||||
|
pure
|
||||||
|
returns (bytes4)
|
||||||
|
{
|
||||||
|
return PROXY_ID;
|
||||||
|
}
|
||||||
|
}
|
||||||
142
contracts/asset-proxy/contracts/src/bridges/Eth2DaiBridge.sol
Normal file
142
contracts/asset-proxy/contracts/src/bridges/Eth2DaiBridge.sol
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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 "../interfaces/IERC20Bridge.sol";
|
||||||
|
import "../interfaces/IEth2Dai.sol";
|
||||||
|
import "../interfaces/IWallet.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 withdrawTo(
|
||||||
|
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.
|
||||||
|
IERC20Token(fromTokenAddress).approve(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`.
|
||||||
|
_transferERC20Token(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Permissively transfers an ERC20 token that may not adhere to
|
||||||
|
/// specs.
|
||||||
|
/// @param tokenAddress The token contract address.
|
||||||
|
/// @param to The token recipient.
|
||||||
|
/// @param amount The amount of tokens to transfer.
|
||||||
|
function _transferERC20Token(
|
||||||
|
address tokenAddress,
|
||||||
|
address to,
|
||||||
|
uint256 amount
|
||||||
|
)
|
||||||
|
private
|
||||||
|
{
|
||||||
|
// Transfer tokens.
|
||||||
|
// We do a raw call so we can check the success separate
|
||||||
|
// from the return data.
|
||||||
|
(bool didSucceed, bytes memory returnData) = tokenAddress.call(
|
||||||
|
abi.encodeWithSelector(
|
||||||
|
IERC20Token(0).transfer.selector,
|
||||||
|
to,
|
||||||
|
amount
|
||||||
|
)
|
||||||
|
);
|
||||||
|
if (!didSucceed) {
|
||||||
|
assembly { revert(add(returnData, 0x20), mload(returnData)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check return data.
|
||||||
|
// If there is no return data, we assume the token incorrectly
|
||||||
|
// does not return a bool. In this case we expect it to revert
|
||||||
|
// on failure, which was handled above.
|
||||||
|
// If the token does return data, we require that it is a single
|
||||||
|
// value that evaluates to true.
|
||||||
|
assembly {
|
||||||
|
if returndatasize {
|
||||||
|
didSucceed := 0
|
||||||
|
if eq(returndatasize, 32) {
|
||||||
|
// First 64 bytes of memory are reserved scratch space
|
||||||
|
returndatacopy(0, 0, 32)
|
||||||
|
didSucceed := mload(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
require(didSucceed, "ERC20_TRANSFER_FAILED");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
|
|
||||||
Copyright 2018 ZeroEx Intl.
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// solhint-disable
|
// solhint-disable
|
||||||
pragma solidity ^0.4.24;
|
pragma solidity ^0.5.9;
|
||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
|
||||||
@@ -26,19 +26,63 @@ pragma experimental ABIEncoderV2;
|
|||||||
// This argument is ABI encoded as one of the methods of this interface.
|
// This argument is ABI encoded as one of the methods of this interface.
|
||||||
interface IAssetData {
|
interface IAssetData {
|
||||||
|
|
||||||
function ERC20Token(address tokenContract)
|
/// @dev Function signature for encoding ERC20 assetData.
|
||||||
|
/// @param tokenAddress Address of ERC20Token contract.
|
||||||
|
function ERC20Token(address tokenAddress)
|
||||||
external;
|
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(
|
function ERC721Token(
|
||||||
address tokenContract,
|
address tokenAddress,
|
||||||
uint256 tokenId
|
uint256 tokenId
|
||||||
)
|
)
|
||||||
external;
|
external;
|
||||||
|
|
||||||
function MultiAsset(
|
/// @dev Function signature for encoding ERC1155 assetData.
|
||||||
uint256[] amounts,
|
/// @param tokenAddress Address of ERC1155 token contract.
|
||||||
bytes[] nestedAssetData
|
/// @param tokenIds Array of ids of tokens to be transferred.
|
||||||
|
/// @param values Array of values that correspond to each token id to be transferred.
|
||||||
|
/// Note that each value will be multiplied by the amount being filled in the order before transferring.
|
||||||
|
/// @param callbackData Extra data to be passed to receiver's `onERC1155Received` callback function.
|
||||||
|
function ERC1155Assets(
|
||||||
|
address tokenAddress,
|
||||||
|
uint256[] calldata tokenIds,
|
||||||
|
uint256[] calldata values,
|
||||||
|
bytes calldata callbackData
|
||||||
|
)
|
||||||
|
external;
|
||||||
|
|
||||||
|
/// @dev Function signature for encoding MultiAsset assetData.
|
||||||
|
/// @param values Array of amounts that correspond to each asset to be transferred.
|
||||||
|
/// Note that each value will be multiplied by the amount being filled in the order before transferring.
|
||||||
|
/// @param nestedAssetData Array of assetData fields that will be be dispatched to their correspnding AssetProxy contract.
|
||||||
|
function MultiAsset(
|
||||||
|
uint256[] calldata values,
|
||||||
|
bytes[] calldata nestedAssetData
|
||||||
|
)
|
||||||
|
external;
|
||||||
|
|
||||||
|
/// @dev Function signature for encoding StaticCall assetData.
|
||||||
|
/// @param staticCallTargetAddress Address that will execute the staticcall.
|
||||||
|
/// @param staticCallData Data that will be executed via staticcall on the staticCallTargetAddress.
|
||||||
|
/// @param expectedReturnDataHash Keccak-256 hash of the expected staticcall return data.
|
||||||
|
function StaticCall(
|
||||||
|
address staticCallTargetAddress,
|
||||||
|
bytes calldata staticCallData,
|
||||||
|
bytes32 expectedReturnDataHash
|
||||||
|
)
|
||||||
|
external;
|
||||||
|
|
||||||
|
/// @dev Function signature for encoding ERC20Bridge assetData.
|
||||||
|
/// @param tokenAddress Address of token to transfer.
|
||||||
|
/// @param bridgeAddress Address of the bridge contract.
|
||||||
|
/// @param bridgeData Arbitrary data to be passed to the bridge contract.
|
||||||
|
function ERC20Bridge(
|
||||||
|
address tokenAddress,
|
||||||
|
address bridgeAddress,
|
||||||
|
bytes calldata bridgeData
|
||||||
)
|
)
|
||||||
external;
|
external;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
|
|
||||||
Copyright 2018 ZeroEx Intl.
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -16,21 +16,18 @@
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pragma solidity ^0.4.24;
|
pragma solidity ^0.5.9;
|
||||||
|
|
||||||
import "./IAuthorizable.sol";
|
|
||||||
|
|
||||||
|
|
||||||
contract IAssetProxy is
|
contract IAssetProxy {
|
||||||
IAuthorizable
|
|
||||||
{
|
|
||||||
/// @dev Transfers assets. Either succeeds or throws.
|
/// @dev Transfers assets. Either succeeds or throws.
|
||||||
/// @param assetData Byte array encoded for the respective asset proxy.
|
/// @param assetData Byte array encoded for the respective asset proxy.
|
||||||
/// @param from Address to transfer asset from.
|
/// @param from Address to transfer asset from.
|
||||||
/// @param to Address to transfer asset to.
|
/// @param to Address to transfer asset to.
|
||||||
/// @param amount Amount of asset to transfer.
|
/// @param amount Amount of asset to transfer.
|
||||||
function transferFrom(
|
function transferFrom(
|
||||||
bytes assetData,
|
bytes calldata assetData,
|
||||||
address from,
|
address from,
|
||||||
address to,
|
address to,
|
||||||
uint256 amount
|
uint256 amount
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
|
|
||||||
Copyright 2018 ZeroEx Intl.
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -16,11 +16,17 @@
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pragma solidity ^0.4.24;
|
pragma solidity ^0.5.9;
|
||||||
|
|
||||||
|
|
||||||
contract IAssetProxyDispatcher {
|
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.
|
/// @dev Registers an asset proxy to its asset proxy id.
|
||||||
/// Once an asset proxy is registered, it cannot be unregistered.
|
/// Once an asset proxy is registered, it cannot be unregistered.
|
||||||
/// @param assetProxy Address of new asset proxy to register.
|
/// @param assetProxy Address of new asset proxy to register.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
|
|
||||||
Copyright 2018 ZeroEx Intl.
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pragma solidity ^0.4.24;
|
pragma solidity ^0.5.9;
|
||||||
|
|
||||||
import "@0x/contracts-utils/contracts/src/interfaces/IOwnable.sol";
|
import "@0x/contracts-utils/contracts/src/interfaces/IOwnable.sol";
|
||||||
|
|
||||||
@@ -24,6 +24,18 @@ import "@0x/contracts-utils/contracts/src/interfaces/IOwnable.sol";
|
|||||||
contract IAuthorizable is
|
contract IAuthorizable is
|
||||||
IOwnable
|
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.
|
/// @dev Authorizes an address.
|
||||||
/// @param target Address to authorize.
|
/// @param target Address to authorize.
|
||||||
function addAuthorizedAddress(address target)
|
function addAuthorizedAddress(address target)
|
||||||
@@ -42,7 +54,7 @@ contract IAuthorizable is
|
|||||||
uint256 index
|
uint256 index
|
||||||
)
|
)
|
||||||
external;
|
external;
|
||||||
|
|
||||||
/// @dev Gets all authorized addresses.
|
/// @dev Gets all authorized addresses.
|
||||||
/// @return Array of authorized addresses.
|
/// @return Array of authorized addresses.
|
||||||
function getAuthorizedAddresses()
|
function getAuthorizedAddresses()
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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 withdrawTo(
|
||||||
|
address tokenAddress,
|
||||||
|
address from,
|
||||||
|
address to,
|
||||||
|
uint256 amount,
|
||||||
|
bytes calldata bridgeData
|
||||||
|
)
|
||||||
|
external
|
||||||
|
returns (bytes4 success);
|
||||||
|
}
|
||||||
38
contracts/asset-proxy/contracts/src/interfaces/IEth2Dai.sol
Normal file
38
contracts/asset-proxy/contracts/src/interfaces/IEth2Dai.sol
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
|
|
||||||
Copyright 2018 ZeroEx Intl.
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -16,22 +16,23 @@
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pragma solidity ^0.4.24;
|
pragma solidity ^0.5.9;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
|
||||||
contract IValidator {
|
contract IWallet {
|
||||||
|
|
||||||
/// @dev Verifies that a signature is valid.
|
bytes4 internal constant LEGACY_WALLET_MAGIC_VALUE = 0xb0671381;
|
||||||
|
|
||||||
|
/// @dev Validates a hash with the `Wallet` signature type.
|
||||||
/// @param hash Message hash that is signed.
|
/// @param hash Message hash that is signed.
|
||||||
/// @param signerAddress Address that should have signed the given hash.
|
|
||||||
/// @param signature Proof of signing.
|
/// @param signature Proof of signing.
|
||||||
/// @return Validity of order signature.
|
/// @return magicValue `bytes4(0xb0671381)` if the signature check succeeds.
|
||||||
function isValidSignature(
|
function isValidSignature(
|
||||||
bytes32 hash,
|
bytes32 hash,
|
||||||
address signerAddress,
|
bytes calldata signature
|
||||||
bytes signature
|
|
||||||
)
|
)
|
||||||
external
|
external
|
||||||
view
|
view
|
||||||
returns (bool isValid);
|
returns (bytes4 magicValue);
|
||||||
}
|
}
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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.4.24;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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.4.24;
|
|
||||||
|
|
||||||
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(); _; }
|
|
||||||
}
|
|
||||||
108
contracts/asset-proxy/contracts/test/TestERC20Bridge.sol
Normal file
108
contracts/asset-proxy/contracts/test/TestERC20Bridge.sol
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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 withdrawTo(
|
||||||
|
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)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
202
contracts/asset-proxy/contracts/test/TestEth2DaiBridge.sol
Normal file
202
contracts/asset-proxy/contracts/test/TestEth2DaiBridge.sol
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.5.9;
|
||||||
|
|
||||||
|
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract TestStaticCallTarget {
|
||||||
|
|
||||||
|
using LibBytes for bytes;
|
||||||
|
|
||||||
|
uint256 internal _state;
|
||||||
|
|
||||||
|
function updateState()
|
||||||
|
external
|
||||||
|
{
|
||||||
|
_state++;
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertEvenNumber(uint256 target)
|
||||||
|
external
|
||||||
|
pure
|
||||||
|
{
|
||||||
|
require(
|
||||||
|
target % 2 == 0,
|
||||||
|
"TARGET_NOT_EVEN"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isOddNumber(uint256 target)
|
||||||
|
external
|
||||||
|
pure
|
||||||
|
returns (bool isOdd)
|
||||||
|
{
|
||||||
|
isOdd = target % 2 == 1;
|
||||||
|
return isOdd;
|
||||||
|
}
|
||||||
|
|
||||||
|
function noInputFunction()
|
||||||
|
external
|
||||||
|
pure
|
||||||
|
{
|
||||||
|
assert(msg.data.length == 4 && msg.data.readBytes4(0) == bytes4(keccak256("noInputFunction()")));
|
||||||
|
}
|
||||||
|
|
||||||
|
function dynamicInputFunction(bytes calldata a)
|
||||||
|
external
|
||||||
|
pure
|
||||||
|
{
|
||||||
|
bytes memory abiEncodedData = abi.encodeWithSignature("dynamicInputFunction(bytes)", a);
|
||||||
|
assert(msg.data.equals(abiEncodedData));
|
||||||
|
}
|
||||||
|
|
||||||
|
function returnComplexType(uint256 a, uint256 b)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (bytes memory result)
|
||||||
|
{
|
||||||
|
result = abi.encodePacked(
|
||||||
|
address(this),
|
||||||
|
a,
|
||||||
|
b
|
||||||
|
);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@0x/contracts-asset-proxy",
|
"name": "@0x/contracts-asset-proxy",
|
||||||
"version": "1.0.5",
|
"version": "2.3.0-beta.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.12"
|
"node": ">=6.12"
|
||||||
},
|
},
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "yarn pre_build && tsc -b",
|
"build": "yarn pre_build && tsc -b",
|
||||||
"build:ci": "yarn build",
|
"build:ci": "yarn build",
|
||||||
"pre_build": "run-s compile generate_contract_wrappers",
|
"pre_build": "run-s compile contracts:gen generate_contract_wrappers",
|
||||||
"test": "yarn run_mocha",
|
"test": "yarn run_mocha",
|
||||||
"rebuild_and_test": "run-s build test",
|
"rebuild_and_test": "run-s build test",
|
||||||
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
||||||
@@ -22,18 +22,20 @@
|
|||||||
"compile": "sol-compiler",
|
"compile": "sol-compiler",
|
||||||
"watch": "sol-compiler -w",
|
"watch": "sol-compiler -w",
|
||||||
"clean": "shx rm -rf lib generated-artifacts generated-wrappers",
|
"clean": "shx rm -rf lib generated-artifacts generated-wrappers",
|
||||||
"generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../../node_modules/@0x/abi-gen-templates/contract.handlebars --partials '../../node_modules/@0x/abi-gen-templates/partials/**/*.handlebars' --output generated-wrappers --backend ethers",
|
"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",
|
"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:text": "istanbul report text",
|
||||||
"coverage:report:html": "istanbul report html && open coverage/index.html",
|
"coverage:report:html": "istanbul report html && open coverage/index.html",
|
||||||
"profiler: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",
|
"coverage:report:lcov": "istanbul report lcov",
|
||||||
"test:circleci": "yarn test",
|
"test:circleci": "yarn test",
|
||||||
"contracts:gen": "contracts-gen",
|
"contracts:gen": "contracts-gen",
|
||||||
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol"
|
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol",
|
||||||
|
"compile:truffle": "truffle compile"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"abis": "./generated-artifacts/@(DummyERC20Token|DummyERC721Receiver|DummyERC721Token|DummyMultipleReturnERC20Token|DummyNoReturnERC20Token|ERC20Proxy|ERC721Proxy|IAssetData|IAssetProxy|IAuthorizable|MixinAuthorizable|MultiAssetProxy).json",
|
"abis": "./generated-artifacts/@(ERC1155Proxy|ERC20BridgeProxy|ERC20Proxy|ERC721Proxy|Eth2DaiBridge|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|IERC20Bridge|IEth2Dai|IWallet|MixinAssetProxyDispatcher|MixinAuthorizable|MultiAssetProxy|Ownable|StaticCallProxy|TestERC20Bridge|TestEth2DaiBridge|TestStaticCallTarget).json",
|
||||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
|
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -46,37 +48,41 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/protocol/README.md",
|
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/protocol/README.md",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@0x/abi-gen": "^2.0.3",
|
"@0x/abi-gen": "^4.3.0-beta.0",
|
||||||
"@0x/contracts-gen": "^1.0.2",
|
"@0x/contracts-gen": "^1.1.0-beta.0",
|
||||||
"@0x/dev-utils": "^2.1.0",
|
"@0x/contracts-test-utils": "^3.2.0-beta.0",
|
||||||
"@0x/sol-compiler": "^3.0.3",
|
"@0x/dev-utils": "^2.4.0-beta.0",
|
||||||
"@0x/tslint-config": "^3.0.0",
|
"@0x/sol-compiler": "^3.2.0-beta.0",
|
||||||
|
"@0x/tslint-config": "^3.0.1",
|
||||||
"@types/lodash": "4.14.104",
|
"@types/lodash": "4.14.104",
|
||||||
|
"@types/mocha": "^5.2.7",
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
"chai": "^4.0.1",
|
"chai": "^4.0.1",
|
||||||
"chai-as-promised": "^7.1.0",
|
"chai-as-promised": "^7.1.0",
|
||||||
"chai-bignumber": "^3.0.0",
|
"chai-bignumber": "^3.0.0",
|
||||||
"dirty-chai": "^2.0.1",
|
"dirty-chai": "^2.0.1",
|
||||||
"make-promises-safe": "^1.1.0",
|
"make-promises-safe": "^1.1.0",
|
||||||
"mocha": "^4.1.0",
|
"mocha": "^6.2.0",
|
||||||
"npm-run-all": "^4.1.2",
|
"npm-run-all": "^4.1.2",
|
||||||
"shx": "^0.2.2",
|
"shx": "^0.2.2",
|
||||||
"solhint": "^1.4.1",
|
"solhint": "^1.4.1",
|
||||||
|
"truffle": "^5.0.32",
|
||||||
"tslint": "5.11.0",
|
"tslint": "5.11.0",
|
||||||
"typescript": "3.0.1"
|
"typescript": "3.0.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@0x/base-contract": "^4.0.3",
|
"@0x/base-contract": "^5.5.0-beta.0",
|
||||||
"@0x/contracts-erc20": "1.0.2",
|
"@0x/contracts-erc1155": "^1.2.0-beta.0",
|
||||||
"@0x/contracts-erc721": "1.0.2",
|
"@0x/contracts-erc20": "^2.3.0-beta.0",
|
||||||
"@0x/contracts-test-utils": "^3.0.4",
|
"@0x/contracts-erc721": "^2.2.0-beta.0",
|
||||||
"@0x/contracts-utils": "2.0.1",
|
"@0x/contracts-utils": "^3.3.0-beta.0",
|
||||||
"@0x/order-utils": "^6.1.0",
|
"@0x/order-utils": "^8.5.0-beta.0",
|
||||||
"@0x/types": "^2.0.2",
|
"@0x/types": "^2.5.0-beta.0",
|
||||||
"@0x/typescript-typings": "^4.0.0",
|
"@0x/typescript-typings": "^4.4.0-beta.0",
|
||||||
"@0x/utils": "^4.1.0",
|
"@0x/utils": "^4.6.0-beta.0",
|
||||||
"@0x/web3-wrapper": "^5.0.0",
|
"@0x/web3-wrapper": "^6.1.0-beta.0",
|
||||||
"ethereum-types": "^2.0.0",
|
"ethereum-types": "^2.2.0-beta.0",
|
||||||
|
"ethereumjs-util": "^5.1.1",
|
||||||
"lodash": "^4.17.11"
|
"lodash": "^4.17.11"
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
|
|||||||
@@ -5,29 +5,45 @@
|
|||||||
*/
|
*/
|
||||||
import { ContractArtifact } from 'ethereum-types';
|
import { ContractArtifact } from 'ethereum-types';
|
||||||
|
|
||||||
import * as DummyERC20Token from '../generated-artifacts/DummyERC20Token.json';
|
import * as ERC1155Proxy from '../generated-artifacts/ERC1155Proxy.json';
|
||||||
import * as DummyERC721Receiver from '../generated-artifacts/DummyERC721Receiver.json';
|
import * as ERC20BridgeProxy from '../generated-artifacts/ERC20BridgeProxy.json';
|
||||||
import * as DummyERC721Token from '../generated-artifacts/DummyERC721Token.json';
|
|
||||||
import * as DummyMultipleReturnERC20Token from '../generated-artifacts/DummyMultipleReturnERC20Token.json';
|
|
||||||
import * as DummyNoReturnERC20Token from '../generated-artifacts/DummyNoReturnERC20Token.json';
|
|
||||||
import * as ERC20Proxy from '../generated-artifacts/ERC20Proxy.json';
|
import * as ERC20Proxy from '../generated-artifacts/ERC20Proxy.json';
|
||||||
import * as ERC721Proxy from '../generated-artifacts/ERC721Proxy.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 IAssetData from '../generated-artifacts/IAssetData.json';
|
||||||
import * as IAssetProxy from '../generated-artifacts/IAssetProxy.json';
|
import * as IAssetProxy from '../generated-artifacts/IAssetProxy.json';
|
||||||
|
import * as IAssetProxyDispatcher from '../generated-artifacts/IAssetProxyDispatcher.json';
|
||||||
import * as IAuthorizable from '../generated-artifacts/IAuthorizable.json';
|
import * as IAuthorizable from '../generated-artifacts/IAuthorizable.json';
|
||||||
|
import * as IERC20Bridge from '../generated-artifacts/IERC20Bridge.json';
|
||||||
|
import * as IEth2Dai from '../generated-artifacts/IEth2Dai.json';
|
||||||
|
import * as IWallet from '../generated-artifacts/IWallet.json';
|
||||||
|
import * as MixinAssetProxyDispatcher from '../generated-artifacts/MixinAssetProxyDispatcher.json';
|
||||||
import * as MixinAuthorizable from '../generated-artifacts/MixinAuthorizable.json';
|
import * as MixinAuthorizable from '../generated-artifacts/MixinAuthorizable.json';
|
||||||
import * as MultiAssetProxy from '../generated-artifacts/MultiAssetProxy.json';
|
import * as MultiAssetProxy from '../generated-artifacts/MultiAssetProxy.json';
|
||||||
|
import * as Ownable from '../generated-artifacts/Ownable.json';
|
||||||
|
import * as StaticCallProxy from '../generated-artifacts/StaticCallProxy.json';
|
||||||
|
import * as TestERC20Bridge from '../generated-artifacts/TestERC20Bridge.json';
|
||||||
|
import * as TestEth2DaiBridge from '../generated-artifacts/TestEth2DaiBridge.json';
|
||||||
|
import * as TestStaticCallTarget from '../generated-artifacts/TestStaticCallTarget.json';
|
||||||
export const artifacts = {
|
export const artifacts = {
|
||||||
DummyERC20Token: DummyERC20Token as ContractArtifact,
|
MixinAssetProxyDispatcher: MixinAssetProxyDispatcher as ContractArtifact,
|
||||||
DummyMultipleReturnERC20Token: DummyMultipleReturnERC20Token as ContractArtifact,
|
MixinAuthorizable: MixinAuthorizable as ContractArtifact,
|
||||||
DummyNoReturnERC20Token: DummyNoReturnERC20Token as ContractArtifact,
|
Ownable: Ownable as ContractArtifact,
|
||||||
DummyERC721Receiver: DummyERC721Receiver as ContractArtifact,
|
ERC1155Proxy: ERC1155Proxy as ContractArtifact,
|
||||||
DummyERC721Token: DummyERC721Token as ContractArtifact,
|
ERC20BridgeProxy: ERC20BridgeProxy as ContractArtifact,
|
||||||
ERC20Proxy: ERC20Proxy as ContractArtifact,
|
ERC20Proxy: ERC20Proxy as ContractArtifact,
|
||||||
ERC721Proxy: ERC721Proxy as ContractArtifact,
|
ERC721Proxy: ERC721Proxy as ContractArtifact,
|
||||||
MixinAuthorizable: MixinAuthorizable as ContractArtifact,
|
|
||||||
MultiAssetProxy: MultiAssetProxy as ContractArtifact,
|
MultiAssetProxy: MultiAssetProxy as ContractArtifact,
|
||||||
|
StaticCallProxy: StaticCallProxy as ContractArtifact,
|
||||||
|
Eth2DaiBridge: Eth2DaiBridge as ContractArtifact,
|
||||||
IAssetData: IAssetData as ContractArtifact,
|
IAssetData: IAssetData as ContractArtifact,
|
||||||
IAssetProxy: IAssetProxy as ContractArtifact,
|
IAssetProxy: IAssetProxy as ContractArtifact,
|
||||||
|
IAssetProxyDispatcher: IAssetProxyDispatcher as ContractArtifact,
|
||||||
IAuthorizable: IAuthorizable as ContractArtifact,
|
IAuthorizable: IAuthorizable as ContractArtifact,
|
||||||
|
IERC20Bridge: IERC20Bridge as ContractArtifact,
|
||||||
|
IEth2Dai: IEth2Dai as ContractArtifact,
|
||||||
|
IWallet: IWallet as ContractArtifact,
|
||||||
|
TestERC20Bridge: TestERC20Bridge as ContractArtifact,
|
||||||
|
TestEth2DaiBridge: TestEth2DaiBridge as ContractArtifact,
|
||||||
|
TestStaticCallTarget: TestStaticCallTarget as ContractArtifact,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,15 +3,23 @@
|
|||||||
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||||
* -----------------------------------------------------------------------------
|
* -----------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
export * from '../generated-wrappers/dummy_erc20_token';
|
export * from '../generated-wrappers/erc1155_proxy';
|
||||||
export * from '../generated-wrappers/dummy_erc721_receiver';
|
export * from '../generated-wrappers/erc20_bridge_proxy';
|
||||||
export * from '../generated-wrappers/dummy_erc721_token';
|
|
||||||
export * from '../generated-wrappers/dummy_multiple_return_erc20_token';
|
|
||||||
export * from '../generated-wrappers/dummy_no_return_erc20_token';
|
|
||||||
export * from '../generated-wrappers/erc20_proxy';
|
export * from '../generated-wrappers/erc20_proxy';
|
||||||
export * from '../generated-wrappers/erc721_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_data';
|
||||||
export * from '../generated-wrappers/i_asset_proxy';
|
export * from '../generated-wrappers/i_asset_proxy';
|
||||||
|
export * from '../generated-wrappers/i_asset_proxy_dispatcher';
|
||||||
export * from '../generated-wrappers/i_authorizable';
|
export * from '../generated-wrappers/i_authorizable';
|
||||||
|
export * from '../generated-wrappers/i_erc20_bridge';
|
||||||
|
export * from '../generated-wrappers/i_eth2_dai';
|
||||||
|
export * from '../generated-wrappers/i_wallet';
|
||||||
|
export * from '../generated-wrappers/mixin_asset_proxy_dispatcher';
|
||||||
export * from '../generated-wrappers/mixin_authorizable';
|
export * from '../generated-wrappers/mixin_authorizable';
|
||||||
export * from '../generated-wrappers/multi_asset_proxy';
|
export * from '../generated-wrappers/multi_asset_proxy';
|
||||||
|
export * from '../generated-wrappers/ownable';
|
||||||
|
export * from '../generated-wrappers/static_call_proxy';
|
||||||
|
export * from '../generated-wrappers/test_erc20_bridge';
|
||||||
|
export * from '../generated-wrappers/test_eth2_dai_bridge';
|
||||||
|
export * from '../generated-wrappers/test_static_call_target';
|
||||||
|
|||||||
@@ -27,9 +27,11 @@ describe('Authorizable', () => {
|
|||||||
before(async () => {
|
before(async () => {
|
||||||
await blockchainLifecycle.startAsync();
|
await blockchainLifecycle.startAsync();
|
||||||
});
|
});
|
||||||
|
|
||||||
after(async () => {
|
after(async () => {
|
||||||
await blockchainLifecycle.revertAsync();
|
await blockchainLifecycle.revertAsync();
|
||||||
});
|
});
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
||||||
[owner, address, notOwner] = _.slice(accounts, 0, 3);
|
[owner, address, notOwner] = _.slice(accounts, 0, 3);
|
||||||
@@ -37,32 +39,40 @@ describe('Authorizable', () => {
|
|||||||
artifacts.MixinAuthorizable,
|
artifacts.MixinAuthorizable,
|
||||||
provider,
|
provider,
|
||||||
txDefaults,
|
txDefaults,
|
||||||
|
artifacts,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await blockchainLifecycle.startAsync();
|
await blockchainLifecycle.startAsync();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
await blockchainLifecycle.revertAsync();
|
await blockchainLifecycle.revertAsync();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('addAuthorizedAddress', () => {
|
describe('addAuthorizedAddress', () => {
|
||||||
it('should throw if not called by owner', async () => {
|
it('should revert if not called by owner', async () => {
|
||||||
return expectTransactionFailedAsync(
|
await expectTransactionFailedAsync(
|
||||||
authorizable.addAuthorizedAddress.sendTransactionAsync(notOwner, { from: notOwner }),
|
authorizable.addAuthorizedAddress.sendTransactionAsync(notOwner, { from: notOwner }),
|
||||||
RevertReason.OnlyContractOwner,
|
RevertReason.OnlyContractOwner,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow owner to add an authorized address', async () => {
|
it('should allow owner to add an authorized address', async () => {
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||||
await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }),
|
address,
|
||||||
|
{ from: owner },
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
);
|
);
|
||||||
const isAuthorized = await authorizable.authorized.callAsync(address);
|
const isAuthorized = await authorizable.authorized.callAsync(address);
|
||||||
expect(isAuthorized).to.be.true();
|
expect(isAuthorized).to.be.true();
|
||||||
});
|
});
|
||||||
it('should throw if owner attempts to authorize a duplicate address', async () => {
|
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
it('should revert if owner attempts to authorize a duplicate address', async () => {
|
||||||
await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }),
|
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||||
|
address,
|
||||||
|
{ from: owner },
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
);
|
);
|
||||||
return expectTransactionFailedAsync(
|
return expectTransactionFailedAsync(
|
||||||
@@ -73,35 +83,34 @@ describe('Authorizable', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('removeAuthorizedAddress', () => {
|
describe('removeAuthorizedAddress', () => {
|
||||||
it('should throw if not called by owner', async () => {
|
it('should revert if not called by owner', async () => {
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||||
await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }),
|
address,
|
||||||
|
{ from: owner },
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
);
|
);
|
||||||
return expectTransactionFailedAsync(
|
await expectTransactionFailedAsync(
|
||||||
authorizable.removeAuthorizedAddress.sendTransactionAsync(address, {
|
authorizable.removeAuthorizedAddress.sendTransactionAsync(address, { from: notOwner }),
|
||||||
from: notOwner,
|
|
||||||
}),
|
|
||||||
RevertReason.OnlyContractOwner,
|
RevertReason.OnlyContractOwner,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow owner to remove an authorized address', async () => {
|
it('should allow owner to remove an authorized address', async () => {
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||||
await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }),
|
address,
|
||||||
|
{ from: owner },
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
);
|
);
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
await authorizable.removeAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||||
await authorizable.removeAuthorizedAddress.sendTransactionAsync(address, {
|
address,
|
||||||
from: owner,
|
{ from: owner },
|
||||||
}),
|
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
);
|
);
|
||||||
const isAuthorized = await authorizable.authorized.callAsync(address);
|
const isAuthorized = await authorizable.authorized.callAsync(address);
|
||||||
expect(isAuthorized).to.be.false();
|
expect(isAuthorized).to.be.false();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if owner attempts to remove an address that is not authorized', async () => {
|
it('should revert if owner attempts to remove an address that is not authorized', async () => {
|
||||||
return expectTransactionFailedAsync(
|
return expectTransactionFailedAsync(
|
||||||
authorizable.removeAuthorizedAddress.sendTransactionAsync(address, {
|
authorizable.removeAuthorizedAddress.sendTransactionAsync(address, {
|
||||||
from: owner,
|
from: owner,
|
||||||
@@ -112,22 +121,25 @@ describe('Authorizable', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('removeAuthorizedAddressAtIndex', () => {
|
describe('removeAuthorizedAddressAtIndex', () => {
|
||||||
it('should throw if not called by owner', async () => {
|
it('should revert if not called by owner', async () => {
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||||
await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }),
|
address,
|
||||||
|
{ from: owner },
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
);
|
);
|
||||||
const index = new BigNumber(0);
|
const index = new BigNumber(0);
|
||||||
return expectTransactionFailedAsync(
|
await expectTransactionFailedAsync(
|
||||||
authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, {
|
authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, {
|
||||||
from: notOwner,
|
from: notOwner,
|
||||||
}),
|
}),
|
||||||
RevertReason.OnlyContractOwner,
|
RevertReason.OnlyContractOwner,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('should throw if index is >= authorities.length', async () => {
|
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
it('should revert if index is >= authorities.length', async () => {
|
||||||
await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }),
|
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||||
|
address,
|
||||||
|
{ from: owner },
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
);
|
);
|
||||||
const index = new BigNumber(1);
|
const index = new BigNumber(1);
|
||||||
@@ -138,7 +150,8 @@ describe('Authorizable', () => {
|
|||||||
RevertReason.IndexOutOfBounds,
|
RevertReason.IndexOutOfBounds,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('should throw if owner attempts to remove an address that is not authorized', async () => {
|
|
||||||
|
it('should revert if owner attempts to remove an address that is not authorized', async () => {
|
||||||
const index = new BigNumber(0);
|
const index = new BigNumber(0);
|
||||||
return expectTransactionFailedAsync(
|
return expectTransactionFailedAsync(
|
||||||
authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, {
|
authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, {
|
||||||
@@ -147,15 +160,18 @@ describe('Authorizable', () => {
|
|||||||
RevertReason.TargetNotAuthorized,
|
RevertReason.TargetNotAuthorized,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('should throw if address at index does not match target', async () => {
|
|
||||||
|
it('should revert if address at index does not match target', async () => {
|
||||||
const address1 = address;
|
const address1 = address;
|
||||||
const address2 = notOwner;
|
const address2 = notOwner;
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||||
await authorizable.addAuthorizedAddress.sendTransactionAsync(address1, { from: owner }),
|
address1,
|
||||||
|
{ from: owner },
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
);
|
);
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||||
await authorizable.addAuthorizedAddress.sendTransactionAsync(address2, { from: owner }),
|
address2,
|
||||||
|
{ from: owner },
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
);
|
);
|
||||||
const address1Index = new BigNumber(0);
|
const address1Index = new BigNumber(0);
|
||||||
@@ -166,16 +182,18 @@ describe('Authorizable', () => {
|
|||||||
RevertReason.AuthorizedAddressMismatch,
|
RevertReason.AuthorizedAddressMismatch,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow owner to remove an authorized address', async () => {
|
it('should allow owner to remove an authorized address', async () => {
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||||
await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }),
|
address,
|
||||||
|
{ from: owner },
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
);
|
);
|
||||||
const index = new BigNumber(0);
|
const index = new BigNumber(0);
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
await authorizable.removeAuthorizedAddressAtIndex.awaitTransactionSuccessAsync(
|
||||||
await authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, {
|
address,
|
||||||
from: owner,
|
index,
|
||||||
}),
|
{ from: owner },
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
);
|
);
|
||||||
const isAuthorized = await authorizable.authorized.callAsync(address);
|
const isAuthorized = await authorizable.authorized.callAsync(address);
|
||||||
@@ -187,20 +205,17 @@ describe('Authorizable', () => {
|
|||||||
it('should return all authorized addresses', async () => {
|
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);
|
expect(initial).to.have.length(0);
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||||
await authorizable.addAuthorizedAddress.sendTransactionAsync(address, {
|
address,
|
||||||
from: owner,
|
{ from: owner },
|
||||||
}),
|
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
);
|
);
|
||||||
const afterAdd = await authorizable.getAuthorizedAddresses.callAsync();
|
const afterAdd = await authorizable.getAuthorizedAddresses.callAsync();
|
||||||
expect(afterAdd).to.have.length(1);
|
expect(afterAdd).to.have.length(1);
|
||||||
expect(afterAdd).to.include(address);
|
expect(afterAdd).to.include(address);
|
||||||
|
await authorizable.removeAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
address,
|
||||||
await authorizable.removeAuthorizedAddress.sendTransactionAsync(address, {
|
{ from: owner },
|
||||||
from: owner,
|
|
||||||
}),
|
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
);
|
);
|
||||||
const afterRemove = await authorizable.getAuthorizedAddresses.callAsync();
|
const afterRemove = await authorizable.getAuthorizedAddresses.callAsync();
|
||||||
|
|||||||
1914
contracts/asset-proxy/test/erc1155_proxy.ts
Normal file
1914
contracts/asset-proxy/test/erc1155_proxy.ts
Normal file
File diff suppressed because it is too large
Load Diff
299
contracts/asset-proxy/test/erc20bridge_proxy.ts
Normal file
299
contracts/asset-proxy/test/erc20bridge_proxy.ts
Normal file
@@ -0,0 +1,299 @@
|
|||||||
|
import {
|
||||||
|
blockchainTests,
|
||||||
|
constants,
|
||||||
|
expect,
|
||||||
|
getRandomInteger,
|
||||||
|
hexLeftPad,
|
||||||
|
hexRightPad,
|
||||||
|
hexSlice,
|
||||||
|
Numberish,
|
||||||
|
randomAddress,
|
||||||
|
} from '@0x/contracts-test-utils';
|
||||||
|
import { AssetProxyId } from '@0x/types';
|
||||||
|
import { AbiEncoder, AuthorizableRevertErrors, BigNumber, StringRevertError } from '@0x/utils';
|
||||||
|
import { DecodedLogs } from 'ethereum-types';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
|
import {
|
||||||
|
artifacts,
|
||||||
|
ERC20BridgeProxyContract,
|
||||||
|
TestERC20BridgeBridgeWithdrawToEventArgs,
|
||||||
|
TestERC20BridgeContract,
|
||||||
|
} from '../src';
|
||||||
|
|
||||||
|
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.awaitTransactionSuccessAsync(owner);
|
||||||
|
});
|
||||||
|
|
||||||
|
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.awaitTransactionSuccessAsync(_owner, new BigNumber(balance));
|
||||||
|
}
|
||||||
|
|
||||||
|
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.awaitTransactionSuccessAsync(
|
||||||
|
encodeAssetData(_opts.assetData),
|
||||||
|
_opts.from,
|
||||||
|
_opts.to,
|
||||||
|
new BigNumber(_opts.amount),
|
||||||
|
{ 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 as TestERC20BridgeBridgeWithdrawToEventArgs;
|
||||||
|
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.awaitTransactionSuccessAsync(
|
||||||
|
truncatedAssetData,
|
||||||
|
opts.from,
|
||||||
|
opts.to,
|
||||||
|
new BigNumber(opts.amount),
|
||||||
|
);
|
||||||
|
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.awaitTransactionSuccessAsync(_owner, balance);
|
||||||
|
const assetData = createAssetData({
|
||||||
|
tokenAddress: testTokenAddress,
|
||||||
|
});
|
||||||
|
const actualBalance = await assetProxy.balanceOf.callAsync(encodeAssetData(assetData), _owner);
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
192
contracts/asset-proxy/test/eth2dai_bridge.ts
Normal file
192
contracts/asset-proxy/test/eth2dai_bridge.ts
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
import {
|
||||||
|
blockchainTests,
|
||||||
|
constants,
|
||||||
|
expect,
|
||||||
|
filterLogsToArguments,
|
||||||
|
getRandomInteger,
|
||||||
|
hexLeftPad,
|
||||||
|
hexRandom,
|
||||||
|
Numberish,
|
||||||
|
randomAddress,
|
||||||
|
TransactionHelper,
|
||||||
|
} 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,
|
||||||
|
TestEth2DaiBridgeContract,
|
||||||
|
TestEth2DaiBridgeEvents,
|
||||||
|
TestEth2DaiBridgeSellAllAmountEventArgs,
|
||||||
|
TestEth2DaiBridgeTokenApproveEventArgs,
|
||||||
|
TestEth2DaiBridgeTokenTransferEventArgs,
|
||||||
|
} from '../src';
|
||||||
|
|
||||||
|
blockchainTests.resets('Eth2DaiBridge unit tests', env => {
|
||||||
|
const txHelper = new TransactionHelper(env.web3Wrapper, artifacts);
|
||||||
|
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.callAsync(hexRandom(), hexRandom(_.random(0, 32)));
|
||||||
|
expect(result).to.eq(LEGACY_WALLET_MAGIC_VALUE);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('withdrawTo()', () => {
|
||||||
|
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.awaitTransactionSuccessAsync(
|
||||||
|
_opts.revertReason,
|
||||||
|
new BigNumber(_opts.fillAmount),
|
||||||
|
);
|
||||||
|
// Create tokens and balances.
|
||||||
|
if (_opts.fromTokenAddress === undefined) {
|
||||||
|
[_opts.fromTokenAddress] = await txHelper.getResultAndReceiptAsync(
|
||||||
|
testContract.createToken,
|
||||||
|
new BigNumber(_opts.fromTokenBalance),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (_opts.toTokenAddress === undefined) {
|
||||||
|
[_opts.toTokenAddress] = await txHelper.getResultAndReceiptAsync(
|
||||||
|
testContract.createToken,
|
||||||
|
constants.ZERO_AMOUNT,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Set the transfer behavior of `toTokenAddress`.
|
||||||
|
await testContract.setTransferBehavior.awaitTransactionSuccessAsync(
|
||||||
|
_opts.toTokenAddress,
|
||||||
|
_opts.toTokentransferRevertReason,
|
||||||
|
_opts.toTokenTransferReturnData,
|
||||||
|
);
|
||||||
|
// Call withdrawTo().
|
||||||
|
const [result, { logs }] = await txHelper.getResultAndReceiptAsync(
|
||||||
|
testContract.withdrawTo,
|
||||||
|
// "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),
|
||||||
|
);
|
||||||
|
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 falsey', async () => {
|
||||||
|
const opts = createWithdrawToOpts({ toTokenTransferReturnData: hexLeftPad(0) });
|
||||||
|
const tx = withdrawToAsync(opts);
|
||||||
|
return expect(tx).to.revertWith('ERC20_TRANSFER_FAILED');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('succeeds if `toTokenAddress.transfer()` returns truthy', async () => {
|
||||||
|
await withdrawToAsync({ toTokenTransferReturnData: hexLeftPad(100) });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
import { env, EnvVars } from '@0x/dev-utils';
|
import { env, EnvVars } from '@0x/dev-utils';
|
||||||
|
|
||||||
import { coverage, profiler, provider } from '@0x/contracts-test-utils';
|
import { coverage, profiler, provider } from '@0x/contracts-test-utils';
|
||||||
|
import { providerUtils } from '@0x/utils';
|
||||||
|
|
||||||
before('start web3 provider', () => {
|
before('start web3 provider', () => {
|
||||||
provider.start();
|
providerUtils.startProviderEngine(provider);
|
||||||
});
|
});
|
||||||
after('generate coverage report', async () => {
|
after('generate coverage report', async () => {
|
||||||
if (env.parseBoolean(EnvVars.SolidityCoverage)) {
|
if (env.parseBoolean(EnvVars.SolidityCoverage)) {
|
||||||
|
|||||||
@@ -1,3 +1,16 @@
|
|||||||
|
import { ERC1155MintableContract, Erc1155Wrapper } from '@0x/contracts-erc1155';
|
||||||
|
import {
|
||||||
|
artifacts as erc20Artifacts,
|
||||||
|
DummyERC20TokenContract,
|
||||||
|
DummyERC20TokenTransferEventArgs,
|
||||||
|
DummyMultipleReturnERC20TokenContract,
|
||||||
|
DummyNoReturnERC20TokenContract,
|
||||||
|
} from '@0x/contracts-erc20';
|
||||||
|
import {
|
||||||
|
artifacts as erc721Artifacts,
|
||||||
|
DummyERC721ReceiverContract,
|
||||||
|
DummyERC721TokenContract,
|
||||||
|
} from '@0x/contracts-erc721';
|
||||||
import {
|
import {
|
||||||
chaiSetup,
|
chaiSetup,
|
||||||
constants,
|
constants,
|
||||||
@@ -10,7 +23,7 @@ import {
|
|||||||
} from '@0x/contracts-test-utils';
|
} from '@0x/contracts-test-utils';
|
||||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||||
import { assetDataUtils } from '@0x/order-utils';
|
import { assetDataUtils } from '@0x/order-utils';
|
||||||
import { RevertReason } from '@0x/types';
|
import { AssetProxyId, RevertReason } from '@0x/types';
|
||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
import * as chai from 'chai';
|
import * as chai from 'chai';
|
||||||
import { LogWithDecodedArgs } from 'ethereum-types';
|
import { LogWithDecodedArgs } from 'ethereum-types';
|
||||||
@@ -18,12 +31,8 @@ import * as _ from 'lodash';
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
artifacts,
|
artifacts,
|
||||||
DummyERC20TokenContract,
|
ERC1155ProxyContract,
|
||||||
DummyERC20TokenTransferEventArgs,
|
ERC1155ProxyWrapper,
|
||||||
DummyERC721ReceiverContract,
|
|
||||||
DummyERC721TokenContract,
|
|
||||||
DummyMultipleReturnERC20TokenContract,
|
|
||||||
DummyNoReturnERC20TokenContract,
|
|
||||||
ERC20ProxyContract,
|
ERC20ProxyContract,
|
||||||
ERC20Wrapper,
|
ERC20Wrapper,
|
||||||
ERC721ProxyContract,
|
ERC721ProxyContract,
|
||||||
@@ -36,16 +45,8 @@ import {
|
|||||||
chaiSetup.configure();
|
chaiSetup.configure();
|
||||||
const expect = chai.expect;
|
const expect = chai.expect;
|
||||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||||
const assetProxyInterface = new IAssetProxyContract(
|
const assetProxyInterface = new IAssetProxyContract(constants.NULL_ADDRESS, provider);
|
||||||
artifacts.IAssetProxy.compilerOutput.abi,
|
const assetDataInterface = new IAssetDataContract(constants.NULL_ADDRESS, provider);
|
||||||
constants.NULL_ADDRESS,
|
|
||||||
provider,
|
|
||||||
);
|
|
||||||
const assetDataInterface = new IAssetDataContract(
|
|
||||||
artifacts.IAssetData.compilerOutput.abi,
|
|
||||||
constants.NULL_ADDRESS,
|
|
||||||
provider,
|
|
||||||
);
|
|
||||||
|
|
||||||
// tslint:disable:no-unnecessary-type-assertion
|
// tslint:disable:no-unnecessary-type-assertion
|
||||||
describe('Asset Transfer Proxies', () => {
|
describe('Asset Transfer Proxies', () => {
|
||||||
@@ -71,6 +72,15 @@ describe('Asset Transfer Proxies', () => {
|
|||||||
let erc721AFromTokenId: BigNumber;
|
let erc721AFromTokenId: BigNumber;
|
||||||
let erc721BFromTokenId: BigNumber;
|
let erc721BFromTokenId: BigNumber;
|
||||||
|
|
||||||
|
let erc1155Proxy: ERC1155ProxyContract;
|
||||||
|
let erc1155ProxyWrapper: ERC1155ProxyWrapper;
|
||||||
|
let erc1155Contract: ERC1155MintableContract;
|
||||||
|
let erc1155Contract2: ERC1155MintableContract;
|
||||||
|
let erc1155Wrapper: Erc1155Wrapper;
|
||||||
|
let erc1155Wrapper2: Erc1155Wrapper;
|
||||||
|
let erc1155FungibleTokens: BigNumber[];
|
||||||
|
let erc1155NonFungibleTokensOwnedBySpender: BigNumber[];
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
await blockchainLifecycle.startAsync();
|
await blockchainLifecycle.startAsync();
|
||||||
});
|
});
|
||||||
@@ -91,53 +101,66 @@ describe('Asset Transfer Proxies', () => {
|
|||||||
artifacts.MultiAssetProxy,
|
artifacts.MultiAssetProxy,
|
||||||
provider,
|
provider,
|
||||||
txDefaults,
|
txDefaults,
|
||||||
|
artifacts,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Configure ERC20Proxy
|
// Configure ERC20Proxy
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
await erc20Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||||
await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(authorized, {
|
authorized,
|
||||||
from: owner,
|
{ from: owner },
|
||||||
}),
|
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
);
|
);
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
await erc20Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||||
await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(multiAssetProxy.address, {
|
multiAssetProxy.address,
|
||||||
from: owner,
|
{ from: owner },
|
||||||
}),
|
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Configure ERC721Proxy
|
// Configure ERC721Proxy
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
await erc721Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||||
await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(authorized, {
|
authorized,
|
||||||
from: owner,
|
{ from: owner },
|
||||||
}),
|
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
);
|
);
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
await erc721Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||||
await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(multiAssetProxy.address, {
|
multiAssetProxy.address,
|
||||||
from: owner,
|
{ from: owner },
|
||||||
}),
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Configure ERC115Proxy
|
||||||
|
erc1155ProxyWrapper = new ERC1155ProxyWrapper(provider, usedAddresses, owner);
|
||||||
|
erc1155Proxy = await erc1155ProxyWrapper.deployProxyAsync();
|
||||||
|
await erc1155Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||||
|
authorized,
|
||||||
|
{ from: owner },
|
||||||
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
|
);
|
||||||
|
await erc1155Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||||
|
multiAssetProxy.address,
|
||||||
|
{ from: owner },
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Configure MultiAssetProxy
|
// Configure MultiAssetProxy
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
await multiAssetProxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||||
await multiAssetProxy.addAuthorizedAddress.sendTransactionAsync(authorized, {
|
authorized,
|
||||||
from: owner,
|
{ from: owner },
|
||||||
}),
|
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
);
|
);
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
await multiAssetProxy.registerAssetProxy.awaitTransactionSuccessAsync(
|
||||||
await multiAssetProxy.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, {
|
erc20Proxy.address,
|
||||||
from: owner,
|
{ from: owner },
|
||||||
}),
|
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
);
|
);
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
await multiAssetProxy.registerAssetProxy.awaitTransactionSuccessAsync(
|
||||||
await multiAssetProxy.registerAssetProxy.sendTransactionAsync(erc721Proxy.address, {
|
erc721Proxy.address,
|
||||||
from: owner,
|
{ from: owner },
|
||||||
}),
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
|
);
|
||||||
|
await multiAssetProxy.registerAssetProxy.awaitTransactionSuccessAsync(
|
||||||
|
erc1155Proxy.address,
|
||||||
|
{ from: owner },
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -148,18 +171,20 @@ describe('Asset Transfer Proxies', () => {
|
|||||||
constants.DUMMY_TOKEN_DECIMALS,
|
constants.DUMMY_TOKEN_DECIMALS,
|
||||||
);
|
);
|
||||||
noReturnErc20Token = await DummyNoReturnERC20TokenContract.deployFrom0xArtifactAsync(
|
noReturnErc20Token = await DummyNoReturnERC20TokenContract.deployFrom0xArtifactAsync(
|
||||||
artifacts.DummyNoReturnERC20Token,
|
erc20Artifacts.DummyNoReturnERC20Token,
|
||||||
provider,
|
provider,
|
||||||
txDefaults,
|
txDefaults,
|
||||||
|
artifacts,
|
||||||
constants.DUMMY_TOKEN_NAME,
|
constants.DUMMY_TOKEN_NAME,
|
||||||
constants.DUMMY_TOKEN_SYMBOL,
|
constants.DUMMY_TOKEN_SYMBOL,
|
||||||
constants.DUMMY_TOKEN_DECIMALS,
|
constants.DUMMY_TOKEN_DECIMALS,
|
||||||
constants.DUMMY_TOKEN_TOTAL_SUPPLY,
|
constants.DUMMY_TOKEN_TOTAL_SUPPLY,
|
||||||
);
|
);
|
||||||
multipleReturnErc20Token = await DummyMultipleReturnERC20TokenContract.deployFrom0xArtifactAsync(
|
multipleReturnErc20Token = await DummyMultipleReturnERC20TokenContract.deployFrom0xArtifactAsync(
|
||||||
artifacts.DummyMultipleReturnERC20Token,
|
erc20Artifacts.DummyMultipleReturnERC20Token,
|
||||||
provider,
|
provider,
|
||||||
txDefaults,
|
txDefaults,
|
||||||
|
artifacts,
|
||||||
constants.DUMMY_TOKEN_NAME,
|
constants.DUMMY_TOKEN_NAME,
|
||||||
constants.DUMMY_TOKEN_SYMBOL,
|
constants.DUMMY_TOKEN_SYMBOL,
|
||||||
constants.DUMMY_TOKEN_DECIMALS,
|
constants.DUMMY_TOKEN_DECIMALS,
|
||||||
@@ -167,46 +192,64 @@ describe('Asset Transfer Proxies', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await erc20Wrapper.setBalancesAndAllowancesAsync();
|
await erc20Wrapper.setBalancesAndAllowancesAsync();
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
await noReturnErc20Token.setBalance.awaitTransactionSuccessAsync(
|
||||||
await noReturnErc20Token.setBalance.sendTransactionAsync(fromAddress, constants.INITIAL_ERC20_BALANCE),
|
fromAddress,
|
||||||
|
constants.INITIAL_ERC20_BALANCE,
|
||||||
|
{
|
||||||
|
from: owner,
|
||||||
|
},
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
);
|
);
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
await noReturnErc20Token.approve.awaitTransactionSuccessAsync(
|
||||||
await noReturnErc20Token.approve.sendTransactionAsync(
|
erc20Proxy.address,
|
||||||
erc20Proxy.address,
|
constants.INITIAL_ERC20_ALLOWANCE,
|
||||||
constants.INITIAL_ERC20_ALLOWANCE,
|
{ from: fromAddress },
|
||||||
{ from: fromAddress },
|
|
||||||
),
|
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
);
|
);
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
await multipleReturnErc20Token.setBalance.awaitTransactionSuccessAsync(
|
||||||
await multipleReturnErc20Token.setBalance.sendTransactionAsync(
|
fromAddress,
|
||||||
fromAddress,
|
constants.INITIAL_ERC20_BALANCE,
|
||||||
constants.INITIAL_ERC20_BALANCE,
|
{
|
||||||
),
|
from: owner,
|
||||||
|
},
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
);
|
);
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
await multipleReturnErc20Token.approve.awaitTransactionSuccessAsync(
|
||||||
await multipleReturnErc20Token.approve.sendTransactionAsync(
|
erc20Proxy.address,
|
||||||
erc20Proxy.address,
|
constants.INITIAL_ERC20_ALLOWANCE,
|
||||||
constants.INITIAL_ERC20_ALLOWANCE,
|
{ from: fromAddress },
|
||||||
{ from: fromAddress },
|
|
||||||
),
|
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Deploy and configure ERC721 tokens and receiver
|
// Deploy and configure ERC721 tokens and receiver
|
||||||
[erc721TokenA, erc721TokenB] = await erc721Wrapper.deployDummyTokensAsync();
|
[erc721TokenA, erc721TokenB] = await erc721Wrapper.deployDummyTokensAsync();
|
||||||
erc721Receiver = await DummyERC721ReceiverContract.deployFrom0xArtifactAsync(
|
erc721Receiver = await DummyERC721ReceiverContract.deployFrom0xArtifactAsync(
|
||||||
artifacts.DummyERC721Receiver,
|
erc721Artifacts.DummyERC721Receiver,
|
||||||
provider,
|
provider,
|
||||||
txDefaults,
|
txDefaults,
|
||||||
|
artifacts,
|
||||||
);
|
);
|
||||||
|
|
||||||
await erc721Wrapper.setBalancesAndAllowancesAsync();
|
await erc721Wrapper.setBalancesAndAllowancesAsync();
|
||||||
const erc721Balances = await erc721Wrapper.getBalancesAsync();
|
const erc721Balances = await erc721Wrapper.getBalancesAsync();
|
||||||
erc721AFromTokenId = erc721Balances[fromAddress][erc721TokenA.address][0];
|
erc721AFromTokenId = erc721Balances[fromAddress][erc721TokenA.address][0];
|
||||||
erc721BFromTokenId = erc721Balances[fromAddress][erc721TokenB.address][0];
|
erc721BFromTokenId = erc721Balances[fromAddress][erc721TokenB.address][0];
|
||||||
|
|
||||||
|
// Deploy & configure ERC1155 tokens and receiver
|
||||||
|
[erc1155Wrapper, erc1155Wrapper2] = await erc1155ProxyWrapper.deployDummyContractsAsync();
|
||||||
|
erc1155Contract = erc1155Wrapper.getContract();
|
||||||
|
erc1155Contract2 = erc1155Wrapper2.getContract();
|
||||||
|
await erc1155ProxyWrapper.setBalancesAndAllowancesAsync();
|
||||||
|
erc1155FungibleTokens = erc1155ProxyWrapper.getFungibleTokenIds();
|
||||||
|
const nonFungibleTokens = erc1155ProxyWrapper.getNonFungibleTokenIds();
|
||||||
|
const tokenBalances = await erc1155ProxyWrapper.getBalancesAsync();
|
||||||
|
erc1155NonFungibleTokensOwnedBySpender = [];
|
||||||
|
_.each(nonFungibleTokens, (nonFungibleToken: BigNumber) => {
|
||||||
|
const nonFungibleTokenAsString = nonFungibleToken.toString();
|
||||||
|
const nonFungibleTokenHeldBySpender =
|
||||||
|
tokenBalances.nonFungible[fromAddress][erc1155Contract.address][nonFungibleTokenAsString][0];
|
||||||
|
erc1155NonFungibleTokensOwnedBySpender.push(nonFungibleTokenHeldBySpender);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await blockchainLifecycle.startAsync();
|
await blockchainLifecycle.startAsync();
|
||||||
@@ -364,10 +407,10 @@ describe('Asset Transfer Proxies', () => {
|
|||||||
toAddress,
|
toAddress,
|
||||||
amount,
|
amount,
|
||||||
);
|
);
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
await erc20TokenA.approve.awaitTransactionSuccessAsync(
|
||||||
await erc20TokenA.approve.sendTransactionAsync(erc20Proxy.address, allowance, {
|
erc20Proxy.address,
|
||||||
from: fromAddress,
|
allowance,
|
||||||
}),
|
{ from: fromAddress },
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
);
|
);
|
||||||
const erc20Balances = await erc20Wrapper.getBalancesAsync();
|
const erc20Balances = await erc20Wrapper.getBalancesAsync();
|
||||||
@@ -396,10 +439,10 @@ describe('Asset Transfer Proxies', () => {
|
|||||||
toAddress,
|
toAddress,
|
||||||
amount,
|
amount,
|
||||||
);
|
);
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
await noReturnErc20Token.approve.awaitTransactionSuccessAsync(
|
||||||
await noReturnErc20Token.approve.sendTransactionAsync(erc20Proxy.address, allowance, {
|
erc20Proxy.address,
|
||||||
from: fromAddress,
|
allowance,
|
||||||
}),
|
{ from: fromAddress },
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
);
|
);
|
||||||
const initialFromBalance = await noReturnErc20Token.balanceOf.callAsync(fromAddress);
|
const initialFromBalance = await noReturnErc20Token.balanceOf.callAsync(fromAddress);
|
||||||
@@ -562,7 +605,7 @@ describe('Asset Transfer Proxies', () => {
|
|||||||
erc721Receiver.address,
|
erc721Receiver.address,
|
||||||
amount,
|
amount,
|
||||||
);
|
);
|
||||||
const logDecoder = new LogDecoder(web3Wrapper, artifacts);
|
const logDecoder = new LogDecoder(web3Wrapper, { ...artifacts, ...erc721Artifacts });
|
||||||
const tx = await logDecoder.getTxWithDecodedLogsAsync(
|
const tx = await logDecoder.getTxWithDecodedLogsAsync(
|
||||||
await web3Wrapper.sendTransactionAsync({
|
await web3Wrapper.sendTransactionAsync({
|
||||||
to: erc721Proxy.address,
|
to: erc721Proxy.address,
|
||||||
@@ -636,11 +679,18 @@ describe('Asset Transfer Proxies', () => {
|
|||||||
// Verify pre-condition
|
// Verify pre-condition
|
||||||
const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
|
const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
|
||||||
expect(ownerFromAsset).to.be.equal(fromAddress);
|
expect(ownerFromAsset).to.be.equal(fromAddress);
|
||||||
// Remove transfer approval for fromAddress.
|
// Remove blanket transfer approval for fromAddress.
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
await erc721TokenA.setApprovalForAll.awaitTransactionSuccessAsync(
|
||||||
await erc721TokenA.approve.sendTransactionAsync(constants.NULL_ADDRESS, erc721AFromTokenId, {
|
erc721Proxy.address,
|
||||||
from: fromAddress,
|
false,
|
||||||
}),
|
{ from: fromAddress },
|
||||||
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
|
);
|
||||||
|
// Remove token transfer approval for fromAddress.
|
||||||
|
await erc721TokenA.approve.awaitTransactionSuccessAsync(
|
||||||
|
constants.NULL_ADDRESS,
|
||||||
|
erc721AFromTokenId,
|
||||||
|
{ from: fromAddress },
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
);
|
);
|
||||||
// Perform a transfer; expect this to fail.
|
// Perform a transfer; expect this to fail.
|
||||||
@@ -754,7 +804,7 @@ describe('Asset Transfer Proxies', () => {
|
|||||||
inputAmount,
|
inputAmount,
|
||||||
);
|
);
|
||||||
const erc20Balances = await erc20Wrapper.getBalancesAsync();
|
const erc20Balances = await erc20Wrapper.getBalancesAsync();
|
||||||
const logDecoder = new LogDecoder(web3Wrapper, artifacts);
|
const logDecoder = new LogDecoder(web3Wrapper, { ...artifacts, ...erc20Artifacts });
|
||||||
const tx = await logDecoder.getTxWithDecodedLogsAsync(
|
const tx = await logDecoder.getTxWithDecodedLogsAsync(
|
||||||
await web3Wrapper.sendTransactionAsync({
|
await web3Wrapper.sendTransactionAsync({
|
||||||
to: multiAssetProxy.address,
|
to: multiAssetProxy.address,
|
||||||
@@ -938,6 +988,314 @@ describe('Asset Transfer Proxies', () => {
|
|||||||
expect(newOwnerFromAsset1).to.be.equal(toAddress);
|
expect(newOwnerFromAsset1).to.be.equal(toAddress);
|
||||||
expect(newOwnerFromAsset2).to.be.equal(toAddress);
|
expect(newOwnerFromAsset2).to.be.equal(toAddress);
|
||||||
});
|
});
|
||||||
|
it('should transfer a fungible ERC1155 token', async () => {
|
||||||
|
// setup test parameters
|
||||||
|
const tokenHolders = [fromAddress, toAddress];
|
||||||
|
const tokensToTransfer = erc1155FungibleTokens.slice(0, 1);
|
||||||
|
const valuesToTransfer = [new BigNumber(25)];
|
||||||
|
const valueMultiplier = new BigNumber(23);
|
||||||
|
const receiverCallbackData = '0x0102030405';
|
||||||
|
// check balances before transfer
|
||||||
|
const expectedInitialBalances = [
|
||||||
|
// from
|
||||||
|
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||||
|
// to
|
||||||
|
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||||
|
];
|
||||||
|
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||||
|
// encode erc1155 asset data
|
||||||
|
const erc1155AssetData = assetDataUtils.encodeERC1155AssetData(
|
||||||
|
erc1155Contract.address,
|
||||||
|
tokensToTransfer,
|
||||||
|
valuesToTransfer,
|
||||||
|
receiverCallbackData,
|
||||||
|
);
|
||||||
|
// encode multi-asset data
|
||||||
|
const multiAssetAmount = new BigNumber(5);
|
||||||
|
const amounts = [valueMultiplier];
|
||||||
|
const nestedAssetData = [erc1155AssetData];
|
||||||
|
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
|
||||||
|
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||||
|
assetData,
|
||||||
|
fromAddress,
|
||||||
|
toAddress,
|
||||||
|
multiAssetAmount,
|
||||||
|
);
|
||||||
|
// execute transfer
|
||||||
|
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||||
|
await web3Wrapper.sendTransactionAsync({
|
||||||
|
to: multiAssetProxy.address,
|
||||||
|
data,
|
||||||
|
from: authorized,
|
||||||
|
}),
|
||||||
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
|
);
|
||||||
|
// check balances
|
||||||
|
const totalValueTransferred = valuesToTransfer[0].times(valueMultiplier).times(multiAssetAmount);
|
||||||
|
const expectedFinalBalances = [
|
||||||
|
// from
|
||||||
|
expectedInitialBalances[0].minus(totalValueTransferred),
|
||||||
|
// to
|
||||||
|
expectedInitialBalances[1].plus(totalValueTransferred),
|
||||||
|
];
|
||||||
|
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||||
|
});
|
||||||
|
it('should successfully transfer multiple fungible tokens of the same ERC1155 contract', async () => {
|
||||||
|
// setup test parameters
|
||||||
|
const tokenHolders = [fromAddress, toAddress];
|
||||||
|
const tokensToTransfer = erc1155FungibleTokens.slice(0, 3);
|
||||||
|
const valuesToTransfer = [new BigNumber(25), new BigNumber(35), new BigNumber(45)];
|
||||||
|
const valueMultiplier = new BigNumber(23);
|
||||||
|
const receiverCallbackData = '0x0102030405';
|
||||||
|
// check balances before transfer
|
||||||
|
const expectedInitialBalances = [
|
||||||
|
// from
|
||||||
|
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||||
|
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||||
|
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||||
|
// to
|
||||||
|
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||||
|
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||||
|
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||||
|
];
|
||||||
|
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||||
|
// encode erc1155 asset data
|
||||||
|
const erc1155AssetData = assetDataUtils.encodeERC1155AssetData(
|
||||||
|
erc1155Contract.address,
|
||||||
|
tokensToTransfer,
|
||||||
|
valuesToTransfer,
|
||||||
|
receiverCallbackData,
|
||||||
|
);
|
||||||
|
// encode multi-asset data
|
||||||
|
const multiAssetAmount = new BigNumber(5);
|
||||||
|
const amounts = [valueMultiplier];
|
||||||
|
const nestedAssetData = [erc1155AssetData];
|
||||||
|
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
|
||||||
|
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||||
|
assetData,
|
||||||
|
fromAddress,
|
||||||
|
toAddress,
|
||||||
|
multiAssetAmount,
|
||||||
|
);
|
||||||
|
// execute transfer
|
||||||
|
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||||
|
await web3Wrapper.sendTransactionAsync({
|
||||||
|
to: multiAssetProxy.address,
|
||||||
|
data,
|
||||||
|
from: authorized,
|
||||||
|
}),
|
||||||
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
|
);
|
||||||
|
// check balances
|
||||||
|
const totalValuesTransferred = _.map(valuesToTransfer, (value: BigNumber) => {
|
||||||
|
return value.times(valueMultiplier).times(multiAssetAmount);
|
||||||
|
});
|
||||||
|
const expectedFinalBalances = [
|
||||||
|
// from
|
||||||
|
expectedInitialBalances[0].minus(totalValuesTransferred[0]),
|
||||||
|
expectedInitialBalances[1].minus(totalValuesTransferred[1]),
|
||||||
|
expectedInitialBalances[2].minus(totalValuesTransferred[2]),
|
||||||
|
// to
|
||||||
|
expectedInitialBalances[3].plus(totalValuesTransferred[0]),
|
||||||
|
expectedInitialBalances[4].plus(totalValuesTransferred[1]),
|
||||||
|
expectedInitialBalances[5].plus(totalValuesTransferred[2]),
|
||||||
|
];
|
||||||
|
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||||
|
});
|
||||||
|
it('should successfully transfer multiple fungible/non-fungible tokens of the same ERC1155 contract', async () => {
|
||||||
|
// setup test parameters
|
||||||
|
const tokenHolders = [fromAddress, toAddress];
|
||||||
|
const fungibleTokensToTransfer = erc1155FungibleTokens.slice(0, 1);
|
||||||
|
const nonFungibleTokensToTransfer = erc1155NonFungibleTokensOwnedBySpender.slice(0, 1);
|
||||||
|
const tokensToTransfer = fungibleTokensToTransfer.concat(nonFungibleTokensToTransfer);
|
||||||
|
const valuesToTransfer = [new BigNumber(25), new BigNumber(1)];
|
||||||
|
const valueMultiplier = new BigNumber(1);
|
||||||
|
const receiverCallbackData = '0x0102030405';
|
||||||
|
// check balances before transfer
|
||||||
|
const nftOwnerBalance = new BigNumber(1);
|
||||||
|
const nftNotOwnerBalance = new BigNumber(0);
|
||||||
|
const expectedInitialBalances = [
|
||||||
|
// from
|
||||||
|
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||||
|
nftOwnerBalance,
|
||||||
|
// to
|
||||||
|
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||||
|
nftNotOwnerBalance,
|
||||||
|
];
|
||||||
|
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||||
|
// encode erc1155 asset data
|
||||||
|
const erc1155AssetData = assetDataUtils.encodeERC1155AssetData(
|
||||||
|
erc1155Contract.address,
|
||||||
|
tokensToTransfer,
|
||||||
|
valuesToTransfer,
|
||||||
|
receiverCallbackData,
|
||||||
|
);
|
||||||
|
// encode multi-asset data
|
||||||
|
const multiAssetAmount = new BigNumber(1);
|
||||||
|
const amounts = [valueMultiplier];
|
||||||
|
const nestedAssetData = [erc1155AssetData];
|
||||||
|
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
|
||||||
|
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||||
|
assetData,
|
||||||
|
fromAddress,
|
||||||
|
toAddress,
|
||||||
|
multiAssetAmount,
|
||||||
|
);
|
||||||
|
// execute transfer
|
||||||
|
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||||
|
await web3Wrapper.sendTransactionAsync({
|
||||||
|
to: multiAssetProxy.address,
|
||||||
|
data,
|
||||||
|
from: authorized,
|
||||||
|
}),
|
||||||
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
|
);
|
||||||
|
// check balances
|
||||||
|
const totalValuesTransferred = _.map(valuesToTransfer, (value: BigNumber) => {
|
||||||
|
return value.times(valueMultiplier).times(multiAssetAmount);
|
||||||
|
});
|
||||||
|
const expectedFinalBalances = [
|
||||||
|
// from
|
||||||
|
expectedInitialBalances[0].minus(totalValuesTransferred[0]),
|
||||||
|
expectedInitialBalances[1].minus(totalValuesTransferred[1]),
|
||||||
|
// to
|
||||||
|
expectedInitialBalances[2].plus(totalValuesTransferred[0]),
|
||||||
|
expectedInitialBalances[3].plus(totalValuesTransferred[1]),
|
||||||
|
];
|
||||||
|
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||||
|
});
|
||||||
|
it('should successfully transfer multiple different ERC1155 tokens', async () => {
|
||||||
|
// setup test parameters
|
||||||
|
const tokenHolders = [fromAddress, toAddress];
|
||||||
|
const tokensToTransfer = erc1155FungibleTokens.slice(0, 1);
|
||||||
|
const valuesToTransfer = [new BigNumber(25)];
|
||||||
|
const valueMultiplier = new BigNumber(23);
|
||||||
|
const receiverCallbackData = '0x0102030405';
|
||||||
|
// check balances before transfer
|
||||||
|
const expectedInitialBalances = [
|
||||||
|
// from
|
||||||
|
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||||
|
// to
|
||||||
|
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||||
|
];
|
||||||
|
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||||
|
await erc1155Wrapper2.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||||
|
// encode erc1155 asset data
|
||||||
|
const erc1155AssetData1 = assetDataUtils.encodeERC1155AssetData(
|
||||||
|
erc1155Contract.address,
|
||||||
|
tokensToTransfer,
|
||||||
|
valuesToTransfer,
|
||||||
|
receiverCallbackData,
|
||||||
|
);
|
||||||
|
const erc1155AssetData2 = assetDataUtils.encodeERC1155AssetData(
|
||||||
|
erc1155Contract2.address,
|
||||||
|
tokensToTransfer,
|
||||||
|
valuesToTransfer,
|
||||||
|
receiverCallbackData,
|
||||||
|
);
|
||||||
|
// encode multi-asset data
|
||||||
|
const multiAssetAmount = new BigNumber(5);
|
||||||
|
const amounts = [valueMultiplier, valueMultiplier];
|
||||||
|
const nestedAssetData = [erc1155AssetData1, erc1155AssetData2];
|
||||||
|
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
|
||||||
|
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||||
|
assetData,
|
||||||
|
fromAddress,
|
||||||
|
toAddress,
|
||||||
|
multiAssetAmount,
|
||||||
|
);
|
||||||
|
// execute transfer
|
||||||
|
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||||
|
await web3Wrapper.sendTransactionAsync({
|
||||||
|
to: multiAssetProxy.address,
|
||||||
|
data,
|
||||||
|
from: authorized,
|
||||||
|
}),
|
||||||
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
|
);
|
||||||
|
// check balances
|
||||||
|
const totalValueTransferred = valuesToTransfer[0].times(valueMultiplier).times(multiAssetAmount);
|
||||||
|
const expectedFinalBalances = [
|
||||||
|
// from
|
||||||
|
expectedInitialBalances[0].minus(totalValueTransferred),
|
||||||
|
// to
|
||||||
|
expectedInitialBalances[1].plus(totalValueTransferred),
|
||||||
|
];
|
||||||
|
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||||
|
await erc1155Wrapper2.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||||
|
});
|
||||||
|
it('should successfully transfer a combination of ERC20, ERC721, and ERC1155 tokens', async () => {
|
||||||
|
// setup test parameters
|
||||||
|
const inputAmount = new BigNumber(1);
|
||||||
|
const erc20Amount = new BigNumber(10);
|
||||||
|
const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
|
||||||
|
const erc721Amount = new BigNumber(1);
|
||||||
|
const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
|
||||||
|
const erc1155TokenHolders = [fromAddress, toAddress];
|
||||||
|
const erc1155TokensToTransfer = erc1155FungibleTokens.slice(0, 1);
|
||||||
|
const erc1155ValuesToTransfer = [new BigNumber(25)];
|
||||||
|
const erc1155Amount = new BigNumber(23);
|
||||||
|
const erc1155ReceiverCallbackData = '0x0102030405';
|
||||||
|
const erc1155AssetData = assetDataUtils.encodeERC1155AssetData(
|
||||||
|
erc1155Contract.address,
|
||||||
|
erc1155TokensToTransfer,
|
||||||
|
erc1155ValuesToTransfer,
|
||||||
|
erc1155ReceiverCallbackData,
|
||||||
|
);
|
||||||
|
const amounts = [erc20Amount, erc721Amount, erc1155Amount];
|
||||||
|
const nestedAssetData = [erc20AssetData, erc721AssetData, erc1155AssetData];
|
||||||
|
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
|
||||||
|
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||||
|
assetData,
|
||||||
|
fromAddress,
|
||||||
|
toAddress,
|
||||||
|
inputAmount,
|
||||||
|
);
|
||||||
|
// check balances before transfer
|
||||||
|
const erc20Balances = await erc20Wrapper.getBalancesAsync();
|
||||||
|
const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
|
||||||
|
expect(ownerFromAsset).to.be.equal(fromAddress);
|
||||||
|
const erc1155ExpectedInitialBalances = [
|
||||||
|
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||||
|
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||||
|
];
|
||||||
|
await erc1155Wrapper.assertBalancesAsync(
|
||||||
|
erc1155TokenHolders,
|
||||||
|
erc1155TokensToTransfer,
|
||||||
|
erc1155ExpectedInitialBalances,
|
||||||
|
);
|
||||||
|
// execute transfer
|
||||||
|
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||||
|
await web3Wrapper.sendTransactionAsync({
|
||||||
|
to: multiAssetProxy.address,
|
||||||
|
data,
|
||||||
|
from: authorized,
|
||||||
|
gas: 1000000,
|
||||||
|
}),
|
||||||
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
|
);
|
||||||
|
// check balances after transfer
|
||||||
|
const newBalances = await erc20Wrapper.getBalancesAsync();
|
||||||
|
const totalAmount = inputAmount.times(erc20Amount);
|
||||||
|
expect(newBalances[fromAddress][erc20TokenA.address]).to.be.bignumber.equal(
|
||||||
|
erc20Balances[fromAddress][erc20TokenA.address].minus(totalAmount),
|
||||||
|
);
|
||||||
|
expect(newBalances[toAddress][erc20TokenA.address]).to.be.bignumber.equal(
|
||||||
|
erc20Balances[toAddress][erc20TokenA.address].plus(totalAmount),
|
||||||
|
);
|
||||||
|
const newOwnerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
|
||||||
|
expect(newOwnerFromAsset).to.be.equal(toAddress);
|
||||||
|
const erc1155TotalValueTransferred = erc1155ValuesToTransfer[0].times(erc1155Amount).times(inputAmount);
|
||||||
|
const expectedFinalBalances = [
|
||||||
|
erc1155ExpectedInitialBalances[0].minus(erc1155TotalValueTransferred),
|
||||||
|
erc1155ExpectedInitialBalances[1].plus(erc1155TotalValueTransferred),
|
||||||
|
];
|
||||||
|
await erc1155Wrapper.assertBalancesAsync(
|
||||||
|
erc1155TokenHolders,
|
||||||
|
erc1155TokensToTransfer,
|
||||||
|
expectedFinalBalances,
|
||||||
|
);
|
||||||
|
});
|
||||||
it('should successfully transfer a combination of ERC20 and ERC721 tokens', async () => {
|
it('should successfully transfer a combination of ERC20 and ERC721 tokens', async () => {
|
||||||
const inputAmount = new BigNumber(1);
|
const inputAmount = new BigNumber(1);
|
||||||
const erc20Amount = new BigNumber(10);
|
const erc20Amount = new BigNumber(10);
|
||||||
@@ -983,7 +1341,7 @@ describe('Asset Transfer Proxies', () => {
|
|||||||
const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
|
const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
|
||||||
const amounts = [erc20Amount, erc721Amount];
|
const amounts = [erc20Amount, erc721Amount];
|
||||||
const nestedAssetData = [erc20AssetData, erc721AssetData];
|
const nestedAssetData = [erc20AssetData, erc721AssetData];
|
||||||
const extraData = '0102030405060708';
|
const extraData = '0102030405060708090001020304050607080900010203040506070809000102';
|
||||||
const assetData = `${assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData)}${extraData}`;
|
const assetData = `${assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData)}${extraData}`;
|
||||||
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||||
assetData,
|
assetData,
|
||||||
@@ -1278,6 +1636,120 @@ describe('Asset Transfer Proxies', () => {
|
|||||||
RevertReason.SenderNotAuthorized,
|
RevertReason.SenderNotAuthorized,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
it('should revert if asset data overflows beyond the bounds of calldata', async () => {
|
||||||
|
const inputAmount = new BigNumber(1);
|
||||||
|
const erc20Amount = new BigNumber(10);
|
||||||
|
const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
|
||||||
|
const erc721Amount = new BigNumber(1);
|
||||||
|
const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
|
||||||
|
const amounts = [erc20Amount, erc721Amount];
|
||||||
|
const nestedAssetData = [erc20AssetData, erc721AssetData];
|
||||||
|
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
|
||||||
|
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||||
|
assetData,
|
||||||
|
fromAddress,
|
||||||
|
toAddress,
|
||||||
|
inputAmount,
|
||||||
|
);
|
||||||
|
// append asset data to end of tx data with a length of 0x300 bytes, which will extend past actual calldata.
|
||||||
|
const offsetToAssetData = '0000000000000000000000000000000000000000000000000000000000000080';
|
||||||
|
const invalidOffsetToAssetData = '00000000000000000000000000000000000000000000000000000000000002a0';
|
||||||
|
const newAssetData = '0000000000000000000000000000000000000000000000000000000000000304';
|
||||||
|
const badData = `${data.replace(offsetToAssetData, invalidOffsetToAssetData)}${newAssetData}`;
|
||||||
|
// execute transfer
|
||||||
|
await expectTransactionFailedAsync(
|
||||||
|
web3Wrapper.sendTransactionAsync({
|
||||||
|
to: multiAssetProxy.address,
|
||||||
|
data: badData,
|
||||||
|
from: authorized,
|
||||||
|
}),
|
||||||
|
RevertReason.InvalidAssetDataEnd,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('should revert if asset data resolves to a location beyond the bounds of calldata', async () => {
|
||||||
|
const inputAmount = new BigNumber(1);
|
||||||
|
const erc20Amount = new BigNumber(10);
|
||||||
|
const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
|
||||||
|
const erc721Amount = new BigNumber(1);
|
||||||
|
const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
|
||||||
|
const amounts = [erc20Amount, erc721Amount];
|
||||||
|
const nestedAssetData = [erc20AssetData, erc721AssetData];
|
||||||
|
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
|
||||||
|
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||||
|
assetData,
|
||||||
|
fromAddress,
|
||||||
|
toAddress,
|
||||||
|
inputAmount,
|
||||||
|
);
|
||||||
|
const offsetToAssetData = '0000000000000000000000000000000000000000000000000000000000000080';
|
||||||
|
const invalidOffsetToAssetData = '0000000000000000000000000000000000000000000000000000000000000400';
|
||||||
|
const badData = data.replace(offsetToAssetData, invalidOffsetToAssetData);
|
||||||
|
// execute transfer
|
||||||
|
// note that this triggers `InvalidAssetDataLength` because the length is zero, otherwise it would
|
||||||
|
// trigger `InvalidAssetDataEnd`.
|
||||||
|
await expectTransactionFailedAsync(
|
||||||
|
web3Wrapper.sendTransactionAsync({
|
||||||
|
to: multiAssetProxy.address,
|
||||||
|
data: badData,
|
||||||
|
from: authorized,
|
||||||
|
}),
|
||||||
|
RevertReason.InvalidAssetDataLength,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('should revert if length of assetData, excluding the selector, is not a multiple of 32', async () => {
|
||||||
|
// setup test parameters
|
||||||
|
const inputAmount = new BigNumber(1);
|
||||||
|
const erc20Amount = new BigNumber(10);
|
||||||
|
const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
|
||||||
|
const erc721Amount = new BigNumber(1);
|
||||||
|
const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
|
||||||
|
const amounts = [erc20Amount, erc721Amount];
|
||||||
|
const nestedAssetData = [erc20AssetData, erc721AssetData];
|
||||||
|
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
|
||||||
|
const extraData = '01';
|
||||||
|
const assetDataWithExtraData = `${assetData}${extraData}`;
|
||||||
|
const badData = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||||
|
assetDataWithExtraData,
|
||||||
|
fromAddress,
|
||||||
|
toAddress,
|
||||||
|
inputAmount,
|
||||||
|
);
|
||||||
|
// execute transfer
|
||||||
|
await expectTransactionFailedAsync(
|
||||||
|
web3Wrapper.sendTransactionAsync({
|
||||||
|
to: multiAssetProxy.address,
|
||||||
|
data: badData,
|
||||||
|
from: authorized,
|
||||||
|
}),
|
||||||
|
RevertReason.InvalidAssetDataLength,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('should revert if length of assetData is less than 68 bytes', async () => {
|
||||||
|
// setup test parameters
|
||||||
|
const inputAmount = new BigNumber(1);
|
||||||
|
// we'll construct asset data that has a 4 byte selector plus
|
||||||
|
// 32 byte payload. This results in asset data that is 36 bytes
|
||||||
|
// long and will trigger the `invalid length` error.
|
||||||
|
// we must be sure to use a # of bytes that is still %32
|
||||||
|
// so that we know the error is not triggered by another check in the code.
|
||||||
|
const zeros32Bytes = '0'.repeat(64);
|
||||||
|
const assetData36Bytes = `${AssetProxyId.MultiAsset}${zeros32Bytes}`;
|
||||||
|
const badData = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||||
|
assetData36Bytes,
|
||||||
|
fromAddress,
|
||||||
|
toAddress,
|
||||||
|
inputAmount,
|
||||||
|
);
|
||||||
|
// execute transfer
|
||||||
|
await expectTransactionFailedAsync(
|
||||||
|
web3Wrapper.sendTransactionAsync({
|
||||||
|
to: multiAssetProxy.address,
|
||||||
|
data: badData,
|
||||||
|
from: authorized,
|
||||||
|
}),
|
||||||
|
RevertReason.InvalidAssetDataLength,
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
248
contracts/asset-proxy/test/static_call_proxy.ts
Normal file
248
contracts/asset-proxy/test/static_call_proxy.ts
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
import {
|
||||||
|
chaiSetup,
|
||||||
|
constants,
|
||||||
|
expectTransactionFailedAsync,
|
||||||
|
expectTransactionFailedWithoutReasonAsync,
|
||||||
|
provider,
|
||||||
|
txDefaults,
|
||||||
|
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, IAssetProxyContract, StaticCallProxyContract, TestStaticCallTargetContract } from '../src';
|
||||||
|
|
||||||
|
chaiSetup.configure();
|
||||||
|
const expect = chai.expect;
|
||||||
|
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||||
|
|
||||||
|
describe('StaticCallProxy', () => {
|
||||||
|
const amount = constants.ZERO_AMOUNT;
|
||||||
|
let fromAddress: string;
|
||||||
|
let toAddress: string;
|
||||||
|
|
||||||
|
let staticCallProxy: IAssetProxyContract;
|
||||||
|
let staticCallTarget: TestStaticCallTargetContract;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
await blockchainLifecycle.startAsync();
|
||||||
|
});
|
||||||
|
after(async () => {
|
||||||
|
await blockchainLifecycle.revertAsync();
|
||||||
|
});
|
||||||
|
before(async () => {
|
||||||
|
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
||||||
|
[fromAddress, toAddress] = accounts.slice(0, 2);
|
||||||
|
const staticCallProxyWithoutTransferFrom = await StaticCallProxyContract.deployFrom0xArtifactAsync(
|
||||||
|
artifacts.StaticCallProxy,
|
||||||
|
provider,
|
||||||
|
txDefaults,
|
||||||
|
artifacts,
|
||||||
|
);
|
||||||
|
staticCallProxy = new IAssetProxyContract(
|
||||||
|
staticCallProxyWithoutTransferFrom.address,
|
||||||
|
provider,
|
||||||
|
txDefaults,
|
||||||
|
{},
|
||||||
|
StaticCallProxyContract.deployedBytecode,
|
||||||
|
);
|
||||||
|
staticCallTarget = await TestStaticCallTargetContract.deployFrom0xArtifactAsync(
|
||||||
|
artifacts.TestStaticCallTarget,
|
||||||
|
provider,
|
||||||
|
txDefaults,
|
||||||
|
artifacts,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
beforeEach(async () => {
|
||||||
|
await blockchainLifecycle.startAsync();
|
||||||
|
});
|
||||||
|
afterEach(async () => {
|
||||||
|
await blockchainLifecycle.revertAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('general', () => {
|
||||||
|
it('should revert if undefined function is called', async () => {
|
||||||
|
const undefinedSelector = '0x01020304';
|
||||||
|
await expectTransactionFailedWithoutReasonAsync(
|
||||||
|
web3Wrapper.sendTransactionAsync({
|
||||||
|
from: fromAddress,
|
||||||
|
to: staticCallProxy.address,
|
||||||
|
value: constants.ZERO_AMOUNT,
|
||||||
|
data: undefinedSelector,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('should have an id of 0xc339d10a', async () => {
|
||||||
|
const proxyId = await staticCallProxy.getProxyId.callAsync();
|
||||||
|
const expectedProxyId = AssetProxyId.StaticCall;
|
||||||
|
expect(proxyId).to.equal(expectedProxyId);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('transferFrom', () => {
|
||||||
|
it('should revert if assetData lies outside the bounds of calldata', async () => {
|
||||||
|
const staticCallData = staticCallTarget.noInputFunction.getABIEncodedTransactionData();
|
||||||
|
const expectedResultHash = constants.KECCAK256_NULL;
|
||||||
|
const assetData = 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);
|
||||||
|
const invalidOffsetToAssetData = ethUtil.bufferToHex(paddedTxDataEndBuffer).slice(2);
|
||||||
|
const newAssetData = '0000000000000000000000000000000000000000000000000000000000000304';
|
||||||
|
const badTxData = `${txData.replace(offsetToAssetData, invalidOffsetToAssetData)}${newAssetData}`;
|
||||||
|
await expectTransactionFailedWithoutReasonAsync(
|
||||||
|
web3Wrapper.sendTransactionAsync({
|
||||||
|
to: staticCallProxy.address,
|
||||||
|
from: fromAddress,
|
||||||
|
data: badTxData,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('should revert if the length of assetData is less than 100 bytes', async () => {
|
||||||
|
const staticCallData = constants.NULL_BYTES;
|
||||||
|
const expectedResultHash = constants.KECCAK256_NULL;
|
||||||
|
const assetData = assetDataUtils
|
||||||
|
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||||
|
.slice(0, -128);
|
||||||
|
const assetDataByteLen = (assetData.length - 2) / 2;
|
||||||
|
expect((assetDataByteLen - 4) % 32).to.equal(0);
|
||||||
|
await expectTransactionFailedWithoutReasonAsync(
|
||||||
|
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 expectedResultHash = constants.KECCAK256_NULL;
|
||||||
|
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);
|
||||||
|
const invalidOffsetToStaticCallData = ethUtil.bufferToHex(paddedAssetDataEndBuffer).slice(2);
|
||||||
|
const newStaticCallData = '0000000000000000000000000000000000000000000000000000000000000304';
|
||||||
|
const badAssetData = `${assetData.replace(
|
||||||
|
offsetToStaticCallData,
|
||||||
|
invalidOffsetToStaticCallData,
|
||||||
|
)}${newStaticCallData}`;
|
||||||
|
await expectTransactionFailedWithoutReasonAsync(
|
||||||
|
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 expectedResultHash = constants.KECCAK256_NULL;
|
||||||
|
const assetData = assetDataUtils.encodeStaticCallAssetData(
|
||||||
|
staticCallTarget.address,
|
||||||
|
staticCallData,
|
||||||
|
expectedResultHash,
|
||||||
|
);
|
||||||
|
await expectTransactionFailedWithoutReasonAsync(
|
||||||
|
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.getABIEncodedTransactionData(new BigNumber(1));
|
||||||
|
const expectedResultHash = constants.KECCAK256_NULL;
|
||||||
|
const assetData = assetDataUtils.encodeStaticCallAssetData(
|
||||||
|
staticCallTarget.address,
|
||||||
|
staticCallData,
|
||||||
|
expectedResultHash,
|
||||||
|
);
|
||||||
|
await expectTransactionFailedAsync(
|
||||||
|
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.getABIEncodedTransactionData(new BigNumber(0));
|
||||||
|
const trueAsBuffer = ethUtil.toBuffer('0x0000000000000000000000000000000000000000000000000000000000000001');
|
||||||
|
const expectedResultHash = ethUtil.bufferToHex(ethUtil.sha3(trueAsBuffer));
|
||||||
|
const assetData = assetDataUtils.encodeStaticCallAssetData(
|
||||||
|
staticCallTarget.address,
|
||||||
|
staticCallData,
|
||||||
|
expectedResultHash,
|
||||||
|
);
|
||||||
|
await expectTransactionFailedAsync(
|
||||||
|
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 expectedResultHash = constants.KECCAK256_NULL;
|
||||||
|
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 = 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.getABIEncodedTransactionData(new BigNumber(1));
|
||||||
|
const trueAsBuffer = ethUtil.toBuffer('0x0000000000000000000000000000000000000000000000000000000000000001');
|
||||||
|
const expectedResultHash = ethUtil.bufferToHex(ethUtil.sha3(trueAsBuffer));
|
||||||
|
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.getABIEncodedTransactionData(dynamicInput);
|
||||||
|
const expectedResultHash = constants.KECCAK256_NULL;
|
||||||
|
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.getABIEncodedTransactionData(a, b);
|
||||||
|
const abiEncoder = new AbiEncoder.DynamicBytes({
|
||||||
|
name: '',
|
||||||
|
type: 'bytes',
|
||||||
|
});
|
||||||
|
const aHex = '0000000000000000000000000000000000000000000000000000000000000001';
|
||||||
|
const bHex = '0000000000000000000000000000000000000000000000000000000000000002';
|
||||||
|
const expectedResults = `${staticCallTarget.address}${aHex}${bHex}`;
|
||||||
|
const offset = '0000000000000000000000000000000000000000000000000000000000000020';
|
||||||
|
const encodedExpectedResultWithOffset = `0x${offset}${abiEncoder.encode(expectedResults).slice(2)}`;
|
||||||
|
const expectedResultHash = ethUtil.bufferToHex(
|
||||||
|
ethUtil.sha3(ethUtil.toBuffer(encodedExpectedResultWithOffset)),
|
||||||
|
);
|
||||||
|
const assetData = assetDataUtils.encodeStaticCallAssetData(
|
||||||
|
staticCallTarget.address,
|
||||||
|
staticCallData,
|
||||||
|
expectedResultHash,
|
||||||
|
);
|
||||||
|
await staticCallProxy.transferFrom.awaitTransactionSuccessAsync(assetData, fromAddress, toAddress, amount);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
419
contracts/asset-proxy/test/utils/erc1155_proxy_wrapper.ts
Normal file
419
contracts/asset-proxy/test/utils/erc1155_proxy_wrapper.ts
Normal file
@@ -0,0 +1,419 @@
|
|||||||
|
import { artifacts as erc1155Artifacts, ERC1155MintableContract, Erc1155Wrapper } from '@0x/contracts-erc1155';
|
||||||
|
import {
|
||||||
|
constants,
|
||||||
|
ERC1155FungibleHoldingsByOwner,
|
||||||
|
ERC1155HoldingsByOwner,
|
||||||
|
ERC1155NonFungibleHoldingsByOwner,
|
||||||
|
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, ERC1155ProxyContract, IAssetProxyContract } from '../../src';
|
||||||
|
|
||||||
|
export class ERC1155ProxyWrapper {
|
||||||
|
private readonly _tokenOwnerAddresses: string[];
|
||||||
|
private readonly _fungibleTokenIds: string[];
|
||||||
|
private readonly _nonFungibleTokenIds: string[];
|
||||||
|
private readonly _nfts: Array<{ id: BigNumber; tokenId: BigNumber }>;
|
||||||
|
private readonly _contractOwnerAddress: string;
|
||||||
|
private readonly _web3Wrapper: Web3Wrapper;
|
||||||
|
private readonly _provider: Provider;
|
||||||
|
private readonly _logDecoder: LogDecoder;
|
||||||
|
private readonly _dummyTokenWrappers: Erc1155Wrapper[];
|
||||||
|
private readonly _assetProxyInterface: IAssetProxyContract;
|
||||||
|
private _proxyContract?: ERC1155ProxyContract;
|
||||||
|
private _proxyIdIfExists?: string;
|
||||||
|
private _initialTokenIdsByOwner: ERC1155HoldingsByOwner = { fungible: {}, nonFungible: {} };
|
||||||
|
|
||||||
|
constructor(provider: Provider, tokenOwnerAddresses: string[], contractOwnerAddress: string) {
|
||||||
|
this._web3Wrapper = new Web3Wrapper(provider);
|
||||||
|
this._provider = provider;
|
||||||
|
const allArtifacts = _.merge(artifacts, erc1155Artifacts);
|
||||||
|
this._logDecoder = new LogDecoder(this._web3Wrapper, allArtifacts);
|
||||||
|
this._dummyTokenWrappers = [];
|
||||||
|
this._assetProxyInterface = new IAssetProxyContract(constants.NULL_ADDRESS, provider);
|
||||||
|
this._tokenOwnerAddresses = tokenOwnerAddresses;
|
||||||
|
this._contractOwnerAddress = contractOwnerAddress;
|
||||||
|
this._fungibleTokenIds = [];
|
||||||
|
this._nonFungibleTokenIds = [];
|
||||||
|
this._nfts = [];
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @dev Deploys dummy ERC1155 contracts
|
||||||
|
* @return An array of ERC1155 wrappers; one for each deployed contract.
|
||||||
|
*/
|
||||||
|
public async deployDummyContractsAsync(): Promise<Erc1155Wrapper[]> {
|
||||||
|
// tslint:disable-next-line:no-unused-variable
|
||||||
|
for (const i of _.times(constants.NUM_DUMMY_ERC1155_CONTRACTS_TO_DEPLOY)) {
|
||||||
|
const erc1155Contract = await ERC1155MintableContract.deployFrom0xArtifactAsync(
|
||||||
|
erc1155Artifacts.ERC1155Mintable,
|
||||||
|
this._provider,
|
||||||
|
txDefaults,
|
||||||
|
artifacts,
|
||||||
|
);
|
||||||
|
const erc1155Wrapper = new Erc1155Wrapper(erc1155Contract, this._provider, this._contractOwnerAddress);
|
||||||
|
this._dummyTokenWrappers.push(erc1155Wrapper);
|
||||||
|
}
|
||||||
|
return this._dummyTokenWrappers;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @dev Deploys the ERC1155 proxy
|
||||||
|
* @return Deployed ERC1155 proxy contract instance
|
||||||
|
*/
|
||||||
|
public async deployProxyAsync(): Promise<ERC1155ProxyContract> {
|
||||||
|
this._proxyContract = await ERC1155ProxyContract.deployFrom0xArtifactAsync(
|
||||||
|
artifacts.ERC1155Proxy,
|
||||||
|
this._provider,
|
||||||
|
txDefaults,
|
||||||
|
artifacts,
|
||||||
|
);
|
||||||
|
this._proxyIdIfExists = await this._proxyContract.getProxyId.callAsync();
|
||||||
|
return this._proxyContract;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @dev Gets the ERC1155 proxy id
|
||||||
|
*/
|
||||||
|
public getProxyId(): string {
|
||||||
|
this._validateProxyContractExistsOrThrow();
|
||||||
|
return this._proxyIdIfExists as string;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @dev generates abi-encoded tx data for transferring erc1155 fungible/non-fungible tokens.
|
||||||
|
* @param from source address
|
||||||
|
* @param to destination address
|
||||||
|
* @param contractAddress address of erc155 contract
|
||||||
|
* @param tokensToTransfer array of erc1155 tokens to transfer
|
||||||
|
* @param valuesToTransfer array of corresponding values for each erc1155 token to transfer
|
||||||
|
* @param valueMultiplier each value in `valuesToTransfer` is multiplied by this
|
||||||
|
* @param receiverCallbackData callback data if `to` is a contract
|
||||||
|
* @param authorizedSender sender of `transferFrom` transaction
|
||||||
|
* @param extraData extra data to append to `transferFrom` transaction. Optional.
|
||||||
|
* @return abi encoded tx data.
|
||||||
|
*/
|
||||||
|
public getTransferFromAbiEncodedTxData(
|
||||||
|
from: string,
|
||||||
|
to: string,
|
||||||
|
contractAddress: string,
|
||||||
|
tokensToTransfer: BigNumber[],
|
||||||
|
valuesToTransfer: BigNumber[],
|
||||||
|
valueMultiplier: BigNumber,
|
||||||
|
receiverCallbackData: string,
|
||||||
|
authorizedSender: string,
|
||||||
|
assetData_?: string,
|
||||||
|
): string {
|
||||||
|
this._validateProxyContractExistsOrThrow();
|
||||||
|
const assetData =
|
||||||
|
assetData_ === undefined
|
||||||
|
? assetDataUtils.encodeERC1155AssetData(
|
||||||
|
contractAddress,
|
||||||
|
tokensToTransfer,
|
||||||
|
valuesToTransfer,
|
||||||
|
receiverCallbackData,
|
||||||
|
)
|
||||||
|
: assetData_;
|
||||||
|
const data = this._assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||||
|
assetData,
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
valueMultiplier,
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @dev transfers erc1155 fungible/non-fungible tokens.
|
||||||
|
* @param txData: abi-encoded tx data
|
||||||
|
* @param authorizedSender sender of `transferFrom` transaction
|
||||||
|
*/
|
||||||
|
public async transferFromRawAsync(
|
||||||
|
txData: string,
|
||||||
|
authorizedSender: string,
|
||||||
|
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||||
|
const txHash = await this._web3Wrapper.sendTransactionAsync({
|
||||||
|
to: (this._proxyContract as ERC1155ProxyContract).address,
|
||||||
|
data: txData,
|
||||||
|
from: authorizedSender,
|
||||||
|
gas: 300000,
|
||||||
|
});
|
||||||
|
const txReceipt = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
|
||||||
|
return txReceipt;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @dev transfers erc1155 fungible/non-fungible tokens.
|
||||||
|
* @param from source address
|
||||||
|
* @param to destination address
|
||||||
|
* @param contractAddress address of erc155 contract
|
||||||
|
* @param tokensToTransfer array of erc1155 tokens to transfer
|
||||||
|
* @param valuesToTransfer array of corresponding values for each erc1155 token to transfer
|
||||||
|
* @param valueMultiplier each value in `valuesToTransfer` is multiplied by this
|
||||||
|
* @param receiverCallbackData callback data if `to` is a contract
|
||||||
|
* @param authorizedSender sender of `transferFrom` transaction
|
||||||
|
* @param extraData extra data to append to `transferFrom` transaction. Optional.
|
||||||
|
* @return tranasction hash.
|
||||||
|
*/
|
||||||
|
public async transferFromAsync(
|
||||||
|
from: string,
|
||||||
|
to: string,
|
||||||
|
contractAddress: string,
|
||||||
|
tokensToTransfer: BigNumber[],
|
||||||
|
valuesToTransfer: BigNumber[],
|
||||||
|
valueMultiplier: BigNumber,
|
||||||
|
receiverCallbackData: string,
|
||||||
|
authorizedSender: string,
|
||||||
|
assetData_?: string,
|
||||||
|
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||||
|
this._validateProxyContractExistsOrThrow();
|
||||||
|
const assetData =
|
||||||
|
assetData_ === undefined
|
||||||
|
? assetDataUtils.encodeERC1155AssetData(
|
||||||
|
contractAddress,
|
||||||
|
tokensToTransfer,
|
||||||
|
valuesToTransfer,
|
||||||
|
receiverCallbackData,
|
||||||
|
)
|
||||||
|
: assetData_;
|
||||||
|
const data = this._assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||||
|
assetData,
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
valueMultiplier,
|
||||||
|
);
|
||||||
|
const txHash = await this._web3Wrapper.sendTransactionAsync({
|
||||||
|
to: (this._proxyContract as ERC1155ProxyContract).address,
|
||||||
|
data,
|
||||||
|
from: authorizedSender,
|
||||||
|
gas: 300000,
|
||||||
|
});
|
||||||
|
const txReceipt = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
|
||||||
|
return txReceipt;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @dev For each deployed ERC1155 contract, this function mints a set of fungible/non-fungible
|
||||||
|
* tokens for each token owner address (`_tokenOwnerAddresses`).
|
||||||
|
* @return Balances of each token owner, across all ERC1155 contracts and tokens.
|
||||||
|
*/
|
||||||
|
public async setBalancesAndAllowancesAsync(): Promise<ERC1155HoldingsByOwner> {
|
||||||
|
this._validateDummyTokenContractsExistOrThrow();
|
||||||
|
this._validateProxyContractExistsOrThrow();
|
||||||
|
this._initialTokenIdsByOwner = {
|
||||||
|
fungible: {},
|
||||||
|
nonFungible: {},
|
||||||
|
};
|
||||||
|
const fungibleHoldingsByOwner: ERC1155FungibleHoldingsByOwner = {};
|
||||||
|
const nonFungibleHoldingsByOwner: ERC1155NonFungibleHoldingsByOwner = {};
|
||||||
|
// Set balances accordingly
|
||||||
|
for (const dummyWrapper of this._dummyTokenWrappers) {
|
||||||
|
const dummyAddress = dummyWrapper.getContract().address;
|
||||||
|
// tslint:disable-next-line:no-unused-variable
|
||||||
|
for (const i of _.times(constants.NUM_ERC1155_FUNGIBLE_TOKENS_MINT)) {
|
||||||
|
// Create a fungible token
|
||||||
|
const tokenId = await dummyWrapper.mintFungibleTokensAsync(
|
||||||
|
this._tokenOwnerAddresses,
|
||||||
|
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||||
|
);
|
||||||
|
const tokenIdAsString = tokenId.toString();
|
||||||
|
this._fungibleTokenIds.push(tokenIdAsString);
|
||||||
|
// Mint tokens for each owner for this token
|
||||||
|
for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
|
||||||
|
// tslint:disable-next-line:no-unused-variable
|
||||||
|
if (fungibleHoldingsByOwner[tokenOwnerAddress] === undefined) {
|
||||||
|
fungibleHoldingsByOwner[tokenOwnerAddress] = {};
|
||||||
|
}
|
||||||
|
if (fungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress] === undefined) {
|
||||||
|
fungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress] = {};
|
||||||
|
}
|
||||||
|
fungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress][tokenIdAsString] =
|
||||||
|
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE;
|
||||||
|
await dummyWrapper.setApprovalForAllAsync(
|
||||||
|
tokenOwnerAddress,
|
||||||
|
(this._proxyContract as ERC1155ProxyContract).address,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Non-fungible tokens
|
||||||
|
// tslint:disable-next-line:no-unused-variable
|
||||||
|
for (const j of _.times(constants.NUM_ERC1155_NONFUNGIBLE_TOKENS_MINT)) {
|
||||||
|
const [tokenId, nftIds] = await dummyWrapper.mintNonFungibleTokensAsync(this._tokenOwnerAddresses);
|
||||||
|
const tokenIdAsString = tokenId.toString();
|
||||||
|
this._nonFungibleTokenIds.push(tokenIdAsString);
|
||||||
|
_.each(this._tokenOwnerAddresses, async (tokenOwnerAddress: string, i: number) => {
|
||||||
|
if (nonFungibleHoldingsByOwner[tokenOwnerAddress] === undefined) {
|
||||||
|
nonFungibleHoldingsByOwner[tokenOwnerAddress] = {};
|
||||||
|
}
|
||||||
|
if (nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress] === undefined) {
|
||||||
|
nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress] = {};
|
||||||
|
}
|
||||||
|
if (nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress][tokenIdAsString] === undefined) {
|
||||||
|
nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress][tokenIdAsString] = [];
|
||||||
|
}
|
||||||
|
this._nfts.push({ id: nftIds[i], tokenId });
|
||||||
|
nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress][tokenIdAsString].push(nftIds[i]);
|
||||||
|
await dummyWrapper.setApprovalForAllAsync(
|
||||||
|
tokenOwnerAddress,
|
||||||
|
(this._proxyContract as ERC1155ProxyContract).address,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._initialTokenIdsByOwner = {
|
||||||
|
fungible: fungibleHoldingsByOwner,
|
||||||
|
nonFungible: nonFungibleHoldingsByOwner,
|
||||||
|
};
|
||||||
|
return this._initialTokenIdsByOwner;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @dev For each deployed ERC1155 contract, this function quieries the set of fungible/non-fungible
|
||||||
|
* tokens for each token owner address (`_tokenOwnerAddresses`).
|
||||||
|
* @return Balances of each token owner, across all ERC1155 contracts and tokens.
|
||||||
|
*/
|
||||||
|
public async getBalancesAsync(): Promise<ERC1155HoldingsByOwner> {
|
||||||
|
this._validateDummyTokenContractsExistOrThrow();
|
||||||
|
this._validateBalancesAndAllowancesSetOrThrow();
|
||||||
|
const tokenHoldingsByOwner: ERC1155FungibleHoldingsByOwner = {};
|
||||||
|
const nonFungibleHoldingsByOwner: ERC1155NonFungibleHoldingsByOwner = {};
|
||||||
|
for (const dummyTokenWrapper of this._dummyTokenWrappers) {
|
||||||
|
const tokenContract = dummyTokenWrapper.getContract();
|
||||||
|
const tokenAddress = tokenContract.address;
|
||||||
|
// Construct batch balance call
|
||||||
|
const tokenOwners: string[] = [];
|
||||||
|
const tokenIds: BigNumber[] = [];
|
||||||
|
for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
|
||||||
|
for (const tokenId of this._fungibleTokenIds) {
|
||||||
|
tokenOwners.push(tokenOwnerAddress);
|
||||||
|
tokenIds.push(new BigNumber(tokenId));
|
||||||
|
}
|
||||||
|
for (const nft of this._nfts) {
|
||||||
|
tokenOwners.push(tokenOwnerAddress);
|
||||||
|
tokenIds.push(nft.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const balances = await dummyTokenWrapper.getBalancesAsync(tokenOwners, tokenIds);
|
||||||
|
// Parse out balances into fungible / non-fungible token holdings
|
||||||
|
let i = 0;
|
||||||
|
for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
|
||||||
|
// Fungible tokens
|
||||||
|
for (const tokenId of this._fungibleTokenIds) {
|
||||||
|
if (tokenHoldingsByOwner[tokenOwnerAddress] === undefined) {
|
||||||
|
tokenHoldingsByOwner[tokenOwnerAddress] = {};
|
||||||
|
}
|
||||||
|
if (tokenHoldingsByOwner[tokenOwnerAddress][tokenAddress] === undefined) {
|
||||||
|
tokenHoldingsByOwner[tokenOwnerAddress][tokenAddress] = {};
|
||||||
|
}
|
||||||
|
tokenHoldingsByOwner[tokenOwnerAddress][tokenAddress][tokenId] = balances[i++];
|
||||||
|
}
|
||||||
|
// Non-fungible tokens
|
||||||
|
for (const nft of this._nfts) {
|
||||||
|
if (nonFungibleHoldingsByOwner[tokenOwnerAddress] === undefined) {
|
||||||
|
nonFungibleHoldingsByOwner[tokenOwnerAddress] = {};
|
||||||
|
}
|
||||||
|
if (nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress] === undefined) {
|
||||||
|
nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress] = {};
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress][nft.tokenId.toString()] ===
|
||||||
|
undefined
|
||||||
|
) {
|
||||||
|
nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress][nft.tokenId.toString()] = [];
|
||||||
|
}
|
||||||
|
const isOwner = balances[i++];
|
||||||
|
if (isOwner.isEqualTo(1)) {
|
||||||
|
nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress][nft.tokenId.toString()].push(
|
||||||
|
nft.id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const holdingsByOwner = {
|
||||||
|
fungible: tokenHoldingsByOwner,
|
||||||
|
nonFungible: nonFungibleHoldingsByOwner,
|
||||||
|
};
|
||||||
|
return holdingsByOwner;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @dev Set the approval for the proxy on behalf of `userAddress` .
|
||||||
|
* @param userAddress owner of ERC1155 tokens.
|
||||||
|
* @param contractAddress address of ERC1155 contract.
|
||||||
|
* @param isApproved Whether to approve the proxy for all or not.
|
||||||
|
*/
|
||||||
|
public async setProxyAllowanceForAllAsync(
|
||||||
|
userAddress: string,
|
||||||
|
contractAddress: string,
|
||||||
|
isApproved: boolean,
|
||||||
|
): Promise<void> {
|
||||||
|
this._validateProxyContractExistsOrThrow();
|
||||||
|
const tokenWrapper = this.getContractWrapper(contractAddress);
|
||||||
|
const operator = (this._proxyContract as ERC1155ProxyContract).address;
|
||||||
|
await tokenWrapper.setApprovalForAllAsync(userAddress, operator, isApproved);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @dev Checks if proxy is approved to transfer tokens on behalf of `userAddress`.
|
||||||
|
* @param userAddress owner of ERC1155 tokens.
|
||||||
|
* @param contractAddress address of ERC1155 contract.
|
||||||
|
* @return True iff the proxy is approved for all. False otherwise.
|
||||||
|
*/
|
||||||
|
public async isProxyApprovedForAllAsync(userAddress: string, contractAddress: string): Promise<boolean> {
|
||||||
|
this._validateProxyContractExistsOrThrow();
|
||||||
|
const tokenContract = this._getContractFromAddress(contractAddress);
|
||||||
|
const operator = (this._proxyContract as ERC1155ProxyContract).address;
|
||||||
|
const didApproveAll = await tokenContract.isApprovedForAll.callAsync(userAddress, operator);
|
||||||
|
return didApproveAll;
|
||||||
|
}
|
||||||
|
public getFungibleTokenIds(): BigNumber[] {
|
||||||
|
const fungibleTokenIds = _.map(this._fungibleTokenIds, (tokenIdAsString: string) => {
|
||||||
|
return new BigNumber(tokenIdAsString);
|
||||||
|
});
|
||||||
|
return fungibleTokenIds;
|
||||||
|
}
|
||||||
|
public getNonFungibleTokenIds(): BigNumber[] {
|
||||||
|
const nonFungibleTokenIds = _.map(this._nonFungibleTokenIds, (tokenIdAsString: string) => {
|
||||||
|
return new BigNumber(tokenIdAsString);
|
||||||
|
});
|
||||||
|
return nonFungibleTokenIds;
|
||||||
|
}
|
||||||
|
public getTokenOwnerAddresses(): string[] {
|
||||||
|
return this._tokenOwnerAddresses;
|
||||||
|
}
|
||||||
|
public getContractWrapper(contractAddress: string): Erc1155Wrapper {
|
||||||
|
const tokenWrapper = _.find(this._dummyTokenWrappers, (wrapper: Erc1155Wrapper) => {
|
||||||
|
return wrapper.getContract().address === contractAddress;
|
||||||
|
});
|
||||||
|
if (tokenWrapper === undefined) {
|
||||||
|
throw new Error(`Contract: ${contractAddress} was not deployed through ERC1155ProxyWrapper`);
|
||||||
|
}
|
||||||
|
return tokenWrapper;
|
||||||
|
}
|
||||||
|
private _getContractFromAddress(tokenAddress: string): ERC1155MintableContract {
|
||||||
|
const tokenContractIfExists = _.find(this._dummyTokenWrappers, c => c.getContract().address === tokenAddress);
|
||||||
|
if (tokenContractIfExists === undefined) {
|
||||||
|
throw new Error(`Token: ${tokenAddress} was not deployed through ERC1155ProxyWrapper`);
|
||||||
|
}
|
||||||
|
return tokenContractIfExists.getContract();
|
||||||
|
}
|
||||||
|
private _validateDummyTokenContractsExistOrThrow(): void {
|
||||||
|
if (this._dummyTokenWrappers === undefined) {
|
||||||
|
throw new Error('Dummy ERC1155 tokens not yet deployed, please call "deployDummyTokensAsync"');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private _validateProxyContractExistsOrThrow(): void {
|
||||||
|
if (this._proxyContract === undefined) {
|
||||||
|
throw new Error('ERC1155 proxy contract not yet deployed, please call "deployProxyAsync"');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private _validateBalancesAndAllowancesSetOrThrow(): void {
|
||||||
|
if (
|
||||||
|
_.keys(this._initialTokenIdsByOwner.fungible).length === 0 ||
|
||||||
|
_.keys(this._initialTokenIdsByOwner.nonFungible).length === 0
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
'Dummy ERC1155 balances and allowances not yet set, please call "setBalancesAndAllowancesAsync"',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,17 +1,16 @@
|
|||||||
|
import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contracts-erc20';
|
||||||
import { constants, ERC20BalancesByOwner, txDefaults } from '@0x/contracts-test-utils';
|
import { constants, ERC20BalancesByOwner, txDefaults } from '@0x/contracts-test-utils';
|
||||||
import { assetDataUtils } from '@0x/order-utils';
|
import { assetDataUtils } from '@0x/order-utils';
|
||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
import { ZeroExProvider } from 'ethereum-types';
|
||||||
import { Provider } from 'ethereum-types';
|
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { artifacts, DummyERC20TokenContract, ERC20ProxyContract } from '../../src';
|
import { artifacts, ERC20ProxyContract } from '../../src';
|
||||||
|
|
||||||
export class ERC20Wrapper {
|
export class ERC20Wrapper {
|
||||||
private readonly _tokenOwnerAddresses: string[];
|
private readonly _tokenOwnerAddresses: string[];
|
||||||
private readonly _contractOwnerAddress: string;
|
private readonly _contractOwnerAddress: string;
|
||||||
private readonly _web3Wrapper: Web3Wrapper;
|
private readonly _provider: ZeroExProvider;
|
||||||
private readonly _provider: Provider;
|
|
||||||
private readonly _dummyTokenContracts: DummyERC20TokenContract[];
|
private readonly _dummyTokenContracts: DummyERC20TokenContract[];
|
||||||
private _proxyContract?: ERC20ProxyContract;
|
private _proxyContract?: ERC20ProxyContract;
|
||||||
private _proxyIdIfExists?: string;
|
private _proxyIdIfExists?: string;
|
||||||
@@ -22,9 +21,8 @@ export class ERC20Wrapper {
|
|||||||
* @param contractOwnerAddress Desired owner of the contract
|
* @param contractOwnerAddress Desired owner of the contract
|
||||||
* Instance of ERC20Wrapper
|
* Instance of ERC20Wrapper
|
||||||
*/
|
*/
|
||||||
constructor(provider: Provider, tokenOwnerAddresses: string[], contractOwnerAddress: string) {
|
constructor(provider: ZeroExProvider, tokenOwnerAddresses: string[], contractOwnerAddress: string) {
|
||||||
this._dummyTokenContracts = [];
|
this._dummyTokenContracts = [];
|
||||||
this._web3Wrapper = new Web3Wrapper(provider);
|
|
||||||
this._provider = provider;
|
this._provider = provider;
|
||||||
this._tokenOwnerAddresses = tokenOwnerAddresses;
|
this._tokenOwnerAddresses = tokenOwnerAddresses;
|
||||||
this._contractOwnerAddress = contractOwnerAddress;
|
this._contractOwnerAddress = contractOwnerAddress;
|
||||||
@@ -36,9 +34,10 @@ export class ERC20Wrapper {
|
|||||||
for (let i = 0; i < numberToDeploy; i++) {
|
for (let i = 0; i < numberToDeploy; i++) {
|
||||||
this._dummyTokenContracts.push(
|
this._dummyTokenContracts.push(
|
||||||
await DummyERC20TokenContract.deployFrom0xArtifactAsync(
|
await DummyERC20TokenContract.deployFrom0xArtifactAsync(
|
||||||
artifacts.DummyERC20Token,
|
erc20Artifacts.DummyERC20Token,
|
||||||
this._provider,
|
this._provider,
|
||||||
txDefaults,
|
txDefaults,
|
||||||
|
artifacts,
|
||||||
constants.DUMMY_TOKEN_NAME,
|
constants.DUMMY_TOKEN_NAME,
|
||||||
constants.DUMMY_TOKEN_SYMBOL,
|
constants.DUMMY_TOKEN_SYMBOL,
|
||||||
decimals,
|
decimals,
|
||||||
@@ -53,6 +52,7 @@ export class ERC20Wrapper {
|
|||||||
artifacts.ERC20Proxy,
|
artifacts.ERC20Proxy,
|
||||||
this._provider,
|
this._provider,
|
||||||
txDefaults,
|
txDefaults,
|
||||||
|
artifacts,
|
||||||
);
|
);
|
||||||
this._proxyIdIfExists = await this._proxyContract.getProxyId.callAsync();
|
this._proxyIdIfExists = await this._proxyContract.getProxyId.callAsync();
|
||||||
return this._proxyContract;
|
return this._proxyContract;
|
||||||
@@ -66,20 +66,16 @@ export class ERC20Wrapper {
|
|||||||
this._validateProxyContractExistsOrThrow();
|
this._validateProxyContractExistsOrThrow();
|
||||||
for (const dummyTokenContract of this._dummyTokenContracts) {
|
for (const dummyTokenContract of this._dummyTokenContracts) {
|
||||||
for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
|
for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
|
||||||
await this._web3Wrapper.awaitTransactionSuccessAsync(
|
await dummyTokenContract.setBalance.awaitTransactionSuccessAsync(
|
||||||
await dummyTokenContract.setBalance.sendTransactionAsync(
|
tokenOwnerAddress,
|
||||||
tokenOwnerAddress,
|
constants.INITIAL_ERC20_BALANCE,
|
||||||
constants.INITIAL_ERC20_BALANCE,
|
{ from: this._contractOwnerAddress },
|
||||||
{ from: this._contractOwnerAddress },
|
|
||||||
),
|
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
);
|
);
|
||||||
await this._web3Wrapper.awaitTransactionSuccessAsync(
|
await dummyTokenContract.approve.awaitTransactionSuccessAsync(
|
||||||
await dummyTokenContract.approve.sendTransactionAsync(
|
(this._proxyContract as ERC20ProxyContract).address,
|
||||||
(this._proxyContract as ERC20ProxyContract).address,
|
constants.INITIAL_ERC20_ALLOWANCE,
|
||||||
constants.INITIAL_ERC20_ALLOWANCE,
|
{ from: tokenOwnerAddress },
|
||||||
{ from: tokenOwnerAddress },
|
|
||||||
),
|
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -92,10 +88,10 @@ export class ERC20Wrapper {
|
|||||||
}
|
}
|
||||||
public async setBalanceAsync(userAddress: string, assetData: string, amount: BigNumber): Promise<void> {
|
public async setBalanceAsync(userAddress: string, assetData: string, amount: BigNumber): Promise<void> {
|
||||||
const tokenContract = this._getTokenContractFromAssetData(assetData);
|
const tokenContract = this._getTokenContractFromAssetData(assetData);
|
||||||
await this._web3Wrapper.awaitTransactionSuccessAsync(
|
await tokenContract.setBalance.awaitTransactionSuccessAsync(
|
||||||
await tokenContract.setBalance.sendTransactionAsync(userAddress, amount, {
|
userAddress,
|
||||||
from: this._contractOwnerAddress,
|
amount,
|
||||||
}),
|
{ from: this._contractOwnerAddress },
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -108,10 +104,10 @@ export class ERC20Wrapper {
|
|||||||
public async setAllowanceAsync(userAddress: string, assetData: string, amount: BigNumber): Promise<void> {
|
public async setAllowanceAsync(userAddress: string, assetData: string, amount: BigNumber): Promise<void> {
|
||||||
const tokenContract = this._getTokenContractFromAssetData(assetData);
|
const tokenContract = this._getTokenContractFromAssetData(assetData);
|
||||||
const proxyAddress = (this._proxyContract as ERC20ProxyContract).address;
|
const proxyAddress = (this._proxyContract as ERC20ProxyContract).address;
|
||||||
await this._web3Wrapper.awaitTransactionSuccessAsync(
|
await tokenContract.approve.awaitTransactionSuccessAsync(
|
||||||
await tokenContract.approve.sendTransactionAsync(proxyAddress, amount, {
|
proxyAddress,
|
||||||
from: userAddress,
|
amount,
|
||||||
}),
|
{ from: userAddress },
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -132,7 +128,7 @@ export class ERC20Wrapper {
|
|||||||
_.forEach(balances, (balance, balanceIndex) => {
|
_.forEach(balances, (balance, balanceIndex) => {
|
||||||
const tokenAddress = balanceInfo[balanceIndex].tokenAddress;
|
const tokenAddress = balanceInfo[balanceIndex].tokenAddress;
|
||||||
const tokenOwnerAddress = balanceInfo[balanceIndex].tokenOwnerAddress;
|
const tokenOwnerAddress = balanceInfo[balanceIndex].tokenOwnerAddress;
|
||||||
if (_.isUndefined(balancesByOwner[tokenOwnerAddress])) {
|
if (balancesByOwner[tokenOwnerAddress] === undefined) {
|
||||||
balancesByOwner[tokenOwnerAddress] = {};
|
balancesByOwner[tokenOwnerAddress] = {};
|
||||||
}
|
}
|
||||||
const wrappedBalance = new BigNumber(balance);
|
const wrappedBalance = new BigNumber(balance);
|
||||||
@@ -141,7 +137,7 @@ export class ERC20Wrapper {
|
|||||||
return balancesByOwner;
|
return balancesByOwner;
|
||||||
}
|
}
|
||||||
public addDummyTokenContract(dummy: DummyERC20TokenContract): void {
|
public addDummyTokenContract(dummy: DummyERC20TokenContract): void {
|
||||||
if (!_.isUndefined(this._dummyTokenContracts)) {
|
if (this._dummyTokenContracts !== undefined) {
|
||||||
this._dummyTokenContracts.push(dummy);
|
this._dummyTokenContracts.push(dummy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -159,18 +155,18 @@ export class ERC20Wrapper {
|
|||||||
const erc20ProxyData = assetDataUtils.decodeERC20AssetData(assetData);
|
const erc20ProxyData = assetDataUtils.decodeERC20AssetData(assetData);
|
||||||
const tokenAddress = erc20ProxyData.tokenAddress;
|
const tokenAddress = erc20ProxyData.tokenAddress;
|
||||||
const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === tokenAddress);
|
const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === tokenAddress);
|
||||||
if (_.isUndefined(tokenContractIfExists)) {
|
if (tokenContractIfExists === undefined) {
|
||||||
throw new Error(`Token: ${tokenAddress} was not deployed through ERC20Wrapper`);
|
throw new Error(`Token: ${tokenAddress} was not deployed through ERC20Wrapper`);
|
||||||
}
|
}
|
||||||
return tokenContractIfExists;
|
return tokenContractIfExists;
|
||||||
}
|
}
|
||||||
private _validateDummyTokenContractsExistOrThrow(): void {
|
private _validateDummyTokenContractsExistOrThrow(): void {
|
||||||
if (_.isUndefined(this._dummyTokenContracts)) {
|
if (this._dummyTokenContracts === undefined) {
|
||||||
throw new Error('Dummy ERC20 tokens not yet deployed, please call "deployDummyTokensAsync"');
|
throw new Error('Dummy ERC20 tokens not yet deployed, please call "deployDummyTokensAsync"');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private _validateProxyContractExistsOrThrow(): void {
|
private _validateProxyContractExistsOrThrow(): void {
|
||||||
if (_.isUndefined(this._proxyContract)) {
|
if (this._proxyContract === undefined) {
|
||||||
throw new Error('ERC20 proxy contract not yet deployed, please call "deployProxyAsync"');
|
throw new Error('ERC20 proxy contract not yet deployed, please call "deployProxyAsync"');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,21 @@
|
|||||||
|
import { artifacts as erc721Artifacts, DummyERC721TokenContract } from '@0x/contracts-erc721';
|
||||||
import { constants, ERC721TokenIdsByOwner, txDefaults } from '@0x/contracts-test-utils';
|
import { constants, ERC721TokenIdsByOwner, txDefaults } from '@0x/contracts-test-utils';
|
||||||
import { generatePseudoRandomSalt } from '@0x/order-utils';
|
import { generatePseudoRandomSalt } from '@0x/order-utils';
|
||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
import { ZeroExProvider } from 'ethereum-types';
|
||||||
import { Provider } from 'ethereum-types';
|
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { artifacts, DummyERC721TokenContract, ERC721ProxyContract } from '../../src';
|
import { artifacts, ERC721ProxyContract } from '../../src';
|
||||||
|
|
||||||
export class ERC721Wrapper {
|
export class ERC721Wrapper {
|
||||||
private readonly _tokenOwnerAddresses: string[];
|
private readonly _tokenOwnerAddresses: string[];
|
||||||
private readonly _contractOwnerAddress: string;
|
private readonly _contractOwnerAddress: string;
|
||||||
private readonly _web3Wrapper: Web3Wrapper;
|
private readonly _provider: ZeroExProvider;
|
||||||
private readonly _provider: Provider;
|
|
||||||
private readonly _dummyTokenContracts: DummyERC721TokenContract[];
|
private readonly _dummyTokenContracts: DummyERC721TokenContract[];
|
||||||
private _proxyContract?: ERC721ProxyContract;
|
private _proxyContract?: ERC721ProxyContract;
|
||||||
private _proxyIdIfExists?: string;
|
private _proxyIdIfExists?: string;
|
||||||
private _initialTokenIdsByOwner: ERC721TokenIdsByOwner = {};
|
private _initialTokenIdsByOwner: ERC721TokenIdsByOwner = {};
|
||||||
constructor(provider: Provider, tokenOwnerAddresses: string[], contractOwnerAddress: string) {
|
constructor(provider: ZeroExProvider, tokenOwnerAddresses: string[], contractOwnerAddress: string) {
|
||||||
this._web3Wrapper = new Web3Wrapper(provider);
|
|
||||||
this._provider = provider;
|
this._provider = provider;
|
||||||
this._dummyTokenContracts = [];
|
this._dummyTokenContracts = [];
|
||||||
this._tokenOwnerAddresses = tokenOwnerAddresses;
|
this._tokenOwnerAddresses = tokenOwnerAddresses;
|
||||||
@@ -28,9 +26,10 @@ export class ERC721Wrapper {
|
|||||||
for (const i of _.times(constants.NUM_DUMMY_ERC721_TO_DEPLOY)) {
|
for (const i of _.times(constants.NUM_DUMMY_ERC721_TO_DEPLOY)) {
|
||||||
this._dummyTokenContracts.push(
|
this._dummyTokenContracts.push(
|
||||||
await DummyERC721TokenContract.deployFrom0xArtifactAsync(
|
await DummyERC721TokenContract.deployFrom0xArtifactAsync(
|
||||||
artifacts.DummyERC721Token,
|
erc721Artifacts.DummyERC721Token,
|
||||||
this._provider,
|
this._provider,
|
||||||
txDefaults,
|
txDefaults,
|
||||||
|
artifacts,
|
||||||
constants.DUMMY_TOKEN_NAME,
|
constants.DUMMY_TOKEN_NAME,
|
||||||
constants.DUMMY_TOKEN_SYMBOL,
|
constants.DUMMY_TOKEN_SYMBOL,
|
||||||
),
|
),
|
||||||
@@ -43,6 +42,7 @@ export class ERC721Wrapper {
|
|||||||
artifacts.ERC721Proxy,
|
artifacts.ERC721Proxy,
|
||||||
this._provider,
|
this._provider,
|
||||||
txDefaults,
|
txDefaults,
|
||||||
|
artifacts,
|
||||||
);
|
);
|
||||||
this._proxyIdIfExists = await this._proxyContract.getProxyId.callAsync();
|
this._proxyIdIfExists = await this._proxyContract.getProxyId.callAsync();
|
||||||
return this._proxyContract;
|
return this._proxyContract;
|
||||||
@@ -61,17 +61,17 @@ export class ERC721Wrapper {
|
|||||||
for (const i of _.times(constants.NUM_ERC721_TOKENS_TO_MINT)) {
|
for (const i of _.times(constants.NUM_ERC721_TOKENS_TO_MINT)) {
|
||||||
const tokenId = generatePseudoRandomSalt();
|
const tokenId = generatePseudoRandomSalt();
|
||||||
await this.mintAsync(dummyTokenContract.address, tokenId, tokenOwnerAddress);
|
await this.mintAsync(dummyTokenContract.address, tokenId, tokenOwnerAddress);
|
||||||
if (_.isUndefined(this._initialTokenIdsByOwner[tokenOwnerAddress])) {
|
if (this._initialTokenIdsByOwner[tokenOwnerAddress] === undefined) {
|
||||||
this._initialTokenIdsByOwner[tokenOwnerAddress] = {
|
this._initialTokenIdsByOwner[tokenOwnerAddress] = {
|
||||||
[dummyTokenContract.address]: [],
|
[dummyTokenContract.address]: [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (_.isUndefined(this._initialTokenIdsByOwner[tokenOwnerAddress][dummyTokenContract.address])) {
|
if (this._initialTokenIdsByOwner[tokenOwnerAddress][dummyTokenContract.address] === undefined) {
|
||||||
this._initialTokenIdsByOwner[tokenOwnerAddress][dummyTokenContract.address] = [];
|
this._initialTokenIdsByOwner[tokenOwnerAddress][dummyTokenContract.address] = [];
|
||||||
}
|
}
|
||||||
this._initialTokenIdsByOwner[tokenOwnerAddress][dummyTokenContract.address].push(tokenId);
|
this._initialTokenIdsByOwner[tokenOwnerAddress][dummyTokenContract.address].push(tokenId);
|
||||||
|
|
||||||
await this.approveProxyAsync(dummyTokenContract.address, tokenId);
|
await this.approveProxyForAllAsync(dummyTokenContract.address, tokenOwnerAddress, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,24 +86,27 @@ export class ERC721Wrapper {
|
|||||||
const proxyAddress = (this._proxyContract as ERC721ProxyContract).address;
|
const proxyAddress = (this._proxyContract as ERC721ProxyContract).address;
|
||||||
await this.approveAsync(proxyAddress, tokenAddress, tokenId);
|
await this.approveAsync(proxyAddress, tokenAddress, tokenId);
|
||||||
}
|
}
|
||||||
public async approveProxyForAllAsync(tokenAddress: string, tokenId: BigNumber, isApproved: boolean): Promise<void> {
|
public async approveProxyForAllAsync(
|
||||||
|
tokenAddress: string,
|
||||||
|
ownerAddress: string,
|
||||||
|
isApproved: boolean,
|
||||||
|
): Promise<void> {
|
||||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||||
const tokenOwner = await this.ownerOfAsync(tokenAddress, tokenId);
|
|
||||||
const proxyAddress = (this._proxyContract as ERC721ProxyContract).address;
|
const proxyAddress = (this._proxyContract as ERC721ProxyContract).address;
|
||||||
await this._web3Wrapper.awaitTransactionSuccessAsync(
|
await tokenContract.setApprovalForAll.awaitTransactionSuccessAsync(
|
||||||
await tokenContract.setApprovalForAll.sendTransactionAsync(proxyAddress, isApproved, {
|
proxyAddress,
|
||||||
from: tokenOwner,
|
isApproved,
|
||||||
}),
|
{ from: ownerAddress },
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
public async approveAsync(to: string, tokenAddress: string, tokenId: BigNumber): Promise<void> {
|
public async approveAsync(to: string, tokenAddress: string, tokenId: BigNumber): Promise<void> {
|
||||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||||
const tokenOwner = await this.ownerOfAsync(tokenAddress, tokenId);
|
const tokenOwner = await this.ownerOfAsync(tokenAddress, tokenId);
|
||||||
await this._web3Wrapper.awaitTransactionSuccessAsync(
|
await tokenContract.approve.awaitTransactionSuccessAsync(
|
||||||
await tokenContract.approve.sendTransactionAsync(to, tokenId, {
|
to,
|
||||||
from: tokenOwner,
|
tokenId,
|
||||||
}),
|
{ from: tokenOwner },
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -114,28 +117,29 @@ export class ERC721Wrapper {
|
|||||||
userAddress: string,
|
userAddress: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||||
await this._web3Wrapper.awaitTransactionSuccessAsync(
|
await tokenContract.transferFrom.awaitTransactionSuccessAsync(
|
||||||
await tokenContract.transferFrom.sendTransactionAsync(currentOwner, userAddress, tokenId, {
|
currentOwner,
|
||||||
from: currentOwner,
|
userAddress,
|
||||||
}),
|
tokenId,
|
||||||
|
{ from: currentOwner },
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
public async mintAsync(tokenAddress: string, tokenId: BigNumber, userAddress: string): Promise<void> {
|
public async mintAsync(tokenAddress: string, tokenId: BigNumber, userAddress: string): Promise<void> {
|
||||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||||
await this._web3Wrapper.awaitTransactionSuccessAsync(
|
await tokenContract.mint.awaitTransactionSuccessAsync(
|
||||||
await tokenContract.mint.sendTransactionAsync(userAddress, tokenId, {
|
userAddress,
|
||||||
from: this._contractOwnerAddress,
|
tokenId,
|
||||||
}),
|
{ from: this._contractOwnerAddress },
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
public async burnAsync(tokenAddress: string, tokenId: BigNumber, owner: string): Promise<void> {
|
public async burnAsync(tokenAddress: string, tokenId: BigNumber, owner: string): Promise<void> {
|
||||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||||
await this._web3Wrapper.awaitTransactionSuccessAsync(
|
await tokenContract.burn.awaitTransactionSuccessAsync(
|
||||||
await tokenContract.burn.sendTransactionAsync(owner, tokenId, {
|
owner,
|
||||||
from: this._contractOwnerAddress,
|
tokenId,
|
||||||
}),
|
{ from: this._contractOwnerAddress },
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -188,12 +192,12 @@ export class ERC721Wrapper {
|
|||||||
_.forEach(tokenOwnerAddresses, (tokenOwnerAddress, ownerIndex) => {
|
_.forEach(tokenOwnerAddresses, (tokenOwnerAddress, ownerIndex) => {
|
||||||
const tokenAddress = tokenInfo[ownerIndex].tokenAddress;
|
const tokenAddress = tokenInfo[ownerIndex].tokenAddress;
|
||||||
const tokenId = tokenInfo[ownerIndex].tokenId;
|
const tokenId = tokenInfo[ownerIndex].tokenId;
|
||||||
if (_.isUndefined(tokenIdsByOwner[tokenOwnerAddress])) {
|
if (tokenIdsByOwner[tokenOwnerAddress] === undefined) {
|
||||||
tokenIdsByOwner[tokenOwnerAddress] = {
|
tokenIdsByOwner[tokenOwnerAddress] = {
|
||||||
[tokenAddress]: [],
|
[tokenAddress]: [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (_.isUndefined(tokenIdsByOwner[tokenOwnerAddress][tokenAddress])) {
|
if (tokenIdsByOwner[tokenOwnerAddress][tokenAddress] === undefined) {
|
||||||
tokenIdsByOwner[tokenOwnerAddress][tokenAddress] = [];
|
tokenIdsByOwner[tokenOwnerAddress][tokenAddress] = [];
|
||||||
}
|
}
|
||||||
tokenIdsByOwner[tokenOwnerAddress][tokenAddress].push(tokenId);
|
tokenIdsByOwner[tokenOwnerAddress][tokenAddress].push(tokenId);
|
||||||
@@ -209,18 +213,18 @@ export class ERC721Wrapper {
|
|||||||
}
|
}
|
||||||
private _getTokenContractFromAssetData(tokenAddress: string): DummyERC721TokenContract {
|
private _getTokenContractFromAssetData(tokenAddress: string): DummyERC721TokenContract {
|
||||||
const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === tokenAddress);
|
const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === tokenAddress);
|
||||||
if (_.isUndefined(tokenContractIfExists)) {
|
if (tokenContractIfExists === undefined) {
|
||||||
throw new Error(`Token: ${tokenAddress} was not deployed through ERC20Wrapper`);
|
throw new Error(`Token: ${tokenAddress} was not deployed through ERC20Wrapper`);
|
||||||
}
|
}
|
||||||
return tokenContractIfExists;
|
return tokenContractIfExists;
|
||||||
}
|
}
|
||||||
private _validateDummyTokenContractsExistOrThrow(): void {
|
private _validateDummyTokenContractsExistOrThrow(): void {
|
||||||
if (_.isUndefined(this._dummyTokenContracts)) {
|
if (this._dummyTokenContracts === undefined) {
|
||||||
throw new Error('Dummy ERC721 tokens not yet deployed, please call "deployDummyTokensAsync"');
|
throw new Error('Dummy ERC721 tokens not yet deployed, please call "deployDummyTokensAsync"');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private _validateProxyContractExistsOrThrow(): void {
|
private _validateProxyContractExistsOrThrow(): void {
|
||||||
if (_.isUndefined(this._proxyContract)) {
|
if (this._proxyContract === undefined) {
|
||||||
throw new Error('ERC721 proxy contract not yet deployed, please call "deployProxyAsync"');
|
throw new Error('ERC721 proxy contract not yet deployed, please call "deployProxyAsync"');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
export * from './erc20_wrapper';
|
export * from './erc20_wrapper';
|
||||||
export * from './erc721_wrapper';
|
export * from './erc721_wrapper';
|
||||||
|
export * from './erc1155_proxy_wrapper';
|
||||||
|
|||||||
96
contracts/asset-proxy/truffle-config.js
Normal file
96
contracts/asset-proxy/truffle-config.js
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
/**
|
||||||
|
* Use this file to configure your truffle project. It's seeded with some
|
||||||
|
* common settings for different networks and features like migrations,
|
||||||
|
* compilation and testing. Uncomment the ones you need or modify
|
||||||
|
* them to suit your project as necessary.
|
||||||
|
*
|
||||||
|
* More information about configuration can be found at:
|
||||||
|
*
|
||||||
|
* truffleframework.com/docs/advanced/configuration
|
||||||
|
*
|
||||||
|
* To deploy via Infura you'll need a wallet provider (like truffle-hdwallet-provider)
|
||||||
|
* to sign your transactions before they're sent to a remote public node. Infura accounts
|
||||||
|
* are available for free at: infura.io/register.
|
||||||
|
*
|
||||||
|
* You'll also need a mnemonic - the twelve word phrase the wallet uses to generate
|
||||||
|
* public/private key pairs. If you're publishing your code to GitHub make sure you load this
|
||||||
|
* phrase from a file you've .gitignored so it doesn't accidentally become public.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// const HDWalletProvider = require('truffle-hdwallet-provider');
|
||||||
|
// const infuraKey = "fj4jll3k.....";
|
||||||
|
//
|
||||||
|
// const fs = require('fs');
|
||||||
|
// const mnemonic = fs.readFileSync(".secret").toString().trim();
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
/**
|
||||||
|
* Networks define how you connect to your ethereum client and let you set the
|
||||||
|
* defaults web3 uses to send transactions. If you don't specify one truffle
|
||||||
|
* will spin up a development blockchain for you on port 9545 when you
|
||||||
|
* run `develop` or `test`. You can ask a truffle command to use a specific
|
||||||
|
* network from the command line, e.g
|
||||||
|
*
|
||||||
|
* $ truffle test --network <network-name>
|
||||||
|
*/
|
||||||
|
|
||||||
|
networks: {
|
||||||
|
// Useful for testing. The `development` name is special - truffle uses it by default
|
||||||
|
// if it's defined here and no other network is specified at the command line.
|
||||||
|
// You should run a client (like ganache-cli, geth or parity) in a separate terminal
|
||||||
|
// tab if you use this network and you must also set the `host`, `port` and `network_id`
|
||||||
|
// options below to some value.
|
||||||
|
//
|
||||||
|
// development: {
|
||||||
|
// host: "127.0.0.1", // Localhost (default: none)
|
||||||
|
// port: 8545, // Standard Ethereum port (default: none)
|
||||||
|
// network_id: "*", // Any network (default: none)
|
||||||
|
// },
|
||||||
|
// Another network with more advanced options...
|
||||||
|
// advanced: {
|
||||||
|
// port: 8777, // Custom port
|
||||||
|
// network_id: 1342, // Custom network
|
||||||
|
// gas: 8500000, // Gas sent with each transaction (default: ~6700000)
|
||||||
|
// gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei)
|
||||||
|
// from: <address>, // Account to send txs from (default: accounts[0])
|
||||||
|
// websockets: true // Enable EventEmitter interface for web3 (default: false)
|
||||||
|
// },
|
||||||
|
// Useful for deploying to a public network.
|
||||||
|
// NB: It's important to wrap the provider as a function.
|
||||||
|
// ropsten: {
|
||||||
|
// provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/v3/YOUR-PROJECT-ID`),
|
||||||
|
// network_id: 3, // Ropsten's id
|
||||||
|
// gas: 5500000, // Ropsten has a lower block limit than mainnet
|
||||||
|
// confirmations: 2, // # of confs to wait between deployments. (default: 0)
|
||||||
|
// timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50)
|
||||||
|
// skipDryRun: true // Skip dry run before migrations? (default: false for public nets )
|
||||||
|
// },
|
||||||
|
// Useful for private networks
|
||||||
|
// private: {
|
||||||
|
// provider: () => new HDWalletProvider(mnemonic, `https://network.io`),
|
||||||
|
// network_id: 2111, // This network is yours, in the cloud.
|
||||||
|
// production: true // Treats this network as if it was a public net. (default: false)
|
||||||
|
// }
|
||||||
|
},
|
||||||
|
|
||||||
|
// Set default mocha options here, use special reporters etc.
|
||||||
|
mocha: {
|
||||||
|
// timeout: 100000
|
||||||
|
},
|
||||||
|
|
||||||
|
// Configure your compilers
|
||||||
|
compilers: {
|
||||||
|
solc: {
|
||||||
|
version: '0.5.9',
|
||||||
|
settings: {
|
||||||
|
evmVersion: 'constantinople',
|
||||||
|
optimizer: {
|
||||||
|
enabled: true,
|
||||||
|
runs: 1000000,
|
||||||
|
details: { yul: true, deduplicate: true, cse: true, constantOptimizer: true },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -3,18 +3,26 @@
|
|||||||
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
|
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
|
||||||
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
|
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
|
||||||
"files": [
|
"files": [
|
||||||
"generated-artifacts/DummyERC20Token.json",
|
"generated-artifacts/ERC1155Proxy.json",
|
||||||
"generated-artifacts/DummyERC721Receiver.json",
|
"generated-artifacts/ERC20BridgeProxy.json",
|
||||||
"generated-artifacts/DummyERC721Token.json",
|
|
||||||
"generated-artifacts/DummyMultipleReturnERC20Token.json",
|
|
||||||
"generated-artifacts/DummyNoReturnERC20Token.json",
|
|
||||||
"generated-artifacts/ERC20Proxy.json",
|
"generated-artifacts/ERC20Proxy.json",
|
||||||
"generated-artifacts/ERC721Proxy.json",
|
"generated-artifacts/ERC721Proxy.json",
|
||||||
|
"generated-artifacts/Eth2DaiBridge.json",
|
||||||
"generated-artifacts/IAssetData.json",
|
"generated-artifacts/IAssetData.json",
|
||||||
"generated-artifacts/IAssetProxy.json",
|
"generated-artifacts/IAssetProxy.json",
|
||||||
|
"generated-artifacts/IAssetProxyDispatcher.json",
|
||||||
"generated-artifacts/IAuthorizable.json",
|
"generated-artifacts/IAuthorizable.json",
|
||||||
|
"generated-artifacts/IERC20Bridge.json",
|
||||||
|
"generated-artifacts/IEth2Dai.json",
|
||||||
|
"generated-artifacts/IWallet.json",
|
||||||
|
"generated-artifacts/MixinAssetProxyDispatcher.json",
|
||||||
"generated-artifacts/MixinAuthorizable.json",
|
"generated-artifacts/MixinAuthorizable.json",
|
||||||
"generated-artifacts/MultiAssetProxy.json"
|
"generated-artifacts/MultiAssetProxy.json",
|
||||||
|
"generated-artifacts/Ownable.json",
|
||||||
|
"generated-artifacts/StaticCallProxy.json",
|
||||||
|
"generated-artifacts/TestERC20Bridge.json",
|
||||||
|
"generated-artifacts/TestEth2DaiBridge.json",
|
||||||
|
"generated-artifacts/TestStaticCallTarget.json"
|
||||||
],
|
],
|
||||||
"exclude": ["./deploy/solc/solc_bin"]
|
"exclude": ["./deploy/solc/solc_bin"]
|
||||||
}
|
}
|
||||||
|
|||||||
2
contracts/coordinator/.solhintignore
Normal file
2
contracts/coordinator/.solhintignore
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# solhint can't parse `abi.decode` syntax.
|
||||||
|
contracts/src/MixinCoordinatorApprovalVerifier.sol
|
||||||
211
contracts/coordinator/CHANGELOG.json
Normal file
211
contracts/coordinator/CHANGELOG.json
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"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",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1567521715,
|
||||||
|
"version": "2.0.12",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1566446343,
|
||||||
|
"version": "2.0.11",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1565296576,
|
||||||
|
"version": "2.0.10",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "2.0.9",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Updated calls to <contract wrapper>.deployFrom0xArtifactAsync to include artifact dependencies.",
|
||||||
|
"pr": 1995
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1564607468
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1563957393,
|
||||||
|
"version": "2.0.8",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1563193019,
|
||||||
|
"version": "2.0.7",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1563047529,
|
||||||
|
"version": "2.0.6",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1563006338,
|
||||||
|
"version": "2.0.5",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1558712885,
|
||||||
|
"version": "2.0.4",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1557961111,
|
||||||
|
"version": "2.0.3",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1557799313,
|
||||||
|
"version": "2.0.2",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1557507213,
|
||||||
|
"version": "2.0.1",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Make `decodeOrdersFromFillData`, `getCoordinatorApprovalHash`, and `getTransactionHash` public",
|
||||||
|
"pr": 1729
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Make `assertValidTransactionOrdersApproval` internal",
|
||||||
|
"pr": 1729
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1554997931
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.1.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Run Web3ProviderEngine without excess block polling",
|
||||||
|
"pr": 1695
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1553183790
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.0.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Created Coordinator package"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Use separate EIP712 domains for transactions and approvals",
|
||||||
|
"pr": 1705
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Add `SignatureType.Invalid`",
|
||||||
|
"pr": 1705
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Set `evmVersion` to `constantinople`",
|
||||||
|
"pr": 1707
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1553091633
|
||||||
|
}
|
||||||
|
]
|
||||||
87
contracts/coordinator/CHANGELOG.md
Normal file
87
contracts/coordinator/CHANGELOG.md
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
<!--
|
||||||
|
changelogUtils.file is auto-generated using the monorepo-scripts package. Don't edit directly.
|
||||||
|
Edit the package's CHANGELOG.json file only.
|
||||||
|
-->
|
||||||
|
|
||||||
|
CHANGELOG
|
||||||
|
|
||||||
|
## 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
|
||||||
|
|
||||||
|
## v2.0.12 - _September 3, 2019_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v2.0.11 - _August 22, 2019_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v2.0.10 - _August 8, 2019_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v2.0.9 - _July 31, 2019_
|
||||||
|
|
||||||
|
* Updated calls to <contract wrapper>.deployFrom0xArtifactAsync to include artifact dependencies. (#1995)
|
||||||
|
|
||||||
|
## v2.0.8 - _July 24, 2019_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v2.0.7 - _July 15, 2019_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v2.0.6 - _July 13, 2019_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v2.0.5 - _July 13, 2019_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v2.0.4 - _May 24, 2019_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v2.0.3 - _May 15, 2019_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v2.0.2 - _May 14, 2019_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v2.0.1 - _May 10, 2019_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v2.0.0 - _April 11, 2019_
|
||||||
|
|
||||||
|
* Make `decodeOrdersFromFillData`, `getCoordinatorApprovalHash`, and `getTransactionHash` public (#1729)
|
||||||
|
* Make `assertValidTransactionOrdersApproval` internal (#1729)
|
||||||
|
|
||||||
|
## v1.1.0 - _March 21, 2019_
|
||||||
|
|
||||||
|
* Run Web3ProviderEngine without excess block polling (#1695)
|
||||||
|
|
||||||
|
## v1.0.0 - _March 20, 2019_
|
||||||
|
|
||||||
|
* Created Coordinator package
|
||||||
|
* Use separate EIP712 domains for transactions and approvals (#1705)
|
||||||
|
* Add `SignatureType.Invalid` (#1705)
|
||||||
|
* Set `evmVersion` to `constantinople` (#1707)
|
||||||
1
contracts/coordinator/DEPLOYS.json
Normal file
1
contracts/coordinator/DEPLOYS.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
[]
|
||||||
73
contracts/coordinator/README.md
Normal file
73
contracts/coordinator/README.md
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
## Coordinator
|
||||||
|
|
||||||
|
This package contains a contract that allows users to call arbitrary functions on the Exchange contract with permission from one or more Coordinators. Addresses of the deployed contracts can be found in this 0x [guide](https://0x.org/docs/guides/0x-cheat-sheet) or the [DEPLOYS](./DEPLOYS.json) file within this package.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
**Install**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install @0x/contracts-coordinator --save
|
||||||
|
```
|
||||||
|
|
||||||
|
## Bug bounty
|
||||||
|
|
||||||
|
A bug bounty for the 2.0.0 contracts is ongoing! Instructions can be found [here](https://0x.org/docs/guides/bug-bounty-program).
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
We strongly recommend that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository.
|
||||||
|
|
||||||
|
For proposals regarding the 0x protocol's smart contract architecture, message format, or additional functionality, go to the [0x Improvement Proposals (ZEIPs)](https://github.com/0xProject/ZEIPs) repository and follow the contribution guidelines provided therein.
|
||||||
|
|
||||||
|
Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started.
|
||||||
|
|
||||||
|
### Install Dependencies
|
||||||
|
|
||||||
|
If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn config set workspaces-experimental true
|
||||||
|
```
|
||||||
|
|
||||||
|
Then install dependencies
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build
|
||||||
|
|
||||||
|
To build this package and all other monorepo packages that it depends on, run the following from the monorepo root directory:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
PKG=@0x/contracts-coordinator yarn build
|
||||||
|
```
|
||||||
|
|
||||||
|
Or continuously rebuild on change:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
PKG=@0x/contracts-coordinator yarn watch
|
||||||
|
```
|
||||||
|
|
||||||
|
### Clean
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn clean
|
||||||
|
```
|
||||||
|
|
||||||
|
### Lint
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn lint
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run Tests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn test
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Testing options
|
||||||
|
|
||||||
|
Contracts testing options like coverage, profiling, revert traces or backing node choosing - are described [here](../TESTING.md).
|
||||||
25
contracts/coordinator/compiler.json
Normal file
25
contracts/coordinator/compiler.json
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"artifactsDir": "./generated-artifacts",
|
||||||
|
"contractsDir": "./contracts",
|
||||||
|
"useDockerisedSolc": false,
|
||||||
|
"compilerSettings": {
|
||||||
|
"evmVersion": "constantinople",
|
||||||
|
"optimizer": {
|
||||||
|
"enabled": true,
|
||||||
|
"runs": 1000000,
|
||||||
|
"details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
|
||||||
|
},
|
||||||
|
"outputSelection": {
|
||||||
|
"*": {
|
||||||
|
"*": [
|
||||||
|
"abi",
|
||||||
|
"devdoc",
|
||||||
|
"evm.bytecode.object",
|
||||||
|
"evm.bytecode.sourceMap",
|
||||||
|
"evm.deployedBytecode.object",
|
||||||
|
"evm.deployedBytecode.sourceMap"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
45
contracts/coordinator/contracts/src/Coordinator.sol
Normal file
45
contracts/coordinator/contracts/src/Coordinator.sol
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.5.9;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibEIP712ExchangeDomain.sol";
|
||||||
|
import "./libs/LibConstants.sol";
|
||||||
|
import "./libs/LibEIP712CoordinatorDomain.sol";
|
||||||
|
import "./MixinSignatureValidator.sol";
|
||||||
|
import "./MixinCoordinatorApprovalVerifier.sol";
|
||||||
|
import "./MixinCoordinatorCore.sol";
|
||||||
|
|
||||||
|
|
||||||
|
// solhint-disable no-empty-blocks
|
||||||
|
contract Coordinator is
|
||||||
|
LibConstants,
|
||||||
|
MixinSignatureValidator,
|
||||||
|
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)
|
||||||
|
public
|
||||||
|
LibConstants(exchange)
|
||||||
|
LibEIP712CoordinatorDomain(chainId, address(0))
|
||||||
|
LibEIP712ExchangeDomain(chainId, exchange)
|
||||||
|
{}
|
||||||
|
}
|
||||||
@@ -0,0 +1,200 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.5.9;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol";
|
||||||
|
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||||
|
import "@0x/contracts-utils/contracts/src/LibAddressArray.sol";
|
||||||
|
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
||||||
|
import "./libs/LibCoordinatorApproval.sol";
|
||||||
|
import "./interfaces/ICoordinatorSignatureValidator.sol";
|
||||||
|
import "./interfaces/ICoordinatorApprovalVerifier.sol";
|
||||||
|
|
||||||
|
|
||||||
|
// solhint-disable avoid-tx-origin
|
||||||
|
contract MixinCoordinatorApprovalVerifier is
|
||||||
|
LibCoordinatorApproval,
|
||||||
|
LibZeroExTransaction,
|
||||||
|
ICoordinatorSignatureValidator,
|
||||||
|
ICoordinatorApprovalVerifier
|
||||||
|
{
|
||||||
|
using LibBytes for bytes;
|
||||||
|
using LibAddressArray for address[];
|
||||||
|
|
||||||
|
/// @dev Validates that the 0x transaction has been approved by all of 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 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
|
||||||
|
view
|
||||||
|
{
|
||||||
|
// Get the orders from the the Exchange calldata in the 0x transaction
|
||||||
|
LibOrder.Order[] memory orders = decodeOrdersFromFillData(transaction.data);
|
||||||
|
|
||||||
|
// No approval is required for non-fill methods
|
||||||
|
if (orders.length > 0) {
|
||||||
|
// Revert if approval is invalid for transaction orders
|
||||||
|
_assertValidTransactionOrdersApproval(
|
||||||
|
transaction,
|
||||||
|
orders,
|
||||||
|
txOrigin,
|
||||||
|
transactionSignature,
|
||||||
|
approvalExpirationTimeSeconds,
|
||||||
|
approvalSignatures
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Decodes the orders from Exchange calldata representing any fill method.
|
||||||
|
/// @param data Exchange calldata representing a fill method.
|
||||||
|
/// @return The orders from the Exchange calldata.
|
||||||
|
function decodeOrdersFromFillData(bytes memory data)
|
||||||
|
public
|
||||||
|
pure
|
||||||
|
returns (LibOrder.Order[] memory orders)
|
||||||
|
{
|
||||||
|
bytes4 selector = data.readBytes4(0);
|
||||||
|
if (
|
||||||
|
selector == IExchange(address(0)).fillOrder.selector ||
|
||||||
|
selector == IExchange(address(0)).fillOrderNoThrow.selector ||
|
||||||
|
selector == IExchange(address(0)).fillOrKillOrder.selector
|
||||||
|
) {
|
||||||
|
// Decode single order
|
||||||
|
(LibOrder.Order memory order) = abi.decode(
|
||||||
|
data.slice(4, data.length),
|
||||||
|
(LibOrder.Order)
|
||||||
|
);
|
||||||
|
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)).marketBuyOrders.selector ||
|
||||||
|
selector == IExchange(address(0)).marketSellOrders.selector
|
||||||
|
) {
|
||||||
|
// Decode all orders
|
||||||
|
// solhint-disable indent
|
||||||
|
(orders) = abi.decode(
|
||||||
|
data.slice(4, data.length),
|
||||||
|
(LibOrder.Order[])
|
||||||
|
);
|
||||||
|
} else if (selector == IExchange(address(0)).matchOrders.selector) {
|
||||||
|
// Decode left and right orders
|
||||||
|
(LibOrder.Order memory leftOrder, LibOrder.Order memory rightOrder) = abi.decode(
|
||||||
|
data.slice(4, data.length),
|
||||||
|
(LibOrder.Order, LibOrder.Order)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create array of orders
|
||||||
|
orders = new LibOrder.Order[](2);
|
||||||
|
orders[0] = leftOrder;
|
||||||
|
orders[1] = rightOrder;
|
||||||
|
}
|
||||||
|
return orders;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @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
|
||||||
|
{
|
||||||
|
// Verify that Ethereum tx signer is the same as the approved txOrigin
|
||||||
|
require(
|
||||||
|
tx.origin == txOrigin,
|
||||||
|
"INVALID_ORIGIN"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Hash 0x transaction
|
||||||
|
bytes32 transactionHash = getTransactionHash(transaction);
|
||||||
|
|
||||||
|
// Create empty list of approval signers
|
||||||
|
address[] memory approvalSignerAddresses = new address[](0);
|
||||||
|
|
||||||
|
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,
|
||||||
|
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]);
|
||||||
|
|
||||||
|
// Add approval signer to list of signers
|
||||||
|
approvalSignerAddresses = approvalSignerAddresses.append(approvalSignerAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ethereum transaction signer gives implicit signature of approval
|
||||||
|
approvalSignerAddresses = approvalSignerAddresses.append(tx.origin);
|
||||||
|
|
||||||
|
uint256 ordersLength = orders.length;
|
||||||
|
for (uint256 i = 0; i != ordersLength; i++) {
|
||||||
|
// Do not check approval if the order's senderAddress is null
|
||||||
|
if (orders[i].senderAddress == address(0)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure feeRecipient of order has approved this 0x transaction
|
||||||
|
address approverAddress = orders[i].feeRecipientAddress;
|
||||||
|
bool isOrderApproved = approvalSignerAddresses.contains(approverAddress);
|
||||||
|
require(
|
||||||
|
isOrderApproved,
|
||||||
|
"INVALID_APPROVAL_SIGNATURE"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
60
contracts/coordinator/contracts/src/MixinCoordinatorCore.sol
Normal file
60
contracts/coordinator/contracts/src/MixinCoordinatorCore.sol
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.5.9;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol";
|
||||||
|
import "./libs/LibConstants.sol";
|
||||||
|
import "./interfaces/ICoordinatorCore.sol";
|
||||||
|
import "./interfaces/ICoordinatorApprovalVerifier.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract MixinCoordinatorCore is
|
||||||
|
LibConstants,
|
||||||
|
ICoordinatorApprovalVerifier,
|
||||||
|
ICoordinatorCore
|
||||||
|
{
|
||||||
|
/// @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 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
|
||||||
|
{
|
||||||
|
// Validate that the 0x transaction has been approves by each feeRecipient
|
||||||
|
assertValidCoordinatorApprovals(
|
||||||
|
transaction,
|
||||||
|
txOrigin,
|
||||||
|
transactionSignature,
|
||||||
|
approvalExpirationTimeSeconds,
|
||||||
|
approvalSignatures
|
||||||
|
);
|
||||||
|
|
||||||
|
// Execute the transaction
|
||||||
|
EXCHANGE.executeTransaction(transaction, transactionSignature);
|
||||||
|
}
|
||||||
|
}
|
||||||
118
contracts/coordinator/contracts/src/MixinSignatureValidator.sol
Normal file
118
contracts/coordinator/contracts/src/MixinSignatureValidator.sol
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.5.9;
|
||||||
|
|
||||||
|
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||||
|
import "./interfaces/ICoordinatorSignatureValidator.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract MixinSignatureValidator is
|
||||||
|
ICoordinatorSignatureValidator
|
||||||
|
{
|
||||||
|
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.
|
||||||
|
function getSignerAddress(bytes32 hash, bytes memory signature)
|
||||||
|
public
|
||||||
|
pure
|
||||||
|
returns (address signerAddress)
|
||||||
|
{
|
||||||
|
require(
|
||||||
|
signature.length > 0,
|
||||||
|
"LENGTH_GREATER_THAN_0_REQUIRED"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Pop last byte off of signature byte array.
|
||||||
|
uint8 signatureTypeRaw = uint8(signature.popLastByte());
|
||||||
|
|
||||||
|
// Ensure signature is supported
|
||||||
|
require(
|
||||||
|
signatureTypeRaw < uint8(SignatureType.NSignatureTypes),
|
||||||
|
"SIGNATURE_UNSUPPORTED"
|
||||||
|
);
|
||||||
|
|
||||||
|
SignatureType signatureType = SignatureType(signatureTypeRaw);
|
||||||
|
|
||||||
|
// Always illegal signature.
|
||||||
|
// This is always an implicit option since a signer can create a
|
||||||
|
// signature array with invalid type or length. We may as well make
|
||||||
|
// it an explicit option. This aids testing and analysis. It is
|
||||||
|
// also the initialization value for the enum type.
|
||||||
|
if (signatureType == SignatureType.Illegal) {
|
||||||
|
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) {
|
||||||
|
require(
|
||||||
|
signature.length == 0,
|
||||||
|
"LENGTH_0_REQUIRED"
|
||||||
|
);
|
||||||
|
revert("SIGNATURE_INVALID");
|
||||||
|
|
||||||
|
// Signature using EIP712
|
||||||
|
} else if (signatureType == SignatureType.EIP712) {
|
||||||
|
require(
|
||||||
|
signature.length == 65,
|
||||||
|
"LENGTH_65_REQUIRED"
|
||||||
|
);
|
||||||
|
uint8 v = uint8(signature[0]);
|
||||||
|
bytes32 r = signature.readBytes32(1);
|
||||||
|
bytes32 s = signature.readBytes32(33);
|
||||||
|
signerAddress = ecrecover(
|
||||||
|
hash,
|
||||||
|
v,
|
||||||
|
r,
|
||||||
|
s
|
||||||
|
);
|
||||||
|
return signerAddress;
|
||||||
|
|
||||||
|
// Signed using web3.eth_sign
|
||||||
|
} else if (signatureType == SignatureType.EthSign) {
|
||||||
|
require(
|
||||||
|
signature.length == 65,
|
||||||
|
"LENGTH_65_REQUIRED"
|
||||||
|
);
|
||||||
|
uint8 v = uint8(signature[0]);
|
||||||
|
bytes32 r = signature.readBytes32(1);
|
||||||
|
bytes32 s = signature.readBytes32(33);
|
||||||
|
signerAddress = ecrecover(
|
||||||
|
keccak256(abi.encodePacked(
|
||||||
|
"\x19Ethereum Signed Message:\n32",
|
||||||
|
hash
|
||||||
|
)),
|
||||||
|
v,
|
||||||
|
r,
|
||||||
|
s
|
||||||
|
);
|
||||||
|
return signerAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Anything else is illegal (We do not return false because
|
||||||
|
// the signature may actually be valid, just not in a format
|
||||||
|
// that we currently support. In this case returning false
|
||||||
|
// may lead the caller to incorrectly believe that the
|
||||||
|
// signature was invalid.)
|
||||||
|
revert("SIGNATURE_UNSUPPORTED");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.5.9;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract ICoordinatorApprovalVerifier {
|
||||||
|
|
||||||
|
/// @dev Validates that the 0x transaction has been approved by all of 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 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
|
||||||
|
view;
|
||||||
|
|
||||||
|
/// @dev Decodes the orders from Exchange calldata representing any fill method.
|
||||||
|
/// @param data Exchange calldata representing a fill method.
|
||||||
|
/// @return The orders from the Exchange calldata.
|
||||||
|
function decodeOrdersFromFillData(bytes memory data)
|
||||||
|
public
|
||||||
|
pure
|
||||||
|
returns (LibOrder.Order[] memory orders);
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.5.9;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/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.
|
||||||
|
/// @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 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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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
|
||||||
|
OrderValidator, // 0x07
|
||||||
|
WalletOrderValidator, // 0x08
|
||||||
|
NSignatureTypes // 0x09, 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.
|
||||||
|
function getSignerAddress(bytes32 hash, bytes memory signature)
|
||||||
|
public
|
||||||
|
pure
|
||||||
|
returns (address signerAddress);
|
||||||
|
}
|
||||||
36
contracts/coordinator/contracts/src/libs/LibConstants.sol
Normal file
36
contracts/coordinator/contracts/src/libs/LibConstants.sol
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.5.9;
|
||||||
|
|
||||||
|
import "@0x/contracts-exchange/contracts/src/interfaces/ITransactions.sol";
|
||||||
|
|
||||||
|
|
||||||
|
// solhint-disable var-name-mixedcase
|
||||||
|
contract LibConstants {
|
||||||
|
|
||||||
|
// The 0x Exchange contract.
|
||||||
|
ITransactions internal EXCHANGE;
|
||||||
|
|
||||||
|
/// @param exchange Address of the 0x Exchange contract.
|
||||||
|
constructor (address exchange)
|
||||||
|
public
|
||||||
|
{
|
||||||
|
EXCHANGE = ITransactions(exchange);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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 "./LibEIP712CoordinatorDomain.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract LibCoordinatorApproval is
|
||||||
|
LibEIP712CoordinatorDomain
|
||||||
|
{
|
||||||
|
// Hash for the EIP712 Coordinator approval message
|
||||||
|
// keccak256(abi.encodePacked(
|
||||||
|
// "CoordinatorApproval(",
|
||||||
|
// "address txOrigin,",
|
||||||
|
// "bytes32 transactionHash,",
|
||||||
|
// "bytes transactionSignature,",
|
||||||
|
// "uint256 approvalExpirationTimeSeconds",
|
||||||
|
// ")"
|
||||||
|
// ));
|
||||||
|
bytes32 constant public 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 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));
|
||||||
|
return approvalHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @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)
|
||||||
|
{
|
||||||
|
bytes32 schemaHash = EIP712_COORDINATOR_APPROVAL_SCHEMA_HASH;
|
||||||
|
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(
|
||||||
|
// EIP712_COORDINATOR_APPROVAL_SCHEMA_HASH,
|
||||||
|
// approval.txOrigin,
|
||||||
|
// approval.transactionHash,
|
||||||
|
// keccak256(approval.transactionSignature)
|
||||||
|
// approval.approvalExpirationTimeSeconds,
|
||||||
|
// ));
|
||||||
|
|
||||||
|
assembly {
|
||||||
|
// Compute hash of transaction signature
|
||||||
|
let transactionSignatureHash := keccak256(add(transactionSignature, 32), mload(transactionSignature))
|
||||||
|
|
||||||
|
// Load free memory pointer
|
||||||
|
let memPtr := mload(64)
|
||||||
|
|
||||||
|
mstore(memPtr, schemaHash) // hash of schema
|
||||||
|
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, 160)
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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 is
|
||||||
|
LibEIP712
|
||||||
|
{
|
||||||
|
|
||||||
|
// 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 = "2.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 EIP712 hash applied to this EIP712 Domain.
|
||||||
|
function _hashEIP712CoordinatorMessage(bytes32 hashStruct)
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (bytes32 result)
|
||||||
|
{
|
||||||
|
return LibEIP712.hashEIP712Message(EIP712_COORDINATOR_DOMAIN_HASH, hashStruct);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.5.9;
|
||||||
|
|
||||||
|
import "./MixinCoordinatorRegistryCore.sol";
|
||||||
|
|
||||||
|
|
||||||
|
// solhint-disable no-empty-blocks
|
||||||
|
contract CoordinatorRegistry is
|
||||||
|
MixinCoordinatorRegistryCore
|
||||||
|
{
|
||||||
|
constructor ()
|
||||||
|
public
|
||||||
|
MixinCoordinatorRegistryCore()
|
||||||
|
{}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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 "./interfaces/ICoordinatorRegistryCore.sol";
|
||||||
|
|
||||||
|
|
||||||
|
// solhint-disable no-empty-blocks
|
||||||
|
contract MixinCoordinatorRegistryCore is
|
||||||
|
ICoordinatorRegistryCore
|
||||||
|
{
|
||||||
|
// mapping from `coordinatorOperator` -> `coordinatorEndpoint`
|
||||||
|
mapping (address => string) internal coordinatorEndpoints;
|
||||||
|
|
||||||
|
/// @dev Called by a Coordinator operator to set the endpoint of their Coordinator.
|
||||||
|
/// @param coordinatorEndpoint endpoint of the Coordinator.
|
||||||
|
function setCoordinatorEndpoint(string calldata coordinatorEndpoint) external {
|
||||||
|
address coordinatorOperator = msg.sender;
|
||||||
|
coordinatorEndpoints[coordinatorOperator] = coordinatorEndpoint;
|
||||||
|
emit CoordinatorEndpointSet(coordinatorOperator, coordinatorEndpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Gets the endpoint for a Coordinator.
|
||||||
|
/// @param coordinatorOperator operator of the Coordinator endpoint.
|
||||||
|
function getCoordinatorEndpoint(address coordinatorOperator)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (string memory coordinatorEndpoint)
|
||||||
|
{
|
||||||
|
return coordinatorEndpoints[coordinatorOperator];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.5.9;
|
||||||
|
|
||||||
|
|
||||||
|
// solhint-disable no-empty-blocks
|
||||||
|
contract ICoordinatorRegistryCore
|
||||||
|
{
|
||||||
|
/// @dev Emitted when a Coordinator endpoint is set.
|
||||||
|
event CoordinatorEndpointSet(
|
||||||
|
address coordinatorOperator,
|
||||||
|
string coordinatorEndpoint
|
||||||
|
);
|
||||||
|
|
||||||
|
/// @dev Called by a Coordinator operator to set the endpoint of their Coordinator.
|
||||||
|
/// @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.
|
||||||
|
function getCoordinatorEndpoint(address coordinatorOperator)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (string memory coordinatorEndpoint);
|
||||||
|
}
|
||||||
92
contracts/coordinator/package.json
Normal file
92
contracts/coordinator/package.json
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
{
|
||||||
|
"name": "@0x/contracts-coordinator",
|
||||||
|
"version": "2.1.0-beta.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.12"
|
||||||
|
},
|
||||||
|
"description": "Smart contract extensions of 0x protocol",
|
||||||
|
"main": "lib/src/index.js",
|
||||||
|
"directories": {
|
||||||
|
"test": "test"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "yarn pre_build && tsc -b",
|
||||||
|
"build:ci": "yarn build",
|
||||||
|
"pre_build": "run-s compile contracts:gen generate_contract_wrappers",
|
||||||
|
"test": "yarn run_mocha",
|
||||||
|
"rebuild_and_test": "run-s build test",
|
||||||
|
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
||||||
|
"test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html",
|
||||||
|
"test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha",
|
||||||
|
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
|
||||||
|
"compile": "sol-compiler",
|
||||||
|
"watch": "sol-compiler -w",
|
||||||
|
"clean": "shx rm -rf lib 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",
|
||||||
|
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol",
|
||||||
|
"compile:truffle": "truffle compile"
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"abis": "./generated-artifacts/@(Coordinator|CoordinatorRegistry).json",
|
||||||
|
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/0xProject/0x-monorepo.git"
|
||||||
|
},
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/0xProject/0x-monorepo/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
|
||||||
|
"devDependencies": {
|
||||||
|
"@0x/abi-gen": "^4.3.0-beta.0",
|
||||||
|
"@0x/contracts-gen": "^1.1.0-beta.0",
|
||||||
|
"@0x/contracts-test-utils": "^3.2.0-beta.0",
|
||||||
|
"@0x/dev-utils": "^2.4.0-beta.0",
|
||||||
|
"@0x/sol-compiler": "^3.2.0-beta.0",
|
||||||
|
"@0x/tslint-config": "^3.0.1",
|
||||||
|
"@types/lodash": "4.14.104",
|
||||||
|
"@types/mocha": "^5.2.7",
|
||||||
|
"@types/node": "*",
|
||||||
|
"chai": "^4.0.1",
|
||||||
|
"chai-as-promised": "^7.1.0",
|
||||||
|
"chai-bignumber": "^3.0.0",
|
||||||
|
"dirty-chai": "^2.0.1",
|
||||||
|
"make-promises-safe": "^1.1.0",
|
||||||
|
"mocha": "^6.2.0",
|
||||||
|
"npm-run-all": "^4.1.2",
|
||||||
|
"shx": "^0.2.2",
|
||||||
|
"solhint": "^1.4.1",
|
||||||
|
"truffle": "^5.0.32",
|
||||||
|
"tslint": "5.11.0",
|
||||||
|
"typescript": "3.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@0x/base-contract": "^5.5.0-beta.0",
|
||||||
|
"@0x/contracts-asset-proxy": "^2.3.0-beta.0",
|
||||||
|
"@0x/contracts-erc20": "^2.3.0-beta.0",
|
||||||
|
"@0x/contracts-exchange": "^2.2.0-beta.0",
|
||||||
|
"@0x/contracts-exchange-libs": "^3.1.0-beta.0",
|
||||||
|
"@0x/contracts-utils": "^3.3.0-beta.0",
|
||||||
|
"@0x/order-utils": "^8.5.0-beta.0",
|
||||||
|
"@0x/types": "^2.5.0-beta.0",
|
||||||
|
"@0x/typescript-typings": "^4.4.0-beta.0",
|
||||||
|
"@0x/utils": "^4.6.0-beta.0",
|
||||||
|
"@0x/web3-wrapper": "^6.1.0-beta.0",
|
||||||
|
"ethereum-types": "^2.2.0-beta.0",
|
||||||
|
"ethereumjs-util": "^5.1.1",
|
||||||
|
"lodash": "^4.17.11"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
}
|
||||||
|
}
|
||||||
13
contracts/coordinator/src/artifacts.ts
Normal file
13
contracts/coordinator/src/artifacts.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
/*
|
||||||
|
* -----------------------------------------------------------------------------
|
||||||
|
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||||
|
* -----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
import { ContractArtifact } from 'ethereum-types';
|
||||||
|
|
||||||
|
import * as Coordinator from '../generated-artifacts/Coordinator.json';
|
||||||
|
import * as CoordinatorRegistry from '../generated-artifacts/CoordinatorRegistry.json';
|
||||||
|
export const artifacts = {
|
||||||
|
Coordinator: Coordinator as ContractArtifact,
|
||||||
|
CoordinatorRegistry: CoordinatorRegistry as ContractArtifact,
|
||||||
|
};
|
||||||
3
contracts/coordinator/src/index.ts
Normal file
3
contracts/coordinator/src/index.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export * from './artifacts';
|
||||||
|
export * from './wrappers';
|
||||||
|
export * from '../test/utils';
|
||||||
7
contracts/coordinator/src/wrappers.ts
Normal file
7
contracts/coordinator/src/wrappers.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
/*
|
||||||
|
* -----------------------------------------------------------------------------
|
||||||
|
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||||
|
* -----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
export * from '../generated-wrappers/coordinator';
|
||||||
|
export * from '../generated-wrappers/coordinator_registry';
|
||||||
531
contracts/coordinator/test/coordinator.ts
Normal file
531
contracts/coordinator/test/coordinator.ts
Normal file
@@ -0,0 +1,531 @@
|
|||||||
|
import { ERC20ProxyContract, ERC20Wrapper } from '@0x/contracts-asset-proxy';
|
||||||
|
import { DummyERC20TokenContract } from '@0x/contracts-erc20';
|
||||||
|
import {
|
||||||
|
artifacts as exchangeArtifacts,
|
||||||
|
constants as exchangeConstants,
|
||||||
|
ExchangeCancelEventArgs,
|
||||||
|
ExchangeCancelUpToEventArgs,
|
||||||
|
ExchangeContract,
|
||||||
|
exchangeDataEncoder,
|
||||||
|
ExchangeFillEventArgs,
|
||||||
|
ExchangeFunctionName,
|
||||||
|
} from '@0x/contracts-exchange';
|
||||||
|
import {
|
||||||
|
chaiSetup,
|
||||||
|
constants,
|
||||||
|
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 } from '@0x/types';
|
||||||
|
import { BigNumber, providerUtils } from '@0x/utils';
|
||||||
|
import * as chai from 'chai';
|
||||||
|
import { LogWithDecodedArgs } from 'ethereum-types';
|
||||||
|
|
||||||
|
import { ApprovalFactory, artifacts, CoordinatorContract } 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 chainId: number;
|
||||||
|
let makerAddress: string;
|
||||||
|
let owner: string;
|
||||||
|
let takerAddress: string;
|
||||||
|
let feeRecipientAddress: string;
|
||||||
|
|
||||||
|
let erc20Proxy: ERC20ProxyContract;
|
||||||
|
let erc20TokenA: DummyERC20TokenContract;
|
||||||
|
let erc20TokenB: DummyERC20TokenContract;
|
||||||
|
let makerFeeToken: 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 () => {
|
||||||
|
chainId = await providerUtils.getChainIdAsync(provider);
|
||||||
|
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, makerFeeToken] = await erc20Wrapper.deployDummyTokensAsync(
|
||||||
|
numDummyErc20ToDeploy,
|
||||||
|
constants.DUMMY_TOKEN_DECIMALS,
|
||||||
|
);
|
||||||
|
await erc20Wrapper.setBalancesAndAllowancesAsync();
|
||||||
|
|
||||||
|
exchange = await ExchangeContract.deployFrom0xArtifactAsync(
|
||||||
|
exchangeArtifacts.Exchange,
|
||||||
|
provider,
|
||||||
|
txDefaults,
|
||||||
|
new BigNumber(chainId),
|
||||||
|
);
|
||||||
|
|
||||||
|
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||||
|
await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { from: owner }),
|
||||||
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
|
);
|
||||||
|
|
||||||
|
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||||
|
await exchange.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, { from: owner }),
|
||||||
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
|
);
|
||||||
|
|
||||||
|
coordinatorContract = await CoordinatorContract.deployFrom0xArtifactAsync(
|
||||||
|
artifacts.Coordinator,
|
||||||
|
provider,
|
||||||
|
txDefaults,
|
||||||
|
artifacts,
|
||||||
|
exchange.address,
|
||||||
|
new BigNumber(chainId),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Configure order defaults
|
||||||
|
const defaultOrderParams = {
|
||||||
|
...constants.STATIC_ORDER_PARAMS,
|
||||||
|
senderAddress: coordinatorContract.address,
|
||||||
|
makerAddress,
|
||||||
|
feeRecipientAddress,
|
||||||
|
makerAssetData: assetDataUtils.encodeERC20AssetData(erc20TokenA.address),
|
||||||
|
takerAssetData: assetDataUtils.encodeERC20AssetData(erc20TokenB.address),
|
||||||
|
makerFeeAssetData: assetDataUtils.encodeERC20AssetData(makerFeeToken.address),
|
||||||
|
takerFeeAssetData: assetDataUtils.encodeERC20AssetData(makerFeeToken.address),
|
||||||
|
exchangeAddress: exchange.address,
|
||||||
|
chainId,
|
||||||
|
};
|
||||||
|
const makerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)];
|
||||||
|
const takerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(takerAddress)];
|
||||||
|
const feeRecipientPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(feeRecipientAddress)];
|
||||||
|
orderFactory = new OrderFactory(makerPrivateKey, defaultOrderParams);
|
||||||
|
makerTransactionFactory = new TransactionFactory(makerPrivateKey, exchange.address, chainId);
|
||||||
|
takerTransactionFactory = new TransactionFactory(takerPrivateKey, exchange.address, chainId);
|
||||||
|
approvalFactory = new ApprovalFactory(feeRecipientPrivateKey, coordinatorContract.address);
|
||||||
|
});
|
||||||
|
beforeEach(async () => {
|
||||||
|
await blockchainLifecycle.startAsync();
|
||||||
|
});
|
||||||
|
afterEach(async () => {
|
||||||
|
await blockchainLifecycle.revertAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('single order fills', () => {
|
||||||
|
for (const fnName of exchangeConstants.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 = await takerTransactionFactory.newSignedTransactionAsync({ 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 },
|
||||||
|
),
|
||||||
|
constants.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 = await takerTransactionFactory.newSignedTransactionAsync({ data });
|
||||||
|
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||||
|
await coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||||
|
transaction,
|
||||||
|
feeRecipientAddress,
|
||||||
|
transaction.signature,
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
{ from: feeRecipientAddress },
|
||||||
|
),
|
||||||
|
constants.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 = await takerTransactionFactory.newSignedTransactionAsync({ data });
|
||||||
|
await expectTransactionFailedAsync(
|
||||||
|
coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||||
|
transaction,
|
||||||
|
takerAddress,
|
||||||
|
transaction.signature,
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
{
|
||||||
|
from: takerAddress,
|
||||||
|
gas: constants.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 = await takerTransactionFactory.newSignedTransactionAsync({ 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 = await takerTransactionFactory.newSignedTransactionAsync({ 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 = await takerTransactionFactory.newSignedTransactionAsync({ 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 [...exchangeConstants.MARKET_FILL_FN_NAMES, ...exchangeConstants.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 = await takerTransactionFactory.newSignedTransactionAsync({ 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: constants.MAX_EXECUTE_TRANSACTION_GAS },
|
||||||
|
),
|
||||||
|
constants.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 = await takerTransactionFactory.newSignedTransactionAsync({ data });
|
||||||
|
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||||
|
await coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||||
|
transaction,
|
||||||
|
feeRecipientAddress,
|
||||||
|
transaction.signature,
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
{ from: feeRecipientAddress, gas: constants.MAX_EXECUTE_TRANSACTION_GAS },
|
||||||
|
),
|
||||||
|
constants.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 = await takerTransactionFactory.newSignedTransactionAsync({ 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 = await takerTransactionFactory.newSignedTransactionAsync({ 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 = await takerTransactionFactory.newSignedTransactionAsync({ 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(ExchangeFunctionName.CancelOrder, orders);
|
||||||
|
const transaction = await makerTransactionFactory.newSignedTransactionAsync({ 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(ExchangeFunctionName.BatchCancelOrders, orders);
|
||||||
|
const transaction = await makerTransactionFactory.newSignedTransactionAsync({ 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 targetEpoch = constants.ZERO_AMOUNT;
|
||||||
|
const data = exchange.cancelOrdersUpTo.getABIEncodedTransactionData(targetEpoch);
|
||||||
|
const transaction = await makerTransactionFactory.newSignedTransactionAsync({ 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.orderSenderAddress).to.eq(coordinatorContract.address);
|
||||||
|
expect(cancelLogArgs.orderEpoch).to.bignumber.eq(targetEpoch.plus(1));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// tslint:disable:max-file-line-count
|
||||||
81
contracts/coordinator/test/coordinator_registry.ts
Normal file
81
contracts/coordinator/test/coordinator_registry.ts
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
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 { CoordinatorRegistryCoordinatorEndpointSetEventArgs } from '../src';
|
||||||
|
|
||||||
|
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
|
||||||
|
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 web3Wrapper.getAvailableAddressesAsync();
|
||||||
|
[, coordinatorOperator] = accounts;
|
||||||
|
// deploy coordinator registry
|
||||||
|
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 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 coordinatorRegistryWrapper.setCoordinatorEndpointAsync(coordinatorOperator, coordinatorEndpoint);
|
||||||
|
let recordedCoordinatorEndpoint = await coordinatorRegistryWrapper.getCoordinatorEndpointAsync(
|
||||||
|
coordinatorOperator,
|
||||||
|
);
|
||||||
|
expect(recordedCoordinatorEndpoint).to.be.equal(coordinatorEndpoint);
|
||||||
|
// unset Coordinator endpoint
|
||||||
|
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 coordinatorRegistryWrapper.setCoordinatorEndpointAsync(
|
||||||
|
coordinatorOperator,
|
||||||
|
coordinatorEndpoint,
|
||||||
|
);
|
||||||
|
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,9 +1,12 @@
|
|||||||
import { env, EnvVars } from '@0x/dev-utils';
|
import { env, EnvVars } from '@0x/dev-utils';
|
||||||
|
|
||||||
import { coverage } from './utils/coverage';
|
import { coverage, profiler, provider } from '@0x/contracts-test-utils';
|
||||||
import { profiler } from './utils/profiler';
|
import { providerUtils } from '@0x/utils';
|
||||||
|
|
||||||
after('generate coverage || profiler report', async () => {
|
before('start web3 provider', () => {
|
||||||
|
providerUtils.startProviderEngine(provider);
|
||||||
|
});
|
||||||
|
after('generate coverage report', async () => {
|
||||||
if (env.parseBoolean(EnvVars.SolidityCoverage)) {
|
if (env.parseBoolean(EnvVars.SolidityCoverage)) {
|
||||||
const coverageSubprovider = coverage.getCoverageSubproviderSingleton();
|
const coverageSubprovider = coverage.getCoverageSubproviderSingleton();
|
||||||
await coverageSubprovider.writeCoverageAsync();
|
await coverageSubprovider.writeCoverageAsync();
|
||||||
@@ -12,4 +15,5 @@ after('generate coverage || profiler report', async () => {
|
|||||||
const profilerSubprovider = profiler.getProfilerSubproviderSingleton();
|
const profilerSubprovider = profiler.getProfilerSubproviderSingleton();
|
||||||
await profilerSubprovider.writeProfilerOutputAsync();
|
await profilerSubprovider.writeProfilerOutputAsync();
|
||||||
}
|
}
|
||||||
|
provider.stop();
|
||||||
});
|
});
|
||||||
91
contracts/coordinator/test/libs.ts
Normal file
91
contracts/coordinator/test/libs.ts
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
import { chaiSetup, constants, provider, randomAddress, txDefaults, web3Wrapper } from '@0x/contracts-test-utils';
|
||||||
|
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||||
|
import { transactionHashUtils } from '@0x/order-utils';
|
||||||
|
import { BigNumber, providerUtils } from '@0x/utils';
|
||||||
|
import * as chai from 'chai';
|
||||||
|
|
||||||
|
import { artifacts, CoordinatorContract, hashUtils } from '../src';
|
||||||
|
|
||||||
|
chaiSetup.configure();
|
||||||
|
const expect = chai.expect;
|
||||||
|
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||||
|
|
||||||
|
describe('Libs tests', () => {
|
||||||
|
let coordinatorContract: CoordinatorContract;
|
||||||
|
let chainId: number;
|
||||||
|
const exchangeAddress = randomAddress();
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
await blockchainLifecycle.startAsync();
|
||||||
|
});
|
||||||
|
after(async () => {
|
||||||
|
await blockchainLifecycle.revertAsync();
|
||||||
|
});
|
||||||
|
before(async () => {
|
||||||
|
chainId = await providerUtils.getChainIdAsync(provider);
|
||||||
|
coordinatorContract = await CoordinatorContract.deployFrom0xArtifactAsync(
|
||||||
|
artifacts.Coordinator,
|
||||||
|
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 = {
|
||||||
|
salt: new BigNumber(0),
|
||||||
|
expirationTimeSeconds: new BigNumber(0),
|
||||||
|
signerAddress: constants.NULL_ADDRESS,
|
||||||
|
data: '0x1234',
|
||||||
|
domain: {
|
||||||
|
verifyingContract: exchangeAddress,
|
||||||
|
chainId,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
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: new BigNumber(0),
|
||||||
|
expirationTimeSeconds: 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 = hashUtils.getApprovalHashHex(
|
||||||
|
signedTx,
|
||||||
|
coordinatorContract.address,
|
||||||
|
txOrigin,
|
||||||
|
approvalExpirationTimeSeconds,
|
||||||
|
);
|
||||||
|
const approvalHash = await coordinatorContract.getCoordinatorApprovalHash.callAsync(approval);
|
||||||
|
expect(expectedApprovalHash).to.eq(approvalHash);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
740
contracts/coordinator/test/mixins.ts
Normal file
740
contracts/coordinator/test/mixins.ts
Normal file
@@ -0,0 +1,740 @@
|
|||||||
|
import { constants as exchangeConstants, exchangeDataEncoder, ExchangeFunctionName } from '@0x/contracts-exchange';
|
||||||
|
import {
|
||||||
|
chaiSetup,
|
||||||
|
constants,
|
||||||
|
expectContractCallFailedAsync,
|
||||||
|
getLatestBlockTimestampAsync,
|
||||||
|
provider,
|
||||||
|
randomAddress,
|
||||||
|
TransactionFactory,
|
||||||
|
txDefaults,
|
||||||
|
web3Wrapper,
|
||||||
|
} from '@0x/contracts-test-utils';
|
||||||
|
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||||
|
import { transactionHashUtils } from '@0x/order-utils';
|
||||||
|
import { RevertReason, SignatureType, SignedOrder } from '@0x/types';
|
||||||
|
import { BigNumber, LibBytesRevertErrors, providerUtils } from '@0x/utils';
|
||||||
|
import * as chai from 'chai';
|
||||||
|
import * as ethUtil from 'ethereumjs-util';
|
||||||
|
|
||||||
|
import { ApprovalFactory, artifacts, CoordinatorContract } from '../src';
|
||||||
|
|
||||||
|
chaiSetup.configure();
|
||||||
|
const expect = chai.expect;
|
||||||
|
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||||
|
|
||||||
|
describe('Mixins tests', () => {
|
||||||
|
let chainId: number;
|
||||||
|
let transactionSignerAddress: string;
|
||||||
|
let approvalSignerAddress1: string;
|
||||||
|
let approvalSignerAddress2: string;
|
||||||
|
let mixins: CoordinatorContract;
|
||||||
|
let transactionFactory: TransactionFactory;
|
||||||
|
let approvalFactory1: ApprovalFactory;
|
||||||
|
let approvalFactory2: ApprovalFactory;
|
||||||
|
let defaultOrder: SignedOrder;
|
||||||
|
const exchangeAddress = randomAddress();
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
await blockchainLifecycle.startAsync();
|
||||||
|
});
|
||||||
|
after(async () => {
|
||||||
|
await blockchainLifecycle.revertAsync();
|
||||||
|
});
|
||||||
|
before(async () => {
|
||||||
|
chainId = await providerUtils.getChainIdAsync(provider);
|
||||||
|
mixins = await CoordinatorContract.deployFrom0xArtifactAsync(
|
||||||
|
artifacts.Coordinator,
|
||||||
|
provider,
|
||||||
|
txDefaults,
|
||||||
|
artifacts,
|
||||||
|
exchangeAddress,
|
||||||
|
new BigNumber(chainId),
|
||||||
|
);
|
||||||
|
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
||||||
|
[transactionSignerAddress, approvalSignerAddress1, approvalSignerAddress2] = accounts.slice(0, 3);
|
||||||
|
defaultOrder = {
|
||||||
|
makerAddress: constants.NULL_ADDRESS,
|
||||||
|
takerAddress: constants.NULL_ADDRESS,
|
||||||
|
senderAddress: mixins.address,
|
||||||
|
feeRecipientAddress: approvalSignerAddress1,
|
||||||
|
makerAssetData: constants.NULL_BYTES,
|
||||||
|
takerAssetData: constants.NULL_BYTES,
|
||||||
|
makerAssetAmount: constants.ZERO_AMOUNT,
|
||||||
|
takerAssetAmount: constants.ZERO_AMOUNT,
|
||||||
|
makerFee: constants.ZERO_AMOUNT,
|
||||||
|
takerFee: constants.ZERO_AMOUNT,
|
||||||
|
makerFeeAssetData: constants.NULL_BYTES,
|
||||||
|
takerFeeAssetData: constants.NULL_BYTES,
|
||||||
|
expirationTimeSeconds: constants.ZERO_AMOUNT,
|
||||||
|
salt: constants.ZERO_AMOUNT,
|
||||||
|
signature: constants.NULL_BYTES,
|
||||||
|
exchangeAddress: constants.NULL_ADDRESS,
|
||||||
|
chainId,
|
||||||
|
};
|
||||||
|
const transactionSignerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(transactionSignerAddress)];
|
||||||
|
const approvalSignerPrivateKey1 = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(approvalSignerAddress1)];
|
||||||
|
const approvalSignerPrivateKey2 = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(approvalSignerAddress2)];
|
||||||
|
transactionFactory = new TransactionFactory(transactionSignerPrivateKey, exchangeAddress, chainId);
|
||||||
|
approvalFactory1 = new ApprovalFactory(approvalSignerPrivateKey1, mixins.address);
|
||||||
|
approvalFactory2 = new ApprovalFactory(approvalSignerPrivateKey2, mixins.address);
|
||||||
|
});
|
||||||
|
beforeEach(async () => {
|
||||||
|
await blockchainLifecycle.startAsync();
|
||||||
|
});
|
||||||
|
afterEach(async () => {
|
||||||
|
await blockchainLifecycle.revertAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getSignerAddress', () => {
|
||||||
|
it('should return the correct address using the EthSign signature type', async () => {
|
||||||
|
const data = constants.NULL_BYTES;
|
||||||
|
const transaction = await transactionFactory.newSignedTransactionAsync({ data }, SignatureType.EthSign);
|
||||||
|
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||||
|
const signerAddress = await mixins.getSignerAddress.callAsync(transactionHash, transaction.signature);
|
||||||
|
expect(transaction.signerAddress).to.eq(signerAddress);
|
||||||
|
});
|
||||||
|
it('should return the correct address using the EIP712 signature type', async () => {
|
||||||
|
const data = constants.NULL_BYTES;
|
||||||
|
const transaction = await transactionFactory.newSignedTransactionAsync({ data }, SignatureType.EIP712);
|
||||||
|
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||||
|
const signerAddress = await mixins.getSignerAddress.callAsync(transactionHash, transaction.signature);
|
||||||
|
expect(transaction.signerAddress).to.eq(signerAddress);
|
||||||
|
});
|
||||||
|
it('should revert with with the Illegal signature type', async () => {
|
||||||
|
const data = constants.NULL_BYTES;
|
||||||
|
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||||
|
const illegalSignatureByte = ethUtil.toBuffer(SignatureType.Illegal).toString('hex');
|
||||||
|
transaction.signature = `${transaction.signature.slice(
|
||||||
|
0,
|
||||||
|
transaction.signature.length - 2,
|
||||||
|
)}${illegalSignatureByte}`;
|
||||||
|
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||||
|
expect(mixins.getSignerAddress.callAsync(transactionHash, transaction.signature)).to.be.rejectedWith(
|
||||||
|
RevertReason.SignatureIllegal,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('should revert with with the Invalid signature type', async () => {
|
||||||
|
const data = constants.NULL_BYTES;
|
||||||
|
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||||
|
const invalidSignatureByte = ethUtil.toBuffer(SignatureType.Invalid).toString('hex');
|
||||||
|
transaction.signature = `0x${invalidSignatureByte}`;
|
||||||
|
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||||
|
expect(mixins.getSignerAddress.callAsync(transactionHash, transaction.signature)).to.be.rejectedWith(
|
||||||
|
RevertReason.SignatureInvalid,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it("should revert with with a signature type that doesn't exist", async () => {
|
||||||
|
const data = constants.NULL_BYTES;
|
||||||
|
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||||
|
const invalidSignatureByte = '04';
|
||||||
|
transaction.signature = `${transaction.signature.slice(
|
||||||
|
0,
|
||||||
|
transaction.signature.length - 2,
|
||||||
|
)}${invalidSignatureByte}`;
|
||||||
|
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||||
|
expect(mixins.getSignerAddress.callAsync(transactionHash, transaction.signature)).to.be.rejectedWith(
|
||||||
|
RevertReason.SignatureUnsupported,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('decodeOrdersFromFillData', () => {
|
||||||
|
for (const fnName of exchangeConstants.SINGLE_FILL_FN_NAMES) {
|
||||||
|
it(`should correctly decode the orders for ${fnName} data`, async () => {
|
||||||
|
const orders = [defaultOrder];
|
||||||
|
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||||
|
const decodedOrders = await mixins.decodeOrdersFromFillData.callAsync(data);
|
||||||
|
const decodedSignedOrders = decodedOrders.map(order => ({
|
||||||
|
...order,
|
||||||
|
signature: constants.NULL_BYTES,
|
||||||
|
exchangeAddress: constants.NULL_ADDRESS,
|
||||||
|
chainId,
|
||||||
|
}));
|
||||||
|
expect(orders).to.deep.eq(decodedSignedOrders);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
for (const fnName of exchangeConstants.BATCH_FILL_FN_NAMES) {
|
||||||
|
it(`should correctly decode the orders for ${fnName} data`, async () => {
|
||||||
|
const orders = [defaultOrder, defaultOrder];
|
||||||
|
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||||
|
const decodedOrders = await mixins.decodeOrdersFromFillData.callAsync(data);
|
||||||
|
const decodedSignedOrders = decodedOrders.map(order => ({
|
||||||
|
...order,
|
||||||
|
signature: constants.NULL_BYTES,
|
||||||
|
exchangeAddress: constants.NULL_ADDRESS,
|
||||||
|
chainId,
|
||||||
|
}));
|
||||||
|
expect(orders).to.deep.eq(decodedSignedOrders);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
for (const fnName of exchangeConstants.MARKET_FILL_FN_NAMES) {
|
||||||
|
it(`should correctly decode the orders for ${fnName} data`, async () => {
|
||||||
|
const orders = [defaultOrder, defaultOrder];
|
||||||
|
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||||
|
const decodedOrders = await mixins.decodeOrdersFromFillData.callAsync(data);
|
||||||
|
const decodedSignedOrders = decodedOrders.map(order => ({
|
||||||
|
...order,
|
||||||
|
signature: constants.NULL_BYTES,
|
||||||
|
exchangeAddress: constants.NULL_ADDRESS,
|
||||||
|
chainId,
|
||||||
|
}));
|
||||||
|
expect(orders).to.deep.eq(decodedSignedOrders);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
for (const fnName of [
|
||||||
|
ExchangeFunctionName.CancelOrder,
|
||||||
|
ExchangeFunctionName.BatchCancelOrders,
|
||||||
|
ExchangeFunctionName.CancelOrdersUpTo,
|
||||||
|
]) {
|
||||||
|
it(`should correctly decode the orders for ${fnName} data`, async () => {
|
||||||
|
const orders = [defaultOrder, defaultOrder];
|
||||||
|
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||||
|
const decodedOrders = await mixins.decodeOrdersFromFillData.callAsync(data);
|
||||||
|
const emptyArray: any[] = [];
|
||||||
|
expect(emptyArray).to.deep.eq(decodedOrders);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
it('should decode an empty array for invalid data', async () => {
|
||||||
|
const data = '0x0123456789';
|
||||||
|
const decodedOrders = await mixins.decodeOrdersFromFillData.callAsync(data);
|
||||||
|
const emptyArray: any[] = [];
|
||||||
|
expect(emptyArray).to.deep.eq(decodedOrders);
|
||||||
|
});
|
||||||
|
it('should revert if data is less than 4 bytes long', async () => {
|
||||||
|
const data = '0x010203';
|
||||||
|
const expectedError = new LibBytesRevertErrors.InvalidByteOperationError(
|
||||||
|
LibBytesRevertErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsFourRequired,
|
||||||
|
new BigNumber(3), // the length of data
|
||||||
|
new BigNumber(4),
|
||||||
|
);
|
||||||
|
return expect(mixins.decodeOrdersFromFillData.callAsync(data)).to.revertWith(expectedError);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Single order approvals', () => {
|
||||||
|
for (const fnName of exchangeConstants.SINGLE_FILL_FN_NAMES) {
|
||||||
|
it(`Should be successful: function=${fnName}, caller=tx_signer, senderAddress=[verifier], approval_sig=[approver1], expiration=[valid]`, async () => {
|
||||||
|
const orders = [defaultOrder];
|
||||||
|
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||||
|
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||||
|
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||||
|
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||||
|
const approval = approvalFactory1.newSignedApproval(
|
||||||
|
transaction,
|
||||||
|
transactionSignerAddress,
|
||||||
|
approvalExpirationTimeSeconds,
|
||||||
|
);
|
||||||
|
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||||
|
transaction,
|
||||||
|
transactionSignerAddress,
|
||||||
|
transaction.signature,
|
||||||
|
[approvalExpirationTimeSeconds],
|
||||||
|
[approval.signature],
|
||||||
|
{ from: transactionSignerAddress },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it(`Should be successful: function=${fnName}, caller=tx_signer, senderAddress=[null], approval_sig=[approver1], expiration=[valid]`, async () => {
|
||||||
|
const order = {
|
||||||
|
...defaultOrder,
|
||||||
|
senderAddress: constants.NULL_ADDRESS,
|
||||||
|
};
|
||||||
|
const orders = [order];
|
||||||
|
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||||
|
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||||
|
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||||
|
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||||
|
const approval = approvalFactory1.newSignedApproval(
|
||||||
|
transaction,
|
||||||
|
transactionSignerAddress,
|
||||||
|
approvalExpirationTimeSeconds,
|
||||||
|
);
|
||||||
|
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||||
|
transaction,
|
||||||
|
transactionSignerAddress,
|
||||||
|
transaction.signature,
|
||||||
|
[approvalExpirationTimeSeconds],
|
||||||
|
[approval.signature],
|
||||||
|
{ from: transactionSignerAddress },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it(`Should be successful: function=${fnName}, caller=approver1, senderAddress=[verifier], approval_sig=[], expiration=[]`, async () => {
|
||||||
|
const orders = [defaultOrder];
|
||||||
|
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||||
|
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||||
|
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||||
|
transaction,
|
||||||
|
approvalSignerAddress1,
|
||||||
|
transaction.signature,
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
{
|
||||||
|
from: approvalSignerAddress1,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it(`Should be successful: function=${fnName}, caller=approver1, senderAddress=[verifier], approval_sig=[approver1], expiration=[invalid]`, async () => {
|
||||||
|
const orders = [defaultOrder];
|
||||||
|
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||||
|
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||||
|
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||||
|
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||||
|
const approval = approvalFactory1.newSignedApproval(
|
||||||
|
transaction,
|
||||||
|
transactionSignerAddress,
|
||||||
|
approvalExpirationTimeSeconds,
|
||||||
|
);
|
||||||
|
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||||
|
transaction,
|
||||||
|
approvalSignerAddress1,
|
||||||
|
transaction.signature,
|
||||||
|
[approvalExpirationTimeSeconds],
|
||||||
|
[approval.signature],
|
||||||
|
{ from: approvalSignerAddress1 },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it(`Should be successful: function=${fnName}, caller=approver1, senderAddress=[verifier], approval_sig=[], expiration=[]`, async () => {
|
||||||
|
const orders = [defaultOrder];
|
||||||
|
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||||
|
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||||
|
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||||
|
transaction,
|
||||||
|
approvalSignerAddress1,
|
||||||
|
transaction.signature,
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
{
|
||||||
|
from: approvalSignerAddress1,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it(`Should revert: function=${fnName}, caller=tx_signer, senderAddress=[verifier], approval_sig=[invalid], expiration=[valid]`, async () => {
|
||||||
|
const orders = [defaultOrder];
|
||||||
|
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||||
|
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||||
|
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||||
|
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||||
|
const approval = approvalFactory1.newSignedApproval(
|
||||||
|
transaction,
|
||||||
|
transactionSignerAddress,
|
||||||
|
approvalExpirationTimeSeconds,
|
||||||
|
);
|
||||||
|
const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`;
|
||||||
|
expectContractCallFailedAsync(
|
||||||
|
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||||
|
transaction,
|
||||||
|
transactionSignerAddress,
|
||||||
|
transaction.signature,
|
||||||
|
[approvalExpirationTimeSeconds],
|
||||||
|
[signature],
|
||||||
|
{ from: transactionSignerAddress },
|
||||||
|
),
|
||||||
|
RevertReason.InvalidApprovalSignature,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it(`Should revert: function=${fnName}, caller=tx_signer, senderAddress=[verifier], approval_sig=[approver1], expiration=[invalid]`, async () => {
|
||||||
|
const orders = [defaultOrder];
|
||||||
|
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||||
|
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||||
|
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||||
|
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER);
|
||||||
|
const approval = approvalFactory1.newSignedApproval(
|
||||||
|
transaction,
|
||||||
|
transactionSignerAddress,
|
||||||
|
approvalExpirationTimeSeconds,
|
||||||
|
);
|
||||||
|
expectContractCallFailedAsync(
|
||||||
|
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||||
|
transaction,
|
||||||
|
transactionSignerAddress,
|
||||||
|
transaction.signature,
|
||||||
|
[approvalExpirationTimeSeconds],
|
||||||
|
[approval.signature],
|
||||||
|
{ from: transactionSignerAddress },
|
||||||
|
),
|
||||||
|
RevertReason.ApprovalExpired,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it(`Should revert: function=${fnName}, caller=approver2, senderAddress=[verifier], approval_sig=[approver1], expiration=[valid]`, async () => {
|
||||||
|
const orders = [defaultOrder];
|
||||||
|
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||||
|
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||||
|
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||||
|
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||||
|
const approval = approvalFactory1.newSignedApproval(
|
||||||
|
transaction,
|
||||||
|
transactionSignerAddress,
|
||||||
|
approvalExpirationTimeSeconds,
|
||||||
|
);
|
||||||
|
expectContractCallFailedAsync(
|
||||||
|
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||||
|
transaction,
|
||||||
|
transactionSignerAddress,
|
||||||
|
transaction.signature,
|
||||||
|
[approvalExpirationTimeSeconds],
|
||||||
|
[approval.signature],
|
||||||
|
{ from: approvalSignerAddress2 },
|
||||||
|
),
|
||||||
|
RevertReason.InvalidOrigin,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
describe('Batch order approvals', () => {
|
||||||
|
for (const fnName of [
|
||||||
|
...exchangeConstants.BATCH_FILL_FN_NAMES,
|
||||||
|
...exchangeConstants.MARKET_FILL_FN_NAMES,
|
||||||
|
ExchangeFunctionName.MatchOrders,
|
||||||
|
]) {
|
||||||
|
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver1], approval_sig=[approver1], expiration=[valid]`, async () => {
|
||||||
|
const orders = [defaultOrder, defaultOrder];
|
||||||
|
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||||
|
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||||
|
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||||
|
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||||
|
const approval = approvalFactory1.newSignedApproval(
|
||||||
|
transaction,
|
||||||
|
transactionSignerAddress,
|
||||||
|
approvalExpirationTimeSeconds,
|
||||||
|
);
|
||||||
|
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||||
|
transaction,
|
||||||
|
transactionSignerAddress,
|
||||||
|
transaction.signature,
|
||||||
|
[approvalExpirationTimeSeconds],
|
||||||
|
[approval.signature],
|
||||||
|
{ from: transactionSignerAddress },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[null,null], feeRecipient=[approver1,approver1], approval_sig=[approver1], expiration=[valid]`, async () => {
|
||||||
|
const orders = [defaultOrder, defaultOrder].map(order => ({
|
||||||
|
...order,
|
||||||
|
senderAddress: constants.NULL_ADDRESS,
|
||||||
|
}));
|
||||||
|
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||||
|
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||||
|
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||||
|
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||||
|
const approval = approvalFactory1.newSignedApproval(
|
||||||
|
transaction,
|
||||||
|
transactionSignerAddress,
|
||||||
|
approvalExpirationTimeSeconds,
|
||||||
|
);
|
||||||
|
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||||
|
transaction,
|
||||||
|
transactionSignerAddress,
|
||||||
|
transaction.signature,
|
||||||
|
[approvalExpirationTimeSeconds],
|
||||||
|
[approval.signature],
|
||||||
|
{ from: transactionSignerAddress },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[null,null], feeRecipient=[approver1,approver1], approval_sig=[], expiration=[]`, async () => {
|
||||||
|
const orders = [defaultOrder, defaultOrder].map(order => ({
|
||||||
|
...order,
|
||||||
|
senderAddress: constants.NULL_ADDRESS,
|
||||||
|
}));
|
||||||
|
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||||
|
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||||
|
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||||
|
transaction,
|
||||||
|
transactionSignerAddress,
|
||||||
|
transaction.signature,
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
{ from: transactionSignerAddress },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[verifier,null], feeRecipient=[approver1,approver1], approval_sig=[approver1], expiration=[valid]`, async () => {
|
||||||
|
const orders = [defaultOrder, { ...defaultOrder, senderAddress: constants.NULL_ADDRESS }];
|
||||||
|
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||||
|
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||||
|
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||||
|
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||||
|
const approval = approvalFactory1.newSignedApproval(
|
||||||
|
transaction,
|
||||||
|
transactionSignerAddress,
|
||||||
|
approvalExpirationTimeSeconds,
|
||||||
|
);
|
||||||
|
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||||
|
transaction,
|
||||||
|
transactionSignerAddress,
|
||||||
|
transaction.signature,
|
||||||
|
[approvalExpirationTimeSeconds],
|
||||||
|
[approval.signature],
|
||||||
|
{ from: transactionSignerAddress },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver2], approval_sig=[approver1,approver2], expiration=[valid,valid]`, async () => {
|
||||||
|
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
|
||||||
|
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||||
|
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||||
|
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||||
|
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||||
|
const approval1 = approvalFactory1.newSignedApproval(
|
||||||
|
transaction,
|
||||||
|
transactionSignerAddress,
|
||||||
|
approvalExpirationTimeSeconds,
|
||||||
|
);
|
||||||
|
const approval2 = approvalFactory2.newSignedApproval(
|
||||||
|
transaction,
|
||||||
|
transactionSignerAddress,
|
||||||
|
approvalExpirationTimeSeconds,
|
||||||
|
);
|
||||||
|
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||||
|
transaction,
|
||||||
|
transactionSignerAddress,
|
||||||
|
transaction.signature,
|
||||||
|
[approvalExpirationTimeSeconds, approvalExpirationTimeSeconds],
|
||||||
|
[approval1.signature, approval2.signature],
|
||||||
|
{ from: transactionSignerAddress },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it(`Should be successful: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver1], approval_sig=[], expiration=[]`, async () => {
|
||||||
|
const orders = [defaultOrder, defaultOrder];
|
||||||
|
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||||
|
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||||
|
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||||
|
transaction,
|
||||||
|
approvalSignerAddress1,
|
||||||
|
transaction.signature,
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
{ from: approvalSignerAddress1 },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it(`Should revert: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver2], approval_sig=[approver2], expiration=[valid]`, async () => {
|
||||||
|
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
|
||||||
|
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||||
|
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||||
|
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||||
|
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||||
|
const approval2 = approvalFactory2.newSignedApproval(
|
||||||
|
transaction,
|
||||||
|
transactionSignerAddress,
|
||||||
|
approvalExpirationTimeSeconds,
|
||||||
|
);
|
||||||
|
expectContractCallFailedAsync(
|
||||||
|
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||||
|
transaction,
|
||||||
|
transactionSignerAddress,
|
||||||
|
transaction.signature,
|
||||||
|
[approvalExpirationTimeSeconds],
|
||||||
|
[approval2.signature],
|
||||||
|
{ from: approvalSignerAddress1 },
|
||||||
|
),
|
||||||
|
RevertReason.InvalidOrigin,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver1], approval_sig=[], expiration=[]`, async () => {
|
||||||
|
const orders = [defaultOrder, defaultOrder];
|
||||||
|
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||||
|
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||||
|
expectContractCallFailedAsync(
|
||||||
|
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||||
|
transaction,
|
||||||
|
transactionSignerAddress,
|
||||||
|
transaction.signature,
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
{ from: transactionSignerAddress },
|
||||||
|
),
|
||||||
|
RevertReason.InvalidApprovalSignature,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver1], approval_sig=[invalid], expiration=[valid]`, async () => {
|
||||||
|
const orders = [defaultOrder, defaultOrder];
|
||||||
|
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||||
|
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||||
|
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||||
|
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||||
|
const approval = approvalFactory1.newSignedApproval(
|
||||||
|
transaction,
|
||||||
|
transactionSignerAddress,
|
||||||
|
approvalExpirationTimeSeconds,
|
||||||
|
);
|
||||||
|
const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`;
|
||||||
|
expectContractCallFailedAsync(
|
||||||
|
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||||
|
transaction,
|
||||||
|
transactionSignerAddress,
|
||||||
|
transaction.signature,
|
||||||
|
[approvalExpirationTimeSeconds],
|
||||||
|
[signature],
|
||||||
|
{ from: transactionSignerAddress },
|
||||||
|
),
|
||||||
|
RevertReason.InvalidApprovalSignature,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[valid,invalid], expiration=[valid,valid]`, async () => {
|
||||||
|
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
|
||||||
|
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||||
|
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||||
|
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||||
|
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||||
|
const approval1 = approvalFactory1.newSignedApproval(
|
||||||
|
transaction,
|
||||||
|
transactionSignerAddress,
|
||||||
|
approvalExpirationTimeSeconds,
|
||||||
|
);
|
||||||
|
const approval2 = approvalFactory2.newSignedApproval(
|
||||||
|
transaction,
|
||||||
|
transactionSignerAddress,
|
||||||
|
approvalExpirationTimeSeconds,
|
||||||
|
);
|
||||||
|
const approvalSignature2 = `${approval2.signature.slice(0, 4)}FFFFFFFF${approval2.signature.slice(12)}`;
|
||||||
|
expectContractCallFailedAsync(
|
||||||
|
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||||
|
transaction,
|
||||||
|
transactionSignerAddress,
|
||||||
|
transaction.signature,
|
||||||
|
[approvalExpirationTimeSeconds, approvalExpirationTimeSeconds],
|
||||||
|
[approval1.signature, approvalSignature2],
|
||||||
|
{ from: transactionSignerAddress },
|
||||||
|
),
|
||||||
|
RevertReason.InvalidApprovalSignature,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it(`Should revert: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[invalid], expiration=[valid]`, async () => {
|
||||||
|
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
|
||||||
|
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||||
|
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||||
|
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||||
|
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||||
|
const approval2 = approvalFactory2.newSignedApproval(
|
||||||
|
transaction,
|
||||||
|
transactionSignerAddress,
|
||||||
|
approvalExpirationTimeSeconds,
|
||||||
|
);
|
||||||
|
const approvalSignature2 = `${approval2.signature.slice(0, 4)}FFFFFFFF${approval2.signature.slice(12)}`;
|
||||||
|
expectContractCallFailedAsync(
|
||||||
|
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||||
|
transaction,
|
||||||
|
approvalSignerAddress1,
|
||||||
|
transaction.signature,
|
||||||
|
[approvalExpirationTimeSeconds],
|
||||||
|
[approvalSignature2],
|
||||||
|
{ from: approvalSignerAddress1 },
|
||||||
|
),
|
||||||
|
RevertReason.InvalidApprovalSignature,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[valid,valid], expiration=[valid,invalid]`, async () => {
|
||||||
|
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
|
||||||
|
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||||
|
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||||
|
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||||
|
const approvalExpirationTimeSeconds1 = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||||
|
const approvalExpirationTimeSeconds2 = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER);
|
||||||
|
const approval1 = approvalFactory1.newSignedApproval(
|
||||||
|
transaction,
|
||||||
|
transactionSignerAddress,
|
||||||
|
approvalExpirationTimeSeconds1,
|
||||||
|
);
|
||||||
|
const approval2 = approvalFactory2.newSignedApproval(
|
||||||
|
transaction,
|
||||||
|
transactionSignerAddress,
|
||||||
|
approvalExpirationTimeSeconds2,
|
||||||
|
);
|
||||||
|
expectContractCallFailedAsync(
|
||||||
|
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||||
|
transaction,
|
||||||
|
transactionSignerAddress,
|
||||||
|
transaction.signature,
|
||||||
|
[approvalExpirationTimeSeconds1, approvalExpirationTimeSeconds2],
|
||||||
|
[approval1.signature, approval2.signature],
|
||||||
|
{ from: transactionSignerAddress },
|
||||||
|
),
|
||||||
|
RevertReason.ApprovalExpired,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it(`Should revert: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[valid], expiration=[invalid]`, async () => {
|
||||||
|
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
|
||||||
|
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||||
|
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||||
|
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||||
|
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER);
|
||||||
|
const approval2 = approvalFactory2.newSignedApproval(
|
||||||
|
transaction,
|
||||||
|
transactionSignerAddress,
|
||||||
|
approvalExpirationTimeSeconds,
|
||||||
|
);
|
||||||
|
expectContractCallFailedAsync(
|
||||||
|
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||||
|
transaction,
|
||||||
|
approvalSignerAddress1,
|
||||||
|
transaction.signature,
|
||||||
|
[approvalExpirationTimeSeconds],
|
||||||
|
[approval2.signature],
|
||||||
|
{ from: approvalSignerAddress1 },
|
||||||
|
),
|
||||||
|
RevertReason.ApprovalExpired,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it(`Should revert: function=${fnName} caller=approver2, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver1], approval_sig=[valid], expiration=[valid]`, async () => {
|
||||||
|
const orders = [defaultOrder, defaultOrder];
|
||||||
|
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||||
|
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||||
|
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||||
|
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||||
|
const approval1 = approvalFactory1.newSignedApproval(
|
||||||
|
transaction,
|
||||||
|
transactionSignerAddress,
|
||||||
|
approvalExpirationTimeSeconds,
|
||||||
|
);
|
||||||
|
expectContractCallFailedAsync(
|
||||||
|
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||||
|
transaction,
|
||||||
|
transactionSignerAddress,
|
||||||
|
transaction.signature,
|
||||||
|
[approvalExpirationTimeSeconds],
|
||||||
|
[approval1.signature],
|
||||||
|
{ from: approvalSignerAddress2 },
|
||||||
|
),
|
||||||
|
RevertReason.InvalidOrigin,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
describe('cancels', () => {
|
||||||
|
it('should allow the tx signer to call `cancelOrder` without approval', async () => {
|
||||||
|
const orders = [defaultOrder];
|
||||||
|
const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.CancelOrder, orders);
|
||||||
|
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||||
|
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||||
|
transaction,
|
||||||
|
transactionSignerAddress,
|
||||||
|
transaction.signature,
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
{ from: transactionSignerAddress },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('should allow the tx signer to call `batchCancelOrders` without approval', async () => {
|
||||||
|
const orders = [defaultOrder, defaultOrder];
|
||||||
|
const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.BatchCancelOrders, orders);
|
||||||
|
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||||
|
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||||
|
transaction,
|
||||||
|
transactionSignerAddress,
|
||||||
|
transaction.signature,
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
{ from: transactionSignerAddress },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('should allow the tx signer to call `cancelOrdersUpTo` without approval', async () => {
|
||||||
|
const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.CancelOrdersUpTo);
|
||||||
|
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||||
|
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||||
|
transaction,
|
||||||
|
transactionSignerAddress,
|
||||||
|
transaction.signature,
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
{ from: transactionSignerAddress },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// tslint:disable:max-file-line-count
|
||||||
38
contracts/coordinator/test/utils/approval_factory.ts
Normal file
38
contracts/coordinator/test/utils/approval_factory.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
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, SignedCoordinatorApproval } from './index';
|
||||||
|
|
||||||
|
export class ApprovalFactory {
|
||||||
|
private readonly _privateKey: Buffer;
|
||||||
|
private readonly _verifyingContractAddress: string;
|
||||||
|
|
||||||
|
constructor(privateKey: Buffer, verifyingContract: string) {
|
||||||
|
this._privateKey = privateKey;
|
||||||
|
this._verifyingContractAddress = verifyingContract;
|
||||||
|
}
|
||||||
|
|
||||||
|
public newSignedApproval(
|
||||||
|
transaction: SignedZeroExTransaction,
|
||||||
|
txOrigin: string,
|
||||||
|
approvalExpirationTimeSeconds: BigNumber,
|
||||||
|
signatureType: SignatureType = SignatureType.EthSign,
|
||||||
|
): SignedCoordinatorApproval {
|
||||||
|
const approvalHashBuff = hashUtils.getApprovalHashBuffer(
|
||||||
|
transaction,
|
||||||
|
this._verifyingContractAddress,
|
||||||
|
txOrigin,
|
||||||
|
approvalExpirationTimeSeconds,
|
||||||
|
);
|
||||||
|
const signatureBuff = signingUtils.signMessage(approvalHashBuff, this._privateKey, signatureType);
|
||||||
|
const signedApproval = {
|
||||||
|
txOrigin,
|
||||||
|
transaction,
|
||||||
|
approvalExpirationTimeSeconds,
|
||||||
|
signature: ethUtil.addHexPrefix(signatureBuff.toString('hex')),
|
||||||
|
};
|
||||||
|
return signedApproval;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
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,
|
||||||
|
verifyingContract: string,
|
||||||
|
txOrigin: string,
|
||||||
|
approvalExpirationTimeSeconds: BigNumber,
|
||||||
|
): Buffer {
|
||||||
|
const typedData = eip712Utils.createCoordinatorApprovalTypedData(
|
||||||
|
transaction,
|
||||||
|
verifyingContract,
|
||||||
|
txOrigin,
|
||||||
|
approvalExpirationTimeSeconds,
|
||||||
|
);
|
||||||
|
const hashBuffer = signTypedDataUtils.generateTypedDataHash(typedData);
|
||||||
|
return hashBuffer;
|
||||||
|
},
|
||||||
|
getApprovalHashHex(
|
||||||
|
transaction: SignedZeroExTransaction,
|
||||||
|
verifyingContract: string,
|
||||||
|
txOrigin: string,
|
||||||
|
approvalExpirationTimeSeconds: BigNumber,
|
||||||
|
): string {
|
||||||
|
const hashHex = `0x${hashUtils
|
||||||
|
.getApprovalHashBuffer(transaction, verifyingContract, txOrigin, approvalExpirationTimeSeconds)
|
||||||
|
.toString('hex')}`;
|
||||||
|
return hashHex;
|
||||||
|
},
|
||||||
|
};
|
||||||
3
contracts/coordinator/test/utils/index.ts
Normal file
3
contracts/coordinator/test/utils/index.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export { hashUtils } from './hash_utils';
|
||||||
|
export { ApprovalFactory } from './approval_factory';
|
||||||
|
export * from './types';
|
||||||
12
contracts/coordinator/test/utils/types.ts
Normal file
12
contracts/coordinator/test/utils/types.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { SignedZeroExTransaction } from '@0x/types';
|
||||||
|
import { BigNumber } from '@0x/utils';
|
||||||
|
|
||||||
|
export interface CoordinatorApproval {
|
||||||
|
transaction: SignedZeroExTransaction;
|
||||||
|
txOrigin: string;
|
||||||
|
approvalExpirationTimeSeconds: BigNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SignedCoordinatorApproval extends CoordinatorApproval {
|
||||||
|
signature: string;
|
||||||
|
}
|
||||||
96
contracts/coordinator/truffle-config.js
Normal file
96
contracts/coordinator/truffle-config.js
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
/**
|
||||||
|
* Use this file to configure your truffle project. It's seeded with some
|
||||||
|
* common settings for different networks and features like migrations,
|
||||||
|
* compilation and testing. Uncomment the ones you need or modify
|
||||||
|
* them to suit your project as necessary.
|
||||||
|
*
|
||||||
|
* More information about configuration can be found at:
|
||||||
|
*
|
||||||
|
* truffleframework.com/docs/advanced/configuration
|
||||||
|
*
|
||||||
|
* To deploy via Infura you'll need a wallet provider (like truffle-hdwallet-provider)
|
||||||
|
* to sign your transactions before they're sent to a remote public node. Infura accounts
|
||||||
|
* are available for free at: infura.io/register.
|
||||||
|
*
|
||||||
|
* You'll also need a mnemonic - the twelve word phrase the wallet uses to generate
|
||||||
|
* public/private key pairs. If you're publishing your code to GitHub make sure you load this
|
||||||
|
* phrase from a file you've .gitignored so it doesn't accidentally become public.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// const HDWalletProvider = require('truffle-hdwallet-provider');
|
||||||
|
// const infuraKey = "fj4jll3k.....";
|
||||||
|
//
|
||||||
|
// const fs = require('fs');
|
||||||
|
// const mnemonic = fs.readFileSync(".secret").toString().trim();
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
/**
|
||||||
|
* Networks define how you connect to your ethereum client and let you set the
|
||||||
|
* defaults web3 uses to send transactions. If you don't specify one truffle
|
||||||
|
* will spin up a development blockchain for you on port 9545 when you
|
||||||
|
* run `develop` or `test`. You can ask a truffle command to use a specific
|
||||||
|
* network from the command line, e.g
|
||||||
|
*
|
||||||
|
* $ truffle test --network <network-name>
|
||||||
|
*/
|
||||||
|
|
||||||
|
networks: {
|
||||||
|
// Useful for testing. The `development` name is special - truffle uses it by default
|
||||||
|
// if it's defined here and no other network is specified at the command line.
|
||||||
|
// You should run a client (like ganache-cli, geth or parity) in a separate terminal
|
||||||
|
// tab if you use this network and you must also set the `host`, `port` and `network_id`
|
||||||
|
// options below to some value.
|
||||||
|
//
|
||||||
|
// development: {
|
||||||
|
// host: "127.0.0.1", // Localhost (default: none)
|
||||||
|
// port: 8545, // Standard Ethereum port (default: none)
|
||||||
|
// network_id: "*", // Any network (default: none)
|
||||||
|
// },
|
||||||
|
// Another network with more advanced options...
|
||||||
|
// advanced: {
|
||||||
|
// port: 8777, // Custom port
|
||||||
|
// network_id: 1342, // Custom network
|
||||||
|
// gas: 8500000, // Gas sent with each transaction (default: ~6700000)
|
||||||
|
// gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei)
|
||||||
|
// from: <address>, // Account to send txs from (default: accounts[0])
|
||||||
|
// websockets: true // Enable EventEmitter interface for web3 (default: false)
|
||||||
|
// },
|
||||||
|
// Useful for deploying to a public network.
|
||||||
|
// NB: It's important to wrap the provider as a function.
|
||||||
|
// ropsten: {
|
||||||
|
// provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/v3/YOUR-PROJECT-ID`),
|
||||||
|
// network_id: 3, // Ropsten's id
|
||||||
|
// gas: 5500000, // Ropsten has a lower block limit than mainnet
|
||||||
|
// confirmations: 2, // # of confs to wait between deployments. (default: 0)
|
||||||
|
// timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50)
|
||||||
|
// skipDryRun: true // Skip dry run before migrations? (default: false for public nets )
|
||||||
|
// },
|
||||||
|
// Useful for private networks
|
||||||
|
// private: {
|
||||||
|
// provider: () => new HDWalletProvider(mnemonic, `https://network.io`),
|
||||||
|
// network_id: 2111, // This network is yours, in the cloud.
|
||||||
|
// production: true // Treats this network as if it was a public net. (default: false)
|
||||||
|
// }
|
||||||
|
},
|
||||||
|
|
||||||
|
// Set default mocha options here, use special reporters etc.
|
||||||
|
mocha: {
|
||||||
|
// timeout: 100000
|
||||||
|
},
|
||||||
|
|
||||||
|
// Configure your compilers
|
||||||
|
compilers: {
|
||||||
|
solc: {
|
||||||
|
version: '0.5.9',
|
||||||
|
settings: {
|
||||||
|
evmVersion: 'constantinople',
|
||||||
|
optimizer: {
|
||||||
|
enabled: true,
|
||||||
|
runs: 1000000,
|
||||||
|
details: { yul: true, deduplicate: true, cse: true, constantOptimizer: true },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
7
contracts/coordinator/tsconfig.json
Normal file
7
contracts/coordinator/tsconfig.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig",
|
||||||
|
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
|
||||||
|
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
|
||||||
|
"files": ["generated-artifacts/Coordinator.json", "generated-artifacts/CoordinatorRegistry.json"],
|
||||||
|
"exclude": ["./deploy/solc/solc_bin"]
|
||||||
|
}
|
||||||
6
contracts/coordinator/tslint.json
Normal file
6
contracts/coordinator/tslint.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"extends": ["@0x/tslint-config"],
|
||||||
|
"rules": {
|
||||||
|
"custom-no-magic-numbers": false
|
||||||
|
}
|
||||||
|
}
|
||||||
152
contracts/dev-utils/CHANGELOG.json
Normal file
152
contracts/dev-utils/CHANGELOG.json
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"version": "0.1.0-beta.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Use built in selectors instead of hard coded constants",
|
||||||
|
"pr": 2055
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Compile and export all contracts, artifacts, and wrappers by default",
|
||||||
|
"pr": 2055
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Add `marketBuy/SellOrdersNoThrow` and `marketBuy/SellOrdersFillOrKill` to `LibTransactionDecoder`.",
|
||||||
|
"pr": 2075
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "`run_mocha` package script runs with `UNLIMITED_CONTRACT_SIZE=true` environment variable.",
|
||||||
|
"pr": 2075
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1570135330
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1568744790,
|
||||||
|
"version": "0.0.10",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1567521715,
|
||||||
|
"version": "0.0.9",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1566446343,
|
||||||
|
"version": "0.0.8",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1565296576,
|
||||||
|
"version": "0.0.7",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1564607468,
|
||||||
|
"version": "0.0.6",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "0.0.5",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Updated calls to <contract wrapper>.deployFrom0xArtifactAsync to include artifact dependencies.",
|
||||||
|
"pr": 1995
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1564604963
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1563957393,
|
||||||
|
"version": "0.0.5",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1563193019,
|
||||||
|
"version": "0.0.4",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1563047529,
|
||||||
|
"version": "0.0.3",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1563006338,
|
||||||
|
"version": "0.0.2",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "0.0.1",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Create dev-utils package",
|
||||||
|
"pr": 1848
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Add `LibAssetData` and `LibTransactionDecoder` contracts",
|
||||||
|
"pr": 1848
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Refactor `LibAssetData` to only check 0x-specific allowances",
|
||||||
|
"pr": 1848
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Refactor `LibAssetData` balance/allowance checks to never revert",
|
||||||
|
"pr": 1848
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Refactor `OrderValidationUtils` to calculate `fillableTakerAssetAmount`",
|
||||||
|
"pr": 1848
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Add support for StaticCallProxy",
|
||||||
|
"pr": 1863
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Add `OrderTransferSimulationUtils` contract for simulating order transfers on-chain",
|
||||||
|
"pr": 1868
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Updated to use the new rich error pattern from @0x/contracts-exchange",
|
||||||
|
"pr": 1913
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
64
contracts/dev-utils/CHANGELOG.md
Normal file
64
contracts/dev-utils/CHANGELOG.md
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
<!--
|
||||||
|
changelogUtils.file is auto-generated using the monorepo-scripts package. Don't edit directly.
|
||||||
|
Edit the package's CHANGELOG.json file only.
|
||||||
|
-->
|
||||||
|
|
||||||
|
CHANGELOG
|
||||||
|
|
||||||
|
## v0.1.0-beta.0 - _October 3, 2019_
|
||||||
|
|
||||||
|
* Use built in selectors instead of hard coded constants (#2055)
|
||||||
|
* Compile and export all contracts, artifacts, and wrappers by default (#2055)
|
||||||
|
* Add `marketBuy/SellOrdersNoThrow` and `marketBuy/SellOrdersFillOrKill` to `LibTransactionDecoder`. (#2075)
|
||||||
|
* `run_mocha` package script runs with `UNLIMITED_CONTRACT_SIZE=true` environment variable. (#2075)
|
||||||
|
|
||||||
|
## v0.0.10 - _September 17, 2019_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v0.0.9 - _September 3, 2019_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v0.0.8 - _August 22, 2019_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v0.0.7 - _August 8, 2019_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v0.0.6 - _July 31, 2019_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v0.0.5 - _July 31, 2019_
|
||||||
|
|
||||||
|
* Updated calls to <contract wrapper>.deployFrom0xArtifactAsync to include artifact dependencies. (#1995)
|
||||||
|
|
||||||
|
## v0.0.5 - _July 24, 2019_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v0.0.4 - _July 15, 2019_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v0.0.3 - _July 13, 2019_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v0.0.2 - _July 13, 2019_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v0.0.1 - _Invalid date_
|
||||||
|
|
||||||
|
* Create dev-utils package (#1848)
|
||||||
|
* Add `LibAssetData` and `LibTransactionDecoder` contracts (#1848)
|
||||||
|
* Refactor `LibAssetData` to only check 0x-specific allowances (#1848)
|
||||||
|
* Refactor `LibAssetData` balance/allowance checks to never revert (#1848)
|
||||||
|
* Refactor `OrderValidationUtils` to calculate `fillableTakerAssetAmount` (#1848)
|
||||||
|
* Add support for StaticCallProxy (#1863)
|
||||||
|
* Add `OrderTransferSimulationUtils` contract for simulating order transfers on-chain (#1868)
|
||||||
|
* Updated to use the new rich error pattern from @0x/contracts-exchange (#1913)
|
||||||
1
contracts/dev-utils/DEPLOYS.json
Normal file
1
contracts/dev-utils/DEPLOYS.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
[]
|
||||||
73
contracts/dev-utils/README.md
Normal file
73
contracts/dev-utils/README.md
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
## Dev-Utils
|
||||||
|
|
||||||
|
This package implements various utilities for developers. For example, the `DevUtils` contract can query batches of balances or allowances given some `assetData`, can validate batches of orders, and can decode 0x-specific calldata. Addresses of the deployed contracts can be found in this 0x [guide](https://0x.org/docs/guides/0x-cheat-sheet) or the [DEPLOYS](./DEPLOYS.json) file within this package.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
**Install**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install @0x/contracts-dev-utils --save
|
||||||
|
```
|
||||||
|
|
||||||
|
## Bug bounty
|
||||||
|
|
||||||
|
A bug bounty for the 2.0.0 contracts is ongoing! Instructions can be found [here](https://0x.org/docs/guides/bug-bounty-program).
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
We strongly recommend that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository.
|
||||||
|
|
||||||
|
For proposals regarding the 0x protocol's smart contract architecture, message format, or additional functionality, go to the [0x Improvement Proposals (ZEIPs)](https://github.com/0xProject/ZEIPs) repository and follow the contribution guidelines provided therein.
|
||||||
|
|
||||||
|
Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started.
|
||||||
|
|
||||||
|
### Install Dependencies
|
||||||
|
|
||||||
|
If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn config set workspaces-experimental true
|
||||||
|
```
|
||||||
|
|
||||||
|
Then install dependencies
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build
|
||||||
|
|
||||||
|
To build this package and all other monorepo packages that it depends on, run the following from the monorepo root directory:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
PKG=@0x/contracts-extensions yarn build
|
||||||
|
```
|
||||||
|
|
||||||
|
Or continuously rebuild on change:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
PKG=@0x/contracts-extensions yarn watch
|
||||||
|
```
|
||||||
|
|
||||||
|
### Clean
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn clean
|
||||||
|
```
|
||||||
|
|
||||||
|
### Lint
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn lint
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run Tests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn test
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Testing options
|
||||||
|
|
||||||
|
Contracts testing options like coverage, profiling, revert traces or backing node choosing - are described [here](../TESTING.md).
|
||||||
26
contracts/dev-utils/compiler.json
Normal file
26
contracts/dev-utils/compiler.json
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"artifactsDir": "./generated-artifacts",
|
||||||
|
"contractsDir": "./contracts",
|
||||||
|
"useDockerisedSolc": false,
|
||||||
|
"isOfflineMode": false,
|
||||||
|
"compilerSettings": {
|
||||||
|
"evmVersion": "constantinople",
|
||||||
|
"optimizer": {
|
||||||
|
"enabled": true,
|
||||||
|
"runs": 10000,
|
||||||
|
"details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
|
||||||
|
},
|
||||||
|
"outputSelection": {
|
||||||
|
"*": {
|
||||||
|
"*": [
|
||||||
|
"abi",
|
||||||
|
"devdoc",
|
||||||
|
"evm.bytecode.object",
|
||||||
|
"evm.bytecode.sourceMap",
|
||||||
|
"evm.deployedBytecode.object",
|
||||||
|
"evm.deployedBytecode.sourceMap"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
40
contracts/dev-utils/contracts/src/DevUtils.sol
Normal file
40
contracts/dev-utils/contracts/src/DevUtils.sol
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.5.5;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "./OrderValidationUtils.sol";
|
||||||
|
import "./OrderTransferSimulationUtils.sol";
|
||||||
|
import "./LibTransactionDecoder.sol";
|
||||||
|
import "./EthBalanceChecker.sol";
|
||||||
|
|
||||||
|
|
||||||
|
// solhint-disable no-empty-blocks
|
||||||
|
contract DevUtils is
|
||||||
|
OrderValidationUtils,
|
||||||
|
LibTransactionDecoder,
|
||||||
|
EthBalanceChecker,
|
||||||
|
OrderTransferSimulationUtils
|
||||||
|
{
|
||||||
|
constructor (address _exchange)
|
||||||
|
public
|
||||||
|
OrderValidationUtils(_exchange)
|
||||||
|
OrderTransferSimulationUtils(_exchange)
|
||||||
|
{}
|
||||||
|
}
|
||||||
39
contracts/dev-utils/contracts/src/EthBalanceChecker.sol
Normal file
39
contracts/dev-utils/contracts/src/EthBalanceChecker.sol
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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 EthBalanceChecker {
|
||||||
|
|
||||||
|
/// @dev Batch fetches ETH balances
|
||||||
|
/// @param addresses Array of addresses.
|
||||||
|
/// @return Array of ETH balances.
|
||||||
|
function getEthBalances(address[] memory addresses)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (uint256[] memory)
|
||||||
|
{
|
||||||
|
uint256[] memory balances = new uint256[](addresses.length);
|
||||||
|
for (uint256 i = 0; i != addresses.length; i++) {
|
||||||
|
balances[i] = addresses[i].balance;
|
||||||
|
}
|
||||||
|
return balances;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
518
contracts/dev-utils/contracts/src/LibAssetData.sol
Normal file
518
contracts/dev-utils/contracts/src/LibAssetData.sol
Normal file
@@ -0,0 +1,518 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||||
|
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
||||||
|
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetData.sol";
|
||||||
|
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetProxy.sol";
|
||||||
|
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||||
|
import "@0x/contracts-erc721/contracts/src/interfaces/IERC721Token.sol";
|
||||||
|
import "@0x/contracts-erc1155/contracts/src/interfaces/IERC1155.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract LibAssetData {
|
||||||
|
|
||||||
|
// 2^256 - 1
|
||||||
|
uint256 constant internal _MAX_UINT256 = uint256(-1);
|
||||||
|
|
||||||
|
using LibBytes for bytes;
|
||||||
|
|
||||||
|
// solhint-disable var-name-mixedcase
|
||||||
|
IExchange internal _EXCHANGE;
|
||||||
|
address internal _ERC20_PROXY_ADDRESS;
|
||||||
|
address internal _ERC721_PROXY_ADDRESS;
|
||||||
|
address internal _ERC1155_PROXY_ADDRESS;
|
||||||
|
address internal _STATIC_CALL_PROXY_ADDRESS;
|
||||||
|
// solhint-enable var-name-mixedcase
|
||||||
|
|
||||||
|
constructor (address _exchange)
|
||||||
|
public
|
||||||
|
{
|
||||||
|
_EXCHANGE = IExchange(_exchange);
|
||||||
|
_ERC20_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(IAssetData(address(0)).ERC20Token.selector);
|
||||||
|
_ERC721_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(IAssetData(address(0)).ERC721Token.selector);
|
||||||
|
_ERC1155_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(IAssetData(address(0)).ERC1155Assets.selector);
|
||||||
|
_STATIC_CALL_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(IAssetData(address(0)).StaticCall.selector);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Returns the owner's balance of the assets(s) specified in
|
||||||
|
/// assetData. When the asset data contains multiple assets (eg in
|
||||||
|
/// ERC1155 or Multi-Asset), the return value indicates how many
|
||||||
|
/// complete "baskets" of those assets are owned by owner.
|
||||||
|
/// @param ownerAddress Owner of the assets specified by assetData.
|
||||||
|
/// @param assetData Details of asset, encoded per the AssetProxy contract specification.
|
||||||
|
/// @return Number of assets (or asset baskets) held by owner.
|
||||||
|
function getBalance(address ownerAddress, bytes memory assetData)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (uint256 balance)
|
||||||
|
{
|
||||||
|
// Get id of AssetProxy contract
|
||||||
|
bytes4 assetProxyId = assetData.readBytes4(0);
|
||||||
|
|
||||||
|
if (assetProxyId == IAssetData(address(0)).ERC20Token.selector) {
|
||||||
|
// Get ERC20 token address
|
||||||
|
address tokenAddress = assetData.readAddress(16);
|
||||||
|
|
||||||
|
// Encode data for `balanceOf(ownerAddress)`
|
||||||
|
bytes memory balanceOfData = abi.encodeWithSelector(
|
||||||
|
IERC20Token(address(0)).balanceOf.selector,
|
||||||
|
ownerAddress
|
||||||
|
);
|
||||||
|
|
||||||
|
// Query balance
|
||||||
|
(bool success, bytes memory returnData) = tokenAddress.staticcall(balanceOfData);
|
||||||
|
balance = success && returnData.length == 32 ? returnData.readUint256(0) : 0;
|
||||||
|
} else if (assetProxyId == IAssetData(address(0)).ERC721Token.selector) {
|
||||||
|
// Get ERC721 token address and id
|
||||||
|
(, address tokenAddress, uint256 tokenId) = decodeERC721AssetData(assetData);
|
||||||
|
|
||||||
|
// Check if id is owned by ownerAddress
|
||||||
|
bytes memory ownerOfCalldata = abi.encodeWithSelector(
|
||||||
|
IERC721Token(address(0)).ownerOf.selector,
|
||||||
|
tokenId
|
||||||
|
);
|
||||||
|
|
||||||
|
(bool success, bytes memory returnData) = tokenAddress.staticcall(ownerOfCalldata);
|
||||||
|
address currentOwnerAddress = (success && returnData.length == 32) ? returnData.readAddress(12) : address(0);
|
||||||
|
balance = currentOwnerAddress == ownerAddress ? 1 : 0;
|
||||||
|
} else if (assetProxyId == IAssetData(address(0)).ERC1155Assets.selector) {
|
||||||
|
// Get ERC1155 token address, array of ids, and array of values
|
||||||
|
(, address tokenAddress, uint256[] memory tokenIds, uint256[] memory tokenValues,) = decodeERC1155AssetData(assetData);
|
||||||
|
|
||||||
|
uint256 length = tokenIds.length;
|
||||||
|
for (uint256 i = 0; i != length; i++) {
|
||||||
|
// Encode data for `balanceOf(ownerAddress, tokenIds[i])
|
||||||
|
bytes memory balanceOfData = abi.encodeWithSelector(
|
||||||
|
IERC1155(address(0)).balanceOf.selector,
|
||||||
|
ownerAddress,
|
||||||
|
tokenIds[i]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Query balance
|
||||||
|
(bool success, bytes memory returnData) = tokenAddress.staticcall(balanceOfData);
|
||||||
|
uint256 totalBalance = success && returnData.length == 32 ? returnData.readUint256(0) : 0;
|
||||||
|
|
||||||
|
// Scale total balance down by corresponding value in assetData
|
||||||
|
uint256 scaledBalance = totalBalance / tokenValues[i];
|
||||||
|
if (scaledBalance < balance || balance == 0) {
|
||||||
|
balance = scaledBalance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (assetProxyId == IAssetData(address(0)).StaticCall.selector) {
|
||||||
|
// Encode data for `staticCallProxy.transferFrom(assetData,...)`
|
||||||
|
bytes memory transferFromData = abi.encodeWithSelector(
|
||||||
|
IAssetProxy(address(0)).transferFrom.selector,
|
||||||
|
assetData,
|
||||||
|
address(0), // `from` address is not used
|
||||||
|
address(0), // `to` address is not used
|
||||||
|
0 // `amount` is not used
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check if staticcall would be successful
|
||||||
|
(bool success,) = _STATIC_CALL_PROXY_ADDRESS.staticcall(transferFromData);
|
||||||
|
|
||||||
|
// Success means that the staticcall can be made an unlimited amount of times
|
||||||
|
balance = success ? _MAX_UINT256 : 0;
|
||||||
|
} else if (assetProxyId == IAssetData(address(0)).MultiAsset.selector) {
|
||||||
|
// Get array of values and array of assetDatas
|
||||||
|
(, uint256[] memory assetAmounts, bytes[] memory nestedAssetData) = decodeMultiAssetData(assetData);
|
||||||
|
|
||||||
|
uint256 length = nestedAssetData.length;
|
||||||
|
for (uint256 i = 0; i != length; i++) {
|
||||||
|
// Query balance of individual assetData
|
||||||
|
uint256 totalBalance = getBalance(ownerAddress, nestedAssetData[i]);
|
||||||
|
|
||||||
|
// Scale total balance down by corresponding value in assetData
|
||||||
|
uint256 scaledBalance = totalBalance / assetAmounts[i];
|
||||||
|
if (scaledBalance < balance || balance == 0) {
|
||||||
|
balance = scaledBalance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Balance will be 0 if assetProxyId is unknown
|
||||||
|
return balance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Calls getBalance() for each element of assetData.
|
||||||
|
/// @param ownerAddress Owner of the assets specified by assetData.
|
||||||
|
/// @param assetData Array of asset details, each encoded per the AssetProxy contract specification.
|
||||||
|
/// @return Array of asset balances from getBalance(), with each element
|
||||||
|
/// corresponding to the same-indexed element in the assetData input.
|
||||||
|
function getBatchBalances(address ownerAddress, bytes[] memory assetData)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (uint256[] memory balances)
|
||||||
|
{
|
||||||
|
uint256 length = assetData.length;
|
||||||
|
balances = new uint256[](length);
|
||||||
|
for (uint256 i = 0; i != length; i++) {
|
||||||
|
balances[i] = getBalance(ownerAddress, assetData[i]);
|
||||||
|
}
|
||||||
|
return balances;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Returns the number of asset(s) (described by assetData) that
|
||||||
|
/// the corresponding AssetProxy contract is authorized to spend. When the asset data contains
|
||||||
|
/// multiple assets (eg for Multi-Asset), the return value indicates
|
||||||
|
/// how many complete "baskets" of those assets may be spent by all of the corresponding
|
||||||
|
/// AssetProxy contracts.
|
||||||
|
/// @param ownerAddress Owner of the assets specified by assetData.
|
||||||
|
/// @param assetData Details of asset, encoded per the AssetProxy contract specification.
|
||||||
|
/// @return Number of assets (or asset baskets) that the corresponding AssetProxy is authorized to spend.
|
||||||
|
function getAssetProxyAllowance(address ownerAddress, bytes memory assetData)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (uint256 allowance)
|
||||||
|
{
|
||||||
|
// Get id of AssetProxy contract
|
||||||
|
bytes4 assetProxyId = assetData.readBytes4(0);
|
||||||
|
|
||||||
|
if (assetProxyId == IAssetData(address(0)).MultiAsset.selector) {
|
||||||
|
// Get array of values and array of assetDatas
|
||||||
|
(, uint256[] memory amounts, bytes[] memory nestedAssetData) = decodeMultiAssetData(assetData);
|
||||||
|
|
||||||
|
uint256 length = nestedAssetData.length;
|
||||||
|
for (uint256 i = 0; i != length; i++) {
|
||||||
|
// Query allowance of individual assetData
|
||||||
|
uint256 totalAllowance = getAssetProxyAllowance(ownerAddress, nestedAssetData[i]);
|
||||||
|
|
||||||
|
// Scale total allowance down by corresponding value in assetData
|
||||||
|
uint256 scaledAllowance = totalAllowance / amounts[i];
|
||||||
|
if (scaledAllowance < allowance || allowance == 0) {
|
||||||
|
allowance = scaledAllowance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return allowance;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (assetProxyId == IAssetData(address(0)).ERC20Token.selector) {
|
||||||
|
// Get ERC20 token address
|
||||||
|
address tokenAddress = assetData.readAddress(16);
|
||||||
|
|
||||||
|
// Encode data for `allowance(ownerAddress, _ERC20_PROXY_ADDRESS)`
|
||||||
|
bytes memory allowanceData = abi.encodeWithSelector(
|
||||||
|
IERC20Token(address(0)).allowance.selector,
|
||||||
|
ownerAddress,
|
||||||
|
_ERC20_PROXY_ADDRESS
|
||||||
|
);
|
||||||
|
|
||||||
|
// Query allowance
|
||||||
|
(bool success, bytes memory returnData) = tokenAddress.staticcall(allowanceData);
|
||||||
|
allowance = success && returnData.length == 32 ? returnData.readUint256(0) : 0;
|
||||||
|
} else if (assetProxyId == IAssetData(address(0)).ERC721Token.selector) {
|
||||||
|
// Get ERC721 token address and id
|
||||||
|
(, address tokenAddress, uint256 tokenId) = decodeERC721AssetData(assetData);
|
||||||
|
|
||||||
|
// Encode data for `isApprovedForAll(ownerAddress, _ERC721_PROXY_ADDRESS)`
|
||||||
|
bytes memory isApprovedForAllData = abi.encodeWithSelector(
|
||||||
|
IERC721Token(address(0)).isApprovedForAll.selector,
|
||||||
|
ownerAddress,
|
||||||
|
_ERC721_PROXY_ADDRESS
|
||||||
|
);
|
||||||
|
|
||||||
|
(bool success, bytes memory returnData) = tokenAddress.staticcall(isApprovedForAllData);
|
||||||
|
|
||||||
|
// If not approved for all, call `getApproved(tokenId)`
|
||||||
|
if (!success || returnData.length != 32 || returnData.readUint256(0) != 1) {
|
||||||
|
// Encode data for `getApproved(tokenId)`
|
||||||
|
bytes memory getApprovedData = abi.encodeWithSelector(IERC721Token(address(0)).getApproved.selector, tokenId);
|
||||||
|
(success, returnData) = tokenAddress.staticcall(getApprovedData);
|
||||||
|
|
||||||
|
// Allowance is 1 if successful and the approved address is the ERC721Proxy
|
||||||
|
allowance = success && returnData.length == 32 && returnData.readAddress(12) == _ERC721_PROXY_ADDRESS ? 1 : 0;
|
||||||
|
} else {
|
||||||
|
// Allowance is 2^256 - 1 if `isApprovedForAll` returned true
|
||||||
|
allowance = _MAX_UINT256;
|
||||||
|
}
|
||||||
|
} else if (assetProxyId == IAssetData(address(0)).ERC1155Assets.selector) {
|
||||||
|
// Get ERC1155 token address
|
||||||
|
(, address tokenAddress, , , ) = decodeERC1155AssetData(assetData);
|
||||||
|
|
||||||
|
// Encode data for `isApprovedForAll(ownerAddress, _ERC1155_PROXY_ADDRESS)`
|
||||||
|
bytes memory isApprovedForAllData = abi.encodeWithSelector(
|
||||||
|
IERC1155(address(0)).isApprovedForAll.selector,
|
||||||
|
ownerAddress,
|
||||||
|
_ERC1155_PROXY_ADDRESS
|
||||||
|
);
|
||||||
|
|
||||||
|
// Query allowance
|
||||||
|
(bool success, bytes memory returnData) = tokenAddress.staticcall(isApprovedForAllData);
|
||||||
|
allowance = success && returnData.length == 32 && returnData.readUint256(0) == 1 ? _MAX_UINT256 : 0;
|
||||||
|
} else if (assetProxyId == IAssetData(address(0)).StaticCall.selector) {
|
||||||
|
// The StaticCallProxy does not require any approvals
|
||||||
|
allowance = _MAX_UINT256;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allowance will be 0 if the assetProxyId is unknown
|
||||||
|
return allowance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Calls getAssetProxyAllowance() for each element of assetData.
|
||||||
|
/// @param ownerAddress Owner of the assets specified by assetData.
|
||||||
|
/// @param assetData Array of asset details, each encoded per the AssetProxy contract specification.
|
||||||
|
/// @return An array of asset allowances from getAllowance(), with each
|
||||||
|
/// element corresponding to the same-indexed element in the assetData input.
|
||||||
|
function getBatchAssetProxyAllowances(address ownerAddress, bytes[] memory assetData)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (uint256[] memory allowances)
|
||||||
|
{
|
||||||
|
uint256 length = assetData.length;
|
||||||
|
allowances = new uint256[](length);
|
||||||
|
for (uint256 i = 0; i != length; i++) {
|
||||||
|
allowances[i] = getAssetProxyAllowance(ownerAddress, assetData[i]);
|
||||||
|
}
|
||||||
|
return allowances;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Calls getBalance() and getAllowance() for assetData.
|
||||||
|
/// @param ownerAddress Owner of the assets specified by assetData.
|
||||||
|
/// @param assetData Details of asset, encoded per the AssetProxy contract specification.
|
||||||
|
/// @return Number of assets (or asset baskets) held by owner, and number
|
||||||
|
/// of assets (or asset baskets) that the corresponding AssetProxy is authorized to spend.
|
||||||
|
function getBalanceAndAssetProxyAllowance(address ownerAddress, bytes memory assetData)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (uint256 balance, uint256 allowance)
|
||||||
|
{
|
||||||
|
balance = getBalance(ownerAddress, assetData);
|
||||||
|
allowance = getAssetProxyAllowance(ownerAddress, assetData);
|
||||||
|
return (balance, allowance);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Calls getBatchBalances() and getBatchAllowances() for each element of assetData.
|
||||||
|
/// @param ownerAddress Owner of the assets specified by assetData.
|
||||||
|
/// @param assetData Array of asset details, each encoded per the AssetProxy contract specification.
|
||||||
|
/// @return An array of asset balances from getBalance(), and an array of
|
||||||
|
/// asset allowances from getAllowance(), with each element
|
||||||
|
/// corresponding to the same-indexed element in the assetData input.
|
||||||
|
function getBatchBalancesAndAssetProxyAllowances(address ownerAddress, bytes[] memory assetData)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (uint256[] memory balances, uint256[] memory allowances)
|
||||||
|
{
|
||||||
|
balances = getBatchBalances(ownerAddress, assetData);
|
||||||
|
allowances = getBatchAssetProxyAllowances(ownerAddress, assetData);
|
||||||
|
return (balances, allowances);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Encode ERC-20 asset data into the format described in the AssetProxy contract specification.
|
||||||
|
/// @param tokenAddress The address of the ERC-20 contract hosting the asset to be traded.
|
||||||
|
/// @return AssetProxy-compliant data describing the asset.
|
||||||
|
function encodeERC20AssetData(address tokenAddress)
|
||||||
|
public
|
||||||
|
pure
|
||||||
|
returns (bytes memory assetData)
|
||||||
|
{
|
||||||
|
assetData = abi.encodeWithSelector(IAssetData(address(0)).ERC20Token.selector, tokenAddress);
|
||||||
|
return assetData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Decode ERC-20 asset data from the format described in the AssetProxy contract specification.
|
||||||
|
/// @param assetData AssetProxy-compliant asset data describing an ERC-20 asset.
|
||||||
|
/// @return The ERC-20 AssetProxy identifier, and the address of the ERC-20
|
||||||
|
/// contract hosting this asset.
|
||||||
|
function decodeERC20AssetData(bytes memory assetData)
|
||||||
|
public
|
||||||
|
pure
|
||||||
|
returns (
|
||||||
|
bytes4 assetProxyId,
|
||||||
|
address tokenAddress
|
||||||
|
)
|
||||||
|
{
|
||||||
|
assetProxyId = assetData.readBytes4(0);
|
||||||
|
|
||||||
|
require(
|
||||||
|
assetProxyId == IAssetData(address(0)).ERC20Token.selector,
|
||||||
|
"WRONG_PROXY_ID"
|
||||||
|
);
|
||||||
|
|
||||||
|
tokenAddress = assetData.readAddress(16);
|
||||||
|
return (assetProxyId, tokenAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Encode ERC-721 asset data into the format described in the AssetProxy specification.
|
||||||
|
/// @param tokenAddress The address of the ERC-721 contract hosting the asset to be traded.
|
||||||
|
/// @param tokenId The identifier of the specific asset to be traded.
|
||||||
|
/// @return AssetProxy-compliant asset data describing the asset.
|
||||||
|
function encodeERC721AssetData(address tokenAddress, uint256 tokenId)
|
||||||
|
public
|
||||||
|
pure
|
||||||
|
returns (bytes memory assetData)
|
||||||
|
{
|
||||||
|
assetData = abi.encodeWithSelector(
|
||||||
|
IAssetData(address(0)).ERC721Token.selector,
|
||||||
|
tokenAddress,
|
||||||
|
tokenId
|
||||||
|
);
|
||||||
|
return assetData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Decode ERC-721 asset data from the format described in the AssetProxy contract specification.
|
||||||
|
/// @param assetData AssetProxy-compliant asset data describing an ERC-721 asset.
|
||||||
|
/// @return The ERC-721 AssetProxy identifier, the address of the ERC-721
|
||||||
|
/// contract hosting this asset, and the identifier of the specific
|
||||||
|
/// asset to be traded.
|
||||||
|
function decodeERC721AssetData(bytes memory assetData)
|
||||||
|
public
|
||||||
|
pure
|
||||||
|
returns (
|
||||||
|
bytes4 assetProxyId,
|
||||||
|
address tokenAddress,
|
||||||
|
uint256 tokenId
|
||||||
|
)
|
||||||
|
{
|
||||||
|
assetProxyId = assetData.readBytes4(0);
|
||||||
|
|
||||||
|
require(
|
||||||
|
assetProxyId == IAssetData(address(0)).ERC721Token.selector,
|
||||||
|
"WRONG_PROXY_ID"
|
||||||
|
);
|
||||||
|
|
||||||
|
tokenAddress = assetData.readAddress(16);
|
||||||
|
tokenId = assetData.readUint256(36);
|
||||||
|
return (assetProxyId, tokenAddress, tokenId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Encode ERC-1155 asset data into the format described in the AssetProxy contract specification.
|
||||||
|
/// @param tokenAddress The address of the ERC-1155 contract hosting the asset(s) to be traded.
|
||||||
|
/// @param tokenIds The identifiers of the specific assets to be traded.
|
||||||
|
/// @param tokenValues The amounts of each asset to be traded.
|
||||||
|
/// @param callbackData Data to be passed to receiving contracts when a transfer is performed.
|
||||||
|
/// @return AssetProxy-compliant asset data describing the set of assets.
|
||||||
|
function encodeERC1155AssetData(
|
||||||
|
address tokenAddress,
|
||||||
|
uint256[] memory tokenIds,
|
||||||
|
uint256[] memory tokenValues,
|
||||||
|
bytes memory callbackData
|
||||||
|
)
|
||||||
|
public
|
||||||
|
pure
|
||||||
|
returns (bytes memory assetData)
|
||||||
|
{
|
||||||
|
assetData = abi.encodeWithSelector(
|
||||||
|
IAssetData(address(0)).ERC1155Assets.selector,
|
||||||
|
tokenAddress,
|
||||||
|
tokenIds,
|
||||||
|
tokenValues,
|
||||||
|
callbackData
|
||||||
|
);
|
||||||
|
return assetData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Decode ERC-1155 asset data from the format described in the AssetProxy contract specification.
|
||||||
|
/// @param assetData AssetProxy-compliant asset data describing an ERC-1155 set of assets.
|
||||||
|
/// @return The ERC-1155 AssetProxy identifier, the address of the ERC-1155
|
||||||
|
/// contract hosting the assets, an array of the identifiers of the
|
||||||
|
/// assets to be traded, an array of asset amounts to be traded, and
|
||||||
|
/// callback data. Each element of the arrays corresponds to the
|
||||||
|
/// same-indexed element of the other array. Return values specified as
|
||||||
|
/// `memory` are returned as pointers to locations within the memory of
|
||||||
|
/// the input parameter `assetData`.
|
||||||
|
function decodeERC1155AssetData(bytes memory assetData)
|
||||||
|
public
|
||||||
|
pure
|
||||||
|
returns (
|
||||||
|
bytes4 assetProxyId,
|
||||||
|
address tokenAddress,
|
||||||
|
uint256[] memory tokenIds,
|
||||||
|
uint256[] memory tokenValues,
|
||||||
|
bytes memory callbackData
|
||||||
|
)
|
||||||
|
{
|
||||||
|
assetProxyId = assetData.readBytes4(0);
|
||||||
|
|
||||||
|
require(
|
||||||
|
assetProxyId == IAssetData(address(0)).ERC1155Assets.selector,
|
||||||
|
"WRONG_PROXY_ID"
|
||||||
|
);
|
||||||
|
|
||||||
|
assembly {
|
||||||
|
// Skip selector and length to get to the first parameter:
|
||||||
|
assetData := add(assetData, 36)
|
||||||
|
// Read the value of the first parameter:
|
||||||
|
tokenAddress := mload(assetData)
|
||||||
|
// Point to the next parameter's data:
|
||||||
|
tokenIds := add(assetData, mload(add(assetData, 32)))
|
||||||
|
// Point to the next parameter's data:
|
||||||
|
tokenValues := add(assetData, mload(add(assetData, 64)))
|
||||||
|
// Point to the next parameter's data:
|
||||||
|
callbackData := add(assetData, mload(add(assetData, 96)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
assetProxyId,
|
||||||
|
tokenAddress,
|
||||||
|
tokenIds,
|
||||||
|
tokenValues,
|
||||||
|
callbackData
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Encode data for multiple assets, per the AssetProxy contract specification.
|
||||||
|
/// @param amounts The amounts of each asset to be traded.
|
||||||
|
/// @param nestedAssetData AssetProxy-compliant data describing each asset to be traded.
|
||||||
|
/// @return AssetProxy-compliant data describing the set of assets.
|
||||||
|
function encodeMultiAssetData(uint256[] memory amounts, bytes[] memory nestedAssetData)
|
||||||
|
public
|
||||||
|
pure
|
||||||
|
returns (bytes memory assetData)
|
||||||
|
{
|
||||||
|
assetData = abi.encodeWithSelector(
|
||||||
|
IAssetData(address(0)).MultiAsset.selector,
|
||||||
|
amounts,
|
||||||
|
nestedAssetData
|
||||||
|
);
|
||||||
|
return assetData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Decode multi-asset data from the format described in the AssetProxy contract specification.
|
||||||
|
/// @param assetData AssetProxy-compliant data describing a multi-asset basket.
|
||||||
|
/// @return The Multi-Asset AssetProxy identifier, an array of the amounts
|
||||||
|
/// of the assets to be traded, and an array of the
|
||||||
|
/// AssetProxy-compliant data describing each asset to be traded. Each
|
||||||
|
/// element of the arrays corresponds to the same-indexed element of the other array.
|
||||||
|
function decodeMultiAssetData(bytes memory assetData)
|
||||||
|
public
|
||||||
|
pure
|
||||||
|
returns (
|
||||||
|
bytes4 assetProxyId,
|
||||||
|
uint256[] memory amounts,
|
||||||
|
bytes[] memory nestedAssetData
|
||||||
|
)
|
||||||
|
{
|
||||||
|
assetProxyId = assetData.readBytes4(0);
|
||||||
|
|
||||||
|
require(
|
||||||
|
assetProxyId == IAssetData(address(0)).MultiAsset.selector,
|
||||||
|
"WRONG_PROXY_ID"
|
||||||
|
);
|
||||||
|
|
||||||
|
// solhint-disable indent
|
||||||
|
(amounts, nestedAssetData) = abi.decode(
|
||||||
|
assetData.slice(4, assetData.length),
|
||||||
|
(uint256[], bytes[])
|
||||||
|
);
|
||||||
|
// solhint-enable indent
|
||||||
|
}
|
||||||
|
}
|
||||||
188
contracts/dev-utils/contracts/src/LibTransactionDecoder.sol
Normal file
188
contracts/dev-utils/contracts/src/LibTransactionDecoder.sol
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||||
|
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract LibTransactionDecoder {
|
||||||
|
|
||||||
|
using LibBytes for bytes;
|
||||||
|
|
||||||
|
/// @dev Decodes the call data for an Exchange contract method call.
|
||||||
|
/// @param transactionData ABI-encoded calldata for an Exchange
|
||||||
|
/// contract method call.
|
||||||
|
/// @return The name of the function called, and the parameters it was
|
||||||
|
/// given. For single-order fills and cancels, the arrays will have
|
||||||
|
/// just one element.
|
||||||
|
function decodeZeroExTransactionData(bytes memory transactionData)
|
||||||
|
public
|
||||||
|
pure
|
||||||
|
returns(
|
||||||
|
string memory functionName,
|
||||||
|
LibOrder.Order[] memory orders,
|
||||||
|
uint256[] memory takerAssetFillAmounts,
|
||||||
|
bytes[] memory signatures
|
||||||
|
)
|
||||||
|
{
|
||||||
|
bytes4 functionSelector = transactionData.readBytes4(0);
|
||||||
|
|
||||||
|
if (functionSelector == IExchange(address(0)).batchCancelOrders.selector) {
|
||||||
|
functionName = "batchCancelOrders";
|
||||||
|
} else if (functionSelector == IExchange(address(0)).batchFillOrders.selector) {
|
||||||
|
functionName = "batchFillOrders";
|
||||||
|
} else if (functionSelector == IExchange(address(0)).batchFillOrdersNoThrow.selector) {
|
||||||
|
functionName = "batchFillOrdersNoThrow";
|
||||||
|
} else if (functionSelector == IExchange(address(0)).batchFillOrKillOrders.selector) {
|
||||||
|
functionName = "batchFillOrKillOrders";
|
||||||
|
} else if (functionSelector == IExchange(address(0)).cancelOrder.selector) {
|
||||||
|
functionName = "cancelOrder";
|
||||||
|
} else if (functionSelector == IExchange(address(0)).fillOrder.selector) {
|
||||||
|
functionName = "fillOrder";
|
||||||
|
} else if (functionSelector == IExchange(address(0)).fillOrKillOrder.selector) {
|
||||||
|
functionName = "fillOrKillOrder";
|
||||||
|
} else if (functionSelector == IExchange(address(0)).marketBuyOrdersNoThrow.selector) {
|
||||||
|
functionName = "marketBuyOrdersNoThrow";
|
||||||
|
} else if (functionSelector == IExchange(address(0)).marketSellOrdersNoThrow.selector) {
|
||||||
|
functionName = "marketSellOrdersNoThrow";
|
||||||
|
} else if (functionSelector == IExchange(address(0)).marketBuyOrdersFillOrKill.selector) {
|
||||||
|
functionName = "marketBuyOrdersFillOrKill";
|
||||||
|
} else if (functionSelector == IExchange(address(0)).marketSellOrdersFillOrKill.selector) {
|
||||||
|
functionName = "marketSellOrdersFillOrKill";
|
||||||
|
} else if (functionSelector == IExchange(address(0)).matchOrders.selector) {
|
||||||
|
functionName = "matchOrders";
|
||||||
|
} else if (
|
||||||
|
functionSelector == IExchange(address(0)).cancelOrdersUpTo.selector ||
|
||||||
|
functionSelector == IExchange(address(0)).executeTransaction.selector
|
||||||
|
) {
|
||||||
|
revert("UNIMPLEMENTED");
|
||||||
|
} else {
|
||||||
|
revert("UNKNOWN_FUNCTION_SELECTOR");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (functionSelector == IExchange(address(0)).batchCancelOrders.selector) {
|
||||||
|
// solhint-disable-next-line indent
|
||||||
|
orders = abi.decode(transactionData.slice(4, transactionData.length), (LibOrder.Order[]));
|
||||||
|
takerAssetFillAmounts = new uint256[](0);
|
||||||
|
signatures = new bytes[](0);
|
||||||
|
} else if (
|
||||||
|
functionSelector == IExchange(address(0)).batchFillOrKillOrders.selector ||
|
||||||
|
functionSelector == IExchange(address(0)).batchFillOrders.selector ||
|
||||||
|
functionSelector == IExchange(address(0)).batchFillOrdersNoThrow.selector
|
||||||
|
) {
|
||||||
|
(orders, takerAssetFillAmounts, signatures) = _makeReturnValuesForBatchFill(transactionData);
|
||||||
|
} else if (functionSelector == IExchange(address(0)).cancelOrder.selector) {
|
||||||
|
orders = new LibOrder.Order[](1);
|
||||||
|
orders[0] = abi.decode(transactionData.slice(4, transactionData.length), (LibOrder.Order));
|
||||||
|
takerAssetFillAmounts = new uint256[](0);
|
||||||
|
signatures = new bytes[](0);
|
||||||
|
} else if (
|
||||||
|
functionSelector == IExchange(address(0)).fillOrKillOrder.selector ||
|
||||||
|
functionSelector == IExchange(address(0)).fillOrder.selector
|
||||||
|
) {
|
||||||
|
(orders, takerAssetFillAmounts, signatures) = _makeReturnValuesForSingleOrderFill(transactionData);
|
||||||
|
} else if (
|
||||||
|
functionSelector == IExchange(address(0)).marketBuyOrdersNoThrow.selector ||
|
||||||
|
functionSelector == IExchange(address(0)).marketSellOrdersNoThrow.selector ||
|
||||||
|
functionSelector == IExchange(address(0)).marketBuyOrdersFillOrKill.selector ||
|
||||||
|
functionSelector == IExchange(address(0)).marketSellOrdersFillOrKill.selector
|
||||||
|
) {
|
||||||
|
(orders, takerAssetFillAmounts, signatures) = _makeReturnValuesForMarketFill(transactionData);
|
||||||
|
} else if (functionSelector == IExchange(address(0)).matchOrders.selector) {
|
||||||
|
(
|
||||||
|
LibOrder.Order memory leftOrder,
|
||||||
|
LibOrder.Order memory rightOrder,
|
||||||
|
bytes memory leftSignature,
|
||||||
|
bytes memory rightSignature
|
||||||
|
) = abi.decode(
|
||||||
|
transactionData.slice(4, transactionData.length),
|
||||||
|
(LibOrder.Order, LibOrder.Order, bytes, bytes)
|
||||||
|
);
|
||||||
|
|
||||||
|
orders = new LibOrder.Order[](2);
|
||||||
|
orders[0] = leftOrder;
|
||||||
|
orders[1] = rightOrder;
|
||||||
|
|
||||||
|
takerAssetFillAmounts = new uint256[](2);
|
||||||
|
takerAssetFillAmounts[0] = leftOrder.takerAssetAmount;
|
||||||
|
takerAssetFillAmounts[1] = rightOrder.takerAssetAmount;
|
||||||
|
|
||||||
|
signatures = new bytes[](2);
|
||||||
|
signatures[0] = leftSignature;
|
||||||
|
signatures[1] = rightSignature;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _makeReturnValuesForSingleOrderFill(bytes memory transactionData)
|
||||||
|
private
|
||||||
|
pure
|
||||||
|
returns(
|
||||||
|
LibOrder.Order[] memory orders,
|
||||||
|
uint256[] memory takerAssetFillAmounts,
|
||||||
|
bytes[] memory signatures
|
||||||
|
)
|
||||||
|
{
|
||||||
|
orders = new LibOrder.Order[](1);
|
||||||
|
takerAssetFillAmounts = new uint256[](1);
|
||||||
|
signatures = new bytes[](1);
|
||||||
|
// solhint-disable-next-line indent
|
||||||
|
(orders[0], takerAssetFillAmounts[0], signatures[0]) = abi.decode(
|
||||||
|
transactionData.slice(4, transactionData.length),
|
||||||
|
(LibOrder.Order, uint256, bytes)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _makeReturnValuesForBatchFill(bytes memory transactionData)
|
||||||
|
private
|
||||||
|
pure
|
||||||
|
returns(
|
||||||
|
LibOrder.Order[] memory orders,
|
||||||
|
uint256[] memory takerAssetFillAmounts,
|
||||||
|
bytes[] memory signatures
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// solhint-disable-next-line indent
|
||||||
|
(orders, takerAssetFillAmounts, signatures) = abi.decode(
|
||||||
|
transactionData.slice(4, transactionData.length),
|
||||||
|
// solhint-disable-next-line indent
|
||||||
|
(LibOrder.Order[], uint256[], bytes[])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _makeReturnValuesForMarketFill(bytes memory transactionData)
|
||||||
|
private
|
||||||
|
pure
|
||||||
|
returns(
|
||||||
|
LibOrder.Order[] memory orders,
|
||||||
|
uint256[] memory takerAssetFillAmounts,
|
||||||
|
bytes[] memory signatures
|
||||||
|
)
|
||||||
|
{
|
||||||
|
takerAssetFillAmounts = new uint256[](1);
|
||||||
|
// solhint-disable-next-line indent
|
||||||
|
(orders, takerAssetFillAmounts[0], signatures) = abi.decode(
|
||||||
|
transactionData.slice(4, transactionData.length),
|
||||||
|
// solhint-disable-next-line indent
|
||||||
|
(LibOrder.Order[], uint256, bytes[])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,160 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
|
||||||
|
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
||||||
|
import "@0x/contracts-exchange/contracts/src/libs/LibExchangeRichErrorDecoder.sol";
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibExchangeRichErrors.sol";
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibFillResults.sol";
|
||||||
|
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract OrderTransferSimulationUtils is
|
||||||
|
LibExchangeRichErrorDecoder
|
||||||
|
{
|
||||||
|
using LibBytes for bytes;
|
||||||
|
|
||||||
|
enum OrderTransferResults {
|
||||||
|
TakerAssetDataFailed, // Transfer of takerAsset failed
|
||||||
|
MakerAssetDataFailed, // Transfer of makerAsset failed
|
||||||
|
TakerFeeAssetDataFailed, // Transfer of takerFeeAsset failed
|
||||||
|
MakerFeeAssetDataFailed, // Transfer of makerFeeAsset failed
|
||||||
|
TransfersSuccessful // All transfers in the order were successful
|
||||||
|
}
|
||||||
|
|
||||||
|
// keccak256(abi.encodeWithSignature("Error(string)", "TRANSFERS_SUCCESSFUL"));
|
||||||
|
bytes32 constant internal _TRANSFERS_SUCCESSFUL_RESULT_HASH = 0xf43f26ea5a94b478394a975e856464913dc1a8a1ca70939d974aa7c238aa0ce0;
|
||||||
|
|
||||||
|
// solhint-disable var-name-mixedcase
|
||||||
|
IExchange internal _EXCHANGE;
|
||||||
|
// solhint-enable var-name-mixedcase
|
||||||
|
|
||||||
|
constructor (address _exchange)
|
||||||
|
public
|
||||||
|
{
|
||||||
|
_EXCHANGE = IExchange(_exchange);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Simulates all of the transfers within an order and returns the index of the first failed transfer.
|
||||||
|
/// @param order The order to simulate transfers for.
|
||||||
|
/// @param takerAddress The address of the taker that will fill the order.
|
||||||
|
/// @param takerAssetFillAmount The amount of takerAsset that the taker wished to fill.
|
||||||
|
/// @return The index of the first failed transfer (or 4 if all transfers are successful).
|
||||||
|
function getSimulatedOrderTransferResults(
|
||||||
|
LibOrder.Order memory order,
|
||||||
|
address takerAddress,
|
||||||
|
uint256 takerAssetFillAmount
|
||||||
|
)
|
||||||
|
public
|
||||||
|
returns (OrderTransferResults orderTransferResults)
|
||||||
|
{
|
||||||
|
LibFillResults.FillResults memory fillResults = LibFillResults.calculateFillResults(
|
||||||
|
order,
|
||||||
|
takerAssetFillAmount,
|
||||||
|
_EXCHANGE.protocolFeeMultiplier(),
|
||||||
|
tx.gasprice
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create input arrays
|
||||||
|
bytes[] memory assetData = new bytes[](4);
|
||||||
|
address[] memory fromAddresses = new address[](4);
|
||||||
|
address[] memory toAddresses = new address[](4);
|
||||||
|
uint256[] memory amounts = new uint256[](4);
|
||||||
|
|
||||||
|
// Transfer `takerAsset` from taker to maker
|
||||||
|
assetData[0] = order.takerAssetData;
|
||||||
|
fromAddresses[0] = takerAddress;
|
||||||
|
toAddresses[0] = order.makerAddress;
|
||||||
|
amounts[0] = takerAssetFillAmount;
|
||||||
|
|
||||||
|
// Transfer `makerAsset` from maker to taker
|
||||||
|
assetData[1] = order.makerAssetData;
|
||||||
|
fromAddresses[1] = order.makerAddress;
|
||||||
|
toAddresses[1] = takerAddress;
|
||||||
|
amounts[1] = fillResults.makerAssetFilledAmount;
|
||||||
|
|
||||||
|
// Transfer `takerFeeAsset` from taker to feeRecipient
|
||||||
|
assetData[2] = order.takerFeeAssetData;
|
||||||
|
fromAddresses[2] = takerAddress;
|
||||||
|
toAddresses[2] = order.feeRecipientAddress;
|
||||||
|
amounts[2] = fillResults.takerFeePaid;
|
||||||
|
|
||||||
|
// Transfer `makerFeeAsset` from maker to feeRecipient
|
||||||
|
assetData[3] = order.makerFeeAssetData;
|
||||||
|
fromAddresses[3] = order.makerAddress;
|
||||||
|
toAddresses[3] = order.feeRecipientAddress;
|
||||||
|
amounts[3] = fillResults.makerFeePaid;
|
||||||
|
|
||||||
|
// Encode data for `simulateDispatchTransferFromCalls(assetData, fromAddresses, toAddresses, amounts)`
|
||||||
|
bytes memory simulateDispatchTransferFromCallsData = abi.encodeWithSelector(
|
||||||
|
IExchange(address(0)).simulateDispatchTransferFromCalls.selector,
|
||||||
|
assetData,
|
||||||
|
fromAddresses,
|
||||||
|
toAddresses,
|
||||||
|
amounts
|
||||||
|
);
|
||||||
|
|
||||||
|
// Perform call and catch revert
|
||||||
|
(, bytes memory returnData) = address(_EXCHANGE).call(simulateDispatchTransferFromCallsData);
|
||||||
|
|
||||||
|
bytes4 selector = returnData.readBytes4(0);
|
||||||
|
if (selector == LibExchangeRichErrors.AssetProxyDispatchErrorSelector()) {
|
||||||
|
// Decode AssetProxyDispatchError and return index of failed transfer
|
||||||
|
(, bytes32 failedTransferIndex,) = decodeAssetProxyDispatchError(returnData);
|
||||||
|
return OrderTransferResults(uint8(uint256(failedTransferIndex)));
|
||||||
|
} else if (selector == LibExchangeRichErrors.AssetProxyTransferErrorSelector()) {
|
||||||
|
// Decode AssetProxyTransferError and return index of failed transfer
|
||||||
|
(bytes32 failedTransferIndex, ,) = decodeAssetProxyTransferError(returnData);
|
||||||
|
return OrderTransferResults(uint8(uint256(failedTransferIndex)));
|
||||||
|
} else if (keccak256(returnData) == _TRANSFERS_SUCCESSFUL_RESULT_HASH) {
|
||||||
|
// All transfers were successful
|
||||||
|
return OrderTransferResults.TransfersSuccessful;
|
||||||
|
} else {
|
||||||
|
revert("UNKNOWN_RETURN_DATA");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Simulates all of the transfers for each given order and returns the indices of each first failed transfer.
|
||||||
|
/// @param orders Array of orders to individually simulate transfers for.
|
||||||
|
/// @param takerAddresses Array of addresses of takers that will fill each order.
|
||||||
|
/// @param takerAssetFillAmounts Array of amounts of takerAsset that will be filled for each order.
|
||||||
|
/// @return The indices of the first failed transfer (or 4 if all transfers are successful) for each order.
|
||||||
|
function getSimulatedOrdersTransferResults(
|
||||||
|
LibOrder.Order[] memory orders,
|
||||||
|
address[] memory takerAddresses,
|
||||||
|
uint256[] memory takerAssetFillAmounts
|
||||||
|
)
|
||||||
|
public
|
||||||
|
returns (OrderTransferResults[] memory orderTransferResults)
|
||||||
|
{
|
||||||
|
uint256 length = orders.length;
|
||||||
|
orderTransferResults = new OrderTransferResults[](length);
|
||||||
|
for (uint256 i = 0; i != length; i++) {
|
||||||
|
orderTransferResults[i] = getSimulatedOrderTransferResults(
|
||||||
|
orders[i],
|
||||||
|
takerAddresses[i],
|
||||||
|
takerAssetFillAmounts[i]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return orderTransferResults;
|
||||||
|
}
|
||||||
|
}
|
||||||
177
contracts/dev-utils/contracts/src/OrderValidationUtils.sol
Normal file
177
contracts/dev-utils/contracts/src/OrderValidationUtils.sol
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
|
||||||
|
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||||
|
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
||||||
|
import "./LibAssetData.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract OrderValidationUtils is
|
||||||
|
LibAssetData
|
||||||
|
{
|
||||||
|
using LibBytes for bytes;
|
||||||
|
using LibSafeMath for uint256;
|
||||||
|
|
||||||
|
constructor (address _exchange)
|
||||||
|
public
|
||||||
|
LibAssetData(_exchange)
|
||||||
|
{}
|
||||||
|
|
||||||
|
/// @dev Fetches all order-relevant information needed to validate if the supplied order is fillable.
|
||||||
|
/// @param order The order structure.
|
||||||
|
/// @param signature Signature provided by maker that proves the order's authenticity.
|
||||||
|
/// `0x01` can always be provided if the signature does not need to be validated.
|
||||||
|
/// @return The orderInfo (hash, status, and `takerAssetAmount` already filled for the given order),
|
||||||
|
/// fillableTakerAssetAmount (amount of the order's `takerAssetAmount` that is fillable given all on-chain state),
|
||||||
|
/// and isValidSignature (validity of the provided signature).
|
||||||
|
/// NOTE: If the `takerAssetData` encodes data for multiple assets, `fillableTakerAssetAmount` will represent a "scaled"
|
||||||
|
/// amount, meaning it must be multiplied by all the individual asset amounts within the `takerAssetData` to get the final
|
||||||
|
/// amount of each asset that can be filled.
|
||||||
|
function getOrderRelevantState(LibOrder.Order memory order, bytes memory signature)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (
|
||||||
|
LibOrder.OrderInfo memory orderInfo,
|
||||||
|
uint256 fillableTakerAssetAmount,
|
||||||
|
bool isValidSignature
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// Get info specific to order
|
||||||
|
orderInfo = _EXCHANGE.getOrderInfo(order);
|
||||||
|
|
||||||
|
// Validate the maker's signature
|
||||||
|
address makerAddress = order.makerAddress;
|
||||||
|
isValidSignature = _EXCHANGE.isValidOrderSignature(
|
||||||
|
order,
|
||||||
|
signature
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get the transferable amount of the `makerAsset`
|
||||||
|
uint256 transferableMakerAssetAmount = getTransferableAssetAmount(makerAddress, order.makerAssetData);
|
||||||
|
|
||||||
|
// Assign to stack variables to reduce redundant mloads/sloads
|
||||||
|
uint256 takerAssetAmount = order.takerAssetAmount;
|
||||||
|
uint256 makerFee = order.makerFee;
|
||||||
|
|
||||||
|
// Get the amount of `takerAsset` that is transferable to maker given the transferability of `makerAsset`, `makerFeeAsset`,
|
||||||
|
// and the total amounts specified in the order
|
||||||
|
uint256 transferableTakerAssetAmount;
|
||||||
|
if (order.makerAssetData.equals(order.makerFeeAssetData)) {
|
||||||
|
// If `makerAsset` equals `makerFeeAsset`, the % that can be filled is
|
||||||
|
// transferableMakerAssetAmount / (makerAssetAmount + makerFee)
|
||||||
|
transferableTakerAssetAmount = LibMath.getPartialAmountFloor(
|
||||||
|
transferableMakerAssetAmount,
|
||||||
|
order.makerAssetAmount.safeAdd(makerFee),
|
||||||
|
takerAssetAmount
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// If `makerFee` is 0, the % that can be filled is (transferableMakerAssetAmount / makerAssetAmount)
|
||||||
|
if (makerFee == 0) {
|
||||||
|
transferableTakerAssetAmount = LibMath.getPartialAmountFloor(
|
||||||
|
transferableMakerAssetAmount,
|
||||||
|
order.makerAssetAmount,
|
||||||
|
takerAssetAmount
|
||||||
|
);
|
||||||
|
|
||||||
|
// If `makerAsset` does not equal `makerFeeAsset`, the % that can be filled is the lower of
|
||||||
|
// (transferableMakerAssetAmount / makerAssetAmount) and (transferableMakerAssetFeeAmount / makerFee)
|
||||||
|
} else {
|
||||||
|
// Get the transferable amount of the `makerFeeAsset`
|
||||||
|
uint256 transferableMakerFeeAssetAmount = getTransferableAssetAmount(makerAddress, order.makerFeeAssetData);
|
||||||
|
|
||||||
|
uint256 transferableMakerToTakerAmount = LibMath.getPartialAmountFloor(
|
||||||
|
transferableMakerAssetAmount,
|
||||||
|
order.makerAssetAmount,
|
||||||
|
takerAssetAmount
|
||||||
|
);
|
||||||
|
uint256 transferableMakerFeeToTakerAmount = LibMath.getPartialAmountFloor(
|
||||||
|
transferableMakerFeeAssetAmount,
|
||||||
|
makerFee,
|
||||||
|
takerAssetAmount
|
||||||
|
);
|
||||||
|
transferableTakerAssetAmount = LibSafeMath.min256(transferableMakerToTakerAmount, transferableMakerFeeToTakerAmount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// `fillableTakerAssetAmount` is the lower of the order's remaining `takerAssetAmount` and the `transferableTakerAssetAmount`
|
||||||
|
fillableTakerAssetAmount = LibSafeMath.min256(
|
||||||
|
takerAssetAmount.safeSub(orderInfo.orderTakerAssetFilledAmount),
|
||||||
|
transferableTakerAssetAmount
|
||||||
|
);
|
||||||
|
|
||||||
|
return (orderInfo, fillableTakerAssetAmount, isValidSignature);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Fetches all order-relevant information needed to validate if the supplied orders are fillable.
|
||||||
|
/// @param orders Array of order structures.
|
||||||
|
/// @param signatures Array of signatures provided by makers that prove the authenticity of the orders.
|
||||||
|
/// `0x01` can always be provided if a signature does not need to be validated.
|
||||||
|
/// @return The ordersInfo (array of the hash, status, and `takerAssetAmount` already filled for each order),
|
||||||
|
/// fillableTakerAssetAmounts (array of amounts for each order's `takerAssetAmount` that is fillable given all on-chain state),
|
||||||
|
/// and isValidSignature (array containing the validity of each provided signature).
|
||||||
|
/// NOTE: If the `takerAssetData` encodes data for multiple assets, each element of `fillableTakerAssetAmounts`
|
||||||
|
/// will represent a "scaled" amount, meaning it must be multiplied by all the individual asset amounts within
|
||||||
|
/// the `takerAssetData` to get the final amount of each asset that can be filled.
|
||||||
|
function getOrderRelevantStates(LibOrder.Order[] memory orders, bytes[] memory signatures)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (
|
||||||
|
LibOrder.OrderInfo[] memory ordersInfo,
|
||||||
|
uint256[] memory fillableTakerAssetAmounts,
|
||||||
|
bool[] memory isValidSignature
|
||||||
|
)
|
||||||
|
{
|
||||||
|
uint256 length = orders.length;
|
||||||
|
ordersInfo = new LibOrder.OrderInfo[](length);
|
||||||
|
fillableTakerAssetAmounts = new uint256[](length);
|
||||||
|
isValidSignature = new bool[](length);
|
||||||
|
|
||||||
|
for (uint256 i = 0; i != length; i++) {
|
||||||
|
(ordersInfo[i], fillableTakerAssetAmounts[i], isValidSignature[i]) = getOrderRelevantState(
|
||||||
|
orders[i],
|
||||||
|
signatures[i]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (ordersInfo, fillableTakerAssetAmounts, isValidSignature);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Gets the amount of an asset transferable by the owner.
|
||||||
|
/// @param ownerAddress Address of the owner of the asset.
|
||||||
|
/// @param assetData Description of tokens, per the AssetProxy contract specification.
|
||||||
|
/// @return The amount of the asset tranferable by the owner.
|
||||||
|
/// NOTE: If the `assetData` encodes data for multiple assets, the `transferableAssetAmount`
|
||||||
|
/// will represent the amount of times the entire `assetData` can be transferred. To calculate
|
||||||
|
/// the total individual transferable amounts, this scaled `transferableAmount` must be multiplied by
|
||||||
|
/// the individual asset amounts located within the `assetData`.
|
||||||
|
function getTransferableAssetAmount(address ownerAddress, bytes memory assetData)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (uint256 transferableAssetAmount)
|
||||||
|
{
|
||||||
|
(uint256 balance, uint256 allowance) = getBalanceAndAssetProxyAllowance(ownerAddress, assetData);
|
||||||
|
transferableAssetAmount = LibSafeMath.min256(balance, allowance);
|
||||||
|
return transferableAssetAmount;
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user