Compare commits
2392 Commits
@0x/contra
...
0x.js@9.1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a133ca36f | ||
|
|
f7252f919a | ||
|
|
e05a03a842 | ||
|
|
dcce8276b8 | ||
|
|
fd47947e55 | ||
|
|
ae151df2eb | ||
|
|
79de188683 | ||
|
|
6e5c788e13 | ||
|
|
f53606007d | ||
|
|
a4ac418bc9 | ||
|
|
a8c09d0bdb | ||
|
|
871105a48a | ||
|
|
3b61129ade | ||
|
|
f471c79b59 | ||
|
|
dfd9443f74 | ||
|
|
a36ff9e365 | ||
|
|
12e65bbf26 | ||
|
|
ab9841e60b | ||
|
|
7a52f12e57 | ||
|
|
11fd4506ac | ||
|
|
0c9c68030e | ||
|
|
55d6eddbb2 | ||
|
|
8341e60edb | ||
|
|
6273a1ca73 | ||
|
|
1b83ebdf89 | ||
|
|
fef7f0506f | ||
|
|
f44eb4e383 | ||
|
|
05df485c4a | ||
|
|
44857c526b | ||
|
|
b8ad5d5d32 | ||
|
|
e3e0d00e21 | ||
|
|
a9b1ea9690 | ||
|
|
8e5dd0f8d9 | ||
|
|
5754c11e34 | ||
|
|
7bd88c1bb8 | ||
|
|
445b686c6c | ||
|
|
0cb7b75214 | ||
|
|
a08399dfee | ||
|
|
4016808fa4 | ||
|
|
8635849977 | ||
|
|
1a9ed4d4fe | ||
|
|
e7b3246dd0 | ||
|
|
19589aec57 | ||
|
|
4d33ff0417 | ||
|
|
93a5ab5b33 | ||
|
|
59ada06cdf | ||
|
|
ae650849b0 | ||
|
|
3e8f9a6b53 | ||
|
|
79362b0dba | ||
|
|
3e3df06d57 | ||
|
|
6b220eb1c5 | ||
|
|
a1c61cae11 | ||
|
|
d48a917bf3 | ||
|
|
646b6dafb2 | ||
|
|
8d10f33a3f | ||
|
|
5d603b2f80 | ||
|
|
0e1b08ff54 | ||
|
|
befc22d718 | ||
|
|
4a62e80967 | ||
|
|
ee9ef9f2c1 | ||
|
|
93dcb68437 | ||
|
|
0691cc7909 | ||
|
|
d82f34fe59 | ||
|
|
d2313b30af | ||
|
|
329719472a | ||
|
|
a2fcab47d4 | ||
|
|
4ab5951c25 | ||
|
|
403cabb201 | ||
|
|
3da7c5d3e2 | ||
|
|
c5e0de51aa | ||
|
|
c581f1bba4 | ||
|
|
b8ac9c2edd | ||
|
|
1027ee2481 | ||
|
|
2f311f7821 | ||
|
|
a98c95b514 | ||
|
|
5bbbae5b23 | ||
|
|
f9c9b9f924 | ||
|
|
5921208ea6 | ||
|
|
f89c78abd1 | ||
|
|
74d3b9334c | ||
|
|
919fc66b9d | ||
|
|
400fb5a5bb | ||
|
|
3bb4f9085c | ||
|
|
714c6cec3c | ||
|
|
cb69921202 | ||
|
|
277a0adac9 | ||
|
|
02d14f504f | ||
|
|
1ab7664a60 | ||
|
|
1be46ffb7e | ||
|
|
6ca52aed0d | ||
|
|
74e20970e2 | ||
|
|
93f2b6b4d8 | ||
|
|
00e616b57a | ||
|
|
e436673304 | ||
|
|
ce04d3ce41 | ||
|
|
4e46bf4697 | ||
|
|
9b95508f99 | ||
|
|
c8c24456c1 | ||
|
|
80a6e82e1b | ||
|
|
80cec20d38 | ||
|
|
26cb22020d | ||
|
|
09d05d09c9 | ||
|
|
160c91f908 | ||
|
|
58f41bcd42 | ||
|
|
0235429995 | ||
|
|
f79697e117 | ||
|
|
6f0019c71e | ||
|
|
1a04a18245 | ||
|
|
0cf3ff8209 | ||
|
|
7f56091fbd | ||
|
|
391f9b31f6 | ||
|
|
1d7c0f504a | ||
|
|
9cdc62f918 | ||
|
|
6027d0481e | ||
|
|
277fb92f9e | ||
|
|
79b6c3c1af | ||
|
|
ad17174119 | ||
|
|
6c2692eec0 | ||
|
|
08640e8575 | ||
|
|
4a2575136f | ||
|
|
e792afad17 | ||
|
|
5ecc4b027d | ||
|
|
bc540f0cf0 | ||
|
|
a24b14c465 | ||
|
|
c3f36c3123 | ||
|
|
1d34da7557 | ||
|
|
27d74a4327 | ||
|
|
8a20cc682c | ||
|
|
9b20359e7b | ||
|
|
8866d0ccef | ||
|
|
7fa91a9971 | ||
|
|
28d1f3eef0 | ||
|
|
f321cf6655 | ||
|
|
14ade737da | ||
|
|
41b1b1f141 | ||
|
|
25dfd47d32 | ||
|
|
6afa9c8b92 | ||
|
|
2fc449da4c | ||
|
|
5dd3b8cf9d | ||
|
|
e834fa0050 | ||
|
|
9a97401606 | ||
|
|
410a3fef18 | ||
|
|
969b9814d5 | ||
|
|
2275e27b87 | ||
|
|
62b06cd204 | ||
|
|
350934ca21 | ||
|
|
6332673434 | ||
|
|
f217840998 | ||
|
|
089ec35ceb | ||
|
|
fecd0b809e | ||
|
|
4707a46561 | ||
|
|
616533c5a8 | ||
|
|
c5b2991821 | ||
|
|
c36d0fdc7c | ||
|
|
544e09cf4b | ||
|
|
c110dc9e6a | ||
|
|
3bf37d6afd | ||
|
|
b80ae5796b | ||
|
|
2083632299 | ||
|
|
3ca2f8ac9e | ||
|
|
7172432084 | ||
|
|
0e6afd147f | ||
|
|
46275a4f43 | ||
|
|
1dca378e03 | ||
|
|
06669594b1 | ||
|
|
c09ac58ac0 | ||
|
|
e01d32ef1a | ||
|
|
5ea3bcf59e | ||
|
|
aa8b14b7ee | ||
|
|
e1722cf739 | ||
|
|
7a7f70e15d | ||
|
|
b3c3ec16e5 | ||
|
|
149f863951 | ||
|
|
684d09faac | ||
|
|
8a42691c80 | ||
|
|
d591b3dd98 | ||
|
|
a90fb4d8b6 | ||
|
|
ebd08d9c63 | ||
|
|
71731d223b | ||
|
|
726ea5e01e | ||
|
|
16c7d2964b | ||
|
|
5a6e494bda | ||
|
|
88c6d89fbb | ||
|
|
de12da18da | ||
|
|
8d10736934 | ||
|
|
2328e02d82 | ||
|
|
87cd5fca90 | ||
|
|
b70cb726c5 | ||
|
|
295811ed5a | ||
|
|
4bc55551c6 | ||
|
|
2b8c6dc8f9 | ||
|
|
8b27380feb | ||
|
|
8de3a90851 | ||
|
|
f8bb94d721 | ||
|
|
0c6d06e7bb | ||
|
|
b0aa5d3af2 | ||
|
|
27d09713fd | ||
|
|
ff18852879 | ||
|
|
e515c91e5e | ||
|
|
1d5800c4f7 | ||
|
|
de8f190945 | ||
|
|
f371e3c8d3 | ||
|
|
b15a6290a7 | ||
|
|
0f151db355 | ||
|
|
fa99b75d1f | ||
|
|
4a299c1f39 | ||
|
|
30a2015a68 | ||
|
|
c7c8a4891f | ||
|
|
fe9fc6b459 | ||
|
|
2113fb490d | ||
|
|
0afedbd252 | ||
|
|
3dec38450a | ||
|
|
d7a00b05e3 | ||
|
|
ff2cc8c887 | ||
|
|
0571a96cea | ||
|
|
b7b457b076 | ||
|
|
d2c12005b2 | ||
|
|
25f26d7e5f | ||
|
|
9d5724e1a0 | ||
|
|
784d23ec87 | ||
|
|
709689a7ee | ||
|
|
35d5d3d995 | ||
|
|
630a8d8a4e | ||
|
|
54eb1d9055 | ||
|
|
cb63caea61 | ||
|
|
8e046bb022 | ||
|
|
189b53b8c4 | ||
|
|
9b7277d464 | ||
|
|
551a65c069 | ||
|
|
4c21a697f4 | ||
|
|
a8506c07ae | ||
|
|
b0feb85b5c | ||
|
|
f371eba8ad | ||
|
|
265fa52ace | ||
|
|
c1f5322d38 | ||
|
|
4415e00b38 | ||
|
|
d4e46c5a9c | ||
|
|
bf5b9949fe | ||
|
|
3c11a2b1da | ||
|
|
1248868169 | ||
|
|
930b95a548 | ||
|
|
27c9f68c7c | ||
|
|
358d4d86a7 | ||
|
|
d55eea2239 | ||
|
|
4507954ea5 | ||
|
|
8e0a83f8d8 | ||
|
|
b6ec09e6cf | ||
|
|
ed4e90623d | ||
|
|
38cdb48748 | ||
|
|
71bfe9b745 | ||
|
|
9e7645a167 | ||
|
|
6dccc37143 | ||
|
|
3310310d8c | ||
|
|
abb499aad8 | ||
|
|
1afc09b08a | ||
|
|
0e86d72f05 | ||
|
|
c9857a2764 | ||
|
|
701ba3902c | ||
|
|
bb3ec970a9 | ||
|
|
1d023e6db5 | ||
|
|
1bd906ecb3 | ||
|
|
7cbffdb86b | ||
|
|
b979196ffd | ||
|
|
2949db5f49 | ||
|
|
47c3ed9705 | ||
|
|
51ca3109eb | ||
|
|
2bcb79dc44 | ||
|
|
ecec985649 | ||
|
|
994908549d | ||
|
|
e00f059a4a | ||
|
|
6808e0d531 | ||
|
|
410c95308a | ||
|
|
bec1f23616 | ||
|
|
34596b7f83 | ||
|
|
5ca7169ee5 | ||
|
|
3300aaa1b9 | ||
|
|
54afc8a4a1 | ||
|
|
f19f4310f4 | ||
|
|
444125a7e1 | ||
|
|
56cbb69401 | ||
|
|
70870ffcd2 | ||
|
|
a556d91673 | ||
|
|
8ecbde8e1e | ||
|
|
a24b293818 | ||
|
|
cab5ebf94b | ||
|
|
a54b5baef2 | ||
|
|
c324fe204e | ||
|
|
37d972ed9e | ||
|
|
e4a3b1cb05 | ||
|
|
49538f272e | ||
|
|
1283232144 | ||
|
|
2f9891f0aa | ||
|
|
865a2b1fb0 | ||
|
|
1fde62eeb6 | ||
|
|
6754cd48e2 | ||
|
|
ccb477687a | ||
|
|
be0e6c8925 | ||
|
|
1c2cb947c0 | ||
|
|
4663eec950 | ||
|
|
fff3c1eb36 | ||
|
|
4b7434d1e8 | ||
|
|
8cc35a60e6 | ||
|
|
130653a1aa | ||
|
|
1dcbebd130 | ||
|
|
faf306ad23 | ||
|
|
d11cdcd5d2 | ||
|
|
0e59bd0bf3 | ||
|
|
c0c6154ec1 | ||
|
|
cb5384c2fb | ||
|
|
038c836fe5 | ||
|
|
6b0f3570b9 | ||
|
|
71de0d04f3 | ||
|
|
99debff5d2 | ||
|
|
3bac6fcb27 | ||
|
|
4b842b81a0 | ||
|
|
e2e4d048ab | ||
|
|
5574c368cd | ||
|
|
0d34f7b92e | ||
|
|
5be0632e01 | ||
|
|
79ea0bf9f4 | ||
|
|
b1929cb688 | ||
|
|
5ad98700e5 | ||
|
|
a54624b697 | ||
|
|
ca34c865af | ||
|
|
dde57b1eca | ||
|
|
264b06938e | ||
|
|
99edb303e2 | ||
|
|
104cc24dfc | ||
|
|
fcbcbac889 | ||
|
|
b86d19028c | ||
|
|
4f17a251d3 | ||
|
|
731a823cc2 | ||
|
|
3d79fe2bf4 | ||
|
|
474399154f | ||
|
|
19f5153d0e | ||
|
|
ce11271866 | ||
|
|
86cf353296 | ||
|
|
36df5dc721 | ||
|
|
1e44a9c942 | ||
|
|
8685cf9036 | ||
|
|
2232870b09 | ||
|
|
b68acd101e | ||
|
|
173ba9b2b5 | ||
|
|
64ed1f87d3 | ||
|
|
1ca085ec4a | ||
|
|
e332b7535c | ||
|
|
79eb613b3e | ||
|
|
5a79ec28d1 | ||
|
|
97e65a02c0 | ||
|
|
e87c786b77 | ||
|
|
251d30d47f | ||
|
|
761d0a0f18 | ||
|
|
ae4b1e74f9 | ||
|
|
ac44618e58 | ||
|
|
d634cbf924 | ||
|
|
21db0e6275 | ||
|
|
ce426fd3f4 | ||
|
|
b5d4c91207 | ||
|
|
b43263be77 | ||
|
|
207cf7ca24 | ||
|
|
00e34758c4 | ||
|
|
7a3f878c11 | ||
|
|
b8439598bc | ||
|
|
7fb0818923 | ||
|
|
a7c435adc4 | ||
|
|
dd90aabad6 | ||
|
|
5bded1946e | ||
|
|
3642e96154 | ||
|
|
9da09ee3a6 | ||
|
|
141c140f53 | ||
|
|
84b660d2ef | ||
|
|
6beedba957 | ||
|
|
d73982819b | ||
|
|
6ac5bcc907 | ||
|
|
389d4d10f1 | ||
|
|
89dcbd0229 | ||
|
|
ad8caa2b51 | ||
|
|
9c42241269 | ||
|
|
38dd45cce2 | ||
|
|
aa90253c62 | ||
|
|
41576652dc | ||
|
|
74830854ca | ||
|
|
2542b1b44d | ||
|
|
51f5e60224 | ||
|
|
bb5885e2bb | ||
|
|
d51bbb0008 | ||
|
|
49e898b189 | ||
|
|
42c4fe5705 | ||
|
|
4b5f2c36b9 | ||
|
|
935dca67e6 | ||
|
|
d431790e19 | ||
|
|
56310b7bd4 | ||
|
|
f15e21faad | ||
|
|
44aa6a2b38 | ||
|
|
9f32347c01 | ||
|
|
3d5b229c46 | ||
|
|
5863ccc0a0 | ||
|
|
d220a16b99 | ||
|
|
79784fc8ee | ||
|
|
a83bc53c6a | ||
|
|
85de0b91b1 | ||
|
|
d91c6e5702 | ||
|
|
ab7689d188 | ||
|
|
c81455c760 | ||
|
|
39bfc97a7a | ||
|
|
88aac78282 | ||
|
|
863e830d24 | ||
|
|
6c705728a4 | ||
|
|
7f00279ffb | ||
|
|
c198d0079e | ||
|
|
1135d5a971 | ||
|
|
e299fa27a0 | ||
|
|
46e0bc940a | ||
|
|
9a552012f2 | ||
|
|
6498d385ee | ||
|
|
dd00f2016f | ||
|
|
64d25e6522 | ||
|
|
1462ab08de | ||
|
|
a8e93a594d | ||
|
|
dea30b37ef | ||
|
|
39571dda0b | ||
|
|
c7d801b6c2 | ||
|
|
57731be689 | ||
|
|
f00524e518 | ||
|
|
5567c40bae | ||
|
|
5d1a7613dd | ||
|
|
fa768dc112 | ||
|
|
27fb51d37f | ||
|
|
d02db3864e | ||
|
|
a26c3036a7 | ||
|
|
0af346aad8 | ||
|
|
c3c8ee7292 | ||
|
|
5fbdfa66d9 | ||
|
|
15b75715ee | ||
|
|
1fd92b6cbd | ||
|
|
2918b5d74e | ||
|
|
669c5be344 | ||
|
|
e1b40ec46e | ||
|
|
15767538eb | ||
|
|
de2b16c464 | ||
|
|
d5e6b38450 | ||
|
|
a636e87a4f | ||
|
|
50d5b4fa37 | ||
|
|
f6d26392fb | ||
|
|
2705bcce15 | ||
|
|
379a31ece6 | ||
|
|
daa593d225 | ||
|
|
ed8340affa | ||
|
|
b3c1e72577 | ||
|
|
3da09d140a | ||
|
|
51f254bbb1 | ||
|
|
30ee456d4c | ||
|
|
460d5f2517 | ||
|
|
5da1fc8445 | ||
|
|
1166b43946 | ||
|
|
0a6903c4c3 | ||
|
|
62fae9af93 | ||
|
|
509a1c2eb5 | ||
|
|
8b94bbbc5e | ||
|
|
bb923d2b7d | ||
|
|
38adc72954 | ||
|
|
362c7c57fa | ||
|
|
6529b7eebf | ||
|
|
439c98a6e5 | ||
|
|
32258ef666 | ||
|
|
176e088d4e | ||
|
|
4fe57ba025 | ||
|
|
2818e56932 | ||
|
|
5428a19617 | ||
|
|
b58cbca61a | ||
|
|
5fc6a03784 | ||
|
|
eb4ad0ba1b | ||
|
|
72cdd1ea50 | ||
|
|
18769f0b8f | ||
|
|
b7d92c3c12 | ||
|
|
b976101dca | ||
|
|
8943b670a4 | ||
|
|
c92ff7c622 | ||
|
|
301b5e1721 | ||
|
|
4e50b9b479 | ||
|
|
f8b7b8cc28 | ||
|
|
2a6ea74be7 | ||
|
|
6d6a0c12cd | ||
|
|
784a03300a | ||
|
|
392f578567 | ||
|
|
a91b1d2dd2 | ||
|
|
400b3d961e | ||
|
|
4f128470bd | ||
|
|
fe06f41136 | ||
|
|
f81a99565e | ||
|
|
81e146650b | ||
|
|
bd4e04d331 | ||
|
|
7663d2c64b | ||
|
|
443c3c2802 | ||
|
|
17a546af5d | ||
|
|
71faf46735 | ||
|
|
ac28744df6 | ||
|
|
adaf304b4e | ||
|
|
16b13f9768 | ||
|
|
d64bf98dc0 | ||
|
|
71f57d13fa | ||
|
|
469c10e45f | ||
|
|
62def596af | ||
|
|
aa10844d9e | ||
|
|
be52079182 | ||
|
|
255aca8789 | ||
|
|
117f4a282d | ||
|
|
e1ea65525f | ||
|
|
d0a3495b5f | ||
|
|
9e9e0d6592 | ||
|
|
cb5f9ba97d | ||
|
|
34538f2ced | ||
|
|
2575644920 | ||
|
|
b4b43a9e9e | ||
|
|
df97b20913 | ||
|
|
9e3cc379ed | ||
|
|
c1d78a94a2 | ||
|
|
7f4cbba076 | ||
|
|
bdca84fe72 | ||
|
|
cf8fd7103b | ||
|
|
8e8cdbd413 | ||
|
|
30f01681d4 | ||
|
|
ecf1ad8da1 | ||
|
|
42dc112a13 | ||
|
|
58276cefce | ||
|
|
4b6501a739 | ||
|
|
724085e068 | ||
|
|
21fab3ef9f | ||
|
|
db8837f4ce | ||
|
|
5781cdf6da | ||
|
|
874eb1602f | ||
|
|
82149917b7 | ||
|
|
f11d8a5bd8 | ||
|
|
f0d7d10fe7 | ||
|
|
9d4d9ce978 | ||
|
|
96a38602b8 | ||
|
|
90d3558d31 | ||
|
|
e491a56dd0 | ||
|
|
4d8eb61924 | ||
|
|
17fab541c6 | ||
|
|
91de35e8e9 | ||
|
|
f61964676a | ||
|
|
41a34c19bb | ||
|
|
d90810d127 | ||
|
|
7f3d281faa | ||
|
|
812c306805 | ||
|
|
fc1c59f374 | ||
|
|
35eac1e3ff | ||
|
|
e16041d7fa | ||
|
|
b8fc84ecc8 | ||
|
|
572c576e15 | ||
|
|
9df7f80fbb | ||
|
|
f003400135 | ||
|
|
ca7616c1d2 | ||
|
|
a4a2bfdf35 | ||
|
|
eb6bbb6e78 | ||
|
|
4d0172f634 | ||
|
|
3b61e0e126 | ||
|
|
1540a91835 | ||
|
|
2bcce9eed0 | ||
|
|
1e53564386 | ||
|
|
d1c72706ef | ||
|
|
bd9e531257 | ||
|
|
48436424db | ||
|
|
4f10d7f859 | ||
|
|
80e5a29444 | ||
|
|
0ec8a4a160 | ||
|
|
810bf7af0c | ||
|
|
e7825206bf | ||
|
|
2b887c336a | ||
|
|
48ecd32d5d | ||
|
|
1f5a0987cb | ||
|
|
f33a9d162a | ||
|
|
c2919bcdb0 | ||
|
|
120d554a6b | ||
|
|
44f268a7ee | ||
|
|
3c7a0bcd85 | ||
|
|
8e2e9e9331 | ||
|
|
91c26fc046 | ||
|
|
afcfe58add | ||
|
|
8d423be223 | ||
|
|
03b7314550 | ||
|
|
1a7e425780 | ||
|
|
8bc5faff3c | ||
|
|
2676278a66 | ||
|
|
6376b3baf3 | ||
|
|
e569abe740 | ||
|
|
71be9ef92a | ||
|
|
4990c4903d | ||
|
|
9d468e2383 | ||
|
|
109cac013c | ||
|
|
0d8a9921cd | ||
|
|
2a5f5f7312 | ||
|
|
fe54fbefbb | ||
|
|
fc824b8d06 | ||
|
|
d91a7b6d0e | ||
|
|
aa4b3f93fa | ||
|
|
efe8225d18 | ||
|
|
b2c0f8c158 | ||
|
|
66dce8794d | ||
|
|
30d54407e6 | ||
|
|
6324a92ec5 | ||
|
|
67e7b5c124 | ||
|
|
35099d9b2f | ||
|
|
e07f7b54e0 | ||
|
|
5c409929b4 | ||
|
|
1a504fdde9 | ||
|
|
4b06fd511b | ||
|
|
def6727286 | ||
|
|
bedaa0db16 | ||
|
|
90640a4fcf | ||
|
|
0142d07f10 | ||
|
|
c9d85cfc7d | ||
|
|
64304c1991 | ||
|
|
993adc3578 | ||
|
|
8813bd26f6 | ||
|
|
35925de320 | ||
|
|
3b426a3f07 | ||
|
|
5104fd5dcf | ||
|
|
a5a9ca9e46 | ||
|
|
ba0f07e3b2 | ||
|
|
8614475324 | ||
|
|
744dda144b | ||
|
|
13d47915f4 | ||
|
|
3059b85e41 | ||
|
|
184ea4a67f | ||
|
|
8032f536ed | ||
|
|
fba3870ef1 | ||
|
|
2915ee08ea | ||
|
|
86b76a3e75 | ||
|
|
bc1dca3f6f | ||
|
|
5db1820123 | ||
|
|
657c35fb86 | ||
|
|
9432a84468 | ||
|
|
15a5bc02ef | ||
|
|
f011be9347 | ||
|
|
b6094fdb34 | ||
|
|
9e6ab9f585 | ||
|
|
869d2c02fa | ||
|
|
3b1dca0e70 | ||
|
|
595358fa69 | ||
|
|
8a8ec79c6c | ||
|
|
6252446bd3 | ||
|
|
403ceebff9 | ||
|
|
4767882ed3 | ||
|
|
3b9d84fa58 | ||
|
|
6fd96a6fd7 | ||
|
|
c93b02d55e | ||
|
|
568f87d5eb | ||
|
|
49ad0f0d54 | ||
|
|
0e642f59e1 | ||
|
|
7c5730fb03 | ||
|
|
45f0f755ab | ||
|
|
1ef2913c5b | ||
|
|
fecbf220b6 | ||
|
|
17a5f05cf3 | ||
|
|
6a852ab0ed | ||
|
|
ec26cff656 | ||
|
|
cdd34a1214 | ||
|
|
857a4042ef | ||
|
|
f51c80adb2 | ||
|
|
e61f23d001 | ||
|
|
cbe4c4fbf9 | ||
|
|
deffdabc30 | ||
|
|
8811a5387a | ||
|
|
9336d4e545 | ||
|
|
f65d8cc325 | ||
|
|
68656c4083 | ||
|
|
44793a9cf9 | ||
|
|
2d0ad6f181 | ||
|
|
a7f0717afb | ||
|
|
a9022352e7 | ||
|
|
9b2231ed24 | ||
|
|
c123200f38 | ||
|
|
3c6c4128a6 | ||
|
|
47e050cbaf | ||
|
|
62d15117c5 | ||
|
|
fb8360edfd | ||
|
|
e557f2fb48 | ||
|
|
c957b48281 | ||
|
|
c15c5e12b0 | ||
|
|
15c3c8074c | ||
|
|
cba72c811d | ||
|
|
28a2e56003 | ||
|
|
8c4c3d56c6 | ||
|
|
911fcc0bed | ||
|
|
55fd16ccf1 | ||
|
|
4e05e41f7f | ||
|
|
ebab80cff7 | ||
|
|
91cb162662 | ||
|
|
fa26f8de51 | ||
|
|
26602ac2db | ||
|
|
0b8af181d8 | ||
|
|
19d661d324 | ||
|
|
7d29b36246 | ||
|
|
8ba7b95e86 | ||
|
|
30c72daed5 | ||
|
|
575cb99839 | ||
|
|
0c064bf85b | ||
|
|
0f3610c92a | ||
|
|
c8ef10baaf | ||
|
|
16dc73bd1e | ||
|
|
4f56d68689 | ||
|
|
8e6d92cad5 | ||
|
|
3a1c464543 | ||
|
|
a0c2f6b7b4 | ||
|
|
7bfbf0ad3a | ||
|
|
82ee6750c7 | ||
|
|
c37fc30c55 | ||
|
|
09d13b2bfa | ||
|
|
af0de72bc3 | ||
|
|
43e32f6a1a | ||
|
|
e9e6452890 | ||
|
|
5f699b0c47 | ||
|
|
bf18b86f9f | ||
|
|
56f7dd7538 | ||
|
|
7aa88307f6 | ||
|
|
8aa69233e0 | ||
|
|
e843333918 | ||
|
|
133a4dc4e1 | ||
|
|
c7945a542e | ||
|
|
b4e00baa07 | ||
|
|
dde570706a | ||
|
|
0b3e3ab990 | ||
|
|
205c895d75 | ||
|
|
6402d29dd4 | ||
|
|
dc18999931 | ||
|
|
43f8101d0b | ||
|
|
3d56c06ff3 | ||
|
|
db9be73fec | ||
|
|
a02892cbc8 | ||
|
|
49c67fbb18 | ||
|
|
6f2e79208a | ||
|
|
ceb3ba4116 | ||
|
|
08d4f1402f | ||
|
|
77fa97f259 | ||
|
|
3ac5d9add5 | ||
|
|
cab89f312a | ||
|
|
8972475389 | ||
|
|
330f2d54e2 | ||
|
|
9c181f09ba | ||
|
|
011ecb8f4b | ||
|
|
bc2a9beb14 | ||
|
|
091f5ed8b8 | ||
|
|
ea9f535a7c | ||
|
|
f246314b1d | ||
|
|
cdfd62a296 | ||
|
|
dcff7d511b | ||
|
|
16a5475d24 | ||
|
|
42468c3fa2 | ||
|
|
9312d5d9f7 | ||
|
|
33a0c22021 | ||
|
|
58e9c70203 | ||
|
|
0067f10a6a | ||
|
|
59210f5e5e | ||
|
|
1c695b2759 | ||
|
|
c7222c17ae | ||
|
|
0f237d22f9 | ||
|
|
b1b1162b60 | ||
|
|
6ee1605a77 | ||
|
|
a22b2e7a9f | ||
|
|
86ed32a007 | ||
|
|
8e8ea6a3ab | ||
|
|
cc7452da8f | ||
|
|
06715201a7 | ||
|
|
281658ba34 | ||
|
|
f192648c76 | ||
|
|
07e1d502e7 | ||
|
|
703e890918 | ||
|
|
096950729e | ||
|
|
8869d79c68 | ||
|
|
752dd04546 | ||
|
|
3e5d166ec4 | ||
|
|
64bc1b0990 | ||
|
|
548b0db6ea | ||
|
|
c9607e8b2c | ||
|
|
c676ecb8cf | ||
|
|
39804fdc83 | ||
|
|
1a1dc89454 | ||
|
|
e427698956 | ||
|
|
575af6b6e8 | ||
|
|
3a1fc9ee5f | ||
|
|
1237c7d479 | ||
|
|
c44e16a88f | ||
|
|
06c180475e | ||
|
|
74a2c3a199 | ||
|
|
9ac715f99d | ||
|
|
22e39f782f | ||
|
|
f5a6b84fa3 | ||
|
|
718407ba6f | ||
|
|
e603a81a46 | ||
|
|
03e35846fb | ||
|
|
c87364f86b | ||
|
|
a794a33551 | ||
|
|
494b437f1a | ||
|
|
92b80fc436 | ||
|
|
d66101cd9d | ||
|
|
89ae04803f | ||
|
|
be95bce4cd | ||
|
|
01aee08c02 | ||
|
|
6cba9fd77f | ||
|
|
673d45361f | ||
|
|
d91a7fc663 | ||
|
|
ce8fd44234 | ||
|
|
6617ad9531 | ||
|
|
10f8051835 | ||
|
|
e7dc7167d0 | ||
|
|
359b804001 | ||
|
|
fd9084b345 | ||
|
|
44dac2cd80 | ||
|
|
a66ea2bf74 | ||
|
|
a362e9d2d8 | ||
|
|
1885957bd3 | ||
|
|
1a409c3731 | ||
|
|
7b7c64fc6a | ||
|
|
102fcd3fb8 | ||
|
|
566e05aea4 | ||
|
|
f014370531 | ||
|
|
dfbbe9daa2 | ||
|
|
6b653fb00d | ||
|
|
21cf2319d5 | ||
|
|
4210477e71 | ||
|
|
93b02e93b9 | ||
|
|
f4cb8cfb7e | ||
|
|
ce9f051d42 | ||
|
|
083216a0c6 | ||
|
|
820b40e227 | ||
|
|
59a38a8db0 | ||
|
|
d0884dcb4d | ||
|
|
c7ca625408 | ||
|
|
e46f51339a | ||
|
|
b45ec47eee | ||
|
|
c50cbd7a75 | ||
|
|
5ddc35fdf2 | ||
|
|
d6c064b9c3 | ||
|
|
caf6329bb3 | ||
|
|
008938cf5b | ||
|
|
3fd29656cb | ||
|
|
ffac52f42e | ||
|
|
9114510c00 | ||
|
|
2dbda6fc42 | ||
|
|
eae4001622 | ||
|
|
727d0498b6 | ||
|
|
e43f2d39bf | ||
|
|
cde0169733 | ||
|
|
0e90b0e7d0 | ||
|
|
b793a31cdd | ||
|
|
23198174f3 | ||
|
|
523bc3f951 | ||
|
|
41d99e77c7 | ||
|
|
90193c8197 | ||
|
|
6f5c62914e | ||
|
|
17faeae47d | ||
|
|
7283a16710 | ||
|
|
52c3dc4ad8 | ||
|
|
1cf8ae5909 | ||
|
|
51282953bd | ||
|
|
a6603d6bd6 | ||
|
|
54a03eacd6 | ||
|
|
43fa753a13 | ||
|
|
9d9fe882b6 | ||
|
|
4f6958b7b5 | ||
|
|
9a5752fff9 | ||
|
|
c21932d149 | ||
|
|
ce6c05637f | ||
|
|
b0699fc238 | ||
|
|
8bf7c4cf48 | ||
|
|
9f6d113fe8 | ||
|
|
646507c41a | ||
|
|
65f2626544 | ||
|
|
7155d878b3 | ||
|
|
361576814c | ||
|
|
aa541d0cad | ||
|
|
7e58385a78 | ||
|
|
b5545255d0 | ||
|
|
a22ba8647c | ||
|
|
22fc0b4337 | ||
|
|
063d6ff24e | ||
|
|
09c0b83fe3 | ||
|
|
a42f3d189c | ||
|
|
7815da7257 | ||
|
|
8e2b971f5a | ||
|
|
3fd7132a0d | ||
|
|
93edb083fa | ||
|
|
9e41c648dc | ||
|
|
a7ef54dbff | ||
|
|
414084a7ad | ||
|
|
681e6eab7a | ||
|
|
701b203c58 | ||
|
|
cbd0ca4b60 | ||
|
|
1626566f93 | ||
|
|
ac75053f69 | ||
|
|
13afc65b54 | ||
|
|
aa0a1bb54d | ||
|
|
2e36c7ef83 | ||
|
|
43399a9ad9 | ||
|
|
2ef546210d | ||
|
|
7b379f3933 | ||
|
|
f8ac986a0f | ||
|
|
dc0a78434d | ||
|
|
d1b0384aef | ||
|
|
7ac7f45c4a | ||
|
|
b3c7ccec57 | ||
|
|
93725ecec0 | ||
|
|
3c31ef188a | ||
|
|
53df2130ea | ||
|
|
8b695f9b98 | ||
|
|
d914f6fce9 | ||
|
|
e2e5152648 | ||
|
|
d3dcf7fb0c | ||
|
|
a0f5a8b64b | ||
|
|
ee508f70bc | ||
|
|
200b3d450f | ||
|
|
52fc7517f9 | ||
|
|
cf517b1459 | ||
|
|
c17984b74f | ||
|
|
589d2212ee | ||
|
|
9b922f746b | ||
|
|
0e7387550c | ||
|
|
dbf22583b5 | ||
|
|
6825eb442b | ||
|
|
45f284973a | ||
|
|
ef6e691646 | ||
|
|
e67888d65f | ||
|
|
584f8b13fe | ||
|
|
f993b6d1ed | ||
|
|
035dc607db | ||
|
|
cf2053ec77 | ||
|
|
3840ebf538 | ||
|
|
80cb6b654b | ||
|
|
ab70c4df74 | ||
|
|
95e461072f | ||
|
|
2593f1ff30 | ||
|
|
c2261a6bbe | ||
|
|
b383781870 | ||
|
|
7d121bafd0 | ||
|
|
6a2911d10f | ||
|
|
17362bcf44 | ||
|
|
87906a3af1 | ||
|
|
c0c27ed637 | ||
|
|
6be5552944 | ||
|
|
b4ae42cc9a | ||
|
|
3c6957095d | ||
|
|
2020d87824 | ||
|
|
ac1063dd68 | ||
|
|
b8e01d7be5 | ||
|
|
24e4567b25 | ||
|
|
ccf40fd65e | ||
|
|
d4729e2669 | ||
|
|
52d38c63de | ||
|
|
086c30831d | ||
|
|
4be83de7e5 | ||
|
|
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 | ||
|
|
650efb95e2 | ||
|
|
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 | ||
|
|
3948f8b66b | ||
|
|
0ff8b12770 | ||
|
|
39a8c0f4e6 | ||
|
|
8c5e12d389 | ||
|
|
71660850af | ||
|
|
b83043648e | ||
|
|
b01de802cb | ||
|
|
e954e9ca20 | ||
|
|
3f8639bd9c | ||
|
|
ffcd297e5b | ||
|
|
56c956df44 | ||
|
|
c4b621e44d | ||
|
|
cfb099c65c | ||
|
|
ba5c702a9e | ||
|
|
9d4010299a | ||
|
|
b5492bb023 | ||
|
|
76724a6c73 | ||
|
|
57ec0858fe | ||
|
|
b281b9aac8 | ||
|
|
f4453c0966 | ||
|
|
ebd328db06 | ||
|
|
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 | ||
|
|
4c78b7d4bb | ||
|
|
8402d211bf | ||
|
|
cc3be9448a | ||
|
|
7ac30c5153 | ||
|
|
64bc99101c | ||
|
|
09b5018e65 | ||
|
|
1dae1d244c | ||
|
|
2da996f493 | ||
|
|
c5d4559300 | ||
|
|
434d027133 | ||
|
|
f66212ce23 | ||
|
|
6b4e632101 | ||
|
|
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 | ||
|
|
dd4541c825 | ||
|
|
98f77394ed | ||
|
|
fc18db10be | ||
|
|
9382e2e8c7 | ||
|
|
a3b2dbf8e2 | ||
|
|
6df190edbb | ||
|
|
5b0b8a9717 | ||
|
|
a8ddbe4127 | ||
|
|
6e1fdda182 | ||
|
|
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 | ||
|
|
e3aa76cd09 | ||
|
|
de897d2ebf | ||
|
|
08118ec36f | ||
|
|
f757a9de52 | ||
|
|
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 | ||
|
|
e7df9d1754 | ||
|
|
d3ab2b077a | ||
|
|
467a11f4b4 | ||
|
|
6cb8c1df42 | ||
|
|
3915c7e8f2 | ||
|
|
5ba0e0dc54 | ||
|
|
9a35e2db77 | ||
|
|
e204a6d1d0 | ||
|
|
47ab2a1b1d | ||
|
|
6fc38292f2 | ||
|
|
89d8df3385 | ||
|
|
9c7df2b41e | ||
|
|
46f6816511 | ||
|
|
858ccfa934 | ||
|
|
b4a3218b13 | ||
|
|
92a4556956 | ||
|
|
bd42c33daa | ||
|
|
e1796a9f0f | ||
|
|
8c5c81fe70 | ||
|
|
bf8fae2025 | ||
|
|
7f2b715ceb | ||
|
|
974189045a | ||
|
|
77feaec444 | ||
|
|
e5b6921de9 | ||
|
|
3ca3a2820d | ||
|
|
9f4933e33d | ||
|
|
3cf48a831b | ||
|
|
930b742663 | ||
|
|
d3870fed1c | ||
|
|
99e242affd | ||
|
|
9792246970 | ||
|
|
020b953166 | ||
|
|
52aa8e914a | ||
|
|
e01eadaecd | ||
|
|
61fc32b7c0 | ||
|
|
a9c8207bb0 | ||
|
|
eac4f172fe | ||
|
|
d6271426fd | ||
|
|
7e59110049 | ||
|
|
8cf4fb9adc | ||
|
|
f044f364cb | ||
|
|
4d39892a11 | ||
|
|
bf1ebe8e53 | ||
|
|
77b4f32274 | ||
|
|
b2ada13a21 | ||
|
|
03fced81f5 | ||
|
|
f9292a8fb8 | ||
|
|
065f46a020 | ||
|
|
6efb7027b5 | ||
|
|
f4f922acb5 | ||
|
|
4a4d2e7079 | ||
|
|
1634c90179 | ||
|
|
9b5ba6806f | ||
|
|
4afca6ca8d | ||
|
|
90d1decb87 | ||
|
|
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 |
@@ -23,37 +23,21 @@ jobs:
|
|||||||
# command: npm set prefix=/home/circleci/npm && echo 'export PATH=$HOME/circleci/npm/bin:$PATH' >> /home/circleci/.bashrc
|
# 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: npm install --global yarn@1.9.4
|
command: npm install --force --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
|
||||||
- save_cache:
|
|
||||||
key: python-contract-wrappers-{{ .Environment.CIRCLE_SHA1 }}
|
|
||||||
paths:
|
|
||||||
- ~/repo/packages/python-contract-wrappers/generated
|
|
||||||
- store_artifacts:
|
|
||||||
path: ~/repo/packages/python-contract-wrappers/generated
|
|
||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
path: ~/repo/packages/abi-gen/test-cli/output
|
path: ~/repo/packages/abi-gen/test-cli/output
|
||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
path: ~/repo/packages/abi-gen-wrappers/generated_docs
|
path: ~/repo/packages/contract-wrappers/generated_docs
|
||||||
build-website:
|
|
||||||
resource_class: medium+
|
|
||||||
docker:
|
|
||||||
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
|
||||||
working_directory: ~/repo
|
|
||||||
steps:
|
|
||||||
- restore_cache:
|
|
||||||
keys:
|
|
||||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
|
||||||
- run: cd packages/website && yarn build:prod
|
|
||||||
test-contracts-ganache:
|
test-contracts-ganache:
|
||||||
resource_class: medium+
|
resource_class: medium+
|
||||||
docker:
|
docker:
|
||||||
@@ -63,19 +47,41 @@ jobs:
|
|||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
- run: yarn wsrun test:circleci @0x/contracts-multisig @0x/contracts-utils @0x/contracts-exchange-libs @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-extensions @0x/contracts-asset-proxy @0x/contracts-exchange @0x/contracts-exchange-forwarder @0x/contracts-coordinator @0x/contracts-dev-utils
|
- run: yarn wsrun test:circleci @0x/contracts-multisig @0x/contracts-utils @0x/contracts-exchange-libs @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-extensions @0x/contracts-asset-proxy @0x/contracts-exchange @0x/contracts-exchange-forwarder @0x/contracts-coordinator @0x/contracts-staking
|
||||||
test-contracts-geth:
|
test-exchange-ganache-3.0:
|
||||||
|
resource_class: medium+
|
||||||
docker:
|
docker:
|
||||||
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
- 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-exchange
|
||||||
# initialized
|
test-integrations-ganache-3.0:
|
||||||
- run: sleep 10 && TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-multisig @0x/contracts-utils @0x/contracts-exchange-libs @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-extensions @0x/contracts-asset-proxy @0x/contracts-exchange @0x/contracts-exchange-forwarder @0x/contracts-coordinator @0x/contracts-dev-utils
|
resource_class: medium+
|
||||||
|
docker:
|
||||||
|
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||||
|
working_directory: ~/repo
|
||||||
|
steps:
|
||||||
|
- restore_cache:
|
||||||
|
keys:
|
||||||
|
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
|
- run: yarn wsrun test:circleci @0x/contracts-integrations
|
||||||
|
test-contracts-rest-ganache-3.0:
|
||||||
|
resource_class: medium+
|
||||||
|
docker:
|
||||||
|
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||||
|
working_directory: ~/repo
|
||||||
|
steps:
|
||||||
|
- restore_cache:
|
||||||
|
keys:
|
||||||
|
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
|
- run: yarn wsrun test:circleci @0x/contracts-multisig @0x/contracts-utils @0x/contracts-exchange-libs @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-asset-proxy @0x/contracts-exchange-forwarder @0x/contracts-staking @0x/contracts-coordinator @0x/contracts-erc20-bridge-sampler
|
||||||
|
# TODO(dorothy-zbornak): Re-enable after updating this package for
|
||||||
|
# 3.0. At that time, also remove exclusion from monorepo
|
||||||
|
# package.json's test script.
|
||||||
|
# - run: yarn wsrun test:circleci @0x/contracts-extensions
|
||||||
test-publish:
|
test-publish:
|
||||||
resource_class: medium+
|
resource_class: medium+
|
||||||
docker:
|
docker:
|
||||||
@@ -86,7 +92,9 @@ jobs:
|
|||||||
- 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: nikolaik/python-nodejs:python3.7-nodejs8
|
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||||
@@ -95,7 +103,9 @@ jobs:
|
|||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
- run: yarn test:generate_docs:circleci
|
- run:
|
||||||
|
command: yarn test:generate_docs:circleci
|
||||||
|
no_output_timeout: 1200
|
||||||
test-rest:
|
test-rest:
|
||||||
docker:
|
docker:
|
||||||
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||||
@@ -106,15 +116,16 @@ 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
|
||||||
- run: yarn wsrun test:circleci @0x/asset-buyer
|
- run: yarn wsrun test:circleci @0x/asset-swapper
|
||||||
- run: yarn wsrun test:circleci @0x/contract-artifacts
|
- 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
|
- run: yarn wsrun test:circleci @0x/connect
|
||||||
- run: yarn wsrun test:circleci @0x/contract-wrappers
|
- run: yarn wsrun test:circleci @0x/contract-wrappers-test
|
||||||
- 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/order-utils
|
- run: yarn wsrun test:circleci @0x/order-utils
|
||||||
|
- run: yarn wsrun test:circleci @0x/orderbook
|
||||||
- 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
|
||||||
@@ -131,9 +142,9 @@ jobs:
|
|||||||
paths:
|
paths:
|
||||||
- ~/repo/packages/assert/coverage/lcov.info
|
- ~/repo/packages/assert/coverage/lcov.info
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: coverage-asset-buyer-{{ .Environment.CIRCLE_SHA1 }}
|
key: coverage-asset-swapper-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
paths:
|
paths:
|
||||||
- ~/repo/packages/asset-buyer/coverage/lcov.info
|
- ~/repo/packages/asset-swapper/coverage/lcov.info
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: coverage-base-contract-{{ .Environment.CIRCLE_SHA1 }}
|
key: coverage-base-contract-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
paths:
|
paths:
|
||||||
@@ -143,9 +154,9 @@ jobs:
|
|||||||
paths:
|
paths:
|
||||||
- ~/repo/packages/connect/coverage/lcov.info
|
- ~/repo/packages/connect/coverage/lcov.info
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: coverage-contract-wrappers-{{ .Environment.CIRCLE_SHA1 }}
|
key: coverage-contract-wrappers-test-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
paths:
|
paths:
|
||||||
- ~/repo/packages/contract-wrappers/coverage/lcov.info
|
- ~/repo/packages/contract-wrappers-test/coverage/lcov.info
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: coverage-dev-utils-{{ .Environment.CIRCLE_SHA1 }}
|
key: coverage-dev-utils-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
paths:
|
paths:
|
||||||
@@ -182,20 +193,34 @@ jobs:
|
|||||||
working_directory: ~/repo
|
working_directory: ~/repo
|
||||||
docker:
|
docker:
|
||||||
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||||
- image: 0xorg/ganache-cli:2.2.2
|
- image: 0xorg/ganache-cli:6.0.0
|
||||||
- image: 0xorg/launch-kit-backend:74bcc39
|
- image: 0xorg/mesh:0xV3
|
||||||
environment:
|
environment:
|
||||||
RPC_URL: http://localhost:8545
|
ETHEREUM_RPC_URL: 'http://localhost:8545'
|
||||||
NETWORK_ID: 50
|
ETHEREUM_CHAIN_ID: '1337'
|
||||||
WHITELIST_ALL_TOKENS: True
|
VERBOSITY: 5
|
||||||
|
BLOCK_POLLING_INTERVAL: '50ms'
|
||||||
|
ETHEREUM_RPC_MAX_REQUESTS_PER_24_HR_UTC: '1778000'
|
||||||
command: |
|
command: |
|
||||||
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"
|
sh -c "waitForGanache () { until printf 'POST /\r\nContent-Length: 26\r\n\r\n{\"method\":\"net_listening\"}' | nc localhost 8545 | grep true; do continue; done }; waitForGanache && ./mesh"
|
||||||
|
- image: 0xorg/launch-kit-backend:v3
|
||||||
|
environment:
|
||||||
|
RPC_URL: 'http://localhost:8545'
|
||||||
|
CHAIN_ID: 1337
|
||||||
|
WHITELIST_ALL_TOKENS: True
|
||||||
|
FEE_RECIPIENT: '0x0000000000000000000000000000000000000001'
|
||||||
|
MAKER_FEE_UNIT_AMOUNT: 0
|
||||||
|
TAKER_FEE_UNIT_AMOUNT: 0
|
||||||
|
MESH_ENDPOINT: 'ws://localhost:60557'
|
||||||
|
command: |
|
||||||
|
sh -c "waitForMesh () { sleep 30; }; waitForMesh && node_modules/.bin/forever ts/lib/index.js"
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
key: installed-py-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
|
key: installed-py-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
key: python-contract-wrappers-{{ .Environment.CIRCLE_SHA1 }}
|
keys:
|
||||||
|
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
- run:
|
- run:
|
||||||
command: |
|
command: |
|
||||||
cd python-packages
|
cd python-packages
|
||||||
@@ -210,8 +235,14 @@ jobs:
|
|||||||
- run:
|
- run:
|
||||||
command: |
|
command: |
|
||||||
cd python-packages
|
cd python-packages
|
||||||
./parallel_without_sra_client coverage run setup.py test
|
./parallel coverage run setup.py test
|
||||||
./build_docs
|
./build_docs
|
||||||
|
- run:
|
||||||
|
command: |
|
||||||
|
# copy generated wrappers into contract_wrappers/build,
|
||||||
|
# JUST so CircleCI will persist them as build artifacts.
|
||||||
|
cd python-packages/contract_wrappers/src/zero_ex
|
||||||
|
for i in contract_wrappers/[^__]*/; do mkdir -p ../../build/$i; cp $i/__init__.py ../../build/$i; done
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: coverage-python-contract-addresses-{{ .Environment.CIRCLE_SHA1 }}
|
key: coverage-python-contract-addresses-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
paths:
|
paths:
|
||||||
@@ -236,10 +267,6 @@ 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/erc20_token/__init__.py
|
|
||||||
- store_artifacts:
|
|
||||||
path: ~/repo/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/__init__.py
|
|
||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
path: ~/repo/python-packages/contract_addresses/build
|
path: ~/repo/python-packages/contract_addresses/build
|
||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
@@ -289,7 +316,8 @@ jobs:
|
|||||||
- restore_cache:
|
- restore_cache:
|
||||||
key: installed-py-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
|
key: installed-py-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
key: python-contract-wrappers-{{ .Environment.CIRCLE_SHA1 }}
|
keys:
|
||||||
|
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
- run:
|
- run:
|
||||||
command: |
|
command: |
|
||||||
python -m ensurepip
|
python -m ensurepip
|
||||||
@@ -298,6 +326,7 @@ jobs:
|
|||||||
./install
|
./install
|
||||||
./lint
|
./lint
|
||||||
static-tests:
|
static-tests:
|
||||||
|
resource_class: large
|
||||||
working_directory: ~/repo
|
working_directory: ~/repo
|
||||||
docker:
|
docker:
|
||||||
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||||
@@ -308,6 +337,7 @@ 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:
|
||||||
@@ -326,7 +356,7 @@ jobs:
|
|||||||
- coverage-assert-{{ .Environment.CIRCLE_SHA1 }}
|
- coverage-assert-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- coverage-asset-buyer-{{ .Environment.CIRCLE_SHA1 }}
|
- coverage-asset-swapper-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- coverage-base-contract-{{ .Environment.CIRCLE_SHA1 }}
|
- coverage-base-contract-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
@@ -335,7 +365,7 @@ jobs:
|
|||||||
- coverage-connect-{{ .Environment.CIRCLE_SHA1 }}
|
- coverage-connect-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- coverage-contract-wrappers-{{ .Environment.CIRCLE_SHA1 }}
|
- coverage-contract-wrappers-test-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- coverage-dev-utils-{{ .Environment.CIRCLE_SHA1 }}
|
- coverage-dev-utils-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
@@ -387,17 +417,15 @@ workflows:
|
|||||||
main:
|
main:
|
||||||
jobs:
|
jobs:
|
||||||
- build
|
- build
|
||||||
- build-website:
|
- test-exchange-ganache-3.0:
|
||||||
requires:
|
requires:
|
||||||
- build
|
- build
|
||||||
- test-contracts-ganache:
|
- test-integrations-ganache-3.0:
|
||||||
|
requires:
|
||||||
|
- build
|
||||||
|
- test-contracts-rest-ganache-3.0:
|
||||||
requires:
|
requires:
|
||||||
- build
|
- build
|
||||||
# TODO(albrow): Tests always fail on Geth right now because our fork
|
|
||||||
# is outdated. Uncomment once we have updated our Geth fork.
|
|
||||||
# - test-contracts-geth:
|
|
||||||
# requires:
|
|
||||||
# - build
|
|
||||||
- test-rest:
|
- test-rest:
|
||||||
requires:
|
requires:
|
||||||
- build
|
- build
|
||||||
@@ -412,13 +440,15 @@ 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:
|
|
||||||
requires:
|
|
||||||
- test-python
|
|
||||||
- test-python:
|
- test-python:
|
||||||
requires:
|
requires:
|
||||||
- build
|
- build
|
||||||
|
- static-tests-python:
|
||||||
|
requires:
|
||||||
|
- build
|
||||||
# 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
|
||||||
|
|||||||
8
.github/autolabeler.yml
vendored
8
.github/autolabeler.yml
vendored
@@ -13,27 +13,21 @@ contracts: ['contracts']
|
|||||||
@0x/instant: ['packages/instant']
|
@0x/instant: ['packages/instant']
|
||||||
@0x/abi-gen-templates: ['packages/abi-gen-templates']
|
@0x/abi-gen-templates: ['packages/abi-gen-templates']
|
||||||
@0x/abi-gen: ['packages/abi-gen']
|
@0x/abi-gen: ['packages/abi-gen']
|
||||||
@0x/website: ['packages/website']
|
|
||||||
@0x/sol-coverage: ['packages/sol-coverage']
|
@0x/sol-coverage: ['packages/sol-coverage']
|
||||||
@0x/sol-profiler: ['packages/sol-profiler']
|
@0x/sol-profiler: ['packages/sol-profiler']
|
||||||
@0x/sol-trace: ['packages/sol-trace']
|
@0x/sol-trace: ['packages/sol-trace']
|
||||||
@0x/sol-tracing-utils: ['packages/sol-tracing-utils']
|
@0x/sol-tracing-utils: ['packages/sol-tracing-utils']
|
||||||
@0x/utils: ['packages/utils']
|
@0x/utils: ['packages/utils']
|
||||||
@0x/tslint-config: ['packages/tslint-config']
|
@0x/tslint-config: ['packages/tslint-config']
|
||||||
@0x/asset-buyer: ['packages/asset-buyer']
|
@0x/asset-swapper: ['packages/asset-swapper']
|
||||||
@0x/order-utils: ['packages/order-utils']
|
@0x/order-utils: ['packages/order-utils']
|
||||||
@0x/assert: ['packages/assert']
|
@0x/assert: ['packages/assert']
|
||||||
@0x/base-contract: ['packages/base-contract']
|
@0x/base-contract: ['packages/base-contract']
|
||||||
@0x/typescript-typings: ['packages/typescript-typings']
|
@0x/typescript-typings: ['packages/typescript-typings']
|
||||||
0x.js: ['packages/0x.js']
|
0x.js: ['packages/0x.js']
|
||||||
@0x/abi-gen-wrappers: ['packages/abi-gen-wrappers']
|
|
||||||
@0x/contract-artifacts: ['packages/contract-artifacts']
|
@0x/contract-artifacts: ['packages/contract-artifacts']
|
||||||
@0x/dev-utils: ['packages/dev-utils']
|
@0x/dev-utils: ['packages/dev-utils']
|
||||||
@0x/contract-wrappers: ['packages/contract-wrappers']
|
@0x/contract-wrappers: ['packages/contract-wrappers']
|
||||||
@0x/json-schemas: ['packages/json-schemas']
|
@0x/json-schemas: ['packages/json-schemas']
|
||||||
@0x/ethereum-types: ['ethereum-types']
|
@0x/ethereum-types: ['ethereum-types']
|
||||||
@0x/connect: ['packages/connect']
|
@0x/connect: ['packages/connect']
|
||||||
@0x/fill-scenarios: ['packages/fill-scenarios']
|
|
||||||
@0x/dev-tools-pages: ['packages/dev-tools-pages']
|
|
||||||
@0x/testnet-faucets: ['packages/testnet-faucets']
|
|
||||||
@0x/monorepo-scripts: ['packages/monorepo-scripts']
|
|
||||||
|
|||||||
133
.gitignore
vendored
133
.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,73 +78,98 @@ TODO.md
|
|||||||
# VSCode file
|
# VSCode file
|
||||||
.vscode
|
.vscode
|
||||||
|
|
||||||
packages/website/public/bundle*
|
|
||||||
packages/dev-tools-pages/public/bundle*
|
|
||||||
|
|
||||||
# server cli
|
|
||||||
packages/testnet-faucets/server/
|
|
||||||
|
|
||||||
# generated contract artifacts/
|
# generated contract artifacts/
|
||||||
|
contracts/broker/generated-artifacts/
|
||||||
|
contracts/broker/test/generated-artifacts/
|
||||||
|
contracts/erc20-bridge-sampler/generated-artifacts/
|
||||||
|
contracts/erc20-bridge-sampler/test/generated-artifacts/
|
||||||
|
contracts/integrations/generated-artifacts/
|
||||||
|
contracts/integrations/test/generated-artifacts/
|
||||||
|
contracts/staking/generated-artifacts/
|
||||||
|
contracts/staking/test/generated-artifacts/
|
||||||
contracts/coordinator/generated-artifacts/
|
contracts/coordinator/generated-artifacts/
|
||||||
|
contracts/coordinator/test/generated-artifacts/
|
||||||
contracts/exchange/generated-artifacts/
|
contracts/exchange/generated-artifacts/
|
||||||
|
contracts/exchange/test/generated-artifacts/
|
||||||
contracts/asset-proxy/generated-artifacts/
|
contracts/asset-proxy/generated-artifacts/
|
||||||
|
contracts/asset-proxy/test/generated-artifacts/
|
||||||
contracts/multisig/generated-artifacts/
|
contracts/multisig/generated-artifacts/
|
||||||
|
contracts/multisig/test/generated-artifacts/
|
||||||
contracts/utils/generated-artifacts/
|
contracts/utils/generated-artifacts/
|
||||||
|
contracts/utils/test/generated-artifacts/
|
||||||
contracts/exchange-libs/generated-artifacts/
|
contracts/exchange-libs/generated-artifacts/
|
||||||
|
contracts/exchange-libs/test/generated-artifacts/
|
||||||
contracts/erc20/generated-artifacts/
|
contracts/erc20/generated-artifacts/
|
||||||
|
contracts/erc20/test/generated-artifacts/
|
||||||
contracts/erc721/generated-artifacts/
|
contracts/erc721/generated-artifacts/
|
||||||
|
contracts/erc721/test/generated-artifacts/
|
||||||
contracts/erc1155/generated-artifacts/
|
contracts/erc1155/generated-artifacts/
|
||||||
|
contracts/erc1155/test/generated-artifacts/
|
||||||
contracts/extensions/generated-artifacts/
|
contracts/extensions/generated-artifacts/
|
||||||
|
contracts/extensions/test/generated-artifacts/
|
||||||
contracts/exchange-forwarder/generated-artifacts/
|
contracts/exchange-forwarder/generated-artifacts/
|
||||||
|
contracts/exchange-forwarder/test/generated-artifacts/
|
||||||
contracts/dev-utils/generated-artifacts/
|
contracts/dev-utils/generated-artifacts/
|
||||||
|
contracts/dev-utils/test/generated-artifacts/
|
||||||
packages/sol-tracing-utils/test/fixtures/artifacts/
|
packages/sol-tracing-utils/test/fixtures/artifacts/
|
||||||
python-packages/contract_artifacts/src/zero_ex/contract_artifacts/artifacts/
|
python-packages/contract_artifacts/src/zero_ex/contract_artifacts/artifacts/
|
||||||
|
|
||||||
# generated contract wrappers
|
# generated truffle contract artifacts/
|
||||||
packages/abi-gen-wrappers/src/generated-wrappers/
|
contracts/broker/build/
|
||||||
packages/python-contract-wrappers/generated/
|
contracts/erc20-bridge-sampler/build/
|
||||||
contracts/coordinator/generated-wrappers/
|
contracts/staking/build/
|
||||||
contracts/exchange/generated-wrappers/
|
contracts/coordinator/build/
|
||||||
contracts/asset-proxy/generated-wrappers/
|
contracts/exchange/build/
|
||||||
contracts/multisig/generated-wrappers/
|
contracts/asset-proxy/build/
|
||||||
contracts/utils/generated-wrappers/
|
contracts/multisig/build/
|
||||||
contracts/exchange-libs/generated-wrappers/
|
contracts/utils/build/
|
||||||
contracts/erc20/generated-wrappers/
|
contracts/exchange-libs/build/
|
||||||
contracts/erc721/generated-wrappers/
|
contracts/erc20/build/
|
||||||
contracts/erc1155/generated-wrappers/
|
contracts/erc721/build/
|
||||||
contracts/extensions/generated-wrappers/
|
contracts/erc1155/build/
|
||||||
contracts/exchange-forwarder/generated-wrappers/
|
contracts/extensions/build/
|
||||||
contracts/dev-utils/generated-wrappers/
|
contracts/exchange-forwarder/build/
|
||||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc20_token/__init__.py
|
contracts/dev-utils/build/
|
||||||
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
|
|
||||||
|
|
||||||
# cli test output
|
# generated contract wrappers
|
||||||
packages/abi-gen/test-cli/output
|
contracts/broker/generated-wrappers/
|
||||||
|
contracts/broker/test/generated-wrappers/
|
||||||
|
packages/python-contract-wrappers/generated/
|
||||||
|
contracts/erc20-bridge-sampler/generated-wrappers/
|
||||||
|
contracts/erc20-bridge-sampler/test/generated-wrappers/
|
||||||
|
contracts/integrations/generated-wrappers/
|
||||||
|
contracts/integrations/test/generated-wrappers/
|
||||||
|
contracts/staking/generated-wrappers/
|
||||||
|
contracts/staking/test/generated-wrappers/
|
||||||
|
contracts/coordinator/generated-wrappers/
|
||||||
|
contracts/coordinator/test/generated-wrappers/
|
||||||
|
contracts/exchange/generated-wrappers/
|
||||||
|
contracts/exchange/test/generated-wrappers/
|
||||||
|
contracts/asset-proxy/generated-wrappers/
|
||||||
|
contracts/asset-proxy/test/generated-wrappers/
|
||||||
|
contracts/multisig/generated-wrappers/
|
||||||
|
contracts/multisig/test/generated-wrappers/
|
||||||
|
contracts/utils/generated-wrappers/
|
||||||
|
contracts/utils/test/generated-wrappers/
|
||||||
|
contracts/exchange-libs/generated-wrappers/
|
||||||
|
contracts/exchange-libs/test/generated-wrappers/
|
||||||
|
contracts/erc20/generated-wrappers/
|
||||||
|
contracts/erc20/test/generated-wrappers/
|
||||||
|
contracts/erc721/generated-wrappers/
|
||||||
|
contracts/erc721/test/generated-wrappers/
|
||||||
|
contracts/erc1155/generated-wrappers/
|
||||||
|
contracts/erc1155/test/generated-wrappers/
|
||||||
|
contracts/extensions/generated-wrappers/
|
||||||
|
contracts/extensions/test/generated-wrappers/
|
||||||
|
contracts/exchange-forwarder/generated-wrappers/
|
||||||
|
contracts/exchange-forwarder/test/generated-wrappers/
|
||||||
|
contracts/dev-utils/generated-wrappers/
|
||||||
|
contracts/dev-utils/test/generated-wrappers/
|
||||||
|
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/*/__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
|
||||||
@@ -152,5 +180,14 @@ __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 keeps package-local copies of json schemas and contract addresses
|
||||||
python-packages/json_schemas/src/zero_ex/json_schemas/schemas
|
python-packages/json_schemas/src/zero_ex/json_schemas/schemas
|
||||||
|
python-packages/contract_addresses/src/zero_ex/contract_addresses/addresses.json
|
||||||
|
|
||||||
|
# Doc README copy
|
||||||
|
packages/*/docs/README.md
|
||||||
|
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# the snapshot that gets built for migrations sure does have a ton of files
|
||||||
|
packages/migrations/0x_ganache_snapshot*
|
||||||
|
|||||||
@@ -1,34 +1,86 @@
|
|||||||
lib
|
lib
|
||||||
.nyc_output
|
.nyc_output
|
||||||
|
/contracts/broker/generated-wrappers
|
||||||
|
/contracts/broker/test/generated-wrappers
|
||||||
|
/contracts/broker/generated-artifacts
|
||||||
|
/contracts/broker/test/generated-artifacts
|
||||||
|
/contracts/integrations/generated-wrappers
|
||||||
|
/contracts/integrations/test/generated-wrappers
|
||||||
|
/contracts/integrations/generated-artifacts
|
||||||
|
/contracts/integrations/test/generated-artifacts
|
||||||
|
/contracts/staking/generated-wrappers
|
||||||
|
/contracts/staking/test/generated-wrappers
|
||||||
|
/contracts/staking/generated-artifacts
|
||||||
|
/contracts/staking/test/generated-artifacts
|
||||||
/contracts/coordinator/generated-wrappers
|
/contracts/coordinator/generated-wrappers
|
||||||
|
/contracts/coordinator/test/generated-wrappers
|
||||||
/contracts/coordinator/generated-artifacts
|
/contracts/coordinator/generated-artifacts
|
||||||
|
/contracts/coordinator/test/generated-artifacts
|
||||||
/contracts/exchange/generated-wrappers
|
/contracts/exchange/generated-wrappers
|
||||||
|
/contracts/exchange/test/generated-wrappers
|
||||||
/contracts/exchange/generated-artifacts
|
/contracts/exchange/generated-artifacts
|
||||||
|
/contracts/exchange/test/generated-artifacts
|
||||||
/contracts/asset-proxy/generated-wrappers
|
/contracts/asset-proxy/generated-wrappers
|
||||||
|
/contracts/asset-proxy/test/generated-wrappers
|
||||||
/contracts/asset-proxy/generated-artifacts
|
/contracts/asset-proxy/generated-artifacts
|
||||||
|
/contracts/asset-proxy/test/generated-artifacts
|
||||||
/contracts/multisig/generated-wrappers
|
/contracts/multisig/generated-wrappers
|
||||||
|
/contracts/multisig/test/generated-wrappers
|
||||||
/contracts/multisig/generated-artifacts
|
/contracts/multisig/generated-artifacts
|
||||||
|
/contracts/multisig/test/generated-artifacts
|
||||||
/contracts/utils/generated-wrappers
|
/contracts/utils/generated-wrappers
|
||||||
|
/contracts/utils/test/generated-wrappers
|
||||||
/contracts/utils/generated-artifacts
|
/contracts/utils/generated-artifacts
|
||||||
|
/contracts/utils/test/generated-artifacts
|
||||||
/contracts/exchange-libs/generated-wrappers
|
/contracts/exchange-libs/generated-wrappers
|
||||||
|
/contracts/exchange-libs/test/generated-wrappers
|
||||||
/contracts/exchange-libs/generated-artifacts
|
/contracts/exchange-libs/generated-artifacts
|
||||||
|
/contracts/exchange-libs/test/generated-artifacts
|
||||||
/contracts/erc20/generated-wrappers
|
/contracts/erc20/generated-wrappers
|
||||||
|
/contracts/erc20/test/generated-wrappers
|
||||||
/contracts/erc20/generated-artifacts
|
/contracts/erc20/generated-artifacts
|
||||||
|
/contracts/erc20/test/generated-artifacts
|
||||||
|
/contracts/erc20-bridge-sampler/generated-wrappers
|
||||||
|
/contracts/erc20-bridge-sampler/test/generated-wrappers
|
||||||
|
/contracts/erc20-bridge-sampler/generated-artifacts
|
||||||
|
/contracts/erc20-bridge-sampler/test/generated-artifacts
|
||||||
/contracts/erc721/generated-wrappers
|
/contracts/erc721/generated-wrappers
|
||||||
|
/contracts/erc721/test/generated-wrappers
|
||||||
/contracts/erc721/generated-artifacts
|
/contracts/erc721/generated-artifacts
|
||||||
|
/contracts/erc721/test/generated-artifacts
|
||||||
/contracts/erc1155/generated-wrappers
|
/contracts/erc1155/generated-wrappers
|
||||||
|
/contracts/erc1155/test/generated-wrappers
|
||||||
/contracts/erc1155/generated-artifacts
|
/contracts/erc1155/generated-artifacts
|
||||||
|
/contracts/erc1155/test/generated-artifacts
|
||||||
/contracts/extensions/generated-wrappers
|
/contracts/extensions/generated-wrappers
|
||||||
|
/contracts/extensions/test/generated-wrappers
|
||||||
/contracts/extensions/generated-artifacts
|
/contracts/extensions/generated-artifacts
|
||||||
|
/contracts/extensions/test/generated-artifacts
|
||||||
/contracts/exchange-forwarder/generated-wrappers
|
/contracts/exchange-forwarder/generated-wrappers
|
||||||
|
/contracts/exchange-forwarder/test/generated-wrappers
|
||||||
/contracts/exchange-forwarder/generated-artifacts
|
/contracts/exchange-forwarder/generated-artifacts
|
||||||
|
/contracts/exchange-forwarder/test/generated-artifacts
|
||||||
/contracts/dev-utils/generated-wrappers
|
/contracts/dev-utils/generated-wrappers
|
||||||
|
/contracts/dev-utils/test/generated-wrappers
|
||||||
/contracts/dev-utils/generated-artifacts
|
/contracts/dev-utils/generated-artifacts
|
||||||
|
/contracts/dev-utils/test/generated-artifacts
|
||||||
|
/contracts/staking/build/
|
||||||
|
/contracts/coordinator/build/
|
||||||
|
/contracts/exchange/build/
|
||||||
|
/contracts/asset-proxy/build/
|
||||||
|
/contracts/multisig/build/
|
||||||
|
/contracts/utils/build/
|
||||||
|
/contracts/exchange-libs/build/
|
||||||
|
/contracts/erc20/build/
|
||||||
|
/contracts/erc721/build/
|
||||||
|
/contracts/erc1155/build/
|
||||||
|
/contracts/extensions/build/
|
||||||
|
/contracts/exchange-forwarder/build/
|
||||||
|
/contracts/dev-utils/build/
|
||||||
/packages/abi-gen/test-cli/output
|
/packages/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/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
|
||||||
@@ -38,3 +90,4 @@ packages/sol-coverage/test/fixtures/artifacts
|
|||||||
packages/abi-gen/test-cli/fixtures/artifacts/AbiGenDummy.json
|
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/LibDummy.json
|
||||||
packages/abi-gen/test-cli/fixtures/artifacts/TestLibDummy.json
|
packages/abi-gen/test-cli/fixtures/artifacts/TestLibDummy.json
|
||||||
|
packages/*/docs
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"tabWidth": 4,
|
|
||||||
"printWidth": 120,
|
"printWidth": 120,
|
||||||
"trailingComma": all,
|
"tabWidth": 4,
|
||||||
"singleQuote": true
|
"singleQuote": true,
|
||||||
|
"trailingComma": "all"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,9 +5,8 @@
|
|||||||
# https://git-scm.com/docs/gitignore#_pattern_format
|
# https://git-scm.com/docs/gitignore#_pattern_format
|
||||||
|
|
||||||
# Website
|
# Website
|
||||||
packages/asset-buyer/ @BMillman19 @fragosti @steveklebanoff
|
packages/asset-swapper/ @BMillman19 @fragosti @dave4506
|
||||||
packages/instant/ @BMillman19 @fragosti @steveklebanoff
|
packages/instant/ @BMillman19 @fragosti @dave4506
|
||||||
packages/website/ @BMillman19 @fragosti @fabioberger @steveklebanoff
|
|
||||||
|
|
||||||
# Dev tools & setup
|
# Dev tools & setup
|
||||||
.circleci/ @LogvinovLeon
|
.circleci/ @LogvinovLeon
|
||||||
@@ -15,8 +14,8 @@ packages/abi-gen/ @feuGeneA
|
|||||||
packages/base-contract/ @xianny
|
packages/base-contract/ @xianny
|
||||||
packages/connect/ @fragosti
|
packages/connect/ @fragosti
|
||||||
packages/abi-gen-templates/ @feuGeneA @xianny
|
packages/abi-gen-templates/ @feuGeneA @xianny
|
||||||
packages/contract-addresses/ @albrow
|
packages/contract-addresses/ @abandeali1
|
||||||
packages/contract-artifacts/ @albrow
|
packages/contract-artifacts/ @abandeali1
|
||||||
packages/dev-utils/ @LogvinovLeon @fabioberger
|
packages/dev-utils/ @LogvinovLeon @fabioberger
|
||||||
packages/devnet/ @albrow
|
packages/devnet/ @albrow
|
||||||
packages/ethereum-types/ @LogvinovLeon
|
packages/ethereum-types/ @LogvinovLeon
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
33
README.md
33
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
|
||||||
|
|
||||||
@@ -48,12 +43,13 @@ These packages are all under development. See [/contracts/README.md](/contracts/
|
|||||||
| [`@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-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-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-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
|
||||||
|
|
||||||
@@ -65,11 +61,9 @@ These packages are all under development. See [/contracts/README.md](/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/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/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-swapper`](/packages/asset-swapper) | [](https://www.npmjs.com/package/@0x/asset-swapper) | Convenience package for discovering and performing swaps for any ERC20 Assets |
|
| [`@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
|
||||||
@@ -97,19 +91,16 @@ These packages are all under development. See [/contracts/README.md](/contracts/
|
|||||||
| [`@0x/assert`](/packages/assert) | [](https://www.npmjs.com/package/@0x/assert) | Type and schema assertions used by our packages |
|
| [`@0x/assert`](/packages/assert) | [](https://www.npmjs.com/package/@0x/assert) | Type and schema assertions used by our packages |
|
||||||
| [`@0x/base-contract`](/packages/base-contract) | [](https://www.npmjs.com/package/@0x/base-contract) | BaseContract used by auto-generated `abi-gen` wrapper contracts |
|
| [`@0x/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/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
|
||||||
|
|
||||||
| Package | Description |
|
| Package | Description |
|
||||||
| -------------------------------------------------- | -------------------------------------------------------------------------------- |
|
| ---------------------------------- | -------------------------------------------------------------------------------- |
|
||||||
| [`@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/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.
|
||||||
@@ -138,6 +129,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:
|
||||||
|
|||||||
@@ -13,4 +13,4 @@
|
|||||||
|
|
||||||
#### Development
|
#### 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.
|
Building solidity files will update the contract artifact in `{package-name}/generated-artifacts/{contract}.json`, but does not automatically update the `contract-artifacts` or `contract-wrappers` packages, which are generated from the artifact JSON. See `contract-artifacts/README.md` for instructions on updating these packages.
|
||||||
|
|||||||
10
contracts/asset-proxy/.npmignore
Normal file
10
contracts/asset-proxy/.npmignore
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# Blacklist all files
|
||||||
|
.*
|
||||||
|
*
|
||||||
|
# Whitelist lib
|
||||||
|
!lib/**/*
|
||||||
|
# Whitelist Solidity contracts
|
||||||
|
!contracts/src/**/*
|
||||||
|
# Blacklist tests in lib
|
||||||
|
/lib/test/*
|
||||||
|
# Package specific ignore
|
||||||
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,252 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"timestamp": 1581748629,
|
||||||
|
"version": "3.2.1",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "3.2.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Fix broken tests.",
|
||||||
|
"pr": 2462
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Remove dependency on `@0x/contracts-dev-utils`",
|
||||||
|
"pr": 2462
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Add asset data decoding functions",
|
||||||
|
"pr": 2462
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1581204851
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1580988106,
|
||||||
|
"version": "3.1.3",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1580811564,
|
||||||
|
"version": "3.1.2",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1579682890,
|
||||||
|
"version": "3.1.1",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "3.1.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Integration tests for DydxBridge with ERC20BridgeProxy.",
|
||||||
|
"pr": 2401
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Fix `UniswapBridge` token -> token transfer call.",
|
||||||
|
"pr": 2412
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Fix `KyberBridge` incorrect `minConversionRate` calculation.",
|
||||||
|
"pr": 2412
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1578272714
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1576540892,
|
||||||
|
"version": "3.0.2",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1575931811,
|
||||||
|
"version": "3.0.1",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "3.0.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Implement `KyberBridge`.",
|
||||||
|
"pr": 2352
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils",
|
||||||
|
"pr": 2330
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "ERC20Wrapper and ERC1155ProxyWrapper constructors now require an instance of DevUtilsContract",
|
||||||
|
"pr": 2034
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Disallow the zero address from being made an authorized address in MixinAuthorizable, and created an archive directory that includes an old version of Ownable",
|
||||||
|
"pr": 2019
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Remove `LibAssetProxyIds` contract",
|
||||||
|
"pr": 2055
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Compile and export all contracts, artifacts, and wrappers by default",
|
||||||
|
"pr": 2055
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Remove unused dependency on IAuthorizable in IAssetProxy",
|
||||||
|
"pr": 1910
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Add `ERC20BridgeProxy`",
|
||||||
|
"pr": 2220
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Add `Eth2DaiBridge`",
|
||||||
|
"pr": 2221
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Add `UniswapBridge`",
|
||||||
|
"pr": 2233
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Replaced `SafeMath` with `LibSafeMath`",
|
||||||
|
"pr": 2254
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1575296764
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "2.3.0-beta.4",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Implement `KyberBridge`.",
|
||||||
|
"pr": 2352
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Implement `DydxBridge`.",
|
||||||
|
"pr": 2365
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1575290197
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "2.3.0-beta.3",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1574238768
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "2.3.0-beta.2",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils",
|
||||||
|
"pr": 2330
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1574030254
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "2.3.0-beta.1",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "ERC20Wrapper and ERC1155ProxyWrapper constructors now require an instance of DevUtilsContract",
|
||||||
|
"pr": 2034
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1573159180
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "2.3.0-beta.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Disallow the zero address from being made an authorized address in MixinAuthorizable, and created an archive directory that includes an old version of Ownable",
|
||||||
|
"pr": 2019
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Remove `LibAssetProxyIds` contract",
|
||||||
|
"pr": 2055
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Compile and export all contracts, artifacts, and wrappers by default",
|
||||||
|
"pr": 2055
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Remove unused dependency on IAuthorizable in IAssetProxy",
|
||||||
|
"pr": 1910
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Add `ERC20BridgeProxy`",
|
||||||
|
"pr": 2220
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Add `Eth2DaiBridge`",
|
||||||
|
"pr": 2221
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Add `UniswapBridge`",
|
||||||
|
"pr": 2233
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Replaced `SafeMath` with `LibSafeMath`",
|
||||||
|
"pr": 2254
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1570135330
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1568744790,
|
||||||
|
"version": "2.2.8",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1567521715,
|
||||||
|
"version": "2.2.7",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1566446343,
|
||||||
|
"version": "2.2.6",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"timestamp": 1565296576,
|
"timestamp": 1565296576,
|
||||||
"version": "2.2.5",
|
"version": "2.2.5",
|
||||||
@@ -96,6 +344,18 @@
|
|||||||
{
|
{
|
||||||
"note": "Update tests to use contract-built-in `awaitTransactionSuccessAsync`",
|
"note": "Update tests to use contract-built-in `awaitTransactionSuccessAsync`",
|
||||||
"pr": 1797
|
"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
|
"timestamp": 1557507213
|
||||||
|
|||||||
@@ -5,6 +5,96 @@ Edit the package's CHANGELOG.json file only.
|
|||||||
|
|
||||||
CHANGELOG
|
CHANGELOG
|
||||||
|
|
||||||
|
## v3.2.1 - _February 15, 2020_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v3.2.0 - _February 8, 2020_
|
||||||
|
|
||||||
|
* Fix broken tests. (#2462)
|
||||||
|
* Remove dependency on `@0x/contracts-dev-utils` (#2462)
|
||||||
|
* Add asset data decoding functions (#2462)
|
||||||
|
|
||||||
|
## v3.1.3 - _February 6, 2020_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v3.1.2 - _February 4, 2020_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v3.1.1 - _January 22, 2020_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v3.1.0 - _January 6, 2020_
|
||||||
|
|
||||||
|
* Integration tests for DydxBridge with ERC20BridgeProxy. (#2401)
|
||||||
|
* Fix `UniswapBridge` token -> token transfer call. (#2412)
|
||||||
|
* Fix `KyberBridge` incorrect `minConversionRate` calculation. (#2412)
|
||||||
|
|
||||||
|
## v3.0.2 - _December 17, 2019_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v3.0.1 - _December 9, 2019_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v3.0.0 - _December 2, 2019_
|
||||||
|
|
||||||
|
* Implement `KyberBridge`. (#2352)
|
||||||
|
* Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330)
|
||||||
|
* ERC20Wrapper and ERC1155ProxyWrapper constructors now require an instance of DevUtilsContract (#2034)
|
||||||
|
* Disallow the zero address from being made an authorized address in MixinAuthorizable, and created an archive directory that includes an old version of Ownable (#2019)
|
||||||
|
* Remove `LibAssetProxyIds` contract (#2055)
|
||||||
|
* Compile and export all contracts, artifacts, and wrappers by default (#2055)
|
||||||
|
* Remove unused dependency on IAuthorizable in IAssetProxy (#1910)
|
||||||
|
* Add `ERC20BridgeProxy` (#2220)
|
||||||
|
* Add `Eth2DaiBridge` (#2221)
|
||||||
|
* Add `UniswapBridge` (#2233)
|
||||||
|
* Replaced `SafeMath` with `LibSafeMath` (#2254)
|
||||||
|
|
||||||
|
## v2.3.0-beta.4 - _December 2, 2019_
|
||||||
|
|
||||||
|
* Implement `KyberBridge`. (#2352)
|
||||||
|
* Implement `DydxBridge`. (#2365)
|
||||||
|
|
||||||
|
## v2.3.0-beta.3 - _November 20, 2019_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v2.3.0-beta.2 - _November 17, 2019_
|
||||||
|
|
||||||
|
* Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330)
|
||||||
|
|
||||||
|
## v2.3.0-beta.1 - _November 7, 2019_
|
||||||
|
|
||||||
|
* ERC20Wrapper and ERC1155ProxyWrapper constructors now require an instance of DevUtilsContract (#2034)
|
||||||
|
|
||||||
|
## v2.3.0-beta.0 - _October 3, 2019_
|
||||||
|
|
||||||
|
* Disallow the zero address from being made an authorized address in MixinAuthorizable, and created an archive directory that includes an old version of Ownable (#2019)
|
||||||
|
* Remove `LibAssetProxyIds` contract (#2055)
|
||||||
|
* Compile and export all contracts, artifacts, and wrappers by default (#2055)
|
||||||
|
* Remove unused dependency on IAuthorizable in IAssetProxy (#1910)
|
||||||
|
* Add `ERC20BridgeProxy` (#2220)
|
||||||
|
* Add `Eth2DaiBridge` (#2221)
|
||||||
|
* Add `UniswapBridge` (#2233)
|
||||||
|
* Replaced `SafeMath` with `LibSafeMath` (#2254)
|
||||||
|
|
||||||
|
## v2.2.8 - _September 17, 2019_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v2.2.7 - _September 3, 2019_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v2.2.6 - _August 22, 2019_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
## v2.2.5 - _August 8, 2019_
|
## v2.2.5 - _August 8, 2019_
|
||||||
|
|
||||||
* Dependencies updated
|
* Dependencies updated
|
||||||
@@ -46,6 +136,9 @@ CHANGELOG
|
|||||||
## v2.1.2 - _May 10, 2019_
|
## v2.1.2 - _May 10, 2019_
|
||||||
|
|
||||||
* Update tests to use contract-built-in `awaitTransactionSuccessAsync` (#1797)
|
* 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_
|
## v2.1.1 - _April 11, 2019_
|
||||||
|
|
||||||
|
|||||||
@@ -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,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"artifactsDir": "./generated-artifacts",
|
"artifactsDir": "./test/generated-artifacts",
|
||||||
"contractsDir": "./contracts",
|
"contractsDir": "./contracts",
|
||||||
"useDockerisedSolc": false,
|
"useDockerisedSolc": false,
|
||||||
"isOfflineMode": false,
|
"isOfflineMode": false,
|
||||||
"compilerSettings": {
|
"compilerSettings": {
|
||||||
"evmVersion": "constantinople",
|
"evmVersion": "istanbul",
|
||||||
"optimizer": {
|
"optimizer": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"runs": 1000000,
|
"runs": 1000000,
|
||||||
@@ -22,17 +22,5 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"contracts": [
|
|
||||||
"src/ERC1155Proxy.sol",
|
|
||||||
"src/ERC20Proxy.sol",
|
|
||||||
"src/ERC721Proxy.sol",
|
|
||||||
"src/MixinAuthorizable.sol",
|
|
||||||
"src/MultiAssetProxy.sol",
|
|
||||||
"src/StaticCallProxy.sol",
|
|
||||||
"src/interfaces/IAssetData.sol",
|
|
||||||
"src/interfaces/IAssetProxy.sol",
|
|
||||||
"src/interfaces/IAuthorizable.sol",
|
|
||||||
"test/TestStaticCallTarget.sol"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,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,16 +16,16 @@
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pragma solidity ^0.5.5;
|
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 => address) public assetProxies;
|
mapping (bytes4 => address) public assetProxies;
|
||||||
@@ -69,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,
|
||||||
@@ -84,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 {
|
||||||
@@ -100,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 |
|
||||||
@@ -127,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:
|
||||||
@@ -142,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)
|
||||||
@@ -159,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.5.5;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.
|
||||||
@@ -19,18 +19,18 @@
|
|||||||
pragma solidity ^0.5.9;
|
pragma solidity ^0.5.9;
|
||||||
|
|
||||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||||
import "@0x/contracts-utils/contracts/src/SafeMath.sol";
|
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
||||||
import "@0x/contracts-erc1155/contracts/src/interfaces/IERC1155.sol";
|
import "@0x/contracts-erc1155/contracts/src/interfaces/IERC1155.sol";
|
||||||
import "./MixinAuthorizable.sol";
|
import "../archive/MixinAuthorizable.sol";
|
||||||
import "./interfaces/IAssetProxy.sol";
|
import "./interfaces/IAssetProxy.sol";
|
||||||
|
|
||||||
|
|
||||||
contract ERC1155Proxy is
|
contract ERC1155Proxy is
|
||||||
MixinAuthorizable,
|
MixinAuthorizable,
|
||||||
SafeMath,
|
|
||||||
IAssetProxy
|
IAssetProxy
|
||||||
{
|
{
|
||||||
using LibBytes for bytes;
|
using LibBytes for bytes;
|
||||||
|
using LibSafeMath for uint256;
|
||||||
|
|
||||||
// Id of this proxy.
|
// Id of this proxy.
|
||||||
bytes4 constant internal PROXY_ID = bytes4(keccak256("ERC1155Assets(address,uint256[],uint256[],bytes)"));
|
bytes4 constant internal PROXY_ID = bytes4(keccak256("ERC1155Assets(address,uint256[],uint256[],bytes)"));
|
||||||
@@ -69,9 +69,9 @@ contract ERC1155Proxy is
|
|||||||
for (uint256 i = 0; i != length; i++) {
|
for (uint256 i = 0; i != length; i++) {
|
||||||
// We write the scaled values to an unused location in memory in order
|
// 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
|
// to avoid copying over `ids` or `data`. This is possible if they are
|
||||||
// identical to `values` and the offsets for each are pointing to the
|
// identical to `values` and the offsets for each are pointing to the
|
||||||
// same location in the ABI encoded calldata.
|
// same location in the ABI encoded calldata.
|
||||||
scaledValues[i] = safeMul(values[i], amount);
|
scaledValues[i] = values[i].safeMul(amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute `safeBatchTransferFrom` call
|
// Execute `safeBatchTransferFrom` call
|
||||||
|
|||||||
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).bridgeTransferFrom(
|
||||||
|
tokenAddress,
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
amount,
|
||||||
|
bridgeData
|
||||||
|
);
|
||||||
|
// Bridge must return the proxy ID to indicate success.
|
||||||
|
require(success == PROXY_ID, "BRIDGE_FAILED");
|
||||||
|
// Ensure that the balance of `to` has increased by at least `amount`.
|
||||||
|
require(
|
||||||
|
balanceBefore.safeAdd(amount) <= balanceOf(tokenAddress, to),
|
||||||
|
"BRIDGE_UNDERPAY"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Gets the proxy id associated with this asset proxy.
|
||||||
|
/// @return proxyId The proxy id.
|
||||||
|
function getProxyId()
|
||||||
|
external
|
||||||
|
pure
|
||||||
|
returns (bytes4 proxyId)
|
||||||
|
{
|
||||||
|
return PROXY_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Retrieves the balance of `owner` for this asset.
|
||||||
|
/// @return balance The balance of the ERC20 token being transferred by this
|
||||||
|
/// asset proxy.
|
||||||
|
function balanceOf(bytes calldata assetData, address owner)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (uint256 balance)
|
||||||
|
{
|
||||||
|
(address tokenAddress) = abi.decode(
|
||||||
|
assetData.sliceDestructive(4, assetData.length),
|
||||||
|
(address)
|
||||||
|
);
|
||||||
|
return balanceOf(tokenAddress, owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Retrieves the balance of `owner` given an ERC20 address.
|
||||||
|
/// @return balance The balance of the ERC20 token for `owner`.
|
||||||
|
function balanceOf(address tokenAddress, address owner)
|
||||||
|
private
|
||||||
|
view
|
||||||
|
returns (uint256 balance)
|
||||||
|
{
|
||||||
|
return IERC20Token(tokenAddress).balanceOf(owner);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
|
|
||||||
Copyright 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.5.5;
|
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.5.5;
|
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.5.5;
|
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
|
||||||
@@ -105,7 +105,7 @@ 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) |
|
||||||
@@ -149,8 +149,8 @@ contract MultiAssetProxy is
|
|||||||
// + 32 (amounts offset)
|
// + 32 (amounts offset)
|
||||||
let nestedAssetDataOffset := calldataload(add(assetDataOffset, 68))
|
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:
|
||||||
// assetDataOffset
|
// assetDataOffset
|
||||||
// + 32 (assetData len)
|
// + 32 (assetData len)
|
||||||
// + 4 (assetProxyId)
|
// + 4 (assetProxyId)
|
||||||
// + amountsOffset
|
// + amountsOffset
|
||||||
@@ -160,8 +160,8 @@ contract MultiAssetProxy is
|
|||||||
// 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:
|
||||||
// assetDataOffset
|
// assetDataOffset
|
||||||
// + 32 (assetData len)
|
// + 32 (assetData len)
|
||||||
// + 4 (assetProxyId)
|
// + 4 (assetProxyId)
|
||||||
// + nestedAssetDataOffset
|
// + nestedAssetDataOffset
|
||||||
@@ -190,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)
|
||||||
|
|
||||||
@@ -208,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)
|
||||||
@@ -228,7 +228,7 @@ 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:
|
||||||
// assetDataOffset
|
// assetDataOffset
|
||||||
// + 32 (assetData len)
|
// + 32 (assetData len)
|
||||||
// + 4 (assetProxyId)
|
// + 4 (assetProxyId)
|
||||||
// + nestedAssetDataOffset
|
// + nestedAssetDataOffset
|
||||||
@@ -274,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")`
|
||||||
@@ -284,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]`
|
||||||
@@ -298,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
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
75
contracts/asset-proxy/contracts/src/bridges/ChaiBridge.sol
Normal file
75
contracts/asset-proxy/contracts/src/bridges/ChaiBridge.sol
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.5.9;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "../interfaces/IERC20Bridge.sol";
|
||||||
|
import "../interfaces/IChai.sol";
|
||||||
|
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
|
||||||
|
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||||
|
|
||||||
|
|
||||||
|
// solhint-disable space-after-comma
|
||||||
|
contract ChaiBridge is
|
||||||
|
IERC20Bridge,
|
||||||
|
DeploymentConstants
|
||||||
|
{
|
||||||
|
/// @dev Withdraws `amount` of `from` address's Dai from the Chai contract.
|
||||||
|
/// Transfers `amount` of Dai to `to` address.
|
||||||
|
/// @param from Address to transfer asset from.
|
||||||
|
/// @param to Address to transfer asset to.
|
||||||
|
/// @param amount Amount of asset to transfer.
|
||||||
|
/// @return success The magic bytes `0x37708e9b` if successful.
|
||||||
|
function bridgeTransferFrom(
|
||||||
|
address /* tokenAddress */,
|
||||||
|
address from,
|
||||||
|
address to,
|
||||||
|
uint256 amount,
|
||||||
|
bytes calldata /* bridgeData */
|
||||||
|
)
|
||||||
|
external
|
||||||
|
returns (bytes4 success)
|
||||||
|
{
|
||||||
|
// Ensure that only the `ERC20BridgeProxy` can call this function.
|
||||||
|
require(
|
||||||
|
msg.sender == _getERC20BridgeProxyAddress(),
|
||||||
|
"ChaiBridge/ONLY_CALLABLE_BY_ERC20_BRIDGE_PROXY"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Withdraw `from` address's Dai.
|
||||||
|
// NOTE: This contract must be approved to spend Chai on behalf of `from`.
|
||||||
|
bytes memory drawCalldata = abi.encodeWithSelector(
|
||||||
|
IChai(address(0)).draw.selector,
|
||||||
|
from,
|
||||||
|
amount
|
||||||
|
);
|
||||||
|
|
||||||
|
(bool success,) = _getChaiAddress().call(drawCalldata);
|
||||||
|
require(
|
||||||
|
success,
|
||||||
|
"ChaiBridge/DRAW_DAI_FAILED"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Transfer Dai to `to`
|
||||||
|
// This will never fail if the `draw` call was successful
|
||||||
|
IERC20Token(_getDaiAddress()).transfer(to, amount);
|
||||||
|
|
||||||
|
return BRIDGE_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
108
contracts/asset-proxy/contracts/src/bridges/CurveBridge.sol
Normal file
108
contracts/asset-proxy/contracts/src/bridges/CurveBridge.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 "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||||
|
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol";
|
||||||
|
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
|
||||||
|
import "../interfaces/IERC20Bridge.sol";
|
||||||
|
import "../interfaces/ICurve.sol";
|
||||||
|
|
||||||
|
|
||||||
|
// solhint-disable not-rely-on-time
|
||||||
|
// solhint-disable space-after-comma
|
||||||
|
contract CurveBridge is
|
||||||
|
IERC20Bridge,
|
||||||
|
IWallet,
|
||||||
|
DeploymentConstants
|
||||||
|
{
|
||||||
|
/// @dev Callback for `ICurve`. Tries to buy `amount` of
|
||||||
|
/// `toTokenAddress` tokens by selling the entirety of the opposing asset
|
||||||
|
/// (DAI, USDC) to the Curve contract, then transfers the bought
|
||||||
|
/// tokens to `to`.
|
||||||
|
/// @param toTokenAddress The token to give to `to` (i.e DAI, USDC, USDT).
|
||||||
|
/// @param to The recipient of the bought tokens.
|
||||||
|
/// @param amount Minimum amount of `toTokenAddress` tokens to buy.
|
||||||
|
/// @param bridgeData The abi-encoeded "from" token address.
|
||||||
|
/// @return success The magic bytes if successful.
|
||||||
|
function bridgeTransferFrom(
|
||||||
|
address toTokenAddress,
|
||||||
|
address /* from */,
|
||||||
|
address to,
|
||||||
|
uint256 amount,
|
||||||
|
bytes calldata bridgeData
|
||||||
|
)
|
||||||
|
external
|
||||||
|
returns (bytes4 success)
|
||||||
|
{
|
||||||
|
// Decode the bridge data to get the Curve metadata.
|
||||||
|
(address curveAddress, int128 fromCoinIdx, int128 toCoinIdx, int128 version) = abi.decode(bridgeData, (address, int128, int128, int128));
|
||||||
|
ICurve exchange = ICurve(curveAddress);
|
||||||
|
|
||||||
|
address fromTokenAddress = exchange.underlying_coins(fromCoinIdx);
|
||||||
|
require(toTokenAddress != fromTokenAddress, "CurveBridge/INVALID_PAIR");
|
||||||
|
// Grant an allowance to the exchange to spend `fromTokenAddress` token.
|
||||||
|
LibERC20Token.approve(fromTokenAddress, address(exchange), uint256(-1));
|
||||||
|
|
||||||
|
// Try to sell all of this contract's `fromTokenAddress` token balance.
|
||||||
|
if (version == 0) {
|
||||||
|
exchange.exchange_underlying(
|
||||||
|
fromCoinIdx,
|
||||||
|
toCoinIdx,
|
||||||
|
// dx
|
||||||
|
IERC20Token(fromTokenAddress).balanceOf(address(this)),
|
||||||
|
// min dy
|
||||||
|
amount,
|
||||||
|
// expires
|
||||||
|
block.timestamp + 1
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
exchange.exchange_underlying(
|
||||||
|
fromCoinIdx,
|
||||||
|
toCoinIdx,
|
||||||
|
// dx
|
||||||
|
IERC20Token(fromTokenAddress).balanceOf(address(this)),
|
||||||
|
// min dy
|
||||||
|
amount
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint256 toTokenBalance = IERC20Token(toTokenAddress).balanceOf(address(this));
|
||||||
|
// Transfer the converted `toToken`s to `to`.
|
||||||
|
LibERC20Token.transfer(toTokenAddress, to, toTokenBalance);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
241
contracts/asset-proxy/contracts/src/bridges/DydxBridge.sol
Normal file
241
contracts/asset-proxy/contracts/src/bridges/DydxBridge.sol
Normal file
@@ -0,0 +1,241 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.5.9;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
|
||||||
|
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
|
||||||
|
import "../interfaces/IERC20Bridge.sol";
|
||||||
|
import "../interfaces/IDydxBridge.sol";
|
||||||
|
import "../interfaces/IDydx.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract DydxBridge is
|
||||||
|
IERC20Bridge,
|
||||||
|
IDydxBridge,
|
||||||
|
DeploymentConstants
|
||||||
|
{
|
||||||
|
|
||||||
|
using LibSafeMath for uint256;
|
||||||
|
|
||||||
|
/// @dev Callback for `IERC20Bridge`. Deposits or withdraws tokens from a dydx account.
|
||||||
|
/// Notes:
|
||||||
|
/// 1. This bridge must be set as an operator of the input dydx account.
|
||||||
|
/// 2. This function may only be called in the context of the 0x Exchange.
|
||||||
|
/// 3. The maker or taker of the 0x order must be the dydx account owner.
|
||||||
|
/// 4. Deposits into dydx are made from the `from` address.
|
||||||
|
/// 5. Withdrawals from dydx are made to the `to` address.
|
||||||
|
/// 6. Calling this function must always withdraw at least `amount`,
|
||||||
|
/// otherwise the `ERC20Bridge` will revert.
|
||||||
|
/// @param from The sender of the tokens and owner of the dydx account.
|
||||||
|
/// @param to The recipient of the tokens.
|
||||||
|
/// @param amount Minimum amount of `toTokenAddress` tokens to deposit or withdraw.
|
||||||
|
/// @param encodedBridgeData An abi-encoded `BridgeData` struct.
|
||||||
|
/// @return success The magic bytes if successful.
|
||||||
|
function bridgeTransferFrom(
|
||||||
|
address,
|
||||||
|
address from,
|
||||||
|
address to,
|
||||||
|
uint256 amount,
|
||||||
|
bytes calldata encodedBridgeData
|
||||||
|
)
|
||||||
|
external
|
||||||
|
returns (bytes4 success)
|
||||||
|
{
|
||||||
|
// Ensure that only the `ERC20BridgeProxy` can call this function.
|
||||||
|
require(
|
||||||
|
msg.sender == _getERC20BridgeProxyAddress(),
|
||||||
|
"DydxBridge/ONLY_CALLABLE_BY_ERC20_BRIDGE_PROXY"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Decode bridge data.
|
||||||
|
(BridgeData memory bridgeData) = abi.decode(encodedBridgeData, (BridgeData));
|
||||||
|
|
||||||
|
// The dydx accounts are owned by the `from` address.
|
||||||
|
IDydx.AccountInfo[] memory accounts = _createAccounts(from, bridgeData);
|
||||||
|
|
||||||
|
// Create dydx actions to run on the dydx accounts.
|
||||||
|
IDydx.ActionArgs[] memory actions = _createActions(
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
amount,
|
||||||
|
bridgeData
|
||||||
|
);
|
||||||
|
|
||||||
|
// Run operation. This will revert on failure.
|
||||||
|
IDydx(_getDydxAddress()).operate(accounts, actions);
|
||||||
|
return BRIDGE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Creates an array of accounts for dydx to operate on.
|
||||||
|
/// All accounts must belong to the same owner.
|
||||||
|
/// @param accountOwner Owner of the dydx account.
|
||||||
|
/// @param bridgeData A `BridgeData` struct.
|
||||||
|
function _createAccounts(
|
||||||
|
address accountOwner,
|
||||||
|
BridgeData memory bridgeData
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
returns (IDydx.AccountInfo[] memory accounts)
|
||||||
|
{
|
||||||
|
uint256[] memory accountNumbers = bridgeData.accountNumbers;
|
||||||
|
uint256 nAccounts = accountNumbers.length;
|
||||||
|
accounts = new IDydx.AccountInfo[](nAccounts);
|
||||||
|
for (uint256 i = 0; i < nAccounts; ++i) {
|
||||||
|
accounts[i] = IDydx.AccountInfo({
|
||||||
|
owner: accountOwner,
|
||||||
|
number: accountNumbers[i]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Creates an array of actions to carry out on dydx.
|
||||||
|
/// @param depositFrom Deposit value from this address (owner of the dydx account).
|
||||||
|
/// @param withdrawTo Withdraw value to this address.
|
||||||
|
/// @param amount The amount of value available to operate on.
|
||||||
|
/// @param bridgeData A `BridgeData` struct.
|
||||||
|
function _createActions(
|
||||||
|
address depositFrom,
|
||||||
|
address withdrawTo,
|
||||||
|
uint256 amount,
|
||||||
|
BridgeData memory bridgeData
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
returns (IDydx.ActionArgs[] memory actions)
|
||||||
|
{
|
||||||
|
BridgeAction[] memory bridgeActions = bridgeData.actions;
|
||||||
|
uint256 nBridgeActions = bridgeActions.length;
|
||||||
|
actions = new IDydx.ActionArgs[](nBridgeActions);
|
||||||
|
for (uint256 i = 0; i < nBridgeActions; ++i) {
|
||||||
|
// Cache current bridge action.
|
||||||
|
BridgeAction memory bridgeAction = bridgeActions[i];
|
||||||
|
|
||||||
|
// Scale amount, if conversion rate is set.
|
||||||
|
uint256 scaledAmount;
|
||||||
|
if (bridgeAction.conversionRateDenominator > 0) {
|
||||||
|
scaledAmount = LibMath.safeGetPartialAmountFloor(
|
||||||
|
bridgeAction.conversionRateNumerator,
|
||||||
|
bridgeAction.conversionRateDenominator,
|
||||||
|
amount
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
scaledAmount = amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct dydx action.
|
||||||
|
if (bridgeAction.actionType == BridgeActionType.Deposit) {
|
||||||
|
// Deposit tokens from the account owner into their dydx account.
|
||||||
|
actions[i] = _createDepositAction(
|
||||||
|
depositFrom,
|
||||||
|
scaledAmount,
|
||||||
|
bridgeAction
|
||||||
|
);
|
||||||
|
} else if (bridgeAction.actionType == BridgeActionType.Withdraw) {
|
||||||
|
// Withdraw tokens from dydx to the `otherAccount`.
|
||||||
|
actions[i] = _createWithdrawAction(
|
||||||
|
withdrawTo,
|
||||||
|
scaledAmount,
|
||||||
|
bridgeAction
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// If all values in the `Action` enum are handled then this
|
||||||
|
// revert is unreachable: Solidity will revert when casting
|
||||||
|
// from `uint8` to `Action`.
|
||||||
|
revert("DydxBridge/UNRECOGNIZED_BRIDGE_ACTION");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Returns a dydx `DepositAction`.
|
||||||
|
/// @param depositFrom Deposit tokens from this address who is also the account owner.
|
||||||
|
/// @param amount of tokens to deposit.
|
||||||
|
/// @param bridgeAction A `BridgeAction` struct.
|
||||||
|
/// @return depositAction The encoded dydx action.
|
||||||
|
function _createDepositAction(
|
||||||
|
address depositFrom,
|
||||||
|
uint256 amount,
|
||||||
|
BridgeAction memory bridgeAction
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (
|
||||||
|
IDydx.ActionArgs memory depositAction
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// Create dydx amount.
|
||||||
|
IDydx.AssetAmount memory dydxAmount = IDydx.AssetAmount({
|
||||||
|
sign: true, // true if positive.
|
||||||
|
denomination: IDydx.AssetDenomination.Wei, // Wei => actual token amount held in account.
|
||||||
|
ref: IDydx.AssetReference.Delta, // Delta => a relative amount.
|
||||||
|
value: amount // amount to deposit.
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create dydx deposit action.
|
||||||
|
depositAction = IDydx.ActionArgs({
|
||||||
|
actionType: IDydx.ActionType.Deposit, // deposit tokens.
|
||||||
|
amount: dydxAmount, // amount to deposit.
|
||||||
|
accountId: bridgeAction.accountId, // index in the `accounts` when calling `operate`.
|
||||||
|
primaryMarketId: bridgeAction.marketId, // indicates which token to deposit.
|
||||||
|
otherAddress: depositFrom, // deposit from the account owner.
|
||||||
|
// unused parameters
|
||||||
|
secondaryMarketId: 0,
|
||||||
|
otherAccountId: 0,
|
||||||
|
data: hex''
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Returns a dydx `WithdrawAction`.
|
||||||
|
/// @param withdrawTo Withdraw tokens to this address.
|
||||||
|
/// @param amount of tokens to withdraw.
|
||||||
|
/// @param bridgeAction A `BridgeAction` struct.
|
||||||
|
/// @return withdrawAction The encoded dydx action.
|
||||||
|
function _createWithdrawAction(
|
||||||
|
address withdrawTo,
|
||||||
|
uint256 amount,
|
||||||
|
BridgeAction memory bridgeAction
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (
|
||||||
|
IDydx.ActionArgs memory withdrawAction
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// Create dydx amount.
|
||||||
|
IDydx.AssetAmount memory amountToWithdraw = IDydx.AssetAmount({
|
||||||
|
sign: false, // false if negative.
|
||||||
|
denomination: IDydx.AssetDenomination.Wei, // Wei => actual token amount held in account.
|
||||||
|
ref: IDydx.AssetReference.Delta, // Delta => a relative amount.
|
||||||
|
value: amount // amount to withdraw.
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create withdraw action.
|
||||||
|
withdrawAction = IDydx.ActionArgs({
|
||||||
|
actionType: IDydx.ActionType.Withdraw, // withdraw tokens.
|
||||||
|
amount: amountToWithdraw, // amount to withdraw.
|
||||||
|
accountId: bridgeAction.accountId, // index in the `accounts` when calling `operate`.
|
||||||
|
primaryMarketId: bridgeAction.marketId, // indicates which token to withdraw.
|
||||||
|
otherAddress: withdrawTo, // withdraw tokens to this address.
|
||||||
|
// unused parameters
|
||||||
|
secondaryMarketId: 0,
|
||||||
|
otherAccountId: 0,
|
||||||
|
data: hex''
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.5.9;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||||
|
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol";
|
||||||
|
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
|
||||||
|
import "../interfaces/IERC20Bridge.sol";
|
||||||
|
import "../interfaces/IEth2Dai.sol";
|
||||||
|
|
||||||
|
|
||||||
|
// solhint-disable space-after-comma
|
||||||
|
contract Eth2DaiBridge is
|
||||||
|
IERC20Bridge,
|
||||||
|
IWallet,
|
||||||
|
DeploymentConstants
|
||||||
|
{
|
||||||
|
/// @dev Callback for `IERC20Bridge`. Tries to buy `amount` of
|
||||||
|
/// `toTokenAddress` tokens by selling the entirety of the opposing asset
|
||||||
|
/// (DAI or WETH) to the Eth2Dai contract, then transfers the bought
|
||||||
|
/// tokens to `to`.
|
||||||
|
/// @param toTokenAddress The token to give to `to` (either DAI or WETH).
|
||||||
|
/// @param to The recipient of the bought tokens.
|
||||||
|
/// @param amount Minimum amount of `toTokenAddress` tokens to buy.
|
||||||
|
/// @param bridgeData The abi-encoeded "from" token address.
|
||||||
|
/// @return success The magic bytes if successful.
|
||||||
|
function bridgeTransferFrom(
|
||||||
|
address toTokenAddress,
|
||||||
|
address /* from */,
|
||||||
|
address to,
|
||||||
|
uint256 amount,
|
||||||
|
bytes calldata bridgeData
|
||||||
|
)
|
||||||
|
external
|
||||||
|
returns (bytes4 success)
|
||||||
|
{
|
||||||
|
// Decode the bridge data to get the `fromTokenAddress`.
|
||||||
|
(address fromTokenAddress) = abi.decode(bridgeData, (address));
|
||||||
|
|
||||||
|
IEth2Dai exchange = IEth2Dai(_getEth2DaiAddress());
|
||||||
|
// Grant an allowance to the exchange to spend `fromTokenAddress` token.
|
||||||
|
LibERC20Token.approve(fromTokenAddress, address(exchange), uint256(-1));
|
||||||
|
|
||||||
|
// Try to sell all of this contract's `fromTokenAddress` token balance.
|
||||||
|
uint256 boughtAmount = exchange.sellAllAmount(
|
||||||
|
fromTokenAddress,
|
||||||
|
IERC20Token(fromTokenAddress).balanceOf(address(this)),
|
||||||
|
toTokenAddress,
|
||||||
|
amount
|
||||||
|
);
|
||||||
|
// Transfer the converted `toToken`s to `to`.
|
||||||
|
LibERC20Token.transfer(toTokenAddress, to, boughtAmount);
|
||||||
|
return BRIDGE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev `SignatureType.Wallet` callback, so that this bridge can be the maker
|
||||||
|
/// and sign for itself in orders. Always succeeds.
|
||||||
|
/// @return magicValue Magic success bytes, always.
|
||||||
|
function isValidSignature(
|
||||||
|
bytes32,
|
||||||
|
bytes calldata
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (bytes4 magicValue)
|
||||||
|
{
|
||||||
|
return LEGACY_WALLET_MAGIC_VALUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
162
contracts/asset-proxy/contracts/src/bridges/KyberBridge.sol
Normal file
162
contracts/asset-proxy/contracts/src/bridges/KyberBridge.sol
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.5.9;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||||
|
import "@0x/contracts-erc20/contracts/src/interfaces/IEtherToken.sol";
|
||||||
|
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol";
|
||||||
|
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
|
||||||
|
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
||||||
|
import "../interfaces/IERC20Bridge.sol";
|
||||||
|
import "../interfaces/IKyberNetworkProxy.sol";
|
||||||
|
|
||||||
|
|
||||||
|
// solhint-disable space-after-comma
|
||||||
|
contract KyberBridge is
|
||||||
|
IERC20Bridge,
|
||||||
|
IWallet,
|
||||||
|
DeploymentConstants
|
||||||
|
{
|
||||||
|
using LibSafeMath for uint256;
|
||||||
|
|
||||||
|
// @dev Structure used internally to get around stack limits.
|
||||||
|
struct TradeState {
|
||||||
|
IKyberNetworkProxy kyber;
|
||||||
|
IEtherToken weth;
|
||||||
|
address fromTokenAddress;
|
||||||
|
uint256 fromTokenBalance;
|
||||||
|
uint256 payableAmount;
|
||||||
|
uint256 conversionRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Kyber ETH pseudo-address.
|
||||||
|
address constant public KYBER_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
|
||||||
|
/// @dev `bridgeTransferFrom()` failure result.
|
||||||
|
bytes4 constant private BRIDGE_FAILED = 0x0;
|
||||||
|
/// @dev Precision of Kyber rates.
|
||||||
|
uint256 constant private KYBER_RATE_BASE = 10 ** 18;
|
||||||
|
|
||||||
|
// solhint-disable no-empty-blocks
|
||||||
|
/// @dev Payable fallback to receive ETH from Kyber.
|
||||||
|
function ()
|
||||||
|
external
|
||||||
|
payable
|
||||||
|
{}
|
||||||
|
|
||||||
|
/// @dev Callback for `IKyberBridge`. Tries to buy `amount` of
|
||||||
|
/// `toTokenAddress` tokens by selling the entirety of the opposing asset
|
||||||
|
/// to the `KyberNetworkProxy` contract, then transfers the bought
|
||||||
|
/// tokens to `to`.
|
||||||
|
/// @param toTokenAddress The token to give to `to`.
|
||||||
|
/// @param to The recipient of the bought tokens.
|
||||||
|
/// @param amount Minimum amount of `toTokenAddress` tokens to buy.
|
||||||
|
/// @param bridgeData The abi-encoeded "from" token address.
|
||||||
|
/// @return success The magic bytes if successful.
|
||||||
|
function bridgeTransferFrom(
|
||||||
|
address toTokenAddress,
|
||||||
|
address /* from */,
|
||||||
|
address to,
|
||||||
|
uint256 amount,
|
||||||
|
bytes calldata bridgeData
|
||||||
|
)
|
||||||
|
external
|
||||||
|
returns (bytes4 success)
|
||||||
|
{
|
||||||
|
TradeState memory state;
|
||||||
|
state.kyber = IKyberNetworkProxy(_getKyberNetworkProxyAddress());
|
||||||
|
state.weth = IEtherToken(_getWethAddress());
|
||||||
|
// Decode the bridge data to get the `fromTokenAddress`.
|
||||||
|
(state.fromTokenAddress) = abi.decode(bridgeData, (address));
|
||||||
|
// Query the balance of "from" tokens.
|
||||||
|
state.fromTokenBalance = IERC20Token(state.fromTokenAddress).balanceOf(address(this));
|
||||||
|
if (state.fromTokenBalance == 0) {
|
||||||
|
// Return failure if no input tokens.
|
||||||
|
return BRIDGE_FAILED;
|
||||||
|
}
|
||||||
|
// Compute the conversion rate, expressed in 18 decimals.
|
||||||
|
// The sequential notation is to get around stack limits.
|
||||||
|
state.conversionRate = KYBER_RATE_BASE;
|
||||||
|
state.conversionRate = state.conversionRate.safeMul(amount);
|
||||||
|
state.conversionRate = state.conversionRate.safeMul(
|
||||||
|
10 ** uint256(LibERC20Token.decimals(state.fromTokenAddress))
|
||||||
|
);
|
||||||
|
state.conversionRate = state.conversionRate.safeDiv(state.fromTokenBalance);
|
||||||
|
state.conversionRate = state.conversionRate.safeDiv(
|
||||||
|
10 ** uint256(LibERC20Token.decimals(toTokenAddress))
|
||||||
|
);
|
||||||
|
if (state.fromTokenAddress == toTokenAddress) {
|
||||||
|
// Just transfer the tokens if they're the same.
|
||||||
|
LibERC20Token.transfer(state.fromTokenAddress, to, state.fromTokenBalance);
|
||||||
|
return BRIDGE_SUCCESS;
|
||||||
|
} else if (state.fromTokenAddress != address(state.weth)) {
|
||||||
|
// If the input token is not WETH, grant an allowance to the exchange
|
||||||
|
// to spend them.
|
||||||
|
LibERC20Token.approve(state.fromTokenAddress, address(state.kyber), uint256(-1));
|
||||||
|
} else {
|
||||||
|
// If the input token is WETH, unwrap it and attach it to the call.
|
||||||
|
state.fromTokenAddress = KYBER_ETH_ADDRESS;
|
||||||
|
state.payableAmount = state.fromTokenBalance;
|
||||||
|
state.weth.withdraw(state.fromTokenBalance);
|
||||||
|
}
|
||||||
|
bool isToTokenWeth = toTokenAddress == address(state.weth);
|
||||||
|
|
||||||
|
// Try to sell all of this contract's input token balance through
|
||||||
|
// `KyberNetworkProxy.trade()`.
|
||||||
|
uint256 boughtAmount = state.kyber.trade.value(state.payableAmount)(
|
||||||
|
// Input token.
|
||||||
|
state.fromTokenAddress,
|
||||||
|
// Sell amount.
|
||||||
|
state.fromTokenBalance,
|
||||||
|
// Output token.
|
||||||
|
isToTokenWeth ? KYBER_ETH_ADDRESS : toTokenAddress,
|
||||||
|
// Transfer to this contract if converting to ETH, otherwise
|
||||||
|
// transfer directly to the recipient.
|
||||||
|
isToTokenWeth ? address(uint160(address(this))) : address(uint160(to)),
|
||||||
|
// Buy as much as possible.
|
||||||
|
uint256(-1),
|
||||||
|
// Compute the minimum conversion rate, which is expressed in units with
|
||||||
|
// 18 decimal places.
|
||||||
|
state.conversionRate,
|
||||||
|
// No affiliate address.
|
||||||
|
address(0)
|
||||||
|
);
|
||||||
|
// Wrap ETH output and transfer to recipient.
|
||||||
|
if (isToTokenWeth) {
|
||||||
|
state.weth.deposit.value(boughtAmount)();
|
||||||
|
state.weth.transfer(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;
|
||||||
|
}
|
||||||
|
}
|
||||||
200
contracts/asset-proxy/contracts/src/bridges/UniswapBridge.sol
Normal file
200
contracts/asset-proxy/contracts/src/bridges/UniswapBridge.sol
Normal file
@@ -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-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||||
|
import "@0x/contracts-erc20/contracts/src/interfaces/IEtherToken.sol";
|
||||||
|
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol";
|
||||||
|
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
|
||||||
|
import "../interfaces/IUniswapExchangeFactory.sol";
|
||||||
|
import "../interfaces/IUniswapExchange.sol";
|
||||||
|
import "../interfaces/IERC20Bridge.sol";
|
||||||
|
|
||||||
|
|
||||||
|
// solhint-disable space-after-comma
|
||||||
|
// solhint-disable not-rely-on-time
|
||||||
|
contract UniswapBridge is
|
||||||
|
IERC20Bridge,
|
||||||
|
IWallet,
|
||||||
|
DeploymentConstants
|
||||||
|
{
|
||||||
|
// Struct to hold `bridgeTransferFrom()` local variables in memory and to avoid
|
||||||
|
// stack overflows.
|
||||||
|
struct WithdrawToState {
|
||||||
|
IUniswapExchange exchange;
|
||||||
|
uint256 fromTokenBalance;
|
||||||
|
IEtherToken weth;
|
||||||
|
}
|
||||||
|
|
||||||
|
// solhint-disable no-empty-blocks
|
||||||
|
/// @dev Payable fallback to receive ETH from uniswap.
|
||||||
|
function ()
|
||||||
|
external
|
||||||
|
payable
|
||||||
|
{}
|
||||||
|
|
||||||
|
/// @dev Callback for `IERC20Bridge`. Tries to buy `amount` of
|
||||||
|
/// `toTokenAddress` tokens by selling the entirety of the `fromTokenAddress`
|
||||||
|
/// token encoded in the bridge data.
|
||||||
|
/// @param toTokenAddress The token to buy and transfer to `to`.
|
||||||
|
/// @param to The recipient of the bought tokens.
|
||||||
|
/// @param amount Minimum amount of `toTokenAddress` tokens to buy.
|
||||||
|
/// @param bridgeData The abi-encoded "from" token address.
|
||||||
|
/// @return success The magic bytes if successful.
|
||||||
|
function bridgeTransferFrom(
|
||||||
|
address toTokenAddress,
|
||||||
|
address /* from */,
|
||||||
|
address to,
|
||||||
|
uint256 amount,
|
||||||
|
bytes calldata bridgeData
|
||||||
|
)
|
||||||
|
external
|
||||||
|
returns (bytes4 success)
|
||||||
|
{
|
||||||
|
// State memory object to avoid stack overflows.
|
||||||
|
WithdrawToState memory state;
|
||||||
|
// Decode the bridge data to get the `fromTokenAddress`.
|
||||||
|
(address fromTokenAddress) = abi.decode(bridgeData, (address));
|
||||||
|
|
||||||
|
// Just transfer the tokens if they're the same.
|
||||||
|
if (fromTokenAddress == toTokenAddress) {
|
||||||
|
LibERC20Token.transfer(fromTokenAddress, to, amount);
|
||||||
|
return BRIDGE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the exchange for the token pair.
|
||||||
|
state.exchange = _getUniswapExchangeForTokenPair(
|
||||||
|
fromTokenAddress,
|
||||||
|
toTokenAddress
|
||||||
|
);
|
||||||
|
// Get our balance of `fromTokenAddress` token.
|
||||||
|
state.fromTokenBalance = IERC20Token(fromTokenAddress).balanceOf(address(this));
|
||||||
|
// Get the weth contract.
|
||||||
|
state.weth = IEtherToken(_getWethAddress());
|
||||||
|
|
||||||
|
// Convert from WETH to a token.
|
||||||
|
if (fromTokenAddress == address(state.weth)) {
|
||||||
|
// Unwrap the WETH.
|
||||||
|
state.weth.withdraw(state.fromTokenBalance);
|
||||||
|
// Buy as much of `toTokenAddress` token with ETH as possible and
|
||||||
|
// transfer it to `to`.
|
||||||
|
state.exchange.ethToTokenTransferInput.value(state.fromTokenBalance)(
|
||||||
|
// Minimum buy amount.
|
||||||
|
amount,
|
||||||
|
// Expires after this block.
|
||||||
|
block.timestamp,
|
||||||
|
// Recipient is `to`.
|
||||||
|
to
|
||||||
|
);
|
||||||
|
|
||||||
|
// Convert from a token to WETH.
|
||||||
|
} else if (toTokenAddress == address(state.weth)) {
|
||||||
|
// Grant the exchange an allowance.
|
||||||
|
_grantExchangeAllowance(state.exchange, fromTokenAddress);
|
||||||
|
// Buy as much ETH with `fromTokenAddress` token as possible.
|
||||||
|
uint256 ethBought = state.exchange.tokenToEthSwapInput(
|
||||||
|
// Sell all tokens we hold.
|
||||||
|
state.fromTokenBalance,
|
||||||
|
// Minimum buy amount.
|
||||||
|
amount,
|
||||||
|
// Expires after this block.
|
||||||
|
block.timestamp
|
||||||
|
);
|
||||||
|
// Wrap the ETH.
|
||||||
|
state.weth.deposit.value(ethBought)();
|
||||||
|
// Transfer the WETH to `to`.
|
||||||
|
IEtherToken(toTokenAddress).transfer(to, ethBought);
|
||||||
|
|
||||||
|
// Convert from one token to another.
|
||||||
|
} else {
|
||||||
|
// Grant the exchange an allowance.
|
||||||
|
_grantExchangeAllowance(state.exchange, fromTokenAddress);
|
||||||
|
// Buy as much `toTokenAddress` token with `fromTokenAddress` token
|
||||||
|
// and transfer it to `to`.
|
||||||
|
state.exchange.tokenToTokenTransferInput(
|
||||||
|
// Sell all tokens we hold.
|
||||||
|
state.fromTokenBalance,
|
||||||
|
// Minimum buy amount.
|
||||||
|
amount,
|
||||||
|
// Must buy at least 1 intermediate ETH.
|
||||||
|
1,
|
||||||
|
// Expires after this block.
|
||||||
|
block.timestamp,
|
||||||
|
// Recipient is `to`.
|
||||||
|
to,
|
||||||
|
// Convert to `toTokenAddress`.
|
||||||
|
toTokenAddress
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return BRIDGE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev `SignatureType.Wallet` callback, so that this bridge can be the maker
|
||||||
|
/// and sign for itself in orders. Always succeeds.
|
||||||
|
/// @return magicValue Success bytes, always.
|
||||||
|
function isValidSignature(
|
||||||
|
bytes32,
|
||||||
|
bytes calldata
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (bytes4 magicValue)
|
||||||
|
{
|
||||||
|
return LEGACY_WALLET_MAGIC_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Grants an unlimited allowance to the exchange for its token
|
||||||
|
/// on behalf of this contract.
|
||||||
|
/// @param exchange The Uniswap token exchange.
|
||||||
|
/// @param tokenAddress The token address for the exchange.
|
||||||
|
function _grantExchangeAllowance(IUniswapExchange exchange, address tokenAddress)
|
||||||
|
private
|
||||||
|
{
|
||||||
|
LibERC20Token.approve(tokenAddress, address(exchange), uint256(-1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Retrieves the uniswap exchange for a given token pair.
|
||||||
|
/// In the case of a WETH-token exchange, this will be the non-WETH token.
|
||||||
|
/// In th ecase of a token-token exchange, this will be the first token.
|
||||||
|
/// @param fromTokenAddress The address of the token we are converting from.
|
||||||
|
/// @param toTokenAddress The address of the token we are converting to.
|
||||||
|
/// @return exchange The uniswap exchange.
|
||||||
|
function _getUniswapExchangeForTokenPair(
|
||||||
|
address fromTokenAddress,
|
||||||
|
address toTokenAddress
|
||||||
|
)
|
||||||
|
private
|
||||||
|
view
|
||||||
|
returns (IUniswapExchange exchange)
|
||||||
|
{
|
||||||
|
address exchangeTokenAddress = fromTokenAddress;
|
||||||
|
// Whichever isn't WETH is the exchange token.
|
||||||
|
if (fromTokenAddress == _getWethAddress()) {
|
||||||
|
exchangeTokenAddress = toTokenAddress;
|
||||||
|
}
|
||||||
|
exchange = IUniswapExchange(
|
||||||
|
IUniswapExchangeFactory(_getUniswapExchangeFactoryAddress())
|
||||||
|
.getExchange(exchangeTokenAddress)
|
||||||
|
);
|
||||||
|
require(address(exchange) != address(0), "NO_UNISWAP_EXCHANGE_FOR_TOKEN");
|
||||||
|
return exchange;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.5.5;
|
pragma solidity ^0.5.9;
|
||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
|
||||||
@@ -26,33 +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 {
|
||||||
|
|
||||||
|
/// @dev Function signature for encoding ERC20 assetData.
|
||||||
|
/// @param tokenAddress Address of ERC20Token contract.
|
||||||
function ERC20Token(address tokenAddress)
|
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 tokenAddress,
|
address tokenAddress,
|
||||||
uint256 tokenId
|
uint256 tokenId
|
||||||
)
|
)
|
||||||
external;
|
external;
|
||||||
|
|
||||||
|
/// @dev Function signature for encoding ERC1155 assetData.
|
||||||
|
/// @param tokenAddress Address of ERC1155 token contract.
|
||||||
|
/// @param tokenIds Array of ids of tokens to be transferred.
|
||||||
|
/// @param values Array of values that correspond to each token id to be transferred.
|
||||||
|
/// Note that each value will be multiplied by the amount being filled in the order before transferring.
|
||||||
|
/// @param callbackData Extra data to be passed to receiver's `onERC1155Received` callback function.
|
||||||
function ERC1155Assets(
|
function ERC1155Assets(
|
||||||
address tokenAddress,
|
address tokenAddress,
|
||||||
uint256[] calldata tokenIds,
|
uint256[] calldata tokenIds,
|
||||||
uint256[] calldata tokenValues,
|
uint256[] calldata values,
|
||||||
bytes calldata callbackData
|
bytes calldata callbackData
|
||||||
)
|
)
|
||||||
external;
|
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(
|
function MultiAsset(
|
||||||
uint256[] calldata amounts,
|
uint256[] calldata values,
|
||||||
bytes[] calldata nestedAssetData
|
bytes[] calldata nestedAssetData
|
||||||
)
|
)
|
||||||
external;
|
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(
|
function StaticCall(
|
||||||
address callTarget,
|
address staticCallTargetAddress,
|
||||||
bytes calldata staticCallData,
|
bytes calldata staticCallData,
|
||||||
bytes32 callResultHash
|
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,9 +16,7 @@
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pragma solidity ^0.5.5;
|
pragma solidity ^0.5.9;
|
||||||
|
|
||||||
import "./IAuthorizable.sol";
|
|
||||||
|
|
||||||
|
|
||||||
contract IAssetProxy {
|
contract IAssetProxy {
|
||||||
|
|||||||
@@ -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.5.5;
|
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.5.5;
|
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()
|
||||||
|
|||||||
66
contracts/asset-proxy/contracts/src/interfaces/IChai.sol
Normal file
66
contracts/asset-proxy/contracts/src/interfaces/IChai.sol
Normal file
@@ -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-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract PotLike {
|
||||||
|
function chi() external returns (uint256);
|
||||||
|
function rho() external returns (uint256);
|
||||||
|
function drip() external returns (uint256);
|
||||||
|
function join(uint256) external;
|
||||||
|
function exit(uint256) external;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// The actual Chai contract can be found here: https://github.com/dapphub/chai
|
||||||
|
contract IChai is
|
||||||
|
IERC20Token
|
||||||
|
{
|
||||||
|
/// @dev Withdraws Dai owned by `src`
|
||||||
|
/// @param src Address that owns Dai.
|
||||||
|
/// @param wad Amount of Dai to withdraw.
|
||||||
|
function draw(
|
||||||
|
address src,
|
||||||
|
uint256 wad
|
||||||
|
)
|
||||||
|
external;
|
||||||
|
|
||||||
|
/// @dev Queries Dai balance of Chai holder.
|
||||||
|
/// @param usr Address of Chai holder.
|
||||||
|
/// @return Dai balance.
|
||||||
|
function dai(address usr)
|
||||||
|
external
|
||||||
|
returns (uint256);
|
||||||
|
|
||||||
|
/// @dev Queries the Pot contract used by the Chai contract.
|
||||||
|
function pot()
|
||||||
|
external
|
||||||
|
returns (PotLike);
|
||||||
|
|
||||||
|
/// @dev Deposits Dai in exchange for Chai
|
||||||
|
/// @param dst Address to receive Chai.
|
||||||
|
/// @param wad Amount of Dai to deposit.
|
||||||
|
function join(
|
||||||
|
address dst,
|
||||||
|
uint256 wad
|
||||||
|
)
|
||||||
|
external;
|
||||||
|
}
|
||||||
87
contracts/asset-proxy/contracts/src/interfaces/ICurve.sol
Normal file
87
contracts/asset-proxy/contracts/src/interfaces/ICurve.sol
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.5.9;
|
||||||
|
|
||||||
|
|
||||||
|
// solhint-disable func-name-mixedcase
|
||||||
|
interface ICurve {
|
||||||
|
|
||||||
|
/// @dev Sell `sellAmount` of `fromToken` token and receive `toToken` token.
|
||||||
|
/// This function exists on early versions of Curve (USDC/DAI)
|
||||||
|
/// @param i The token index being sold.
|
||||||
|
/// @param j The token index being bought.
|
||||||
|
/// @param sellAmount The amount of token being bought.
|
||||||
|
/// @param minBuyAmount The minimum buy amount of the token being bought.
|
||||||
|
/// @param deadline The time in seconds when this operation should expire.
|
||||||
|
function exchange_underlying(
|
||||||
|
int128 i,
|
||||||
|
int128 j,
|
||||||
|
uint256 sellAmount,
|
||||||
|
uint256 minBuyAmount,
|
||||||
|
uint256 deadline
|
||||||
|
)
|
||||||
|
external;
|
||||||
|
|
||||||
|
/// @dev Sell `sellAmount` of `fromToken` token and receive `toToken` token.
|
||||||
|
/// This function exists on later versions of Curve (USDC/DAI/USDT)
|
||||||
|
/// @param i The token index being sold.
|
||||||
|
/// @param j The token index being bought.
|
||||||
|
/// @param sellAmount The amount of token being bought.
|
||||||
|
/// @param minBuyAmount The minimum buy amount of the token being bought.
|
||||||
|
function exchange_underlying(
|
||||||
|
int128 i,
|
||||||
|
int128 j,
|
||||||
|
uint256 sellAmount,
|
||||||
|
uint256 minBuyAmount
|
||||||
|
)
|
||||||
|
external;
|
||||||
|
|
||||||
|
/// @dev Get the amount of `toToken` by selling `sellAmount` of `fromToken`
|
||||||
|
/// @param i The token index being sold.
|
||||||
|
/// @param j The token index being bought.
|
||||||
|
/// @param sellAmount The amount of token being bought.
|
||||||
|
function get_dy_underlying(
|
||||||
|
int128 i,
|
||||||
|
int128 j,
|
||||||
|
uint256 sellAmount
|
||||||
|
)
|
||||||
|
external
|
||||||
|
returns (uint256 dy);
|
||||||
|
|
||||||
|
/// @dev Get the amount of `fromToken` by buying `buyAmount` of `toToken`
|
||||||
|
/// This function exists on later versions of Curve (USDC/DAI/USDT)
|
||||||
|
/// @param i The token index being sold.
|
||||||
|
/// @param j The token index being bought.
|
||||||
|
/// @param buyAmount The amount of token being bought.
|
||||||
|
function get_dx_underlying(
|
||||||
|
int128 i,
|
||||||
|
int128 j,
|
||||||
|
uint256 buyAmount
|
||||||
|
)
|
||||||
|
external
|
||||||
|
returns (uint256 dx);
|
||||||
|
|
||||||
|
/// @dev Get the underlying token address from the token index
|
||||||
|
/// @param i The token index.
|
||||||
|
function underlying_coins(
|
||||||
|
int128 i
|
||||||
|
)
|
||||||
|
external
|
||||||
|
returns (address tokenAddress);
|
||||||
|
}
|
||||||
89
contracts/asset-proxy/contracts/src/interfaces/IDydx.sol
Normal file
89
contracts/asset-proxy/contracts/src/interfaces/IDydx.sol
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.5.9;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
|
||||||
|
interface IDydx {
|
||||||
|
|
||||||
|
/// @dev Represents the unique key that specifies an account
|
||||||
|
struct AccountInfo {
|
||||||
|
address owner; // The address that owns the account
|
||||||
|
uint256 number; // A nonce that allows a single address to control many accounts
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ActionType {
|
||||||
|
Deposit, // supply tokens
|
||||||
|
Withdraw, // borrow tokens
|
||||||
|
Transfer, // transfer balance between accounts
|
||||||
|
Buy, // buy an amount of some token (externally)
|
||||||
|
Sell, // sell an amount of some token (externally)
|
||||||
|
Trade, // trade tokens against another account
|
||||||
|
Liquidate, // liquidate an undercollateralized or expiring account
|
||||||
|
Vaporize, // use excess tokens to zero-out a completely negative account
|
||||||
|
Call // send arbitrary data to an address
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Arguments that are passed to Solo in an ordered list as part of a single operation.
|
||||||
|
/// Each ActionArgs has an actionType which specifies which action struct that this data will be
|
||||||
|
/// parsed into before being processed.
|
||||||
|
struct ActionArgs {
|
||||||
|
ActionType actionType;
|
||||||
|
uint256 accountId;
|
||||||
|
AssetAmount amount;
|
||||||
|
uint256 primaryMarketId;
|
||||||
|
uint256 secondaryMarketId;
|
||||||
|
address otherAddress;
|
||||||
|
uint256 otherAccountId;
|
||||||
|
bytes data;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum AssetDenomination {
|
||||||
|
Wei, // the amount is denominated in wei
|
||||||
|
Par // the amount is denominated in par
|
||||||
|
}
|
||||||
|
|
||||||
|
enum AssetReference {
|
||||||
|
Delta, // the amount is given as a delta from the current value
|
||||||
|
Target // the amount is given as an exact number to end up at
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AssetAmount {
|
||||||
|
bool sign; // true if positive
|
||||||
|
AssetDenomination denomination;
|
||||||
|
AssetReference ref;
|
||||||
|
uint256 value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev The main entry-point to Solo that allows users and contracts to manage accounts.
|
||||||
|
/// Take one or more actions on one or more accounts. The msg.sender must be the owner or
|
||||||
|
/// operator of all accounts except for those being liquidated, vaporized, or traded with.
|
||||||
|
/// One call to operate() is considered a singular "operation". Account collateralization is
|
||||||
|
/// ensured only after the completion of the entire operation.
|
||||||
|
/// @param accounts A list of all accounts that will be used in this operation. Cannot contain
|
||||||
|
/// duplicates. In each action, the relevant account will be referred-to by its
|
||||||
|
/// index in the list.
|
||||||
|
/// @param actions An ordered list of all actions that will be taken in this operation. The
|
||||||
|
/// actions will be processed in order.
|
||||||
|
function operate(
|
||||||
|
AccountInfo[] calldata accounts,
|
||||||
|
ActionArgs[] calldata actions
|
||||||
|
)
|
||||||
|
external;
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.5.9;
|
||||||
|
|
||||||
|
|
||||||
|
interface IDydxBridge {
|
||||||
|
|
||||||
|
/// @dev This is the subset of `IDydx.ActionType` that are supported by the bridge.
|
||||||
|
enum BridgeActionType {
|
||||||
|
Deposit, // Deposit tokens into dydx account.
|
||||||
|
Withdraw // Withdraw tokens from dydx account.
|
||||||
|
}
|
||||||
|
|
||||||
|
struct BridgeAction {
|
||||||
|
BridgeActionType actionType; // Action to run on dydx account.
|
||||||
|
uint256 accountId; // Index in `BridgeData.accountNumbers` for this action.
|
||||||
|
uint256 marketId; // Market to operate on.
|
||||||
|
uint256 conversionRateNumerator; // Optional. If set, transfer amount is scaled by (conversionRateNumerator/conversionRateDenominator).
|
||||||
|
uint256 conversionRateDenominator; // Optional. If set, transfer amount is scaled by (conversionRateNumerator/conversionRateDenominator).
|
||||||
|
}
|
||||||
|
|
||||||
|
struct BridgeData {
|
||||||
|
uint256[] accountNumbers; // Account number used to identify the owner's specific account.
|
||||||
|
BridgeAction[] actions; // Actions to carry out on the owner's accounts.
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 bridgeTransferFrom(
|
||||||
|
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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.5.9;
|
||||||
|
|
||||||
|
|
||||||
|
interface IKyberNetworkProxy {
|
||||||
|
|
||||||
|
/// @dev Sells `sellTokenAddress` tokens for `buyTokenAddress` tokens.
|
||||||
|
/// @param sellTokenAddress Token to sell.
|
||||||
|
/// @param sellAmount Amount of tokens to sell.
|
||||||
|
/// @param buyTokenAddress Token to buy.
|
||||||
|
/// @param recipientAddress Address to send bought tokens to.
|
||||||
|
/// @param maxBuyTokenAmount A limit on the amount of tokens to buy.
|
||||||
|
/// @param minConversionRate The minimal conversion rate. If actual rate
|
||||||
|
/// is lower, trade is canceled.
|
||||||
|
/// @param walletId The wallet ID to send part of the fees
|
||||||
|
/// @return boughtAmount Amount of tokens bought.
|
||||||
|
function trade(
|
||||||
|
address sellTokenAddress,
|
||||||
|
uint256 sellAmount,
|
||||||
|
address buyTokenAddress,
|
||||||
|
address payable recipientAddress,
|
||||||
|
uint256 maxBuyTokenAmount,
|
||||||
|
uint256 minConversionRate,
|
||||||
|
address walletId
|
||||||
|
)
|
||||||
|
external
|
||||||
|
payable
|
||||||
|
returns(uint256 boughtAmount);
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.5.9;
|
||||||
|
|
||||||
|
|
||||||
|
interface IUniswapExchange {
|
||||||
|
|
||||||
|
/// @dev Buys at least `minTokensBought` tokens with ETH and transfer them
|
||||||
|
/// to `recipient`.
|
||||||
|
/// @param minTokensBought The minimum number of tokens to buy.
|
||||||
|
/// @param deadline Time when this order expires.
|
||||||
|
/// @param recipient Who to transfer the tokens to.
|
||||||
|
/// @return tokensBought Amount of tokens bought.
|
||||||
|
function ethToTokenTransferInput(
|
||||||
|
uint256 minTokensBought,
|
||||||
|
uint256 deadline,
|
||||||
|
address recipient
|
||||||
|
)
|
||||||
|
external
|
||||||
|
payable
|
||||||
|
returns (uint256 tokensBought);
|
||||||
|
|
||||||
|
/// @dev Buys at least `minEthBought` ETH with tokens.
|
||||||
|
/// @param tokensSold Amount of tokens to sell.
|
||||||
|
/// @param minEthBought The minimum amount of ETH to buy.
|
||||||
|
/// @param deadline Time when this order expires.
|
||||||
|
/// @return ethBought Amount of tokens bought.
|
||||||
|
function tokenToEthSwapInput(
|
||||||
|
uint256 tokensSold,
|
||||||
|
uint256 minEthBought,
|
||||||
|
uint256 deadline
|
||||||
|
)
|
||||||
|
external
|
||||||
|
returns (uint256 ethBought);
|
||||||
|
|
||||||
|
/// @dev Buys at least `minTokensBought` tokens with the exchange token
|
||||||
|
/// and transfer them to `recipient`.
|
||||||
|
/// @param minTokensBought The minimum number of tokens to buy.
|
||||||
|
/// @param minEthBought The minimum amount of intermediate ETH to buy.
|
||||||
|
/// @param deadline Time when this order expires.
|
||||||
|
/// @param recipient Who to transfer the tokens to.
|
||||||
|
/// @param toTokenAddress The token being bought.
|
||||||
|
/// @return tokensBought Amount of tokens bought.
|
||||||
|
function tokenToTokenTransferInput(
|
||||||
|
uint256 tokensSold,
|
||||||
|
uint256 minTokensBought,
|
||||||
|
uint256 minEthBought,
|
||||||
|
uint256 deadline,
|
||||||
|
address recipient,
|
||||||
|
address toTokenAddress
|
||||||
|
)
|
||||||
|
external
|
||||||
|
returns (uint256 tokensBought);
|
||||||
|
}
|
||||||
@@ -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 "./IUniswapExchange.sol";
|
||||||
|
|
||||||
|
|
||||||
|
interface IUniswapExchangeFactory {
|
||||||
|
|
||||||
|
/// @dev Get the exchange for a token.
|
||||||
|
/// @param tokenAddress The address of the token contract.
|
||||||
|
function getExchange(address tokenAddress)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (address);
|
||||||
|
}
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright 2019 ZeroEx Intl.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
pragma solidity ^0.5.5;
|
|
||||||
|
|
||||||
|
|
||||||
contract LibAssetProxyIds {
|
|
||||||
|
|
||||||
// AssetProxy Ids are equiavalent the first 4 bytes of the keccak256 hash of the function signature assigned to each AssetProxy.
|
|
||||||
|
|
||||||
// ERC20Token(address)
|
|
||||||
bytes4 constant public ERC20_PROXY_ID = 0xf47261b0;
|
|
||||||
|
|
||||||
// ERC721Token(address,uint256)
|
|
||||||
bytes4 constant public ERC721_PROXY_ID = 0x02571792;
|
|
||||||
|
|
||||||
// ERC1155Assets(address,uint256[],uint256[],bytes)
|
|
||||||
bytes4 constant public ERC1155_PROXY_ID = 0xa7cb5fb7;
|
|
||||||
|
|
||||||
// MultiAsset(uint256[],bytes[])
|
|
||||||
bytes4 constant public MULTI_ASSET_PROXY_ID = 0x94cfcdd7;
|
|
||||||
|
|
||||||
// StaticCall(address,bytes,bytes32)
|
|
||||||
bytes4 constant public STATIC_CALL_PROXY_ID = 0xc339d10a;
|
|
||||||
}
|
|
||||||
@@ -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.5.5;
|
|
||||||
|
|
||||||
import "../interfaces/IAssetProxyDispatcher.sol";
|
|
||||||
|
|
||||||
|
|
||||||
contract MAssetProxyDispatcher is
|
|
||||||
IAssetProxyDispatcher
|
|
||||||
{
|
|
||||||
// Logs registration of new asset proxy
|
|
||||||
event AssetProxyRegistered(
|
|
||||||
bytes4 id, // Id of new registered AssetProxy.
|
|
||||||
address assetProxy // Address of new registered AssetProxy.
|
|
||||||
);
|
|
||||||
|
|
||||||
/// @dev Forwards arguments to assetProxy and calls `transferFrom`. Either succeeds or throws.
|
|
||||||
/// @param assetData Byte array encoded for the asset.
|
|
||||||
/// @param from Address to transfer token from.
|
|
||||||
/// @param to Address to transfer token to.
|
|
||||||
/// @param amount Amount of token to transfer.
|
|
||||||
function dispatchTransferFrom(
|
|
||||||
bytes memory assetData,
|
|
||||||
address from,
|
|
||||||
address to,
|
|
||||||
uint256 amount
|
|
||||||
)
|
|
||||||
internal;
|
|
||||||
}
|
|
||||||
@@ -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.5.5;
|
|
||||||
|
|
||||||
import "../interfaces/IAuthorizable.sol";
|
|
||||||
|
|
||||||
|
|
||||||
contract MAuthorizable is
|
|
||||||
IAuthorizable
|
|
||||||
{
|
|
||||||
// Event logged when a new address is authorized.
|
|
||||||
event AuthorizedAddressAdded(
|
|
||||||
address indexed target,
|
|
||||||
address indexed caller
|
|
||||||
);
|
|
||||||
|
|
||||||
// Event logged when a currently authorized address is unauthorized.
|
|
||||||
event AuthorizedAddressRemoved(
|
|
||||||
address indexed target,
|
|
||||||
address indexed caller
|
|
||||||
);
|
|
||||||
|
|
||||||
/// @dev Only authorized addresses can invoke functions with this modifier.
|
|
||||||
modifier onlyAuthorized { revert(); _; }
|
|
||||||
}
|
|
||||||
80
contracts/asset-proxy/contracts/test/TestChaiBridge.sol
Normal file
80
contracts/asset-proxy/contracts/test/TestChaiBridge.sol
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.5.9;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "../src/bridges/ChaiBridge.sol";
|
||||||
|
import "@0x/contracts-erc20/contracts/src/ERC20Token.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract TestChaiDai is
|
||||||
|
ERC20Token
|
||||||
|
{
|
||||||
|
address private constant ALWAYS_REVERT_ADDRESS = address(1);
|
||||||
|
|
||||||
|
function draw(
|
||||||
|
address from,
|
||||||
|
uint256 amount
|
||||||
|
)
|
||||||
|
external
|
||||||
|
{
|
||||||
|
if (from == ALWAYS_REVERT_ADDRESS) {
|
||||||
|
revert();
|
||||||
|
}
|
||||||
|
balances[msg.sender] += amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
contract TestChaiBridge is
|
||||||
|
ChaiBridge
|
||||||
|
{
|
||||||
|
address public testChaiDai;
|
||||||
|
address private constant ALWAYS_REVERT_ADDRESS = address(1);
|
||||||
|
|
||||||
|
constructor()
|
||||||
|
public
|
||||||
|
{
|
||||||
|
testChaiDai = address(new TestChaiDai());
|
||||||
|
}
|
||||||
|
|
||||||
|
function _getDaiAddress()
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (address)
|
||||||
|
{
|
||||||
|
return testChaiDai;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _getChaiAddress()
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (address)
|
||||||
|
{
|
||||||
|
return testChaiDai;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _getERC20BridgeProxyAddress()
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (address)
|
||||||
|
{
|
||||||
|
return msg.sender == ALWAYS_REVERT_ADDRESS ? address(0) : msg.sender;
|
||||||
|
}
|
||||||
|
}
|
||||||
191
contracts/asset-proxy/contracts/test/TestDydxBridge.sol
Normal file
191
contracts/asset-proxy/contracts/test/TestDydxBridge.sol
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.5.9;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||||
|
import "../src/bridges/DydxBridge.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract TestDydxBridgeToken {
|
||||||
|
|
||||||
|
uint256 private constant INIT_HOLDER_BALANCE = 10 * 10**18; // 10 tokens
|
||||||
|
mapping (address => uint256) private _balances;
|
||||||
|
|
||||||
|
/// @dev Sets initial balance of token holders.
|
||||||
|
constructor(address[] memory holders)
|
||||||
|
public
|
||||||
|
{
|
||||||
|
for (uint256 i = 0; i != holders.length; ++i) {
|
||||||
|
_balances[holders[i]] = INIT_HOLDER_BALANCE;
|
||||||
|
}
|
||||||
|
_balances[msg.sender] = INIT_HOLDER_BALANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Basic transferFrom implementation.
|
||||||
|
function transferFrom(address from, address to, uint256 amount)
|
||||||
|
external
|
||||||
|
returns (bool)
|
||||||
|
{
|
||||||
|
if (_balances[from] < amount || _balances[to] + amount < _balances[to]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_balances[from] -= amount;
|
||||||
|
_balances[to] += amount;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Returns balance of `holder`.
|
||||||
|
function balanceOf(address holder)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (uint256)
|
||||||
|
{
|
||||||
|
return _balances[holder];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// solhint-disable space-after-comma
|
||||||
|
contract TestDydxBridge is
|
||||||
|
IDydx,
|
||||||
|
DydxBridge
|
||||||
|
{
|
||||||
|
|
||||||
|
address private constant ALWAYS_REVERT_ADDRESS = address(1);
|
||||||
|
address private _testTokenAddress;
|
||||||
|
bool private _shouldRevertOnOperate;
|
||||||
|
|
||||||
|
event OperateAccount(
|
||||||
|
address owner,
|
||||||
|
uint256 number
|
||||||
|
);
|
||||||
|
|
||||||
|
event OperateAction(
|
||||||
|
ActionType actionType,
|
||||||
|
uint256 accountId,
|
||||||
|
bool amountSign,
|
||||||
|
AssetDenomination amountDenomination,
|
||||||
|
AssetReference amountRef,
|
||||||
|
uint256 amountValue,
|
||||||
|
uint256 primaryMarketId,
|
||||||
|
uint256 secondaryMarketId,
|
||||||
|
address otherAddress,
|
||||||
|
uint256 otherAccountId,
|
||||||
|
bytes data
|
||||||
|
);
|
||||||
|
|
||||||
|
constructor(address[] memory holders)
|
||||||
|
public
|
||||||
|
{
|
||||||
|
// Deploy a test token. This represents the asset being deposited/withdrawn from dydx.
|
||||||
|
_testTokenAddress = address(new TestDydxBridgeToken(holders));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Simulates `operate` in dydx contract.
|
||||||
|
/// Emits events so that arguments can be validated client-side.
|
||||||
|
function operate(
|
||||||
|
AccountInfo[] calldata accounts,
|
||||||
|
ActionArgs[] calldata actions
|
||||||
|
)
|
||||||
|
external
|
||||||
|
{
|
||||||
|
if (_shouldRevertOnOperate) {
|
||||||
|
revert("TestDydxBridge/SHOULD_REVERT_ON_OPERATE");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint i = 0; i < accounts.length; ++i) {
|
||||||
|
emit OperateAccount(
|
||||||
|
accounts[i].owner,
|
||||||
|
accounts[i].number
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint i = 0; i < actions.length; ++i) {
|
||||||
|
emit OperateAction(
|
||||||
|
actions[i].actionType,
|
||||||
|
actions[i].accountId,
|
||||||
|
actions[i].amount.sign,
|
||||||
|
actions[i].amount.denomination,
|
||||||
|
actions[i].amount.ref,
|
||||||
|
actions[i].amount.value,
|
||||||
|
actions[i].primaryMarketId,
|
||||||
|
actions[i].secondaryMarketId,
|
||||||
|
actions[i].otherAddress,
|
||||||
|
actions[i].otherAccountId,
|
||||||
|
actions[i].data
|
||||||
|
);
|
||||||
|
|
||||||
|
if (actions[i].actionType == IDydx.ActionType.Withdraw) {
|
||||||
|
require(
|
||||||
|
IERC20Token(_testTokenAddress).transferFrom(
|
||||||
|
address(this),
|
||||||
|
actions[i].otherAddress,
|
||||||
|
actions[i].amount.value
|
||||||
|
),
|
||||||
|
"TestDydxBridge/WITHDRAW_FAILED"
|
||||||
|
);
|
||||||
|
} else if (actions[i].actionType == IDydx.ActionType.Deposit) {
|
||||||
|
require(
|
||||||
|
IERC20Token(_testTokenAddress).transferFrom(
|
||||||
|
actions[i].otherAddress,
|
||||||
|
address(this),
|
||||||
|
actions[i].amount.value
|
||||||
|
),
|
||||||
|
"TestDydxBridge/DEPOSIT_FAILED"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
revert("TestDydxBridge/UNSUPPORTED_ACTION");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev If `true` then subsequent calls to `operate` will revert.
|
||||||
|
function setRevertOnOperate(bool shouldRevert)
|
||||||
|
external
|
||||||
|
{
|
||||||
|
_shouldRevertOnOperate = shouldRevert;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Returns test token.
|
||||||
|
function getTestToken()
|
||||||
|
external
|
||||||
|
returns (address)
|
||||||
|
{
|
||||||
|
return _testTokenAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev overrides `_getDydxAddress()` from `DeploymentConstants` to return this address.
|
||||||
|
function _getDydxAddress()
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (address)
|
||||||
|
{
|
||||||
|
return address(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev overrides `_getERC20BridgeProxyAddress()` from `DeploymentConstants` for testing.
|
||||||
|
function _getERC20BridgeProxyAddress()
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (address)
|
||||||
|
{
|
||||||
|
return msg.sender == ALWAYS_REVERT_ADDRESS ? address(0) : msg.sender;
|
||||||
|
}
|
||||||
|
}
|
||||||
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 bridgeTransferFrom(
|
||||||
|
address tokenAddress,
|
||||||
|
address from,
|
||||||
|
address to,
|
||||||
|
uint256 amount,
|
||||||
|
bytes calldata bridgeData
|
||||||
|
)
|
||||||
|
external
|
||||||
|
returns (bytes4)
|
||||||
|
{
|
||||||
|
emit BridgeWithdrawTo(
|
||||||
|
tokenAddress,
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
amount,
|
||||||
|
bridgeData
|
||||||
|
);
|
||||||
|
// Unpack the bridgeData.
|
||||||
|
(
|
||||||
|
int256 transferAmount,
|
||||||
|
bytes memory revertData,
|
||||||
|
bytes memory returnData
|
||||||
|
) = abi.decode(bridgeData, (int256, bytes, bytes));
|
||||||
|
|
||||||
|
// If `revertData` is set, revert.
|
||||||
|
if (revertData.length != 0) {
|
||||||
|
assembly { revert(add(revertData, 0x20), mload(revertData)) }
|
||||||
|
}
|
||||||
|
// Increase `to`'s balance by `transferAmount`.
|
||||||
|
TestERC20BridgeToken(tokenAddress).addBalance(to, transferAmount);
|
||||||
|
// Return `returnData`.
|
||||||
|
assembly { return(add(returnData, 0x20), mload(returnData)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
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 _getEth2DaiAddress()
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (address)
|
||||||
|
{
|
||||||
|
return address(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
324
contracts/asset-proxy/contracts/test/TestKyberBridge.sol
Normal file
324
contracts/asset-proxy/contracts/test/TestKyberBridge.sol
Normal file
@@ -0,0 +1,324 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.5.9;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||||
|
import "../src/bridges/KyberBridge.sol";
|
||||||
|
import "../src/interfaces/IKyberNetworkProxy.sol";
|
||||||
|
|
||||||
|
|
||||||
|
// solhint-disable no-simple-event-func-name
|
||||||
|
interface ITestContract {
|
||||||
|
|
||||||
|
function wethWithdraw(
|
||||||
|
address payable ownerAddress,
|
||||||
|
uint256 amount
|
||||||
|
)
|
||||||
|
external;
|
||||||
|
|
||||||
|
function wethDeposit(
|
||||||
|
address ownerAddress
|
||||||
|
)
|
||||||
|
external
|
||||||
|
payable;
|
||||||
|
|
||||||
|
function tokenTransfer(
|
||||||
|
address ownerAddress,
|
||||||
|
address recipientAddress,
|
||||||
|
uint256 amount
|
||||||
|
)
|
||||||
|
external
|
||||||
|
returns (bool success);
|
||||||
|
|
||||||
|
function tokenApprove(
|
||||||
|
address ownerAddress,
|
||||||
|
address spenderAddress,
|
||||||
|
uint256 allowance
|
||||||
|
)
|
||||||
|
external
|
||||||
|
returns (bool success);
|
||||||
|
|
||||||
|
function tokenBalanceOf(
|
||||||
|
address ownerAddress
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (uint256 balance);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @dev A minimalist ERC20/WETH token.
|
||||||
|
contract TestToken {
|
||||||
|
|
||||||
|
uint8 public decimals;
|
||||||
|
ITestContract private _testContract;
|
||||||
|
|
||||||
|
constructor(uint8 decimals_) public {
|
||||||
|
decimals = decimals_;
|
||||||
|
_testContract = ITestContract(msg.sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
function approve(address spender, uint256 allowance)
|
||||||
|
external
|
||||||
|
returns (bool)
|
||||||
|
{
|
||||||
|
return _testContract.tokenApprove(
|
||||||
|
msg.sender,
|
||||||
|
spender,
|
||||||
|
allowance
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function transfer(address recipient, uint256 amount)
|
||||||
|
external
|
||||||
|
returns (bool)
|
||||||
|
{
|
||||||
|
return _testContract.tokenTransfer(
|
||||||
|
msg.sender,
|
||||||
|
recipient,
|
||||||
|
amount
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function withdraw(uint256 amount)
|
||||||
|
external
|
||||||
|
{
|
||||||
|
return _testContract.wethWithdraw(msg.sender, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
function deposit()
|
||||||
|
external
|
||||||
|
payable
|
||||||
|
{
|
||||||
|
return _testContract.wethDeposit.value(msg.value)(msg.sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
function balanceOf(address owner)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (uint256)
|
||||||
|
{
|
||||||
|
return _testContract.tokenBalanceOf(owner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @dev KyberBridge overridden to mock tokens and implement IKyberBridge.
|
||||||
|
contract TestKyberBridge is
|
||||||
|
KyberBridge,
|
||||||
|
ITestContract,
|
||||||
|
IKyberNetworkProxy
|
||||||
|
{
|
||||||
|
event KyberBridgeTrade(
|
||||||
|
uint256 msgValue,
|
||||||
|
address sellTokenAddress,
|
||||||
|
uint256 sellAmount,
|
||||||
|
address buyTokenAddress,
|
||||||
|
address payable recipientAddress,
|
||||||
|
uint256 maxBuyTokenAmount,
|
||||||
|
uint256 minConversionRate,
|
||||||
|
address walletId
|
||||||
|
);
|
||||||
|
|
||||||
|
event KyberBridgeWethWithdraw(
|
||||||
|
address ownerAddress,
|
||||||
|
uint256 amount
|
||||||
|
);
|
||||||
|
|
||||||
|
event KyberBridgeWethDeposit(
|
||||||
|
uint256 msgValue,
|
||||||
|
address ownerAddress,
|
||||||
|
uint256 amount
|
||||||
|
);
|
||||||
|
|
||||||
|
event KyberBridgeTokenApprove(
|
||||||
|
address tokenAddress,
|
||||||
|
address ownerAddress,
|
||||||
|
address spenderAddress,
|
||||||
|
uint256 allowance
|
||||||
|
);
|
||||||
|
|
||||||
|
event KyberBridgeTokenTransfer(
|
||||||
|
address tokenAddress,
|
||||||
|
address ownerAddress,
|
||||||
|
address recipientAddress,
|
||||||
|
uint256 amount
|
||||||
|
);
|
||||||
|
|
||||||
|
IEtherToken public weth;
|
||||||
|
mapping (address => mapping (address => uint256)) private _tokenBalances;
|
||||||
|
uint256 private _nextFillAmount;
|
||||||
|
|
||||||
|
constructor() public {
|
||||||
|
weth = IEtherToken(address(new TestToken(18)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Implementation of `IKyberNetworkProxy.trade()`
|
||||||
|
function trade(
|
||||||
|
address sellTokenAddress,
|
||||||
|
uint256 sellAmount,
|
||||||
|
address buyTokenAddress,
|
||||||
|
address payable recipientAddress,
|
||||||
|
uint256 maxBuyTokenAmount,
|
||||||
|
uint256 minConversionRate,
|
||||||
|
address walletId
|
||||||
|
)
|
||||||
|
external
|
||||||
|
payable
|
||||||
|
returns(uint256 boughtAmount)
|
||||||
|
{
|
||||||
|
emit KyberBridgeTrade(
|
||||||
|
msg.value,
|
||||||
|
sellTokenAddress,
|
||||||
|
sellAmount,
|
||||||
|
buyTokenAddress,
|
||||||
|
recipientAddress,
|
||||||
|
maxBuyTokenAmount,
|
||||||
|
minConversionRate,
|
||||||
|
walletId
|
||||||
|
);
|
||||||
|
return _nextFillAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createToken(uint8 decimals)
|
||||||
|
external
|
||||||
|
returns (address tokenAddress)
|
||||||
|
{
|
||||||
|
return address(new TestToken(decimals));
|
||||||
|
}
|
||||||
|
|
||||||
|
function setNextFillAmount(uint256 amount)
|
||||||
|
external
|
||||||
|
payable
|
||||||
|
{
|
||||||
|
if (msg.value != 0) {
|
||||||
|
require(amount == msg.value, "VALUE_AMOUNT_MISMATCH");
|
||||||
|
grantTokensTo(address(weth), address(this), msg.value);
|
||||||
|
}
|
||||||
|
_nextFillAmount = amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
function wethDeposit(
|
||||||
|
address ownerAddress
|
||||||
|
)
|
||||||
|
external
|
||||||
|
payable
|
||||||
|
{
|
||||||
|
require(msg.sender == address(weth), "ONLY_WETH");
|
||||||
|
grantTokensTo(address(weth), ownerAddress, msg.value);
|
||||||
|
emit KyberBridgeWethDeposit(
|
||||||
|
msg.value,
|
||||||
|
ownerAddress,
|
||||||
|
msg.value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function wethWithdraw(
|
||||||
|
address payable ownerAddress,
|
||||||
|
uint256 amount
|
||||||
|
)
|
||||||
|
external
|
||||||
|
{
|
||||||
|
require(msg.sender == address(weth), "ONLY_WETH");
|
||||||
|
_tokenBalances[address(weth)][ownerAddress] -= amount;
|
||||||
|
ownerAddress.transfer(amount);
|
||||||
|
emit KyberBridgeWethWithdraw(
|
||||||
|
ownerAddress,
|
||||||
|
amount
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function tokenApprove(
|
||||||
|
address ownerAddress,
|
||||||
|
address spenderAddress,
|
||||||
|
uint256 allowance
|
||||||
|
)
|
||||||
|
external
|
||||||
|
returns (bool success)
|
||||||
|
{
|
||||||
|
emit KyberBridgeTokenApprove(
|
||||||
|
msg.sender,
|
||||||
|
ownerAddress,
|
||||||
|
spenderAddress,
|
||||||
|
allowance
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function tokenTransfer(
|
||||||
|
address ownerAddress,
|
||||||
|
address recipientAddress,
|
||||||
|
uint256 amount
|
||||||
|
)
|
||||||
|
external
|
||||||
|
returns (bool success)
|
||||||
|
{
|
||||||
|
_tokenBalances[msg.sender][ownerAddress] -= amount;
|
||||||
|
_tokenBalances[msg.sender][recipientAddress] += amount;
|
||||||
|
emit KyberBridgeTokenTransfer(
|
||||||
|
msg.sender,
|
||||||
|
ownerAddress,
|
||||||
|
recipientAddress,
|
||||||
|
amount
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function tokenBalanceOf(
|
||||||
|
address ownerAddress
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (uint256 balance)
|
||||||
|
{
|
||||||
|
return _tokenBalances[msg.sender][ownerAddress];
|
||||||
|
}
|
||||||
|
|
||||||
|
function grantTokensTo(address tokenAddress, address ownerAddress, uint256 amount)
|
||||||
|
public
|
||||||
|
payable
|
||||||
|
{
|
||||||
|
_tokenBalances[tokenAddress][ownerAddress] += amount;
|
||||||
|
if (tokenAddress != address(weth)) {
|
||||||
|
// Send back ether if not WETH.
|
||||||
|
msg.sender.transfer(msg.value);
|
||||||
|
} else {
|
||||||
|
require(msg.value == amount, "VALUE_AMOUNT_MISMATCH");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// @dev overridden to point to this contract.
|
||||||
|
function _getKyberNetworkProxyAddress()
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (address)
|
||||||
|
{
|
||||||
|
return address(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @dev overridden to point to test WETH.
|
||||||
|
function _getWethAddress()
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (address)
|
||||||
|
{
|
||||||
|
return address(weth);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,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.
|
||||||
|
|||||||
432
contracts/asset-proxy/contracts/test/TestUniswapBridge.sol
Normal file
432
contracts/asset-proxy/contracts/test/TestUniswapBridge.sol
Normal file
@@ -0,0 +1,432 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.5.9;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||||
|
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
||||||
|
import "../src/bridges/UniswapBridge.sol";
|
||||||
|
import "../src/interfaces/IUniswapExchangeFactory.sol";
|
||||||
|
import "../src/interfaces/IUniswapExchange.sol";
|
||||||
|
|
||||||
|
|
||||||
|
// solhint-disable no-simple-event-func-name
|
||||||
|
contract TestEventsRaiser {
|
||||||
|
|
||||||
|
event TokenTransfer(
|
||||||
|
address token,
|
||||||
|
address from,
|
||||||
|
address to,
|
||||||
|
uint256 amount
|
||||||
|
);
|
||||||
|
|
||||||
|
event TokenApprove(
|
||||||
|
address spender,
|
||||||
|
uint256 allowance
|
||||||
|
);
|
||||||
|
|
||||||
|
event WethDeposit(
|
||||||
|
uint256 amount
|
||||||
|
);
|
||||||
|
|
||||||
|
event WethWithdraw(
|
||||||
|
uint256 amount
|
||||||
|
);
|
||||||
|
|
||||||
|
event EthToTokenTransferInput(
|
||||||
|
address exchange,
|
||||||
|
uint256 minTokensBought,
|
||||||
|
uint256 deadline,
|
||||||
|
address recipient
|
||||||
|
);
|
||||||
|
|
||||||
|
event TokenToEthSwapInput(
|
||||||
|
address exchange,
|
||||||
|
uint256 tokensSold,
|
||||||
|
uint256 minEthBought,
|
||||||
|
uint256 deadline
|
||||||
|
);
|
||||||
|
|
||||||
|
event TokenToTokenTransferInput(
|
||||||
|
address exchange,
|
||||||
|
uint256 tokensSold,
|
||||||
|
uint256 minTokensBought,
|
||||||
|
uint256 minEthBought,
|
||||||
|
uint256 deadline,
|
||||||
|
address recipient,
|
||||||
|
address toTokenAddress
|
||||||
|
);
|
||||||
|
|
||||||
|
function raiseEthToTokenTransferInput(
|
||||||
|
uint256 minTokensBought,
|
||||||
|
uint256 deadline,
|
||||||
|
address recipient
|
||||||
|
)
|
||||||
|
external
|
||||||
|
{
|
||||||
|
emit EthToTokenTransferInput(
|
||||||
|
msg.sender,
|
||||||
|
minTokensBought,
|
||||||
|
deadline,
|
||||||
|
recipient
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function raiseTokenToEthSwapInput(
|
||||||
|
uint256 tokensSold,
|
||||||
|
uint256 minEthBought,
|
||||||
|
uint256 deadline
|
||||||
|
)
|
||||||
|
external
|
||||||
|
{
|
||||||
|
emit TokenToEthSwapInput(
|
||||||
|
msg.sender,
|
||||||
|
tokensSold,
|
||||||
|
minEthBought,
|
||||||
|
deadline
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function raiseTokenToTokenTransferInput(
|
||||||
|
uint256 tokensSold,
|
||||||
|
uint256 minTokensBought,
|
||||||
|
uint256 minEthBought,
|
||||||
|
uint256 deadline,
|
||||||
|
address recipient,
|
||||||
|
address toTokenAddress
|
||||||
|
)
|
||||||
|
external
|
||||||
|
{
|
||||||
|
emit TokenToTokenTransferInput(
|
||||||
|
msg.sender,
|
||||||
|
tokensSold,
|
||||||
|
minTokensBought,
|
||||||
|
minEthBought,
|
||||||
|
deadline,
|
||||||
|
recipient,
|
||||||
|
toTokenAddress
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function raiseTokenTransfer(
|
||||||
|
address from,
|
||||||
|
address to,
|
||||||
|
uint256 amount
|
||||||
|
)
|
||||||
|
external
|
||||||
|
{
|
||||||
|
emit TokenTransfer(
|
||||||
|
msg.sender,
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
amount
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function raiseTokenApprove(address spender, uint256 allowance)
|
||||||
|
external
|
||||||
|
{
|
||||||
|
emit TokenApprove(spender, allowance);
|
||||||
|
}
|
||||||
|
|
||||||
|
function raiseWethDeposit(uint256 amount)
|
||||||
|
external
|
||||||
|
{
|
||||||
|
emit WethDeposit(amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
function raiseWethWithdraw(uint256 amount)
|
||||||
|
external
|
||||||
|
{
|
||||||
|
emit WethWithdraw(amount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @dev A minimalist ERC20/WETH token.
|
||||||
|
contract TestToken {
|
||||||
|
|
||||||
|
using LibSafeMath for uint256;
|
||||||
|
|
||||||
|
mapping (address => uint256) public balances;
|
||||||
|
string private _nextRevertReason;
|
||||||
|
|
||||||
|
/// @dev Set the balance for `owner`.
|
||||||
|
function setBalance(address owner)
|
||||||
|
external
|
||||||
|
payable
|
||||||
|
{
|
||||||
|
balances[owner] = msg.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Set the revert reason for `transfer()`,
|
||||||
|
/// `deposit()`, and `withdraw()`.
|
||||||
|
function setRevertReason(string calldata reason)
|
||||||
|
external
|
||||||
|
{
|
||||||
|
_nextRevertReason = reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Just calls `raiseTokenTransfer()` on the caller.
|
||||||
|
function transfer(address to, uint256 amount)
|
||||||
|
external
|
||||||
|
returns (bool)
|
||||||
|
{
|
||||||
|
_revertIfReasonExists();
|
||||||
|
TestEventsRaiser(msg.sender).raiseTokenTransfer(msg.sender, to, amount);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Just calls `raiseTokenApprove()` on the caller.
|
||||||
|
function approve(address spender, uint256 allowance)
|
||||||
|
external
|
||||||
|
returns (bool)
|
||||||
|
{
|
||||||
|
TestEventsRaiser(msg.sender).raiseTokenApprove(spender, allowance);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev `IWETH.deposit()` that increases balances and calls
|
||||||
|
/// `raiseWethDeposit()` on the caller.
|
||||||
|
function deposit()
|
||||||
|
external
|
||||||
|
payable
|
||||||
|
{
|
||||||
|
_revertIfReasonExists();
|
||||||
|
balances[msg.sender] += balances[msg.sender].safeAdd(msg.value);
|
||||||
|
TestEventsRaiser(msg.sender).raiseWethDeposit(msg.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev `IWETH.withdraw()` that just reduces balances and calls
|
||||||
|
/// `raiseWethWithdraw()` on the caller.
|
||||||
|
function withdraw(uint256 amount)
|
||||||
|
external
|
||||||
|
{
|
||||||
|
_revertIfReasonExists();
|
||||||
|
balances[msg.sender] = balances[msg.sender].safeSub(amount);
|
||||||
|
msg.sender.transfer(amount);
|
||||||
|
TestEventsRaiser(msg.sender).raiseWethWithdraw(amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Retrieve the balance for `owner`.
|
||||||
|
function balanceOf(address owner)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (uint256)
|
||||||
|
{
|
||||||
|
return balances[owner];
|
||||||
|
}
|
||||||
|
|
||||||
|
function _revertIfReasonExists()
|
||||||
|
private
|
||||||
|
view
|
||||||
|
{
|
||||||
|
if (bytes(_nextRevertReason).length != 0) {
|
||||||
|
revert(_nextRevertReason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
contract TestExchange is
|
||||||
|
IUniswapExchange
|
||||||
|
{
|
||||||
|
address public tokenAddress;
|
||||||
|
string private _nextRevertReason;
|
||||||
|
|
||||||
|
constructor(address _tokenAddress) public {
|
||||||
|
tokenAddress = _tokenAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setFillBehavior(
|
||||||
|
string calldata revertReason
|
||||||
|
)
|
||||||
|
external
|
||||||
|
payable
|
||||||
|
{
|
||||||
|
_nextRevertReason = revertReason;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ethToTokenTransferInput(
|
||||||
|
uint256 minTokensBought,
|
||||||
|
uint256 deadline,
|
||||||
|
address recipient
|
||||||
|
)
|
||||||
|
external
|
||||||
|
payable
|
||||||
|
returns (uint256 tokensBought)
|
||||||
|
{
|
||||||
|
TestEventsRaiser(msg.sender).raiseEthToTokenTransferInput(
|
||||||
|
minTokensBought,
|
||||||
|
deadline,
|
||||||
|
recipient
|
||||||
|
);
|
||||||
|
_revertIfReasonExists();
|
||||||
|
return address(this).balance;
|
||||||
|
}
|
||||||
|
|
||||||
|
function tokenToEthSwapInput(
|
||||||
|
uint256 tokensSold,
|
||||||
|
uint256 minEthBought,
|
||||||
|
uint256 deadline
|
||||||
|
)
|
||||||
|
external
|
||||||
|
returns (uint256 ethBought)
|
||||||
|
{
|
||||||
|
TestEventsRaiser(msg.sender).raiseTokenToEthSwapInput(
|
||||||
|
tokensSold,
|
||||||
|
minEthBought,
|
||||||
|
deadline
|
||||||
|
);
|
||||||
|
_revertIfReasonExists();
|
||||||
|
uint256 fillAmount = address(this).balance;
|
||||||
|
msg.sender.transfer(fillAmount);
|
||||||
|
return fillAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
function tokenToTokenTransferInput(
|
||||||
|
uint256 tokensSold,
|
||||||
|
uint256 minTokensBought,
|
||||||
|
uint256 minEthBought,
|
||||||
|
uint256 deadline,
|
||||||
|
address recipient,
|
||||||
|
address toTokenAddress
|
||||||
|
)
|
||||||
|
external
|
||||||
|
returns (uint256 tokensBought)
|
||||||
|
{
|
||||||
|
TestEventsRaiser(msg.sender).raiseTokenToTokenTransferInput(
|
||||||
|
tokensSold,
|
||||||
|
minTokensBought,
|
||||||
|
minEthBought,
|
||||||
|
deadline,
|
||||||
|
recipient,
|
||||||
|
toTokenAddress
|
||||||
|
);
|
||||||
|
_revertIfReasonExists();
|
||||||
|
return address(this).balance;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toTokenAddress()
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (address _tokenAddress)
|
||||||
|
{
|
||||||
|
return tokenAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _revertIfReasonExists()
|
||||||
|
private
|
||||||
|
view
|
||||||
|
{
|
||||||
|
if (bytes(_nextRevertReason).length != 0) {
|
||||||
|
revert(_nextRevertReason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @dev UniswapBridge overridden to mock tokens and implement IUniswapExchangeFactory.
|
||||||
|
contract TestUniswapBridge is
|
||||||
|
IUniswapExchangeFactory,
|
||||||
|
TestEventsRaiser,
|
||||||
|
UniswapBridge
|
||||||
|
{
|
||||||
|
TestToken public wethToken;
|
||||||
|
// Token address to TestToken instance.
|
||||||
|
mapping (address => TestToken) private _testTokens;
|
||||||
|
// Token address to TestExchange instance.
|
||||||
|
mapping (address => TestExchange) private _testExchanges;
|
||||||
|
|
||||||
|
constructor() public {
|
||||||
|
wethToken = new TestToken();
|
||||||
|
_testTokens[address(wethToken)] = wethToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Sets the balance of this contract for an existing token.
|
||||||
|
/// The wei attached will be the balance.
|
||||||
|
function setTokenBalance(address tokenAddress)
|
||||||
|
external
|
||||||
|
payable
|
||||||
|
{
|
||||||
|
TestToken token = _testTokens[tokenAddress];
|
||||||
|
token.deposit.value(msg.value)();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Sets the revert reason for an existing token.
|
||||||
|
function setTokenRevertReason(address tokenAddress, string calldata revertReason)
|
||||||
|
external
|
||||||
|
{
|
||||||
|
TestToken token = _testTokens[tokenAddress];
|
||||||
|
token.setRevertReason(revertReason);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Create a token and exchange (if they don't exist) for a new token
|
||||||
|
/// and sets the exchange revert and fill behavior. The wei attached
|
||||||
|
/// will be the fill amount for the exchange.
|
||||||
|
/// @param tokenAddress The token address. If zero, one will be created.
|
||||||
|
/// @param revertReason The revert reason for exchange operations.
|
||||||
|
function createTokenAndExchange(
|
||||||
|
address tokenAddress,
|
||||||
|
string calldata revertReason
|
||||||
|
)
|
||||||
|
external
|
||||||
|
payable
|
||||||
|
returns (TestToken token, TestExchange exchange)
|
||||||
|
{
|
||||||
|
token = TestToken(tokenAddress);
|
||||||
|
if (tokenAddress == address(0)) {
|
||||||
|
token = new TestToken();
|
||||||
|
}
|
||||||
|
_testTokens[address(token)] = token;
|
||||||
|
exchange = _testExchanges[address(token)];
|
||||||
|
if (address(exchange) == address(0)) {
|
||||||
|
_testExchanges[address(token)] = exchange = new TestExchange(address(token));
|
||||||
|
}
|
||||||
|
exchange.setFillBehavior.value(msg.value)(revertReason);
|
||||||
|
return (token, exchange);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev `IUniswapExchangeFactory.getExchange`
|
||||||
|
function getExchange(address tokenAddress)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (address)
|
||||||
|
{
|
||||||
|
return address(_testExchanges[tokenAddress]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @dev Use `wethToken`.
|
||||||
|
function _getWethAddress()
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (address)
|
||||||
|
{
|
||||||
|
return address(wethToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @dev This contract will double as the Uniswap contract.
|
||||||
|
function _getUniswapExchangeFactoryAddress()
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (address)
|
||||||
|
{
|
||||||
|
return address(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@0x/contracts-asset-proxy",
|
"name": "@0x/contracts-asset-proxy",
|
||||||
"version": "2.2.5",
|
"version": "3.2.1",
|
||||||
"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 contracts:copy",
|
||||||
"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",
|
||||||
@@ -21,20 +21,24 @@
|
|||||||
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
|
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
|
||||||
"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 test/generated-artifacts test/generated-wrappers 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 --debug --abis ${npm_package_config_abis} --output test/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 ./test/generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude ./test/generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||||
"fix": "tslint --fix --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
"fix": "tslint --fix --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./test/generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude ./test/generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||||
"coverage:report:text": "istanbul report text",
|
"coverage:report: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 generate",
|
||||||
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol"
|
"contracts:copy": "contracts-gen copy",
|
||||||
|
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol",
|
||||||
|
"compile:truffle": "truffle compile",
|
||||||
|
"docs:md": "ts-doc-gen --sourceDir='$PROJECT_FILES' --output=$MD_FILE_DIR --fileExtension=mdx --tsconfig=./typedoc-tsconfig.json",
|
||||||
|
"docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"abis": "./generated-artifacts/@(ERC1155Proxy|ERC20Proxy|ERC721Proxy|IAssetData|IAssetProxy|IAuthorizable|MixinAuthorizable|MultiAssetProxy|StaticCallProxy|TestStaticCallTarget).json",
|
"abis": "./test/generated-artifacts/@(ChaiBridge|CurveBridge|DydxBridge|ERC1155Proxy|ERC20BridgeProxy|ERC20Proxy|ERC721Proxy|Eth2DaiBridge|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|IChai|ICurve|IDydx|IDydxBridge|IERC20Bridge|IEth2Dai|IKyberNetworkProxy|IUniswapExchange|IUniswapExchangeFactory|KyberBridge|MixinAssetProxyDispatcher|MixinAuthorizable|MultiAssetProxy|Ownable|StaticCallProxy|TestChaiBridge|TestDydxBridge|TestERC20Bridge|TestEth2DaiBridge|TestKyberBridge|TestStaticCallTarget|TestUniswapBridge|UniswapBridge).json",
|
||||||
"abis: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": {
|
||||||
@@ -47,12 +51,14 @@
|
|||||||
},
|
},
|
||||||
"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": "^4.1.0",
|
"@0x/abi-gen": "^5.2.0",
|
||||||
"@0x/contracts-gen": "^1.0.13",
|
"@0x/contracts-gen": "^2.0.7",
|
||||||
"@0x/contracts-test-utils": "^3.1.13",
|
"@0x/contracts-test-utils": "^5.1.5",
|
||||||
"@0x/dev-utils": "^2.3.0",
|
"@0x/contracts-utils": "^4.3.1",
|
||||||
"@0x/sol-compiler": "^3.1.12",
|
"@0x/dev-utils": "^3.2.0",
|
||||||
"@0x/tslint-config": "^3.0.1",
|
"@0x/sol-compiler": "^4.0.7",
|
||||||
|
"@0x/ts-doc-gen": "^0.0.22",
|
||||||
|
"@0x/tslint-config": "^4.0.0",
|
||||||
"@types/lodash": "4.14.104",
|
"@types/lodash": "4.14.104",
|
||||||
"@types/mocha": "^5.2.7",
|
"@types/mocha": "^5.2.7",
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
@@ -60,27 +66,29 @@
|
|||||||
"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",
|
||||||
|
"ethereumjs-util": "^5.1.1",
|
||||||
"make-promises-safe": "^1.1.0",
|
"make-promises-safe": "^1.1.0",
|
||||||
"mocha": "^6.2.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",
|
||||||
|
"typedoc": "^0.15.0",
|
||||||
"typescript": "3.0.1"
|
"typescript": "3.0.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@0x/base-contract": "^5.3.1",
|
"@0x/base-contract": "^6.2.0",
|
||||||
"@0x/contracts-erc1155": "^1.1.12",
|
"@0x/contracts-erc1155": "^2.1.1",
|
||||||
"@0x/contracts-erc20": "^2.2.11",
|
"@0x/contracts-erc20": "^3.1.1",
|
||||||
"@0x/contracts-erc721": "^2.1.12",
|
"@0x/contracts-erc721": "^3.1.1",
|
||||||
"@0x/contracts-utils": "^3.2.1",
|
"@0x/contracts-exchange-libs": "^4.3.1",
|
||||||
"@0x/order-utils": "^8.2.5",
|
"@0x/order-utils": "^10.2.1",
|
||||||
"@0x/types": "^2.4.1",
|
"@0x/types": "^3.1.2",
|
||||||
"@0x/typescript-typings": "^4.2.4",
|
"@0x/typescript-typings": "^5.0.2",
|
||||||
"@0x/utils": "^4.5.0",
|
"@0x/utils": "^5.4.0",
|
||||||
"@0x/web3-wrapper": "^6.0.10",
|
"@0x/web3-wrapper": "^7.0.6",
|
||||||
"ethereum-types": "^2.1.4",
|
"ethereum-types": "^3.1.0",
|
||||||
"ethereumjs-util": "^5.1.1",
|
|
||||||
"lodash": "^4.17.11"
|
"lodash": "^4.17.11"
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
|
|||||||
@@ -5,25 +5,75 @@
|
|||||||
*/
|
*/
|
||||||
import { ContractArtifact } from 'ethereum-types';
|
import { ContractArtifact } from 'ethereum-types';
|
||||||
|
|
||||||
|
import * as ChaiBridge from '../generated-artifacts/ChaiBridge.json';
|
||||||
|
import * as CurveBridge from '../generated-artifacts/CurveBridge.json';
|
||||||
|
import * as DydxBridge from '../generated-artifacts/DydxBridge.json';
|
||||||
import * as ERC1155Proxy from '../generated-artifacts/ERC1155Proxy.json';
|
import * as ERC1155Proxy from '../generated-artifacts/ERC1155Proxy.json';
|
||||||
|
import * as ERC20BridgeProxy from '../generated-artifacts/ERC20BridgeProxy.json';
|
||||||
import * as ERC20Proxy from '../generated-artifacts/ERC20Proxy.json';
|
import * as 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 IChai from '../generated-artifacts/IChai.json';
|
||||||
|
import * as ICurve from '../generated-artifacts/ICurve.json';
|
||||||
|
import * as IDydx from '../generated-artifacts/IDydx.json';
|
||||||
|
import * as IDydxBridge from '../generated-artifacts/IDydxBridge.json';
|
||||||
|
import * as IERC20Bridge from '../generated-artifacts/IERC20Bridge.json';
|
||||||
|
import * as IEth2Dai from '../generated-artifacts/IEth2Dai.json';
|
||||||
|
import * as IKyberNetworkProxy from '../generated-artifacts/IKyberNetworkProxy.json';
|
||||||
|
import * as IUniswapExchange from '../generated-artifacts/IUniswapExchange.json';
|
||||||
|
import * as IUniswapExchangeFactory from '../generated-artifacts/IUniswapExchangeFactory.json';
|
||||||
|
import * as KyberBridge from '../generated-artifacts/KyberBridge.json';
|
||||||
|
import * as MixinAssetProxyDispatcher from '../generated-artifacts/MixinAssetProxyDispatcher.json';
|
||||||
import * as MixinAuthorizable from '../generated-artifacts/MixinAuthorizable.json';
|
import * as 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 StaticCallProxy from '../generated-artifacts/StaticCallProxy.json';
|
||||||
|
import * as TestChaiBridge from '../generated-artifacts/TestChaiBridge.json';
|
||||||
|
import * as TestDydxBridge from '../generated-artifacts/TestDydxBridge.json';
|
||||||
|
import * as TestERC20Bridge from '../generated-artifacts/TestERC20Bridge.json';
|
||||||
|
import * as TestEth2DaiBridge from '../generated-artifacts/TestEth2DaiBridge.json';
|
||||||
|
import * as TestKyberBridge from '../generated-artifacts/TestKyberBridge.json';
|
||||||
import * as TestStaticCallTarget from '../generated-artifacts/TestStaticCallTarget.json';
|
import * as TestStaticCallTarget from '../generated-artifacts/TestStaticCallTarget.json';
|
||||||
|
import * as TestUniswapBridge from '../generated-artifacts/TestUniswapBridge.json';
|
||||||
|
import * as UniswapBridge from '../generated-artifacts/UniswapBridge.json';
|
||||||
export const artifacts = {
|
export const artifacts = {
|
||||||
|
MixinAssetProxyDispatcher: MixinAssetProxyDispatcher as ContractArtifact,
|
||||||
|
MixinAuthorizable: MixinAuthorizable as ContractArtifact,
|
||||||
|
Ownable: Ownable as ContractArtifact,
|
||||||
ERC1155Proxy: ERC1155Proxy as ContractArtifact,
|
ERC1155Proxy: ERC1155Proxy 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,
|
StaticCallProxy: StaticCallProxy as ContractArtifact,
|
||||||
|
ChaiBridge: ChaiBridge as ContractArtifact,
|
||||||
|
CurveBridge: CurveBridge as ContractArtifact,
|
||||||
|
DydxBridge: DydxBridge as ContractArtifact,
|
||||||
|
Eth2DaiBridge: Eth2DaiBridge as ContractArtifact,
|
||||||
|
KyberBridge: KyberBridge as ContractArtifact,
|
||||||
|
UniswapBridge: UniswapBridge 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,
|
||||||
|
IChai: IChai as ContractArtifact,
|
||||||
|
ICurve: ICurve as ContractArtifact,
|
||||||
|
IDydx: IDydx as ContractArtifact,
|
||||||
|
IDydxBridge: IDydxBridge as ContractArtifact,
|
||||||
|
IERC20Bridge: IERC20Bridge as ContractArtifact,
|
||||||
|
IEth2Dai: IEth2Dai as ContractArtifact,
|
||||||
|
IKyberNetworkProxy: IKyberNetworkProxy as ContractArtifact,
|
||||||
|
IUniswapExchange: IUniswapExchange as ContractArtifact,
|
||||||
|
IUniswapExchangeFactory: IUniswapExchangeFactory as ContractArtifact,
|
||||||
|
TestChaiBridge: TestChaiBridge as ContractArtifact,
|
||||||
|
TestDydxBridge: TestDydxBridge as ContractArtifact,
|
||||||
|
TestERC20Bridge: TestERC20Bridge as ContractArtifact,
|
||||||
|
TestEth2DaiBridge: TestEth2DaiBridge as ContractArtifact,
|
||||||
|
TestKyberBridge: TestKyberBridge as ContractArtifact,
|
||||||
TestStaticCallTarget: TestStaticCallTarget as ContractArtifact,
|
TestStaticCallTarget: TestStaticCallTarget as ContractArtifact,
|
||||||
|
TestUniswapBridge: TestUniswapBridge as ContractArtifact,
|
||||||
};
|
};
|
||||||
|
|||||||
112
contracts/asset-proxy/src/asset_data.ts
Normal file
112
contracts/asset-proxy/src/asset_data.ts
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
import { AssetProxyId } from '@0x/types';
|
||||||
|
import { BigNumber, hexUtils } from '@0x/utils';
|
||||||
|
|
||||||
|
import { IAssetDataContract } from './wrappers';
|
||||||
|
|
||||||
|
const assetDataIface = new IAssetDataContract('0x0000000000000000000000000000000000000000', { isEIP1193: true } as any);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the proxy ID from encoded asset data.
|
||||||
|
*/
|
||||||
|
export function getAssetDataProxyId(encoded: string): AssetProxyId {
|
||||||
|
// tslint:disable-next-line: no-unnecessary-type-assertion
|
||||||
|
return hexUtils.slice(encoded, 0, 4) as AssetProxyId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode ERC20 asset data.
|
||||||
|
*/
|
||||||
|
export function decodeERC20AssetData(encoded: string): string {
|
||||||
|
return assetDataIface.getABIDecodedTransactionData<string>('ERC20Token', encoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode ERC721 asset data.
|
||||||
|
*/
|
||||||
|
export function decodeERC721AssetData(encoded: string): [string, BigNumber] {
|
||||||
|
return assetDataIface.getABIDecodedTransactionData<[string, BigNumber]>('ERC721Token', encoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode ERC1155 asset data.
|
||||||
|
*/
|
||||||
|
export function decodeERC1155AssetData(encoded: string): [string, BigNumber[], BigNumber[], string] {
|
||||||
|
return assetDataIface.getABIDecodedTransactionData<[string, BigNumber[], BigNumber[], string]>(
|
||||||
|
'ERC1155Assets',
|
||||||
|
encoded,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode MultiAsset asset data.
|
||||||
|
*/
|
||||||
|
export function decodeMultiAssetData(encoded: string): [BigNumber[], string[]] {
|
||||||
|
return assetDataIface.getABIDecodedTransactionData<[BigNumber[], string[]]>('MultiAsset', encoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode StaticCall asset data.
|
||||||
|
*/
|
||||||
|
export function decodeStaticCallAssetData(encoded: string): [string, string, string] {
|
||||||
|
return assetDataIface.getABIDecodedTransactionData<[string, string, string]>('StaticCall', encoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode ERC20Bridge asset data.
|
||||||
|
*/
|
||||||
|
export function decodeERC20BridgeAssetData(encoded: string): [string, string, string] {
|
||||||
|
return assetDataIface.getABIDecodedTransactionData<[string, string, string]>('ERC20Bridge', encoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode ERC20 asset data.
|
||||||
|
*/
|
||||||
|
export function encodeERC20AssetData(tokenAddress: string): string {
|
||||||
|
return assetDataIface.ERC20Token(tokenAddress).getABIEncodedTransactionData();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode ERC721 asset data.
|
||||||
|
*/
|
||||||
|
export function encodeERC721AssetData(tokenAddress: string, tokenId: BigNumber): string {
|
||||||
|
return assetDataIface.ERC721Token(tokenAddress, tokenId).getABIEncodedTransactionData();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode ERC1155 asset data.
|
||||||
|
*/
|
||||||
|
export function encodeERC1155AssetData(
|
||||||
|
tokenAddress: string,
|
||||||
|
tokenIds: BigNumber[],
|
||||||
|
values: BigNumber[],
|
||||||
|
callbackData: string,
|
||||||
|
): string {
|
||||||
|
return assetDataIface.ERC1155Assets(tokenAddress, tokenIds, values, callbackData).getABIEncodedTransactionData();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode MultiAsset asset data.
|
||||||
|
*/
|
||||||
|
export function encodeMultiAssetData(values: BigNumber[], nestedAssetData: string[]): string {
|
||||||
|
return assetDataIface.MultiAsset(values, nestedAssetData).getABIEncodedTransactionData();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode StaticCall asset data.
|
||||||
|
*/
|
||||||
|
export function encodeStaticCallAssetData(
|
||||||
|
staticCallTargetAddress: string,
|
||||||
|
staticCallData: string,
|
||||||
|
expectedReturnDataHash: string,
|
||||||
|
): string {
|
||||||
|
return assetDataIface
|
||||||
|
.StaticCall(staticCallTargetAddress, staticCallData, expectedReturnDataHash)
|
||||||
|
.getABIEncodedTransactionData();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode ERC20Bridge asset data.
|
||||||
|
*/
|
||||||
|
export function encodeERC20BridgeAssetData(tokenAddress: string, bridgeAddress: string, bridgeData: string): string {
|
||||||
|
return assetDataIface.ERC20Bridge(tokenAddress, bridgeAddress, bridgeData).getABIEncodedTransactionData();
|
||||||
|
}
|
||||||
40
contracts/asset-proxy/src/dydx_bridge_encoder.ts
Normal file
40
contracts/asset-proxy/src/dydx_bridge_encoder.ts
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import { AbiEncoder, BigNumber } from '@0x/utils';
|
||||||
|
|
||||||
|
export enum DydxBridgeActionType {
|
||||||
|
Deposit,
|
||||||
|
Withdraw,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DydxBridgeAction {
|
||||||
|
actionType: DydxBridgeActionType;
|
||||||
|
accountId: BigNumber;
|
||||||
|
marketId: BigNumber;
|
||||||
|
conversionRateNumerator: BigNumber;
|
||||||
|
conversionRateDenominator: BigNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DydxBridgeData {
|
||||||
|
accountNumbers: BigNumber[];
|
||||||
|
actions: DydxBridgeAction[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const dydxBridgeDataEncoder = AbiEncoder.create([
|
||||||
|
{
|
||||||
|
name: 'bridgeData',
|
||||||
|
type: 'tuple',
|
||||||
|
components: [
|
||||||
|
{ name: 'accountNumbers', type: 'uint256[]' },
|
||||||
|
{
|
||||||
|
name: 'actions',
|
||||||
|
type: 'tuple[]',
|
||||||
|
components: [
|
||||||
|
{ name: 'actionType', type: 'uint8' },
|
||||||
|
{ name: 'accountId', type: 'uint256' },
|
||||||
|
{ name: 'marketId', type: 'uint256' },
|
||||||
|
{ name: 'conversionRateNumerator', type: 'uint256' },
|
||||||
|
{ name: 'conversionRateDenominator', type: 'uint256' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]);
|
||||||
@@ -7,13 +7,14 @@ import {
|
|||||||
LogDecoder,
|
LogDecoder,
|
||||||
txDefaults,
|
txDefaults,
|
||||||
} from '@0x/contracts-test-utils';
|
} from '@0x/contracts-test-utils';
|
||||||
import { assetDataUtils } from '@0x/order-utils';
|
|
||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||||
import { Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
import { Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { artifacts, ERC1155ProxyContract, IAssetProxyContract } from '../../src';
|
import { artifacts } from './artifacts';
|
||||||
|
|
||||||
|
import { ERC1155ProxyContract, IAssetDataContract, IAssetProxyContract } from './wrappers';
|
||||||
|
|
||||||
export class ERC1155ProxyWrapper {
|
export class ERC1155ProxyWrapper {
|
||||||
private readonly _tokenOwnerAddresses: string[];
|
private readonly _tokenOwnerAddresses: string[];
|
||||||
@@ -26,6 +27,7 @@ export class ERC1155ProxyWrapper {
|
|||||||
private readonly _logDecoder: LogDecoder;
|
private readonly _logDecoder: LogDecoder;
|
||||||
private readonly _dummyTokenWrappers: Erc1155Wrapper[];
|
private readonly _dummyTokenWrappers: Erc1155Wrapper[];
|
||||||
private readonly _assetProxyInterface: IAssetProxyContract;
|
private readonly _assetProxyInterface: IAssetProxyContract;
|
||||||
|
private readonly _assetDataInterface: IAssetDataContract;
|
||||||
private _proxyContract?: ERC1155ProxyContract;
|
private _proxyContract?: ERC1155ProxyContract;
|
||||||
private _proxyIdIfExists?: string;
|
private _proxyIdIfExists?: string;
|
||||||
private _initialTokenIdsByOwner: ERC1155HoldingsByOwner = { fungible: {}, nonFungible: {} };
|
private _initialTokenIdsByOwner: ERC1155HoldingsByOwner = { fungible: {}, nonFungible: {} };
|
||||||
@@ -37,6 +39,7 @@ export class ERC1155ProxyWrapper {
|
|||||||
this._logDecoder = new LogDecoder(this._web3Wrapper, allArtifacts);
|
this._logDecoder = new LogDecoder(this._web3Wrapper, allArtifacts);
|
||||||
this._dummyTokenWrappers = [];
|
this._dummyTokenWrappers = [];
|
||||||
this._assetProxyInterface = new IAssetProxyContract(constants.NULL_ADDRESS, provider);
|
this._assetProxyInterface = new IAssetProxyContract(constants.NULL_ADDRESS, provider);
|
||||||
|
this._assetDataInterface = new IAssetDataContract(constants.NULL_ADDRESS, provider);
|
||||||
this._tokenOwnerAddresses = tokenOwnerAddresses;
|
this._tokenOwnerAddresses = tokenOwnerAddresses;
|
||||||
this._contractOwnerAddress = contractOwnerAddress;
|
this._contractOwnerAddress = contractOwnerAddress;
|
||||||
this._fungibleTokenIds = [];
|
this._fungibleTokenIds = [];
|
||||||
@@ -56,7 +59,7 @@ export class ERC1155ProxyWrapper {
|
|||||||
txDefaults,
|
txDefaults,
|
||||||
artifacts,
|
artifacts,
|
||||||
);
|
);
|
||||||
const erc1155Wrapper = new Erc1155Wrapper(erc1155Contract, this._provider, this._contractOwnerAddress);
|
const erc1155Wrapper = new Erc1155Wrapper(erc1155Contract, this._contractOwnerAddress);
|
||||||
this._dummyTokenWrappers.push(erc1155Wrapper);
|
this._dummyTokenWrappers.push(erc1155Wrapper);
|
||||||
}
|
}
|
||||||
return this._dummyTokenWrappers;
|
return this._dummyTokenWrappers;
|
||||||
@@ -72,7 +75,7 @@ export class ERC1155ProxyWrapper {
|
|||||||
txDefaults,
|
txDefaults,
|
||||||
artifacts,
|
artifacts,
|
||||||
);
|
);
|
||||||
this._proxyIdIfExists = await this._proxyContract.getProxyId.callAsync();
|
this._proxyIdIfExists = await this._proxyContract.getProxyId().callAsync();
|
||||||
return this._proxyContract;
|
return this._proxyContract;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@@ -95,7 +98,7 @@ export class ERC1155ProxyWrapper {
|
|||||||
* @param extraData extra data to append to `transferFrom` transaction. Optional.
|
* @param extraData extra data to append to `transferFrom` transaction. Optional.
|
||||||
* @return abi encoded tx data.
|
* @return abi encoded tx data.
|
||||||
*/
|
*/
|
||||||
public getTransferFromAbiEncodedTxData(
|
public async getTransferFromAbiEncodedTxDataAsync(
|
||||||
from: string,
|
from: string,
|
||||||
to: string,
|
to: string,
|
||||||
contractAddress: string,
|
contractAddress: string,
|
||||||
@@ -105,23 +108,17 @@ export class ERC1155ProxyWrapper {
|
|||||||
receiverCallbackData: string,
|
receiverCallbackData: string,
|
||||||
authorizedSender: string,
|
authorizedSender: string,
|
||||||
assetData_?: string,
|
assetData_?: string,
|
||||||
): string {
|
): Promise<string> {
|
||||||
this._validateProxyContractExistsOrThrow();
|
this._validateProxyContractExistsOrThrow();
|
||||||
const assetData =
|
const assetData =
|
||||||
assetData_ === undefined
|
assetData_ === undefined
|
||||||
? assetDataUtils.encodeERC1155AssetData(
|
? this._assetDataInterface
|
||||||
contractAddress,
|
.ERC1155Assets(contractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
|
||||||
tokensToTransfer,
|
.getABIEncodedTransactionData()
|
||||||
valuesToTransfer,
|
|
||||||
receiverCallbackData,
|
|
||||||
)
|
|
||||||
: assetData_;
|
: assetData_;
|
||||||
const data = this._assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
const data = this._assetProxyInterface
|
||||||
assetData,
|
.transferFrom(assetData, from, to, valueMultiplier)
|
||||||
from,
|
.getABIEncodedTransactionData();
|
||||||
to,
|
|
||||||
valueMultiplier,
|
|
||||||
);
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@@ -169,19 +166,13 @@ export class ERC1155ProxyWrapper {
|
|||||||
this._validateProxyContractExistsOrThrow();
|
this._validateProxyContractExistsOrThrow();
|
||||||
const assetData =
|
const assetData =
|
||||||
assetData_ === undefined
|
assetData_ === undefined
|
||||||
? assetDataUtils.encodeERC1155AssetData(
|
? this._assetDataInterface
|
||||||
contractAddress,
|
.ERC1155Assets(contractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
|
||||||
tokensToTransfer,
|
.getABIEncodedTransactionData()
|
||||||
valuesToTransfer,
|
|
||||||
receiverCallbackData,
|
|
||||||
)
|
|
||||||
: assetData_;
|
: assetData_;
|
||||||
const data = this._assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
const data = this._assetProxyInterface
|
||||||
assetData,
|
.transferFrom(assetData, from, to, valueMultiplier)
|
||||||
from,
|
.getABIEncodedTransactionData();
|
||||||
to,
|
|
||||||
valueMultiplier,
|
|
||||||
);
|
|
||||||
const txHash = await this._web3Wrapper.sendTransactionAsync({
|
const txHash = await this._web3Wrapper.sendTransactionAsync({
|
||||||
to: (this._proxyContract as ERC1155ProxyContract).address,
|
to: (this._proxyContract as ERC1155ProxyContract).address,
|
||||||
data,
|
data,
|
||||||
@@ -336,6 +327,22 @@ export class ERC1155ProxyWrapper {
|
|||||||
};
|
};
|
||||||
return holdingsByOwner;
|
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`.
|
* @dev Checks if proxy is approved to transfer tokens on behalf of `userAddress`.
|
||||||
* @param userAddress owner of ERC1155 tokens.
|
* @param userAddress owner of ERC1155 tokens.
|
||||||
@@ -346,7 +353,7 @@ export class ERC1155ProxyWrapper {
|
|||||||
this._validateProxyContractExistsOrThrow();
|
this._validateProxyContractExistsOrThrow();
|
||||||
const tokenContract = this._getContractFromAddress(contractAddress);
|
const tokenContract = this._getContractFromAddress(contractAddress);
|
||||||
const operator = (this._proxyContract as ERC1155ProxyContract).address;
|
const operator = (this._proxyContract as ERC1155ProxyContract).address;
|
||||||
const didApproveAll = await tokenContract.isApprovedForAll.callAsync(userAddress, operator);
|
const didApproveAll = await tokenContract.isApprovedForAll(userAddress, operator).callAsync();
|
||||||
return didApproveAll;
|
return didApproveAll;
|
||||||
}
|
}
|
||||||
public getFungibleTokenIds(): BigNumber[] {
|
public getFungibleTokenIds(): BigNumber[] {
|
||||||
@@ -1,17 +1,19 @@
|
|||||||
import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contracts-erc20';
|
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 { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
import { ZeroExProvider } from 'ethereum-types';
|
import { ZeroExProvider } from 'ethereum-types';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { artifacts, ERC20ProxyContract } from '../../src';
|
import { artifacts } from './artifacts';
|
||||||
|
|
||||||
|
import { ERC20ProxyContract, IAssetDataContract } from './wrappers';
|
||||||
|
|
||||||
export class ERC20Wrapper {
|
export class ERC20Wrapper {
|
||||||
private readonly _tokenOwnerAddresses: string[];
|
private readonly _tokenOwnerAddresses: string[];
|
||||||
private readonly _contractOwnerAddress: string;
|
private readonly _contractOwnerAddress: string;
|
||||||
private readonly _provider: ZeroExProvider;
|
private readonly _provider: ZeroExProvider;
|
||||||
private readonly _dummyTokenContracts: DummyERC20TokenContract[];
|
private readonly _dummyTokenContracts: DummyERC20TokenContract[];
|
||||||
|
private readonly _assetDataInterface: IAssetDataContract;
|
||||||
private _proxyContract?: ERC20ProxyContract;
|
private _proxyContract?: ERC20ProxyContract;
|
||||||
private _proxyIdIfExists?: string;
|
private _proxyIdIfExists?: string;
|
||||||
/**
|
/**
|
||||||
@@ -26,6 +28,7 @@ export class ERC20Wrapper {
|
|||||||
this._provider = provider;
|
this._provider = provider;
|
||||||
this._tokenOwnerAddresses = tokenOwnerAddresses;
|
this._tokenOwnerAddresses = tokenOwnerAddresses;
|
||||||
this._contractOwnerAddress = contractOwnerAddress;
|
this._contractOwnerAddress = contractOwnerAddress;
|
||||||
|
this._assetDataInterface = new IAssetDataContract(constants.NULL_ADDRESS, provider);
|
||||||
}
|
}
|
||||||
public async deployDummyTokensAsync(
|
public async deployDummyTokensAsync(
|
||||||
numberToDeploy: number,
|
numberToDeploy: number,
|
||||||
@@ -54,7 +57,7 @@ export class ERC20Wrapper {
|
|||||||
txDefaults,
|
txDefaults,
|
||||||
artifacts,
|
artifacts,
|
||||||
);
|
);
|
||||||
this._proxyIdIfExists = await this._proxyContract.getProxyId.callAsync();
|
this._proxyIdIfExists = await this._proxyContract.getProxyId().callAsync();
|
||||||
return this._proxyContract;
|
return this._proxyContract;
|
||||||
}
|
}
|
||||||
public getProxyId(): string {
|
public getProxyId(): string {
|
||||||
@@ -66,50 +69,39 @@ 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 dummyTokenContract.setBalance.awaitTransactionSuccessAsync(
|
await dummyTokenContract
|
||||||
tokenOwnerAddress,
|
.setBalance(tokenOwnerAddress, constants.INITIAL_ERC20_BALANCE)
|
||||||
constants.INITIAL_ERC20_BALANCE,
|
.awaitTransactionSuccessAsync({ from: this._contractOwnerAddress });
|
||||||
{ from: this._contractOwnerAddress },
|
await dummyTokenContract
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
.approve((this._proxyContract as ERC20ProxyContract).address, constants.INITIAL_ERC20_ALLOWANCE)
|
||||||
);
|
.awaitTransactionSuccessAsync({ from: tokenOwnerAddress });
|
||||||
await dummyTokenContract.approve.awaitTransactionSuccessAsync(
|
|
||||||
(this._proxyContract as ERC20ProxyContract).address,
|
|
||||||
constants.INITIAL_ERC20_ALLOWANCE,
|
|
||||||
{ from: tokenOwnerAddress },
|
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public async getBalanceAsync(userAddress: string, assetData: string): Promise<BigNumber> {
|
public async getBalanceAsync(userAddress: string, assetData: string): Promise<BigNumber> {
|
||||||
const tokenContract = this._getTokenContractFromAssetData(assetData);
|
const tokenContract = await this._getTokenContractFromAssetDataAsync(assetData);
|
||||||
const balance = new BigNumber(await tokenContract.balanceOf.callAsync(userAddress));
|
const balance = new BigNumber(await tokenContract.balanceOf(userAddress).callAsync());
|
||||||
return balance;
|
return balance;
|
||||||
}
|
}
|
||||||
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 = await this._getTokenContractFromAssetDataAsync(assetData);
|
||||||
await tokenContract.setBalance.awaitTransactionSuccessAsync(
|
await tokenContract
|
||||||
userAddress,
|
.setBalance(userAddress, amount)
|
||||||
amount,
|
.awaitTransactionSuccessAsync(
|
||||||
{ from: this._contractOwnerAddress },
|
{ from: this._contractOwnerAddress },
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
{ pollingIntervalMs: constants.AWAIT_TRANSACTION_MINED_MS },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
public async getProxyAllowanceAsync(userAddress: string, assetData: string): Promise<BigNumber> {
|
public async getProxyAllowanceAsync(userAddress: string, assetData: string): Promise<BigNumber> {
|
||||||
const tokenContract = this._getTokenContractFromAssetData(assetData);
|
const tokenContract = await this._getTokenContractFromAssetDataAsync(assetData);
|
||||||
const proxyAddress = (this._proxyContract as ERC20ProxyContract).address;
|
const proxyAddress = (this._proxyContract as ERC20ProxyContract).address;
|
||||||
const allowance = new BigNumber(await tokenContract.allowance.callAsync(userAddress, proxyAddress));
|
const allowance = new BigNumber(await tokenContract.allowance(userAddress, proxyAddress).callAsync());
|
||||||
return allowance;
|
return allowance;
|
||||||
}
|
}
|
||||||
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 = await this._getTokenContractFromAssetDataAsync(assetData);
|
||||||
const proxyAddress = (this._proxyContract as ERC20ProxyContract).address;
|
const proxyAddress = (this._proxyContract as ERC20ProxyContract).address;
|
||||||
await tokenContract.approve.awaitTransactionSuccessAsync(
|
await tokenContract.approve(proxyAddress, amount).awaitTransactionSuccessAsync({ from: userAddress });
|
||||||
proxyAddress,
|
|
||||||
amount,
|
|
||||||
{ from: userAddress },
|
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
public async getBalancesAsync(): Promise<ERC20BalancesByOwner> {
|
public async getBalancesAsync(): Promise<ERC20BalancesByOwner> {
|
||||||
this._validateDummyTokenContractsExistOrThrow();
|
this._validateDummyTokenContractsExistOrThrow();
|
||||||
@@ -118,7 +110,7 @@ export class ERC20Wrapper {
|
|||||||
const balanceInfo: Array<{ tokenOwnerAddress: string; tokenAddress: string }> = [];
|
const balanceInfo: Array<{ tokenOwnerAddress: string; tokenAddress: string }> = [];
|
||||||
for (const dummyTokenContract of this._dummyTokenContracts) {
|
for (const dummyTokenContract of this._dummyTokenContracts) {
|
||||||
for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
|
for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
|
||||||
balances.push(await dummyTokenContract.balanceOf.callAsync(tokenOwnerAddress));
|
balances.push(await dummyTokenContract.balanceOf(tokenOwnerAddress).callAsync());
|
||||||
balanceInfo.push({
|
balanceInfo.push({
|
||||||
tokenOwnerAddress,
|
tokenOwnerAddress,
|
||||||
tokenAddress: dummyTokenContract.address,
|
tokenAddress: dummyTokenContract.address,
|
||||||
@@ -151,9 +143,8 @@ export class ERC20Wrapper {
|
|||||||
const tokenAddresses = _.map(this._dummyTokenContracts, dummyTokenContract => dummyTokenContract.address);
|
const tokenAddresses = _.map(this._dummyTokenContracts, dummyTokenContract => dummyTokenContract.address);
|
||||||
return tokenAddresses;
|
return tokenAddresses;
|
||||||
}
|
}
|
||||||
private _getTokenContractFromAssetData(assetData: string): DummyERC20TokenContract {
|
private async _getTokenContractFromAssetDataAsync(assetData: string): Promise<DummyERC20TokenContract> {
|
||||||
const erc20ProxyData = assetDataUtils.decodeERC20AssetData(assetData);
|
const tokenAddress = this._assetDataInterface.getABIDecodedTransactionData<string>('ERC20Token', assetData); // tslint:disable-line:no-unused-variable
|
||||||
const tokenAddress = erc20ProxyData.tokenAddress;
|
|
||||||
const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === tokenAddress);
|
const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === tokenAddress);
|
||||||
if (tokenContractIfExists === undefined) {
|
if (tokenContractIfExists === undefined) {
|
||||||
throw new Error(`Token: ${tokenAddress} was not deployed through ERC20Wrapper`);
|
throw new Error(`Token: ${tokenAddress} was not deployed through ERC20Wrapper`);
|
||||||
@@ -5,7 +5,9 @@ import { BigNumber } from '@0x/utils';
|
|||||||
import { ZeroExProvider } from 'ethereum-types';
|
import { ZeroExProvider } from 'ethereum-types';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { artifacts, ERC721ProxyContract } from '../../src';
|
import { artifacts } from './artifacts';
|
||||||
|
|
||||||
|
import { ERC721ProxyContract } from './wrappers';
|
||||||
|
|
||||||
export class ERC721Wrapper {
|
export class ERC721Wrapper {
|
||||||
private readonly _tokenOwnerAddresses: string[];
|
private readonly _tokenOwnerAddresses: string[];
|
||||||
@@ -44,7 +46,7 @@ export class ERC721Wrapper {
|
|||||||
txDefaults,
|
txDefaults,
|
||||||
artifacts,
|
artifacts,
|
||||||
);
|
);
|
||||||
this._proxyIdIfExists = await this._proxyContract.getProxyId.callAsync();
|
this._proxyIdIfExists = await this._proxyContract.getProxyId().callAsync();
|
||||||
return this._proxyContract;
|
return this._proxyContract;
|
||||||
}
|
}
|
||||||
public getProxyId(): string {
|
public getProxyId(): string {
|
||||||
@@ -71,14 +73,14 @@ export class ERC721Wrapper {
|
|||||||
}
|
}
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public async doesTokenExistAsync(tokenAddress: string, tokenId: BigNumber): Promise<boolean> {
|
public async doesTokenExistAsync(tokenAddress: string, tokenId: BigNumber): Promise<boolean> {
|
||||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||||
const owner = await tokenContract.ownerOf.callAsync(tokenId);
|
const owner = await tokenContract.ownerOf(tokenId).callAsync();
|
||||||
const doesExist = owner !== constants.NULL_ADDRESS;
|
const doesExist = owner !== constants.NULL_ADDRESS;
|
||||||
return doesExist;
|
return doesExist;
|
||||||
}
|
}
|
||||||
@@ -86,26 +88,21 @@ 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 tokenContract.setApprovalForAll.awaitTransactionSuccessAsync(
|
await tokenContract.setApprovalForAll(proxyAddress, isApproved).awaitTransactionSuccessAsync({
|
||||||
proxyAddress,
|
from: ownerAddress,
|
||||||
isApproved,
|
});
|
||||||
{ from: tokenOwner },
|
|
||||||
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 tokenContract.approve.awaitTransactionSuccessAsync(
|
await tokenContract.approve(to, tokenId).awaitTransactionSuccessAsync({ from: tokenOwner });
|
||||||
to,
|
|
||||||
tokenId,
|
|
||||||
{ from: tokenOwner },
|
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
public async transferFromAsync(
|
public async transferFromAsync(
|
||||||
tokenAddress: string,
|
tokenAddress: string,
|
||||||
@@ -114,40 +111,28 @@ export class ERC721Wrapper {
|
|||||||
userAddress: string,
|
userAddress: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||||
await tokenContract.transferFrom.awaitTransactionSuccessAsync(
|
await tokenContract.transferFrom(currentOwner, userAddress, tokenId).awaitTransactionSuccessAsync({
|
||||||
currentOwner,
|
from: currentOwner,
|
||||||
userAddress,
|
});
|
||||||
tokenId,
|
|
||||||
{ from: currentOwner },
|
|
||||||
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 tokenContract.mint.awaitTransactionSuccessAsync(
|
await tokenContract.mint(userAddress, tokenId).awaitTransactionSuccessAsync({
|
||||||
userAddress,
|
from: this._contractOwnerAddress,
|
||||||
tokenId,
|
});
|
||||||
{ from: this._contractOwnerAddress },
|
|
||||||
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 tokenContract.burn.awaitTransactionSuccessAsync(
|
await tokenContract.burn(owner, tokenId).awaitTransactionSuccessAsync({ from: this._contractOwnerAddress });
|
||||||
owner,
|
|
||||||
tokenId,
|
|
||||||
{ from: this._contractOwnerAddress },
|
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
public async ownerOfAsync(tokenAddress: string, tokenId: BigNumber): Promise<string> {
|
public async ownerOfAsync(tokenAddress: string, tokenId: BigNumber): Promise<string> {
|
||||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||||
const owner = await tokenContract.ownerOf.callAsync(tokenId);
|
const owner = await tokenContract.ownerOf(tokenId).callAsync();
|
||||||
return owner;
|
return owner;
|
||||||
}
|
}
|
||||||
public async isOwnerAsync(userAddress: string, tokenAddress: string, tokenId: BigNumber): Promise<boolean> {
|
public async isOwnerAsync(userAddress: string, tokenAddress: string, tokenId: BigNumber): Promise<boolean> {
|
||||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||||
const tokenOwner = await tokenContract.ownerOf.callAsync(tokenId);
|
const tokenOwner = await tokenContract.ownerOf(tokenId).callAsync();
|
||||||
const isOwner = tokenOwner === userAddress;
|
const isOwner = tokenOwner === userAddress;
|
||||||
return isOwner;
|
return isOwner;
|
||||||
}
|
}
|
||||||
@@ -155,13 +140,13 @@ export class ERC721Wrapper {
|
|||||||
this._validateProxyContractExistsOrThrow();
|
this._validateProxyContractExistsOrThrow();
|
||||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||||
const operator = (this._proxyContract as ERC721ProxyContract).address;
|
const operator = (this._proxyContract as ERC721ProxyContract).address;
|
||||||
const didApproveAll = await tokenContract.isApprovedForAll.callAsync(userAddress, operator);
|
const didApproveAll = await tokenContract.isApprovedForAll(userAddress, operator).callAsync();
|
||||||
return didApproveAll;
|
return didApproveAll;
|
||||||
}
|
}
|
||||||
public async isProxyApprovedAsync(tokenAddress: string, tokenId: BigNumber): Promise<boolean> {
|
public async isProxyApprovedAsync(tokenAddress: string, tokenId: BigNumber): Promise<boolean> {
|
||||||
this._validateProxyContractExistsOrThrow();
|
this._validateProxyContractExistsOrThrow();
|
||||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||||
const approvedAddress = await tokenContract.getApproved.callAsync(tokenId);
|
const approvedAddress = await tokenContract.getApproved(tokenId).callAsync();
|
||||||
const proxyAddress = (this._proxyContract as ERC721ProxyContract).address;
|
const proxyAddress = (this._proxyContract as ERC721ProxyContract).address;
|
||||||
const isProxyAnApprovedOperator = approvedAddress === proxyAddress;
|
const isProxyAnApprovedOperator = approvedAddress === proxyAddress;
|
||||||
return isProxyAnApprovedOperator;
|
return isProxyAnApprovedOperator;
|
||||||
@@ -178,7 +163,7 @@ export class ERC721Wrapper {
|
|||||||
dummyTokenContract.address
|
dummyTokenContract.address
|
||||||
];
|
];
|
||||||
for (const tokenId of initialTokenOwnerIds) {
|
for (const tokenId of initialTokenOwnerIds) {
|
||||||
tokenOwnerAddresses.push(await dummyTokenContract.ownerOf.callAsync(tokenId));
|
tokenOwnerAddresses.push(await dummyTokenContract.ownerOf(tokenId).callAsync());
|
||||||
tokenInfo.push({
|
tokenInfo.push({
|
||||||
tokenId,
|
tokenId,
|
||||||
tokenAddress: dummyTokenContract.address,
|
tokenAddress: dummyTokenContract.address,
|
||||||
@@ -1,3 +1,89 @@
|
|||||||
export * from './artifacts';
|
export { artifacts } from './artifacts';
|
||||||
export * from './wrappers';
|
export {
|
||||||
export * from '../test/utils';
|
ERC1155ProxyContract,
|
||||||
|
ERC20BridgeProxyContract,
|
||||||
|
ERC20ProxyContract,
|
||||||
|
ERC721ProxyContract,
|
||||||
|
Eth2DaiBridgeContract,
|
||||||
|
DydxBridgeContract,
|
||||||
|
TestDydxBridgeContract,
|
||||||
|
IAssetDataContract,
|
||||||
|
IAssetProxyContract,
|
||||||
|
MultiAssetProxyContract,
|
||||||
|
StaticCallProxyContract,
|
||||||
|
TestStaticCallTargetContract,
|
||||||
|
UniswapBridgeContract,
|
||||||
|
KyberBridgeContract,
|
||||||
|
ChaiBridgeContract,
|
||||||
|
IChaiContract,
|
||||||
|
} from './wrappers';
|
||||||
|
|
||||||
|
export { ERC20Wrapper } from './erc20_wrapper';
|
||||||
|
export { ERC721Wrapper } from './erc721_wrapper';
|
||||||
|
export { ERC1155ProxyWrapper } from './erc1155_proxy_wrapper';
|
||||||
|
export { ERC1155MintableContract, Erc1155Wrapper } from '@0x/contracts-erc1155';
|
||||||
|
export { DummyERC20TokenContract } from '@0x/contracts-erc20';
|
||||||
|
export { DummyERC721TokenContract } from '@0x/contracts-erc721';
|
||||||
|
export { AssetProxyId } from '@0x/types';
|
||||||
|
export {
|
||||||
|
ERC1155HoldingsByOwner,
|
||||||
|
ERC20BalancesByOwner,
|
||||||
|
ERC721TokenIdsByOwner,
|
||||||
|
ERC1155FungibleHoldingsByOwner,
|
||||||
|
ERC1155NonFungibleHoldingsByOwner,
|
||||||
|
} from '@0x/contracts-test-utils';
|
||||||
|
export {
|
||||||
|
TransactionReceiptWithDecodedLogs,
|
||||||
|
Provider,
|
||||||
|
ZeroExProvider,
|
||||||
|
JSONRPCRequestPayload,
|
||||||
|
JSONRPCErrorCallback,
|
||||||
|
TransactionReceiptStatus,
|
||||||
|
JSONRPCResponsePayload,
|
||||||
|
JSONRPCResponseError,
|
||||||
|
ContractArtifact,
|
||||||
|
ContractChains,
|
||||||
|
CompilerOpts,
|
||||||
|
StandardContractOutput,
|
||||||
|
CompilerSettings,
|
||||||
|
ContractChainData,
|
||||||
|
ContractAbi,
|
||||||
|
DevdocOutput,
|
||||||
|
EvmOutput,
|
||||||
|
CompilerSettingsMetadata,
|
||||||
|
OptimizerSettings,
|
||||||
|
OutputField,
|
||||||
|
ParamDescription,
|
||||||
|
EvmBytecodeOutput,
|
||||||
|
EvmBytecodeOutputLinkReferences,
|
||||||
|
AbiDefinition,
|
||||||
|
FunctionAbi,
|
||||||
|
EventAbi,
|
||||||
|
RevertErrorAbi,
|
||||||
|
EventParameter,
|
||||||
|
DataItem,
|
||||||
|
MethodAbi,
|
||||||
|
ConstructorAbi,
|
||||||
|
FallbackAbi,
|
||||||
|
ConstructorStateMutability,
|
||||||
|
TupleDataItem,
|
||||||
|
StateMutability,
|
||||||
|
} from 'ethereum-types';
|
||||||
|
|
||||||
|
export {
|
||||||
|
decodeERC1155AssetData,
|
||||||
|
decodeERC20AssetData,
|
||||||
|
decodeERC20BridgeAssetData,
|
||||||
|
decodeERC721AssetData,
|
||||||
|
decodeMultiAssetData,
|
||||||
|
decodeStaticCallAssetData,
|
||||||
|
encodeERC1155AssetData,
|
||||||
|
encodeERC20AssetData,
|
||||||
|
encodeERC20BridgeAssetData,
|
||||||
|
encodeERC721AssetData,
|
||||||
|
encodeMultiAssetData,
|
||||||
|
encodeStaticCallAssetData,
|
||||||
|
getAssetDataProxyId,
|
||||||
|
} from './asset_data';
|
||||||
|
|
||||||
|
export * from './dydx_bridge_encoder';
|
||||||
|
|||||||
@@ -3,13 +3,38 @@
|
|||||||
* 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/chai_bridge';
|
||||||
|
export * from '../generated-wrappers/curve_bridge';
|
||||||
|
export * from '../generated-wrappers/dydx_bridge';
|
||||||
export * from '../generated-wrappers/erc1155_proxy';
|
export * from '../generated-wrappers/erc1155_proxy';
|
||||||
|
export * from '../generated-wrappers/erc20_bridge_proxy';
|
||||||
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_chai';
|
||||||
|
export * from '../generated-wrappers/i_curve';
|
||||||
|
export * from '../generated-wrappers/i_dydx';
|
||||||
|
export * from '../generated-wrappers/i_dydx_bridge';
|
||||||
|
export * from '../generated-wrappers/i_erc20_bridge';
|
||||||
|
export * from '../generated-wrappers/i_eth2_dai';
|
||||||
|
export * from '../generated-wrappers/i_kyber_network_proxy';
|
||||||
|
export * from '../generated-wrappers/i_uniswap_exchange';
|
||||||
|
export * from '../generated-wrappers/i_uniswap_exchange_factory';
|
||||||
|
export * from '../generated-wrappers/kyber_bridge';
|
||||||
|
export * from '../generated-wrappers/mixin_asset_proxy_dispatcher';
|
||||||
export * from '../generated-wrappers/mixin_authorizable';
|
export * from '../generated-wrappers/mixin_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/static_call_proxy';
|
||||||
|
export * from '../generated-wrappers/test_chai_bridge';
|
||||||
|
export * from '../generated-wrappers/test_dydx_bridge';
|
||||||
|
export * from '../generated-wrappers/test_erc20_bridge';
|
||||||
|
export * from '../generated-wrappers/test_eth2_dai_bridge';
|
||||||
|
export * from '../generated-wrappers/test_kyber_bridge';
|
||||||
export * from '../generated-wrappers/test_static_call_target';
|
export * from '../generated-wrappers/test_static_call_target';
|
||||||
|
export * from '../generated-wrappers/test_uniswap_bridge';
|
||||||
|
export * from '../generated-wrappers/uniswap_bridge';
|
||||||
|
|||||||
79
contracts/asset-proxy/test/artifacts.ts
Normal file
79
contracts/asset-proxy/test/artifacts.ts
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
* -----------------------------------------------------------------------------
|
||||||
|
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||||
|
* -----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
import { ContractArtifact } from 'ethereum-types';
|
||||||
|
|
||||||
|
import * as ChaiBridge from '../test/generated-artifacts/ChaiBridge.json';
|
||||||
|
import * as CurveBridge from '../test/generated-artifacts/CurveBridge.json';
|
||||||
|
import * as DydxBridge from '../test/generated-artifacts/DydxBridge.json';
|
||||||
|
import * as ERC1155Proxy from '../test/generated-artifacts/ERC1155Proxy.json';
|
||||||
|
import * as ERC20BridgeProxy from '../test/generated-artifacts/ERC20BridgeProxy.json';
|
||||||
|
import * as ERC20Proxy from '../test/generated-artifacts/ERC20Proxy.json';
|
||||||
|
import * as ERC721Proxy from '../test/generated-artifacts/ERC721Proxy.json';
|
||||||
|
import * as Eth2DaiBridge from '../test/generated-artifacts/Eth2DaiBridge.json';
|
||||||
|
import * as IAssetData from '../test/generated-artifacts/IAssetData.json';
|
||||||
|
import * as IAssetProxy from '../test/generated-artifacts/IAssetProxy.json';
|
||||||
|
import * as IAssetProxyDispatcher from '../test/generated-artifacts/IAssetProxyDispatcher.json';
|
||||||
|
import * as IAuthorizable from '../test/generated-artifacts/IAuthorizable.json';
|
||||||
|
import * as IChai from '../test/generated-artifacts/IChai.json';
|
||||||
|
import * as ICurve from '../test/generated-artifacts/ICurve.json';
|
||||||
|
import * as IDydx from '../test/generated-artifacts/IDydx.json';
|
||||||
|
import * as IDydxBridge from '../test/generated-artifacts/IDydxBridge.json';
|
||||||
|
import * as IERC20Bridge from '../test/generated-artifacts/IERC20Bridge.json';
|
||||||
|
import * as IEth2Dai from '../test/generated-artifacts/IEth2Dai.json';
|
||||||
|
import * as IKyberNetworkProxy from '../test/generated-artifacts/IKyberNetworkProxy.json';
|
||||||
|
import * as IUniswapExchange from '../test/generated-artifacts/IUniswapExchange.json';
|
||||||
|
import * as IUniswapExchangeFactory from '../test/generated-artifacts/IUniswapExchangeFactory.json';
|
||||||
|
import * as KyberBridge from '../test/generated-artifacts/KyberBridge.json';
|
||||||
|
import * as MixinAssetProxyDispatcher from '../test/generated-artifacts/MixinAssetProxyDispatcher.json';
|
||||||
|
import * as MixinAuthorizable from '../test/generated-artifacts/MixinAuthorizable.json';
|
||||||
|
import * as MultiAssetProxy from '../test/generated-artifacts/MultiAssetProxy.json';
|
||||||
|
import * as Ownable from '../test/generated-artifacts/Ownable.json';
|
||||||
|
import * as StaticCallProxy from '../test/generated-artifacts/StaticCallProxy.json';
|
||||||
|
import * as TestChaiBridge from '../test/generated-artifacts/TestChaiBridge.json';
|
||||||
|
import * as TestDydxBridge from '../test/generated-artifacts/TestDydxBridge.json';
|
||||||
|
import * as TestERC20Bridge from '../test/generated-artifacts/TestERC20Bridge.json';
|
||||||
|
import * as TestEth2DaiBridge from '../test/generated-artifacts/TestEth2DaiBridge.json';
|
||||||
|
import * as TestKyberBridge from '../test/generated-artifacts/TestKyberBridge.json';
|
||||||
|
import * as TestStaticCallTarget from '../test/generated-artifacts/TestStaticCallTarget.json';
|
||||||
|
import * as TestUniswapBridge from '../test/generated-artifacts/TestUniswapBridge.json';
|
||||||
|
import * as UniswapBridge from '../test/generated-artifacts/UniswapBridge.json';
|
||||||
|
export const artifacts = {
|
||||||
|
MixinAssetProxyDispatcher: MixinAssetProxyDispatcher as ContractArtifact,
|
||||||
|
MixinAuthorizable: MixinAuthorizable as ContractArtifact,
|
||||||
|
Ownable: Ownable as ContractArtifact,
|
||||||
|
ERC1155Proxy: ERC1155Proxy as ContractArtifact,
|
||||||
|
ERC20BridgeProxy: ERC20BridgeProxy as ContractArtifact,
|
||||||
|
ERC20Proxy: ERC20Proxy as ContractArtifact,
|
||||||
|
ERC721Proxy: ERC721Proxy as ContractArtifact,
|
||||||
|
MultiAssetProxy: MultiAssetProxy as ContractArtifact,
|
||||||
|
StaticCallProxy: StaticCallProxy as ContractArtifact,
|
||||||
|
ChaiBridge: ChaiBridge as ContractArtifact,
|
||||||
|
CurveBridge: CurveBridge as ContractArtifact,
|
||||||
|
DydxBridge: DydxBridge as ContractArtifact,
|
||||||
|
Eth2DaiBridge: Eth2DaiBridge as ContractArtifact,
|
||||||
|
KyberBridge: KyberBridge as ContractArtifact,
|
||||||
|
UniswapBridge: UniswapBridge as ContractArtifact,
|
||||||
|
IAssetData: IAssetData as ContractArtifact,
|
||||||
|
IAssetProxy: IAssetProxy as ContractArtifact,
|
||||||
|
IAssetProxyDispatcher: IAssetProxyDispatcher as ContractArtifact,
|
||||||
|
IAuthorizable: IAuthorizable as ContractArtifact,
|
||||||
|
IChai: IChai as ContractArtifact,
|
||||||
|
ICurve: ICurve as ContractArtifact,
|
||||||
|
IDydx: IDydx as ContractArtifact,
|
||||||
|
IDydxBridge: IDydxBridge as ContractArtifact,
|
||||||
|
IERC20Bridge: IERC20Bridge as ContractArtifact,
|
||||||
|
IEth2Dai: IEth2Dai as ContractArtifact,
|
||||||
|
IKyberNetworkProxy: IKyberNetworkProxy as ContractArtifact,
|
||||||
|
IUniswapExchange: IUniswapExchange as ContractArtifact,
|
||||||
|
IUniswapExchangeFactory: IUniswapExchangeFactory as ContractArtifact,
|
||||||
|
TestChaiBridge: TestChaiBridge as ContractArtifact,
|
||||||
|
TestDydxBridge: TestDydxBridge as ContractArtifact,
|
||||||
|
TestERC20Bridge: TestERC20Bridge as ContractArtifact,
|
||||||
|
TestEth2DaiBridge: TestEth2DaiBridge as ContractArtifact,
|
||||||
|
TestKyberBridge: TestKyberBridge as ContractArtifact,
|
||||||
|
TestStaticCallTarget: TestStaticCallTarget as ContractArtifact,
|
||||||
|
TestUniswapBridge: TestUniswapBridge as ContractArtifact,
|
||||||
|
};
|
||||||
@@ -1,38 +1,20 @@
|
|||||||
import {
|
import { blockchainTests, expect, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils';
|
||||||
chaiSetup,
|
|
||||||
constants,
|
|
||||||
expectTransactionFailedAsync,
|
|
||||||
provider,
|
|
||||||
txDefaults,
|
|
||||||
web3Wrapper,
|
|
||||||
} from '@0x/contracts-test-utils';
|
|
||||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
|
||||||
import { RevertReason } from '@0x/types';
|
import { RevertReason } from '@0x/types';
|
||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
import * as chai from 'chai';
|
|
||||||
import * as _ from 'lodash';
|
|
||||||
|
|
||||||
import { artifacts, MixinAuthorizableContract } from '../src';
|
import { artifacts } from './artifacts';
|
||||||
|
|
||||||
chaiSetup.configure();
|
import { MixinAuthorizableContract } from './wrappers';
|
||||||
const expect = chai.expect;
|
|
||||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
|
||||||
|
|
||||||
describe('Authorizable', () => {
|
blockchainTests.resets('Authorizable', () => {
|
||||||
let owner: string;
|
let owner: string;
|
||||||
let notOwner: string;
|
let notOwner: string;
|
||||||
let address: string;
|
let address: string;
|
||||||
let authorizable: MixinAuthorizableContract;
|
let authorizable: MixinAuthorizableContract;
|
||||||
|
|
||||||
before(async () => {
|
|
||||||
await blockchainLifecycle.startAsync();
|
|
||||||
});
|
|
||||||
after(async () => {
|
|
||||||
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] = accounts.slice(0, 3);
|
||||||
authorizable = await MixinAuthorizableContract.deployFrom0xArtifactAsync(
|
authorizable = await MixinAuthorizableContract.deployFrom0xArtifactAsync(
|
||||||
artifacts.MixinAuthorizable,
|
artifacts.MixinAuthorizable,
|
||||||
provider,
|
provider,
|
||||||
@@ -40,176 +22,106 @@ describe('Authorizable', () => {
|
|||||||
artifacts,
|
artifacts,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
beforeEach(async () => {
|
|
||||||
await blockchainLifecycle.startAsync();
|
|
||||||
});
|
|
||||||
afterEach(async () => {
|
|
||||||
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(
|
const tx = authorizable.addAuthorizedAddress(notOwner).sendTransactionAsync({ from: notOwner });
|
||||||
authorizable.addAuthorizedAddress.sendTransactionAsync(notOwner, { from: notOwner }),
|
return expect(tx).to.revertWith(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 authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
||||||
address,
|
const isAuthorized = await authorizable.authorized(address).callAsync();
|
||||||
{ from: owner },
|
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
||||||
);
|
|
||||||
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 authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
it('should revert if owner attempts to authorize a duplicate address', async () => {
|
||||||
address,
|
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
||||||
{ from: owner },
|
const tx = authorizable.addAuthorizedAddress(address).sendTransactionAsync({ from: owner });
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
return expect(tx).to.revertWith(RevertReason.TargetAlreadyAuthorized);
|
||||||
);
|
|
||||||
return expectTransactionFailedAsync(
|
|
||||||
authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }),
|
|
||||||
RevertReason.TargetAlreadyAuthorized,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('removeAuthorizedAddress', () => {
|
describe('removeAuthorizedAddress', () => {
|
||||||
it('should throw if not called by owner', async () => {
|
it('should revert if not called by owner', async () => {
|
||||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
||||||
address,
|
const tx = authorizable.removeAuthorizedAddress(address).sendTransactionAsync({ from: notOwner });
|
||||||
{ from: owner },
|
return expect(tx).to.revertWith(RevertReason.OnlyContractOwner);
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
||||||
);
|
|
||||||
return expectTransactionFailedAsync(
|
|
||||||
authorizable.removeAuthorizedAddress.sendTransactionAsync(address, {
|
|
||||||
from: notOwner,
|
|
||||||
}),
|
|
||||||
RevertReason.OnlyContractOwner,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow owner to remove an authorized address', async () => {
|
it('should allow owner to remove an authorized address', async () => {
|
||||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
||||||
address,
|
await authorizable.removeAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
||||||
{ from: owner },
|
const isAuthorized = await authorizable.authorized(address).callAsync();
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
||||||
);
|
|
||||||
await authorizable.removeAuthorizedAddress.awaitTransactionSuccessAsync(
|
|
||||||
address,
|
|
||||||
{ from: owner },
|
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
||||||
);
|
|
||||||
const isAuthorized = await authorizable.authorized.callAsync(address);
|
|
||||||
expect(isAuthorized).to.be.false();
|
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(
|
const tx = authorizable.removeAuthorizedAddress(address).sendTransactionAsync({ from: owner });
|
||||||
authorizable.removeAuthorizedAddress.sendTransactionAsync(address, {
|
return expect(tx).to.revertWith(RevertReason.TargetNotAuthorized);
|
||||||
from: owner,
|
|
||||||
}),
|
|
||||||
RevertReason.TargetNotAuthorized,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('removeAuthorizedAddressAtIndex', () => {
|
describe('removeAuthorizedAddressAtIndex', () => {
|
||||||
it('should throw if not called by owner', async () => {
|
it('should revert if not called by owner', async () => {
|
||||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
||||||
address,
|
|
||||||
{ from: owner },
|
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
||||||
);
|
|
||||||
const index = new BigNumber(0);
|
const index = new BigNumber(0);
|
||||||
return expectTransactionFailedAsync(
|
const tx = authorizable
|
||||||
authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, {
|
.removeAuthorizedAddressAtIndex(address, index)
|
||||||
from: notOwner,
|
.sendTransactionAsync({ from: notOwner });
|
||||||
}),
|
return expect(tx).to.revertWith(RevertReason.OnlyContractOwner);
|
||||||
RevertReason.OnlyContractOwner,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
it('should throw if index is >= authorities.length', async () => {
|
|
||||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
it('should revert if index is >= authorities.length', async () => {
|
||||||
address,
|
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
||||||
{ from: owner },
|
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
||||||
);
|
|
||||||
const index = new BigNumber(1);
|
const index = new BigNumber(1);
|
||||||
return expectTransactionFailedAsync(
|
const tx = authorizable
|
||||||
authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, {
|
.removeAuthorizedAddressAtIndex(address, index)
|
||||||
from: owner,
|
.sendTransactionAsync({ from: owner });
|
||||||
}),
|
return expect(tx).to.revertWith(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(
|
const tx = authorizable
|
||||||
authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, {
|
.removeAuthorizedAddressAtIndex(address, index)
|
||||||
from: owner,
|
.sendTransactionAsync({ from: owner });
|
||||||
}),
|
return expect(tx).to.revertWith(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 authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
await authorizable.addAuthorizedAddress(address1).awaitTransactionSuccessAsync({ from: owner });
|
||||||
address1,
|
await authorizable.addAuthorizedAddress(address2).awaitTransactionSuccessAsync({ from: owner });
|
||||||
{ from: owner },
|
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
||||||
);
|
|
||||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
|
||||||
address2,
|
|
||||||
{ from: owner },
|
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
||||||
);
|
|
||||||
const address1Index = new BigNumber(0);
|
const address1Index = new BigNumber(0);
|
||||||
return expectTransactionFailedAsync(
|
const tx = authorizable
|
||||||
authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address2, address1Index, {
|
.removeAuthorizedAddressAtIndex(address2, address1Index)
|
||||||
from: owner,
|
.sendTransactionAsync({ from: owner });
|
||||||
}),
|
return expect(tx).to.revertWith(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 authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
||||||
address,
|
|
||||||
{ from: owner },
|
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
||||||
);
|
|
||||||
const index = new BigNumber(0);
|
const index = new BigNumber(0);
|
||||||
await authorizable.removeAuthorizedAddressAtIndex.awaitTransactionSuccessAsync(
|
await authorizable.removeAuthorizedAddressAtIndex(address, index).awaitTransactionSuccessAsync({
|
||||||
address,
|
from: owner,
|
||||||
index,
|
});
|
||||||
{ from: owner },
|
const isAuthorized = await authorizable.authorized(address).callAsync();
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
||||||
);
|
|
||||||
const isAuthorized = await authorizable.authorized.callAsync(address);
|
|
||||||
expect(isAuthorized).to.be.false();
|
expect(isAuthorized).to.be.false();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getAuthorizedAddresses', () => {
|
describe('getAuthorizedAddresses', () => {
|
||||||
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 authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
||||||
address,
|
const afterAdd = await authorizable.getAuthorizedAddresses().callAsync();
|
||||||
{ from: owner },
|
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
||||||
);
|
|
||||||
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 authorizable.removeAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
||||||
address,
|
const afterRemove = await authorizable.getAuthorizedAddresses().callAsync();
|
||||||
{ from: owner },
|
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
||||||
);
|
|
||||||
const afterRemove = await authorizable.getAuthorizedAddresses.callAsync();
|
|
||||||
expect(afterRemove).to.have.length(0);
|
expect(afterRemove).to.have.length(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
60
contracts/asset-proxy/test/chai_bridge.ts
Normal file
60
contracts/asset-proxy/test/chai_bridge.ts
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import { ERC20TokenContract } from '@0x/contracts-erc20';
|
||||||
|
import { blockchainTests, constants, expect, randomAddress } from '@0x/contracts-test-utils';
|
||||||
|
import { AssetProxyId, RevertReason } from '@0x/types';
|
||||||
|
import { BigNumber } from '@0x/utils';
|
||||||
|
|
||||||
|
import { artifacts } from './artifacts';
|
||||||
|
import { TestChaiBridgeContract } from './wrappers';
|
||||||
|
|
||||||
|
blockchainTests.resets('ChaiBridge unit tests', env => {
|
||||||
|
let chaiBridgeContract: TestChaiBridgeContract;
|
||||||
|
let testDaiContract: ERC20TokenContract;
|
||||||
|
let fromAddress: string;
|
||||||
|
let toAddress: string;
|
||||||
|
|
||||||
|
const alwaysRevertAddress = '0x0000000000000000000000000000000000000001';
|
||||||
|
const amount = new BigNumber(1);
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
[fromAddress, toAddress] = await env.getAccountAddressesAsync();
|
||||||
|
chaiBridgeContract = await TestChaiBridgeContract.deployFrom0xArtifactAsync(
|
||||||
|
artifacts.TestChaiBridge,
|
||||||
|
env.provider,
|
||||||
|
env.txDefaults,
|
||||||
|
artifacts,
|
||||||
|
);
|
||||||
|
const testChaiDaiAddress = await chaiBridgeContract.testChaiDai().callAsync();
|
||||||
|
testDaiContract = new ERC20TokenContract(testChaiDaiAddress, env.provider, env.txDefaults);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('bridgeTransferFrom()', () => {
|
||||||
|
it('fails if not called by ERC20BridgeProxy', async () => {
|
||||||
|
return expect(
|
||||||
|
chaiBridgeContract
|
||||||
|
.bridgeTransferFrom(randomAddress(), fromAddress, toAddress, amount, constants.NULL_BYTES)
|
||||||
|
.awaitTransactionSuccessAsync({ from: alwaysRevertAddress }),
|
||||||
|
).to.revertWith(RevertReason.ChaiBridgeOnlyCallableByErc20BridgeProxy);
|
||||||
|
});
|
||||||
|
it('returns magic bytes upon success', async () => {
|
||||||
|
const magicBytes = await chaiBridgeContract
|
||||||
|
.bridgeTransferFrom(randomAddress(), fromAddress, toAddress, amount, constants.NULL_BYTES)
|
||||||
|
.callAsync();
|
||||||
|
expect(magicBytes).to.eq(AssetProxyId.ERC20Bridge);
|
||||||
|
});
|
||||||
|
it('should increase the Dai balance of `toAddress` by `amount` if successful', async () => {
|
||||||
|
const initialBalance = await testDaiContract.balanceOf(toAddress).callAsync();
|
||||||
|
await chaiBridgeContract
|
||||||
|
.bridgeTransferFrom(randomAddress(), fromAddress, toAddress, amount, constants.NULL_BYTES)
|
||||||
|
.awaitTransactionSuccessAsync();
|
||||||
|
const endBalance = await testDaiContract.balanceOf(toAddress).callAsync();
|
||||||
|
expect(endBalance).to.bignumber.eq(initialBalance.plus(amount));
|
||||||
|
});
|
||||||
|
it('fails if the `chai.draw` call fails', async () => {
|
||||||
|
return expect(
|
||||||
|
chaiBridgeContract
|
||||||
|
.bridgeTransferFrom(randomAddress(), alwaysRevertAddress, toAddress, amount, constants.NULL_BYTES)
|
||||||
|
.awaitTransactionSuccessAsync(),
|
||||||
|
).to.revertWith(RevertReason.ChaiBridgeDrawDaiFailed);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
399
contracts/asset-proxy/test/dydx_bridge.ts
Normal file
399
contracts/asset-proxy/test/dydx_bridge.ts
Normal file
@@ -0,0 +1,399 @@
|
|||||||
|
import { LibMathRevertErrors } from '@0x/contracts-exchange-libs';
|
||||||
|
import { blockchainTests, constants, expect, verifyEventsFromLogs } from '@0x/contracts-test-utils';
|
||||||
|
import { AssetProxyId, RevertReason } from '@0x/types';
|
||||||
|
import { BigNumber } from '@0x/utils';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
|
import { DydxBridgeActionType, DydxBridgeData, dydxBridgeDataEncoder } from '../src/dydx_bridge_encoder';
|
||||||
|
import { ERC20BridgeProxyContract, IAssetDataContract } from '../src/wrappers';
|
||||||
|
|
||||||
|
import { artifacts } from './artifacts';
|
||||||
|
import { TestDydxBridgeContract, TestDydxBridgeEvents } from './wrappers';
|
||||||
|
|
||||||
|
blockchainTests.resets('DydxBridge unit tests', env => {
|
||||||
|
const defaultAccountNumber = new BigNumber(1);
|
||||||
|
const marketId = new BigNumber(2);
|
||||||
|
const defaultAmount = new BigNumber(4);
|
||||||
|
const notAuthorized = '0x0000000000000000000000000000000000000001';
|
||||||
|
const defaultDepositAction = {
|
||||||
|
actionType: DydxBridgeActionType.Deposit,
|
||||||
|
accountId: constants.ZERO_AMOUNT,
|
||||||
|
marketId,
|
||||||
|
conversionRateNumerator: constants.ZERO_AMOUNT,
|
||||||
|
conversionRateDenominator: constants.ZERO_AMOUNT,
|
||||||
|
};
|
||||||
|
const defaultWithdrawAction = {
|
||||||
|
actionType: DydxBridgeActionType.Withdraw,
|
||||||
|
accountId: constants.ZERO_AMOUNT,
|
||||||
|
marketId,
|
||||||
|
conversionRateNumerator: constants.ZERO_AMOUNT,
|
||||||
|
conversionRateDenominator: constants.ZERO_AMOUNT,
|
||||||
|
};
|
||||||
|
let testContract: TestDydxBridgeContract;
|
||||||
|
let testProxyContract: ERC20BridgeProxyContract;
|
||||||
|
let assetDataEncoder: IAssetDataContract;
|
||||||
|
let owner: string;
|
||||||
|
let authorized: string;
|
||||||
|
let accountOwner: string;
|
||||||
|
let receiver: string;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
// Get accounts
|
||||||
|
const accounts = await env.web3Wrapper.getAvailableAddressesAsync();
|
||||||
|
[owner, authorized, accountOwner, receiver] = accounts;
|
||||||
|
|
||||||
|
// Deploy dydx bridge
|
||||||
|
testContract = await TestDydxBridgeContract.deployFrom0xArtifactAsync(
|
||||||
|
artifacts.TestDydxBridge,
|
||||||
|
env.provider,
|
||||||
|
env.txDefaults,
|
||||||
|
artifacts,
|
||||||
|
[accountOwner, receiver],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Deploy test erc20 bridge proxy
|
||||||
|
testProxyContract = await ERC20BridgeProxyContract.deployFrom0xArtifactAsync(
|
||||||
|
artifacts.ERC20BridgeProxy,
|
||||||
|
env.provider,
|
||||||
|
env.txDefaults,
|
||||||
|
artifacts,
|
||||||
|
);
|
||||||
|
await testProxyContract.addAuthorizedAddress(authorized).awaitTransactionSuccessAsync({ from: owner });
|
||||||
|
|
||||||
|
// Setup asset data encoder
|
||||||
|
assetDataEncoder = new IAssetDataContract(constants.NULL_ADDRESS, env.provider);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('bridgeTransferFrom()', () => {
|
||||||
|
const callBridgeTransferFrom = async (
|
||||||
|
from: string,
|
||||||
|
to: string,
|
||||||
|
amount: BigNumber,
|
||||||
|
bridgeData: DydxBridgeData,
|
||||||
|
sender: string,
|
||||||
|
): Promise<string> => {
|
||||||
|
const returnValue = await testContract
|
||||||
|
.bridgeTransferFrom(
|
||||||
|
constants.NULL_ADDRESS,
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
amount,
|
||||||
|
dydxBridgeDataEncoder.encode({ bridgeData }),
|
||||||
|
)
|
||||||
|
.callAsync({ from: sender });
|
||||||
|
return returnValue;
|
||||||
|
};
|
||||||
|
const executeBridgeTransferFromAndVerifyEvents = async (
|
||||||
|
from: string,
|
||||||
|
to: string,
|
||||||
|
amount: BigNumber,
|
||||||
|
bridgeData: DydxBridgeData,
|
||||||
|
sender: string,
|
||||||
|
): Promise<void> => {
|
||||||
|
// Execute transaction.
|
||||||
|
const txReceipt = await testContract
|
||||||
|
.bridgeTransferFrom(
|
||||||
|
constants.NULL_ADDRESS,
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
amount,
|
||||||
|
dydxBridgeDataEncoder.encode({ bridgeData }),
|
||||||
|
)
|
||||||
|
.awaitTransactionSuccessAsync({ from: sender });
|
||||||
|
|
||||||
|
// Verify `OperateAccount` event.
|
||||||
|
const expectedOperateAccountEvents = [];
|
||||||
|
for (const accountNumber of bridgeData.accountNumbers) {
|
||||||
|
expectedOperateAccountEvents.push({
|
||||||
|
owner: accountOwner,
|
||||||
|
number: accountNumber,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
verifyEventsFromLogs(txReceipt.logs, expectedOperateAccountEvents, TestDydxBridgeEvents.OperateAccount);
|
||||||
|
|
||||||
|
// Verify `OperateAction` event.
|
||||||
|
const weiDenomination = 0;
|
||||||
|
const deltaAmountRef = 0;
|
||||||
|
const expectedOperateActionEvents = [];
|
||||||
|
for (const action of bridgeData.actions) {
|
||||||
|
expectedOperateActionEvents.push({
|
||||||
|
actionType: action.actionType as number,
|
||||||
|
accountId: action.accountId,
|
||||||
|
amountSign: action.actionType === DydxBridgeActionType.Deposit ? true : false,
|
||||||
|
amountDenomination: weiDenomination,
|
||||||
|
amountRef: deltaAmountRef,
|
||||||
|
amountValue: action.conversionRateDenominator.gt(0)
|
||||||
|
? amount
|
||||||
|
.times(action.conversionRateNumerator)
|
||||||
|
.dividedToIntegerBy(action.conversionRateDenominator)
|
||||||
|
: amount,
|
||||||
|
primaryMarketId: marketId,
|
||||||
|
secondaryMarketId: constants.ZERO_AMOUNT,
|
||||||
|
otherAddress: action.actionType === DydxBridgeActionType.Deposit ? from : to,
|
||||||
|
otherAccountId: constants.ZERO_AMOUNT,
|
||||||
|
data: '0x',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
verifyEventsFromLogs(txReceipt.logs, expectedOperateActionEvents, TestDydxBridgeEvents.OperateAction);
|
||||||
|
};
|
||||||
|
it('succeeds when calling with zero amount', async () => {
|
||||||
|
const bridgeData = {
|
||||||
|
accountNumbers: [defaultAccountNumber],
|
||||||
|
actions: [defaultDepositAction],
|
||||||
|
};
|
||||||
|
await executeBridgeTransferFromAndVerifyEvents(
|
||||||
|
accountOwner,
|
||||||
|
receiver,
|
||||||
|
constants.ZERO_AMOUNT,
|
||||||
|
bridgeData,
|
||||||
|
authorized,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('succeeds when calling with no accounts', async () => {
|
||||||
|
const bridgeData = {
|
||||||
|
accountNumbers: [],
|
||||||
|
actions: [defaultDepositAction],
|
||||||
|
};
|
||||||
|
await executeBridgeTransferFromAndVerifyEvents(
|
||||||
|
accountOwner,
|
||||||
|
receiver,
|
||||||
|
defaultAmount,
|
||||||
|
bridgeData,
|
||||||
|
authorized,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('succeeds when calling with no actions', async () => {
|
||||||
|
const bridgeData = {
|
||||||
|
accountNumbers: [defaultAccountNumber],
|
||||||
|
actions: [],
|
||||||
|
};
|
||||||
|
await executeBridgeTransferFromAndVerifyEvents(
|
||||||
|
accountOwner,
|
||||||
|
receiver,
|
||||||
|
defaultAmount,
|
||||||
|
bridgeData,
|
||||||
|
authorized,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('succeeds when calling `operate` with the `deposit` action and a single account', async () => {
|
||||||
|
const bridgeData = {
|
||||||
|
accountNumbers: [defaultAccountNumber],
|
||||||
|
actions: [defaultDepositAction],
|
||||||
|
};
|
||||||
|
await executeBridgeTransferFromAndVerifyEvents(
|
||||||
|
accountOwner,
|
||||||
|
receiver,
|
||||||
|
defaultAmount,
|
||||||
|
bridgeData,
|
||||||
|
authorized,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('succeeds when calling `operate` with the `deposit` action and multiple accounts', async () => {
|
||||||
|
const bridgeData = {
|
||||||
|
accountNumbers: [defaultAccountNumber, defaultAccountNumber.plus(1)],
|
||||||
|
actions: [defaultDepositAction],
|
||||||
|
};
|
||||||
|
await executeBridgeTransferFromAndVerifyEvents(
|
||||||
|
accountOwner,
|
||||||
|
receiver,
|
||||||
|
defaultAmount,
|
||||||
|
bridgeData,
|
||||||
|
authorized,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('succeeds when calling `operate` with the `withdraw` action and a single account', async () => {
|
||||||
|
const bridgeData = {
|
||||||
|
accountNumbers: [defaultAccountNumber],
|
||||||
|
actions: [defaultWithdrawAction],
|
||||||
|
};
|
||||||
|
await executeBridgeTransferFromAndVerifyEvents(
|
||||||
|
accountOwner,
|
||||||
|
receiver,
|
||||||
|
defaultAmount,
|
||||||
|
bridgeData,
|
||||||
|
authorized,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('succeeds when calling `operate` with the `withdraw` action and multiple accounts', async () => {
|
||||||
|
const bridgeData = {
|
||||||
|
accountNumbers: [defaultAccountNumber, defaultAccountNumber.plus(1)],
|
||||||
|
actions: [defaultWithdrawAction],
|
||||||
|
};
|
||||||
|
await executeBridgeTransferFromAndVerifyEvents(
|
||||||
|
accountOwner,
|
||||||
|
receiver,
|
||||||
|
defaultAmount,
|
||||||
|
bridgeData,
|
||||||
|
authorized,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('succeeds when calling `operate` with the `deposit` action and multiple accounts', async () => {
|
||||||
|
const bridgeData = {
|
||||||
|
accountNumbers: [defaultAccountNumber, defaultAccountNumber.plus(1)],
|
||||||
|
actions: [defaultWithdrawAction, defaultDepositAction],
|
||||||
|
};
|
||||||
|
await executeBridgeTransferFromAndVerifyEvents(
|
||||||
|
accountOwner,
|
||||||
|
receiver,
|
||||||
|
defaultAmount,
|
||||||
|
bridgeData,
|
||||||
|
authorized,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('succeeds when calling `operate` with multiple actions under a single account', async () => {
|
||||||
|
const bridgeData = {
|
||||||
|
accountNumbers: [defaultAccountNumber],
|
||||||
|
actions: [defaultWithdrawAction, defaultDepositAction],
|
||||||
|
};
|
||||||
|
await executeBridgeTransferFromAndVerifyEvents(
|
||||||
|
accountOwner,
|
||||||
|
receiver,
|
||||||
|
defaultAmount,
|
||||||
|
bridgeData,
|
||||||
|
authorized,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('succeeds when scaling the `amount` to deposit', async () => {
|
||||||
|
const conversionRateNumerator = new BigNumber(1);
|
||||||
|
const conversionRateDenominator = new BigNumber(2);
|
||||||
|
const bridgeData = {
|
||||||
|
accountNumbers: [defaultAccountNumber],
|
||||||
|
actions: [
|
||||||
|
defaultWithdrawAction,
|
||||||
|
{
|
||||||
|
...defaultDepositAction,
|
||||||
|
conversionRateNumerator,
|
||||||
|
conversionRateDenominator,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
await executeBridgeTransferFromAndVerifyEvents(
|
||||||
|
accountOwner,
|
||||||
|
receiver,
|
||||||
|
defaultAmount,
|
||||||
|
bridgeData,
|
||||||
|
authorized,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('succeeds when scaling the `amount` to withdraw', async () => {
|
||||||
|
const conversionRateNumerator = new BigNumber(1);
|
||||||
|
const conversionRateDenominator = new BigNumber(2);
|
||||||
|
const bridgeData = {
|
||||||
|
accountNumbers: [defaultAccountNumber],
|
||||||
|
actions: [
|
||||||
|
defaultDepositAction,
|
||||||
|
{
|
||||||
|
...defaultWithdrawAction,
|
||||||
|
conversionRateNumerator,
|
||||||
|
conversionRateDenominator,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
await executeBridgeTransferFromAndVerifyEvents(
|
||||||
|
accountOwner,
|
||||||
|
receiver,
|
||||||
|
defaultAmount,
|
||||||
|
bridgeData,
|
||||||
|
authorized,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('reverts if not called by the ERC20 Bridge Proxy', async () => {
|
||||||
|
const bridgeData = {
|
||||||
|
accountNumbers: [defaultAccountNumber],
|
||||||
|
actions: [defaultDepositAction],
|
||||||
|
};
|
||||||
|
const callBridgeTransferFromPromise = callBridgeTransferFrom(
|
||||||
|
accountOwner,
|
||||||
|
receiver,
|
||||||
|
defaultAmount,
|
||||||
|
bridgeData,
|
||||||
|
notAuthorized,
|
||||||
|
);
|
||||||
|
const expectedError = RevertReason.DydxBridgeOnlyCallableByErc20BridgeProxy;
|
||||||
|
return expect(callBridgeTransferFromPromise).to.revertWith(expectedError);
|
||||||
|
});
|
||||||
|
it('should return magic bytes if call succeeds', async () => {
|
||||||
|
const bridgeData = {
|
||||||
|
accountNumbers: [defaultAccountNumber],
|
||||||
|
actions: [defaultDepositAction],
|
||||||
|
};
|
||||||
|
const returnValue = await callBridgeTransferFrom(
|
||||||
|
accountOwner,
|
||||||
|
receiver,
|
||||||
|
defaultAmount,
|
||||||
|
bridgeData,
|
||||||
|
authorized,
|
||||||
|
);
|
||||||
|
expect(returnValue).to.equal(AssetProxyId.ERC20Bridge);
|
||||||
|
});
|
||||||
|
it('should revert when `Operate` reverts', async () => {
|
||||||
|
// Set revert flag.
|
||||||
|
await testContract.setRevertOnOperate(true).awaitTransactionSuccessAsync();
|
||||||
|
|
||||||
|
// Execute transfer.
|
||||||
|
const bridgeData = {
|
||||||
|
accountNumbers: [defaultAccountNumber],
|
||||||
|
actions: [defaultDepositAction],
|
||||||
|
};
|
||||||
|
const tx = callBridgeTransferFrom(accountOwner, receiver, defaultAmount, bridgeData, authorized);
|
||||||
|
const expectedError = 'TestDydxBridge/SHOULD_REVERT_ON_OPERATE';
|
||||||
|
return expect(tx).to.revertWith(expectedError);
|
||||||
|
});
|
||||||
|
it('should revert when there is a rounding error', async () => {
|
||||||
|
// Setup a rounding error
|
||||||
|
const conversionRateNumerator = new BigNumber(5318);
|
||||||
|
const conversionRateDenominator = new BigNumber(47958);
|
||||||
|
const amount = new BigNumber(9000);
|
||||||
|
const bridgeData = {
|
||||||
|
accountNumbers: [defaultAccountNumber],
|
||||||
|
actions: [
|
||||||
|
defaultDepositAction,
|
||||||
|
{
|
||||||
|
...defaultWithdrawAction,
|
||||||
|
conversionRateNumerator,
|
||||||
|
conversionRateDenominator,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
// Execute transfer and assert error.
|
||||||
|
const tx = callBridgeTransferFrom(accountOwner, receiver, amount, bridgeData, authorized);
|
||||||
|
const expectedError = new LibMathRevertErrors.RoundingError(
|
||||||
|
conversionRateNumerator,
|
||||||
|
conversionRateDenominator,
|
||||||
|
amount,
|
||||||
|
);
|
||||||
|
return expect(tx).to.revertWith(expectedError);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('ERC20BridgeProxy.transferFrom()', () => {
|
||||||
|
const bridgeData = {
|
||||||
|
accountNumbers: [defaultAccountNumber],
|
||||||
|
actions: [defaultWithdrawAction],
|
||||||
|
};
|
||||||
|
let assetData: string;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
const testTokenAddress = await testContract.getTestToken().callAsync();
|
||||||
|
assetData = assetDataEncoder
|
||||||
|
.ERC20Bridge(testTokenAddress, testContract.address, dydxBridgeDataEncoder.encode({ bridgeData }))
|
||||||
|
.getABIEncodedTransactionData();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should succeed if `bridgeTransferFrom` succeeds', async () => {
|
||||||
|
await testProxyContract
|
||||||
|
.transferFrom(assetData, accountOwner, receiver, defaultAmount)
|
||||||
|
.awaitTransactionSuccessAsync({ from: authorized });
|
||||||
|
});
|
||||||
|
it('should revert if `bridgeTransferFrom` reverts', async () => {
|
||||||
|
// Set revert flag.
|
||||||
|
await testContract.setRevertOnOperate(true).awaitTransactionSuccessAsync();
|
||||||
|
const tx = testProxyContract
|
||||||
|
.transferFrom(assetData, accountOwner, receiver, defaultAmount)
|
||||||
|
.awaitTransactionSuccessAsync({ from: authorized });
|
||||||
|
const expectedError = 'TestDydxBridge/SHOULD_REVERT_ON_OPERATE';
|
||||||
|
return expect(tx).to.revertWith(expectedError);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -14,8 +14,8 @@ import {
|
|||||||
txDefaults,
|
txDefaults,
|
||||||
web3Wrapper,
|
web3Wrapper,
|
||||||
} from '@0x/contracts-test-utils';
|
} from '@0x/contracts-test-utils';
|
||||||
|
import { SafeMathRevertErrors } from '@0x/contracts-utils';
|
||||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||||
import { assetDataUtils } from '@0x/order-utils';
|
|
||||||
import { AssetProxyId, 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';
|
||||||
@@ -23,7 +23,10 @@ import { LogWithDecodedArgs } from 'ethereum-types';
|
|||||||
import * as ethUtil from 'ethereumjs-util';
|
import * as ethUtil from 'ethereumjs-util';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { artifacts, ERC1155ProxyContract, ERC1155ProxyWrapper } from '../src';
|
import { ERC1155ProxyWrapper } from '../src/erc1155_proxy_wrapper';
|
||||||
|
import { ERC1155ProxyContract, IAssetDataContract } from '../src/wrappers';
|
||||||
|
|
||||||
|
import { artifacts } from './artifacts';
|
||||||
|
|
||||||
chaiSetup.configure();
|
chaiSetup.configure();
|
||||||
const expect = chai.expect;
|
const expect = chai.expect;
|
||||||
@@ -59,6 +62,8 @@ describe('ERC1155Proxy', () => {
|
|||||||
// tokens
|
// tokens
|
||||||
let fungibleTokens: BigNumber[];
|
let fungibleTokens: BigNumber[];
|
||||||
let nonFungibleTokensOwnedBySpender: BigNumber[];
|
let nonFungibleTokensOwnedBySpender: BigNumber[];
|
||||||
|
// IAssetData for encoding and decoding assetData
|
||||||
|
let assetDataContract: IAssetDataContract;
|
||||||
// tests
|
// tests
|
||||||
before(async () => {
|
before(async () => {
|
||||||
await blockchainLifecycle.startAsync();
|
await blockchainLifecycle.startAsync();
|
||||||
@@ -72,16 +77,8 @@ describe('ERC1155Proxy', () => {
|
|||||||
const usedAddresses = ([owner, notAuthorized, authorized, spender, receiver] = _.slice(accounts, 0, 5));
|
const usedAddresses = ([owner, notAuthorized, authorized, spender, receiver] = _.slice(accounts, 0, 5));
|
||||||
erc1155ProxyWrapper = new ERC1155ProxyWrapper(provider, usedAddresses, owner);
|
erc1155ProxyWrapper = new ERC1155ProxyWrapper(provider, usedAddresses, owner);
|
||||||
erc1155Proxy = await erc1155ProxyWrapper.deployProxyAsync();
|
erc1155Proxy = await erc1155ProxyWrapper.deployProxyAsync();
|
||||||
await erc1155Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
await erc1155Proxy.addAuthorizedAddress(authorized).awaitTransactionSuccessAsync({ from: owner });
|
||||||
authorized,
|
await erc1155Proxy.addAuthorizedAddress(erc1155Proxy.address).awaitTransactionSuccessAsync({ from: owner });
|
||||||
{ from: owner },
|
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
||||||
);
|
|
||||||
await erc1155Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
|
||||||
erc1155Proxy.address,
|
|
||||||
{ from: owner },
|
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
||||||
);
|
|
||||||
// deploy & configure ERC1155 tokens and receiver
|
// deploy & configure ERC1155 tokens and receiver
|
||||||
[erc1155Wrapper] = await erc1155ProxyWrapper.deployDummyContractsAsync();
|
[erc1155Wrapper] = await erc1155ProxyWrapper.deployDummyContractsAsync();
|
||||||
erc1155Contract = erc1155Wrapper.getContract();
|
erc1155Contract = erc1155Wrapper.getContract();
|
||||||
@@ -103,6 +100,8 @@ describe('ERC1155Proxy', () => {
|
|||||||
tokenBalances.nonFungible[spender][erc1155Contract.address][nonFungibleTokenAsString][0];
|
tokenBalances.nonFungible[spender][erc1155Contract.address][nonFungibleTokenAsString][0];
|
||||||
nonFungibleTokensOwnedBySpender.push(nonFungibleTokenHeldBySpender);
|
nonFungibleTokensOwnedBySpender.push(nonFungibleTokenHeldBySpender);
|
||||||
});
|
});
|
||||||
|
// set up assetDataContract
|
||||||
|
assetDataContract = new IAssetDataContract(constants.NULL_ADDRESS, provider, { from: owner });
|
||||||
});
|
});
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await blockchainLifecycle.startAsync();
|
await blockchainLifecycle.startAsync();
|
||||||
@@ -123,7 +122,7 @@ describe('ERC1155Proxy', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('should have an id of 0xa7cb5fb7', async () => {
|
it('should have an id of 0xa7cb5fb7', async () => {
|
||||||
const proxyId = await erc1155Proxy.getProxyId.callAsync();
|
const proxyId = await erc1155Proxy.getProxyId().callAsync();
|
||||||
const expectedProxyId = AssetProxyId.ERC1155;
|
const expectedProxyId = AssetProxyId.ERC1155;
|
||||||
expect(proxyId).to.equal(expectedProxyId);
|
expect(proxyId).to.equal(expectedProxyId);
|
||||||
});
|
});
|
||||||
@@ -638,12 +637,9 @@ describe('ERC1155Proxy', () => {
|
|||||||
return value.times(valueMultiplier);
|
return value.times(valueMultiplier);
|
||||||
});
|
});
|
||||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||||
const assetData = assetDataUtils.encodeERC1155AssetData(
|
const assetData = assetDataContract
|
||||||
erc1155ContractAddress,
|
.ERC1155Assets(erc1155ContractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
|
||||||
tokensToTransfer,
|
.getABIEncodedTransactionData();
|
||||||
valuesToTransfer,
|
|
||||||
receiverCallbackData,
|
|
||||||
);
|
|
||||||
const extraData = '0102030405060708091001020304050607080910010203040506070809100102';
|
const extraData = '0102030405060708091001020304050607080910010203040506070809100102';
|
||||||
const assetDataWithExtraData = `${assetData}${extraData}`;
|
const assetDataWithExtraData = `${assetData}${extraData}`;
|
||||||
// check balances before transfer
|
// check balances before transfer
|
||||||
@@ -696,25 +692,20 @@ describe('ERC1155Proxy', () => {
|
|||||||
const tokenUri = '';
|
const tokenUri = '';
|
||||||
for (const tokenToCreate of tokensToCreate) {
|
for (const tokenToCreate of tokensToCreate) {
|
||||||
// create token
|
// create token
|
||||||
await erc1155Wrapper.getContract().createWithType.awaitTransactionSuccessAsync(
|
await erc1155Wrapper
|
||||||
tokenToCreate,
|
.getContract()
|
||||||
tokenUri,
|
.createWithType(tokenToCreate, tokenUri)
|
||||||
{
|
.awaitTransactionSuccessAsync({
|
||||||
from: owner,
|
from: owner,
|
||||||
},
|
});
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
||||||
);
|
|
||||||
|
|
||||||
// mint balance for spender
|
// mint balance for spender
|
||||||
await erc1155Wrapper.getContract().mintFungible.awaitTransactionSuccessAsync(
|
await erc1155Wrapper
|
||||||
tokenToCreate,
|
.getContract()
|
||||||
[spender],
|
.mintFungible(tokenToCreate, [spender], [spenderInitialBalance])
|
||||||
[spenderInitialBalance],
|
.awaitTransactionSuccessAsync({
|
||||||
{
|
|
||||||
from: owner,
|
from: owner,
|
||||||
},
|
});
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
///// Step 2/5 /////
|
///// Step 2/5 /////
|
||||||
// Check balances before transfer
|
// Check balances before transfer
|
||||||
@@ -747,18 +738,15 @@ describe('ERC1155Proxy', () => {
|
|||||||
const tokensToTransfer = [new BigNumber(1), new BigNumber(2)];
|
const tokensToTransfer = [new BigNumber(1), new BigNumber(2)];
|
||||||
const valuesToTransfer = tokensToTransfer;
|
const valuesToTransfer = tokensToTransfer;
|
||||||
const valueMultiplier = new BigNumber(2);
|
const valueMultiplier = new BigNumber(2);
|
||||||
const assetData = assetDataUtils.encodeERC1155AssetData(
|
|
||||||
erc1155ContractAddress,
|
// hand encode optimized assetData because our tooling (based on LibAssetData.sol/ERC1155Assets) does not use optimized encoding
|
||||||
tokensToTransfer,
|
const selector = assetDataContract.getSelector('ERC1155Assets');
|
||||||
valuesToTransfer,
|
const assetDataWithoutContractAddress =
|
||||||
receiverCallbackData,
|
|
||||||
);
|
|
||||||
// remove the function selector and contract address from check, as these change on each test
|
|
||||||
const offsetToTokenIds = 74;
|
|
||||||
const assetDataWithoutContractAddress = assetData.substr(offsetToTokenIds);
|
|
||||||
const expectedAssetDataWithoutContractAddress =
|
|
||||||
'0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040102030400000000000000000000000000000000000000000000000000000000';
|
'0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040102030400000000000000000000000000000000000000000000000000000000';
|
||||||
expect(assetDataWithoutContractAddress).to.be.equal(expectedAssetDataWithoutContractAddress);
|
const assetData = `${selector}000000000000000000000000${erc1155ContractAddress.substr(
|
||||||
|
2,
|
||||||
|
)}${assetDataWithoutContractAddress}`;
|
||||||
|
|
||||||
///// Step 4/5 /////
|
///// Step 4/5 /////
|
||||||
// Transfer token IDs [1, 2] and amounts [1, 2] with a multiplier of 2;
|
// Transfer token IDs [1, 2] and amounts [1, 2] with a multiplier of 2;
|
||||||
// the expected trade will be token IDs [1, 2] and amounts [2, 4]
|
// the expected trade will be token IDs [1, 2] and amounts [2, 4]
|
||||||
@@ -805,25 +793,20 @@ describe('ERC1155Proxy', () => {
|
|||||||
const tokenUri = '';
|
const tokenUri = '';
|
||||||
for (const tokenToCreate of tokensToCreate) {
|
for (const tokenToCreate of tokensToCreate) {
|
||||||
// create token
|
// create token
|
||||||
await erc1155Wrapper.getContract().createWithType.awaitTransactionSuccessAsync(
|
await erc1155Wrapper
|
||||||
tokenToCreate,
|
.getContract()
|
||||||
tokenUri,
|
.createWithType(tokenToCreate, tokenUri)
|
||||||
{
|
.awaitTransactionSuccessAsync({
|
||||||
from: owner,
|
from: owner,
|
||||||
},
|
});
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
||||||
);
|
|
||||||
|
|
||||||
// mint balance for spender
|
// mint balance for spender
|
||||||
await erc1155Wrapper.getContract().mintFungible.awaitTransactionSuccessAsync(
|
await erc1155Wrapper
|
||||||
tokenToCreate,
|
.getContract()
|
||||||
[spender],
|
.mintFungible(tokenToCreate, [spender], [spenderInitialBalance])
|
||||||
[spenderInitialBalance],
|
.awaitTransactionSuccessAsync({
|
||||||
{
|
|
||||||
from: owner,
|
from: owner,
|
||||||
},
|
});
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
///// Step 2/5 /////
|
///// Step 2/5 /////
|
||||||
// Check balances before transfer
|
// Check balances before transfer
|
||||||
@@ -867,12 +850,9 @@ describe('ERC1155Proxy', () => {
|
|||||||
const valuesToTransfer = [new BigNumber(2), new BigNumber(2)];
|
const valuesToTransfer = [new BigNumber(2), new BigNumber(2)];
|
||||||
const valueMultiplier = new BigNumber(2);
|
const valueMultiplier = new BigNumber(2);
|
||||||
// create callback data that is the encoded version of `valuesToTransfer`
|
// create callback data that is the encoded version of `valuesToTransfer`
|
||||||
const generatedAssetData = assetDataUtils.encodeERC1155AssetData(
|
const generatedAssetData = assetDataContract
|
||||||
erc1155ContractAddress,
|
.ERC1155Assets(erc1155ContractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
|
||||||
tokensToTransfer,
|
.getABIEncodedTransactionData();
|
||||||
valuesToTransfer,
|
|
||||||
receiverCallbackData,
|
|
||||||
);
|
|
||||||
// remove the function selector and contract address from check, as these change on each test
|
// remove the function selector and contract address from check, as these change on each test
|
||||||
const offsetToTokenIds = 74;
|
const offsetToTokenIds = 74;
|
||||||
const assetDataSelectorAndContractAddress = generatedAssetData.substr(0, offsetToTokenIds);
|
const assetDataSelectorAndContractAddress = generatedAssetData.substr(0, offsetToTokenIds);
|
||||||
@@ -937,25 +917,20 @@ describe('ERC1155Proxy', () => {
|
|||||||
const tokenUri = '';
|
const tokenUri = '';
|
||||||
for (const tokenToCreate of tokensToCreate) {
|
for (const tokenToCreate of tokensToCreate) {
|
||||||
// create token
|
// create token
|
||||||
await erc1155Wrapper.getContract().createWithType.awaitTransactionSuccessAsync(
|
await erc1155Wrapper
|
||||||
tokenToCreate,
|
.getContract()
|
||||||
tokenUri,
|
.createWithType(tokenToCreate, tokenUri)
|
||||||
{
|
.awaitTransactionSuccessAsync({
|
||||||
from: owner,
|
from: owner,
|
||||||
},
|
});
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
||||||
);
|
|
||||||
|
|
||||||
// mint balance for spender
|
// mint balance for spender
|
||||||
await erc1155Wrapper.getContract().mintFungible.awaitTransactionSuccessAsync(
|
await erc1155Wrapper
|
||||||
tokenToCreate,
|
.getContract()
|
||||||
[spender],
|
.mintFungible(tokenToCreate, [spender], [spenderInitialBalance])
|
||||||
[spenderInitialBalance],
|
.awaitTransactionSuccessAsync({
|
||||||
{
|
|
||||||
from: owner,
|
from: owner,
|
||||||
},
|
});
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
///// Step 2/5 /////
|
///// Step 2/5 /////
|
||||||
// Check balances before transfer
|
// Check balances before transfer
|
||||||
@@ -996,12 +971,9 @@ describe('ERC1155Proxy', () => {
|
|||||||
const valuesToTransfer = [new BigNumber(1), new BigNumber(2)];
|
const valuesToTransfer = [new BigNumber(1), new BigNumber(2)];
|
||||||
const valueMultiplier = new BigNumber(2);
|
const valueMultiplier = new BigNumber(2);
|
||||||
// create callback data that is the encoded version of `valuesToTransfer`
|
// create callback data that is the encoded version of `valuesToTransfer`
|
||||||
const generatedAssetData = assetDataUtils.encodeERC1155AssetData(
|
const generatedAssetData = assetDataContract
|
||||||
erc1155ContractAddress,
|
.ERC1155Assets(erc1155ContractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
|
||||||
tokensToTransfer,
|
.getABIEncodedTransactionData();
|
||||||
valuesToTransfer,
|
|
||||||
receiverCallbackData,
|
|
||||||
);
|
|
||||||
// remove the function selector and contract address from check, as these change on each test
|
// remove the function selector and contract address from check, as these change on each test
|
||||||
const offsetToTokenIds = 74;
|
const offsetToTokenIds = 74;
|
||||||
const assetDataSelectorAndContractAddress = generatedAssetData.substr(0, offsetToTokenIds);
|
const assetDataSelectorAndContractAddress = generatedAssetData.substr(0, offsetToTokenIds);
|
||||||
@@ -1059,12 +1031,9 @@ describe('ERC1155Proxy', () => {
|
|||||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||||
const valueMultiplier = valueMultiplierSmall;
|
const valueMultiplier = valueMultiplierSmall;
|
||||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||||
const assetData = assetDataUtils.encodeERC1155AssetData(
|
const assetData = assetDataContract
|
||||||
erc1155ContractAddress,
|
.ERC1155Assets(erc1155ContractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
|
||||||
tokensToTransfer,
|
.getABIEncodedTransactionData();
|
||||||
valuesToTransfer,
|
|
||||||
receiverCallbackData,
|
|
||||||
);
|
|
||||||
// The asset data we just generated will look like this:
|
// The asset data we just generated will look like this:
|
||||||
// a7cb5fb7
|
// a7cb5fb7
|
||||||
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
|
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
|
||||||
@@ -1106,12 +1075,9 @@ describe('ERC1155Proxy', () => {
|
|||||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||||
const valueMultiplier = valueMultiplierSmall;
|
const valueMultiplier = valueMultiplierSmall;
|
||||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||||
const assetData = assetDataUtils.encodeERC1155AssetData(
|
const assetData = assetDataContract
|
||||||
erc1155ContractAddress,
|
.ERC1155Assets(erc1155ContractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
|
||||||
tokensToTransfer,
|
.getABIEncodedTransactionData();
|
||||||
valuesToTransfer,
|
|
||||||
receiverCallbackData,
|
|
||||||
);
|
|
||||||
// The asset data we just generated will look like this:
|
// The asset data we just generated will look like this:
|
||||||
// a7cb5fb7
|
// a7cb5fb7
|
||||||
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
|
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
|
||||||
@@ -1157,12 +1123,9 @@ describe('ERC1155Proxy', () => {
|
|||||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||||
const valueMultiplier = valueMultiplierSmall;
|
const valueMultiplier = valueMultiplierSmall;
|
||||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||||
const assetData = assetDataUtils.encodeERC1155AssetData(
|
const assetData = assetDataContract
|
||||||
erc1155ContractAddress,
|
.ERC1155Assets(erc1155ContractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
|
||||||
tokensToTransfer,
|
.getABIEncodedTransactionData();
|
||||||
valuesToTransfer,
|
|
||||||
receiverCallbackData,
|
|
||||||
);
|
|
||||||
// The asset data we just generated will look like this:
|
// The asset data we just generated will look like this:
|
||||||
// a7cb5fb7
|
// a7cb5fb7
|
||||||
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
|
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
|
||||||
@@ -1208,12 +1171,9 @@ describe('ERC1155Proxy', () => {
|
|||||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||||
const valueMultiplier = valueMultiplierSmall;
|
const valueMultiplier = valueMultiplierSmall;
|
||||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||||
const assetData = assetDataUtils.encodeERC1155AssetData(
|
const assetData = assetDataContract
|
||||||
erc1155ContractAddress,
|
.ERC1155Assets(erc1155ContractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
|
||||||
tokensToTransfer,
|
.getABIEncodedTransactionData();
|
||||||
valuesToTransfer,
|
|
||||||
receiverCallbackData,
|
|
||||||
);
|
|
||||||
// The asset data we just generated will look like this:
|
// The asset data we just generated will look like this:
|
||||||
// a7cb5fb7
|
// a7cb5fb7
|
||||||
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
|
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
|
||||||
@@ -1259,12 +1219,9 @@ describe('ERC1155Proxy', () => {
|
|||||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||||
const valueMultiplier = valueMultiplierSmall;
|
const valueMultiplier = valueMultiplierSmall;
|
||||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||||
const assetData = assetDataUtils.encodeERC1155AssetData(
|
const assetData = assetDataContract
|
||||||
erc1155ContractAddress,
|
.ERC1155Assets(erc1155ContractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
|
||||||
tokensToTransfer,
|
.getABIEncodedTransactionData();
|
||||||
valuesToTransfer,
|
|
||||||
receiverCallbackData,
|
|
||||||
);
|
|
||||||
// The asset data we just generated will look like this:
|
// The asset data we just generated will look like this:
|
||||||
// a7cb5fb7
|
// a7cb5fb7
|
||||||
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
|
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
|
||||||
@@ -1311,12 +1268,9 @@ describe('ERC1155Proxy', () => {
|
|||||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||||
const valueMultiplier = valueMultiplierSmall;
|
const valueMultiplier = valueMultiplierSmall;
|
||||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||||
const assetData = assetDataUtils.encodeERC1155AssetData(
|
const assetData = assetDataContract
|
||||||
erc1155ContractAddress,
|
.ERC1155Assets(erc1155ContractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
|
||||||
tokensToTransfer,
|
.getABIEncodedTransactionData();
|
||||||
valuesToTransfer,
|
|
||||||
receiverCallbackData,
|
|
||||||
);
|
|
||||||
// The asset data we just generated will look like this:
|
// The asset data we just generated will look like this:
|
||||||
// a7cb5fb7
|
// a7cb5fb7
|
||||||
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
|
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
|
||||||
@@ -1358,12 +1312,9 @@ describe('ERC1155Proxy', () => {
|
|||||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||||
const valueMultiplier = valueMultiplierSmall;
|
const valueMultiplier = valueMultiplierSmall;
|
||||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||||
const assetData = assetDataUtils.encodeERC1155AssetData(
|
const assetData = assetDataContract
|
||||||
erc1155ContractAddress,
|
.ERC1155Assets(erc1155ContractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
|
||||||
tokensToTransfer,
|
.getABIEncodedTransactionData();
|
||||||
valuesToTransfer,
|
|
||||||
receiverCallbackData,
|
|
||||||
);
|
|
||||||
// The asset data we just generated will look like this:
|
// The asset data we just generated will look like this:
|
||||||
// a7cb5fb7
|
// a7cb5fb7
|
||||||
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
|
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
|
||||||
@@ -1409,12 +1360,9 @@ describe('ERC1155Proxy', () => {
|
|||||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||||
const valueMultiplier = valueMultiplierSmall;
|
const valueMultiplier = valueMultiplierSmall;
|
||||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||||
const assetData = assetDataUtils.encodeERC1155AssetData(
|
const assetData = assetDataContract
|
||||||
erc1155ContractAddress,
|
.ERC1155Assets(erc1155ContractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
|
||||||
tokensToTransfer,
|
.getABIEncodedTransactionData();
|
||||||
valuesToTransfer,
|
|
||||||
receiverCallbackData,
|
|
||||||
);
|
|
||||||
// The asset data we just generated will look like this:
|
// The asset data we just generated will look like this:
|
||||||
// a7cb5fb7
|
// a7cb5fb7
|
||||||
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
|
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
|
||||||
@@ -1456,12 +1404,9 @@ describe('ERC1155Proxy', () => {
|
|||||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||||
const valueMultiplier = valueMultiplierSmall;
|
const valueMultiplier = valueMultiplierSmall;
|
||||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||||
const assetData = assetDataUtils.encodeERC1155AssetData(
|
const assetData = assetDataContract
|
||||||
erc1155ContractAddress,
|
.ERC1155Assets(erc1155ContractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
|
||||||
tokensToTransfer,
|
.getABIEncodedTransactionData();
|
||||||
valuesToTransfer,
|
|
||||||
receiverCallbackData,
|
|
||||||
);
|
|
||||||
// The asset data we just generated will look like this:
|
// The asset data we just generated will look like this:
|
||||||
// a7cb5fb7
|
// a7cb5fb7
|
||||||
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
|
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
|
||||||
@@ -1507,13 +1452,10 @@ describe('ERC1155Proxy', () => {
|
|||||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||||
const valueMultiplier = valueMultiplierSmall;
|
const valueMultiplier = valueMultiplierSmall;
|
||||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||||
const assetData = assetDataUtils.encodeERC1155AssetData(
|
const assetData = assetDataContract
|
||||||
erc1155ContractAddress,
|
.ERC1155Assets(erc1155ContractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
|
||||||
tokensToTransfer,
|
.getABIEncodedTransactionData();
|
||||||
valuesToTransfer,
|
const txData = await erc1155ProxyWrapper.getTransferFromAbiEncodedTxDataAsync(
|
||||||
receiverCallbackData,
|
|
||||||
);
|
|
||||||
const txData = erc1155ProxyWrapper.getTransferFromAbiEncodedTxData(
|
|
||||||
spender,
|
spender,
|
||||||
receiverContract,
|
receiverContract,
|
||||||
erc1155Contract.address,
|
erc1155Contract.address,
|
||||||
@@ -1538,13 +1480,10 @@ describe('ERC1155Proxy', () => {
|
|||||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||||
const valueMultiplier = valueMultiplierSmall;
|
const valueMultiplier = valueMultiplierSmall;
|
||||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||||
const assetData = assetDataUtils.encodeERC1155AssetData(
|
const assetData = assetDataContract
|
||||||
erc1155ContractAddress,
|
.ERC1155Assets(erc1155ContractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
|
||||||
tokensToTransfer,
|
.getABIEncodedTransactionData();
|
||||||
valuesToTransfer,
|
const txData = await erc1155ProxyWrapper.getTransferFromAbiEncodedTxDataAsync(
|
||||||
receiverCallbackData,
|
|
||||||
);
|
|
||||||
const txData = erc1155ProxyWrapper.getTransferFromAbiEncodedTxData(
|
|
||||||
spender,
|
spender,
|
||||||
receiverContract,
|
receiverContract,
|
||||||
erc1155Contract.address,
|
erc1155Contract.address,
|
||||||
@@ -1667,13 +1606,9 @@ describe('ERC1155Proxy', () => {
|
|||||||
it('should propagate revert reason from erc1155 contract failure', async () => {
|
it('should propagate revert reason from erc1155 contract failure', async () => {
|
||||||
// disable transfers
|
// disable transfers
|
||||||
const shouldRejectTransfer = true;
|
const shouldRejectTransfer = true;
|
||||||
await erc1155Receiver.setRejectTransferFlag.awaitTransactionSuccessAsync(
|
await erc1155Receiver.setRejectTransferFlag(shouldRejectTransfer).awaitTransactionSuccessAsync({
|
||||||
shouldRejectTransfer,
|
from: owner,
|
||||||
{
|
});
|
||||||
from: owner,
|
|
||||||
},
|
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
||||||
);
|
|
||||||
// setup test parameters
|
// setup test parameters
|
||||||
const tokenHolders = [spender, receiverContract];
|
const tokenHolders = [spender, receiverContract];
|
||||||
const tokensToTransfer = fungibleTokens.slice(0, 1);
|
const tokensToTransfer = fungibleTokens.slice(0, 1);
|
||||||
@@ -1748,9 +1683,14 @@ describe('ERC1155Proxy', () => {
|
|||||||
nftNotOwnerBalance,
|
nftNotOwnerBalance,
|
||||||
];
|
];
|
||||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||||
|
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
|
||||||
|
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
|
||||||
|
maxUintValue,
|
||||||
|
valueMultiplier,
|
||||||
|
);
|
||||||
// execute transfer
|
// execute transfer
|
||||||
// note - this will overflow because we are trying to transfer `maxUintValue * 2` of the 2nd token
|
// note - this will overflow because we are trying to transfer `maxUintValue * 2` of the 2nd token
|
||||||
await expectTransactionFailedAsync(
|
await expect(
|
||||||
erc1155ProxyWrapper.transferFromAsync(
|
erc1155ProxyWrapper.transferFromAsync(
|
||||||
spender,
|
spender,
|
||||||
receiver,
|
receiver,
|
||||||
@@ -1761,8 +1701,7 @@ describe('ERC1155Proxy', () => {
|
|||||||
receiverCallbackData,
|
receiverCallbackData,
|
||||||
authorized,
|
authorized,
|
||||||
),
|
),
|
||||||
RevertReason.Uint256Overflow,
|
).to.revertWith(expectedError);
|
||||||
);
|
|
||||||
});
|
});
|
||||||
it('should revert if transferring > 1 instances of a non-fungible token (valueMultiplier field >1)', async () => {
|
it('should revert if transferring > 1 instances of a non-fungible token (valueMultiplier field >1)', async () => {
|
||||||
// setup test parameters
|
// setup test parameters
|
||||||
@@ -1832,20 +1771,23 @@ describe('ERC1155Proxy', () => {
|
|||||||
// check balances before transfer
|
// check balances before transfer
|
||||||
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
|
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
|
||||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||||
// execute transfer
|
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
|
||||||
await expectTransactionFailedAsync(
|
SafeMathRevertErrors.BinOpErrorCodes.SubtractionUnderflow,
|
||||||
erc1155ProxyWrapper.transferFromAsync(
|
spenderInitialFungibleBalance,
|
||||||
spender,
|
valuesToTransfer[0].times(valueMultiplier),
|
||||||
receiver,
|
|
||||||
erc1155Contract.address,
|
|
||||||
tokensToTransfer,
|
|
||||||
valuesToTransfer,
|
|
||||||
valueMultiplier,
|
|
||||||
receiverCallbackData,
|
|
||||||
authorized,
|
|
||||||
),
|
|
||||||
RevertReason.Uint256Underflow,
|
|
||||||
);
|
);
|
||||||
|
// execute transfer
|
||||||
|
const tx = erc1155ProxyWrapper.transferFromAsync(
|
||||||
|
spender,
|
||||||
|
receiver,
|
||||||
|
erc1155Contract.address,
|
||||||
|
tokensToTransfer,
|
||||||
|
valuesToTransfer,
|
||||||
|
valueMultiplier,
|
||||||
|
receiverCallbackData,
|
||||||
|
authorized,
|
||||||
|
);
|
||||||
|
return expect(tx).to.revertWith(expectedError);
|
||||||
});
|
});
|
||||||
it('should revert if sender allowance is insufficient', async () => {
|
it('should revert if sender allowance is insufficient', async () => {
|
||||||
// dremove allowance for ERC1155 proxy
|
// dremove allowance for ERC1155 proxy
|
||||||
|
|||||||
287
contracts/asset-proxy/test/erc20bridge_proxy.ts
Normal file
287
contracts/asset-proxy/test/erc20bridge_proxy.ts
Normal file
@@ -0,0 +1,287 @@
|
|||||||
|
import {
|
||||||
|
blockchainTests,
|
||||||
|
constants,
|
||||||
|
expect,
|
||||||
|
getRandomInteger,
|
||||||
|
Numberish,
|
||||||
|
randomAddress,
|
||||||
|
} from '@0x/contracts-test-utils';
|
||||||
|
import { AuthorizableRevertErrors } from '@0x/contracts-utils';
|
||||||
|
import { AssetProxyId } from '@0x/types';
|
||||||
|
import { AbiEncoder, BigNumber, hexUtils, StringRevertError } from '@0x/utils';
|
||||||
|
import { DecodedLogs } from 'ethereum-types';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
|
import { artifacts } from './artifacts';
|
||||||
|
|
||||||
|
import { ERC20BridgeProxyContract, TestERC20BridgeContract } from './wrappers';
|
||||||
|
|
||||||
|
blockchainTests.resets('ERC20BridgeProxy unit tests', env => {
|
||||||
|
const PROXY_ID = AssetProxyId.ERC20Bridge;
|
||||||
|
const BRIDGE_SUCCESS_RETURN_DATA = hexUtils.rightPad(PROXY_ID);
|
||||||
|
let owner: string;
|
||||||
|
let badCaller: string;
|
||||||
|
let assetProxy: ERC20BridgeProxyContract;
|
||||||
|
let bridgeContract: TestERC20BridgeContract;
|
||||||
|
let testTokenAddress: string;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
[owner, badCaller] = await env.getAccountAddressesAsync();
|
||||||
|
assetProxy = await ERC20BridgeProxyContract.deployFrom0xArtifactAsync(
|
||||||
|
artifacts.ERC20BridgeProxy,
|
||||||
|
env.provider,
|
||||||
|
env.txDefaults,
|
||||||
|
artifacts,
|
||||||
|
);
|
||||||
|
bridgeContract = await TestERC20BridgeContract.deployFrom0xArtifactAsync(
|
||||||
|
artifacts.TestERC20Bridge,
|
||||||
|
env.provider,
|
||||||
|
env.txDefaults,
|
||||||
|
artifacts,
|
||||||
|
);
|
||||||
|
testTokenAddress = await bridgeContract.testToken().callAsync();
|
||||||
|
await assetProxy.addAuthorizedAddress(owner).awaitTransactionSuccessAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
interface AssetDataOpts {
|
||||||
|
tokenAddress: string;
|
||||||
|
bridgeAddress: string;
|
||||||
|
bridgeData: BridgeDataOpts;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BridgeDataOpts {
|
||||||
|
transferAmount: Numberish;
|
||||||
|
revertError?: string;
|
||||||
|
returnData: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createAssetData(opts?: Partial<AssetDataOpts>): AssetDataOpts {
|
||||||
|
return _.merge(
|
||||||
|
{
|
||||||
|
tokenAddress: testTokenAddress,
|
||||||
|
bridgeAddress: bridgeContract.address,
|
||||||
|
bridgeData: createBridgeData(),
|
||||||
|
},
|
||||||
|
opts,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createBridgeData(opts?: Partial<BridgeDataOpts>): BridgeDataOpts {
|
||||||
|
return _.merge(
|
||||||
|
{
|
||||||
|
transferAmount: constants.ZERO_AMOUNT,
|
||||||
|
returnData: BRIDGE_SUCCESS_RETURN_DATA,
|
||||||
|
},
|
||||||
|
opts,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function encodeAssetData(opts: AssetDataOpts): string {
|
||||||
|
const encoder = AbiEncoder.createMethod('ERC20BridgeProxy', [
|
||||||
|
{ name: 'tokenAddress', type: 'address' },
|
||||||
|
{ name: 'bridgeAddress', type: 'address' },
|
||||||
|
{ name: 'bridgeData', type: 'bytes' },
|
||||||
|
]);
|
||||||
|
return encoder.encode([opts.tokenAddress, opts.bridgeAddress, encodeBridgeData(opts.bridgeData)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function encodeBridgeData(opts: BridgeDataOpts): string {
|
||||||
|
const encoder = AbiEncoder.create([
|
||||||
|
{ name: 'transferAmount', type: 'int256' },
|
||||||
|
{ name: 'revertData', type: 'bytes' },
|
||||||
|
{ name: 'returnData', type: 'bytes' },
|
||||||
|
]);
|
||||||
|
const revertErrorBytes =
|
||||||
|
opts.revertError !== undefined ? new StringRevertError(opts.revertError).encode() : '0x';
|
||||||
|
return encoder.encode([new BigNumber(opts.transferAmount), revertErrorBytes, opts.returnData]);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setTestTokenBalanceAsync(_owner: string, balance: Numberish): Promise<void> {
|
||||||
|
await bridgeContract.setTestTokenBalance(_owner, new BigNumber(balance)).awaitTransactionSuccessAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('transferFrom()', () => {
|
||||||
|
interface TransferFromOpts {
|
||||||
|
assetData: AssetDataOpts;
|
||||||
|
from: string;
|
||||||
|
to: string;
|
||||||
|
amount: Numberish;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createTransferFromOpts(opts?: Partial<TransferFromOpts>): TransferFromOpts {
|
||||||
|
const transferAmount = _.get(opts, ['amount'], getRandomInteger(1, 100e18)) as BigNumber;
|
||||||
|
return _.merge(
|
||||||
|
{
|
||||||
|
assetData: createAssetData({
|
||||||
|
bridgeData: createBridgeData({
|
||||||
|
transferAmount,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
from: randomAddress(),
|
||||||
|
to: randomAddress(),
|
||||||
|
amount: transferAmount,
|
||||||
|
},
|
||||||
|
opts,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function transferFromAsync(opts?: Partial<TransferFromOpts>, caller?: string): Promise<DecodedLogs> {
|
||||||
|
const _opts = createTransferFromOpts(opts);
|
||||||
|
const { logs } = await assetProxy
|
||||||
|
.transferFrom(encodeAssetData(_opts.assetData), _opts.from, _opts.to, new BigNumber(_opts.amount))
|
||||||
|
.awaitTransactionSuccessAsync({ from: caller });
|
||||||
|
return (logs as any) as DecodedLogs;
|
||||||
|
}
|
||||||
|
|
||||||
|
it('succeeds if the bridge succeeds and balance increases by `amount`', async () => {
|
||||||
|
const tx = transferFromAsync();
|
||||||
|
return expect(tx).to.be.fulfilled('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('succeeds if balance increases more than `amount`', async () => {
|
||||||
|
const amount = getRandomInteger(1, 100e18);
|
||||||
|
const tx = transferFromAsync({
|
||||||
|
amount,
|
||||||
|
assetData: createAssetData({
|
||||||
|
bridgeData: createBridgeData({
|
||||||
|
transferAmount: amount.plus(1),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
return expect(tx).to.be.fulfilled('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('passes the correct arguments to the bridge contract', async () => {
|
||||||
|
const opts = createTransferFromOpts();
|
||||||
|
const logs = await transferFromAsync(opts);
|
||||||
|
expect(logs.length).to.eq(1);
|
||||||
|
const args = logs[0].args;
|
||||||
|
expect(args.tokenAddress).to.eq(opts.assetData.tokenAddress);
|
||||||
|
expect(args.from).to.eq(opts.from);
|
||||||
|
expect(args.to).to.eq(opts.to);
|
||||||
|
expect(args.amount).to.bignumber.eq(opts.amount);
|
||||||
|
expect(args.bridgeData).to.eq(encodeBridgeData(opts.assetData.bridgeData));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails if not called by an authorized address', async () => {
|
||||||
|
const tx = transferFromAsync({}, badCaller);
|
||||||
|
return expect(tx).to.revertWith(new AuthorizableRevertErrors.SenderNotAuthorizedError(badCaller));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails if asset data is truncated', async () => {
|
||||||
|
const opts = createTransferFromOpts();
|
||||||
|
const truncatedAssetData = hexUtils.slice(encodeAssetData(opts.assetData), 0, -1);
|
||||||
|
const tx = assetProxy
|
||||||
|
.transferFrom(truncatedAssetData, opts.from, opts.to, new BigNumber(opts.amount))
|
||||||
|
.awaitTransactionSuccessAsync();
|
||||||
|
return expect(tx).to.be.rejected();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails if bridge returns nothing', async () => {
|
||||||
|
const tx = transferFromAsync({
|
||||||
|
assetData: createAssetData({
|
||||||
|
bridgeData: createBridgeData({
|
||||||
|
returnData: '0x',
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
// This will actually revert when the AP tries to decode the return
|
||||||
|
// value.
|
||||||
|
return expect(tx).to.be.rejected();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails if bridge returns true', async () => {
|
||||||
|
const tx = transferFromAsync({
|
||||||
|
assetData: createAssetData({
|
||||||
|
bridgeData: createBridgeData({
|
||||||
|
returnData: hexUtils.leftPad('0x1'),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
// This will actually revert when the AP tries to decode the return
|
||||||
|
// value.
|
||||||
|
return expect(tx).to.be.rejected();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails if bridge returns 0x1', async () => {
|
||||||
|
const tx = transferFromAsync({
|
||||||
|
assetData: createAssetData({
|
||||||
|
bridgeData: createBridgeData({
|
||||||
|
returnData: hexUtils.rightPad('0x1'),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
return expect(tx).to.revertWith('BRIDGE_FAILED');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails if bridge is an EOA', async () => {
|
||||||
|
const tx = transferFromAsync({
|
||||||
|
assetData: createAssetData({
|
||||||
|
bridgeAddress: randomAddress(),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
// This will actually revert when the AP tries to decode the return
|
||||||
|
// value.
|
||||||
|
return expect(tx).to.be.rejected();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails if bridge reverts', async () => {
|
||||||
|
const revertError = 'FOOBAR';
|
||||||
|
const tx = transferFromAsync({
|
||||||
|
assetData: createAssetData({
|
||||||
|
bridgeData: createBridgeData({
|
||||||
|
revertError,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
return expect(tx).to.revertWith(revertError);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails if balance of `to` increases by less than `amount`', async () => {
|
||||||
|
const amount = getRandomInteger(1, 100e18);
|
||||||
|
const tx = transferFromAsync({
|
||||||
|
amount,
|
||||||
|
assetData: createAssetData({
|
||||||
|
bridgeData: createBridgeData({
|
||||||
|
transferAmount: amount.minus(1),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
return expect(tx).to.revertWith('BRIDGE_UNDERPAY');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails if balance of `to` decreases', async () => {
|
||||||
|
const toAddress = randomAddress();
|
||||||
|
await setTestTokenBalanceAsync(toAddress, 1e18);
|
||||||
|
const tx = transferFromAsync({
|
||||||
|
to: toAddress,
|
||||||
|
assetData: createAssetData({
|
||||||
|
bridgeData: createBridgeData({
|
||||||
|
transferAmount: -1,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
return expect(tx).to.revertWith('BRIDGE_UNDERPAY');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('balanceOf()', () => {
|
||||||
|
it('retrieves the balance of the encoded token', async () => {
|
||||||
|
const _owner = randomAddress();
|
||||||
|
const balance = getRandomInteger(1, 100e18);
|
||||||
|
await bridgeContract.setTestTokenBalance(_owner, balance).awaitTransactionSuccessAsync();
|
||||||
|
const assetData = createAssetData({
|
||||||
|
tokenAddress: testTokenAddress,
|
||||||
|
});
|
||||||
|
const actualBalance = await assetProxy.balanceOf(encodeAssetData(assetData), _owner).callAsync();
|
||||||
|
expect(actualBalance).to.bignumber.eq(balance);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getProxyId()', () => {
|
||||||
|
it('returns the correct proxy ID', async () => {
|
||||||
|
const proxyId = await assetProxy.getProxyId().callAsync();
|
||||||
|
expect(proxyId).to.eq(PROXY_ID);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
191
contracts/asset-proxy/test/eth2dai_bridge.ts
Normal file
191
contracts/asset-proxy/test/eth2dai_bridge.ts
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
import {
|
||||||
|
blockchainTests,
|
||||||
|
constants,
|
||||||
|
expect,
|
||||||
|
filterLogsToArguments,
|
||||||
|
getRandomInteger,
|
||||||
|
Numberish,
|
||||||
|
randomAddress,
|
||||||
|
} from '@0x/contracts-test-utils';
|
||||||
|
import { AssetProxyId } from '@0x/types';
|
||||||
|
import { BigNumber, hexUtils, RawRevertError } from '@0x/utils';
|
||||||
|
import { DecodedLogs } from 'ethereum-types';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
|
import { artifacts } from './artifacts';
|
||||||
|
|
||||||
|
import {
|
||||||
|
TestEth2DaiBridgeContract,
|
||||||
|
TestEth2DaiBridgeEvents,
|
||||||
|
TestEth2DaiBridgeSellAllAmountEventArgs,
|
||||||
|
TestEth2DaiBridgeTokenApproveEventArgs,
|
||||||
|
TestEth2DaiBridgeTokenTransferEventArgs,
|
||||||
|
} from './wrappers';
|
||||||
|
|
||||||
|
blockchainTests.resets('Eth2DaiBridge unit tests', env => {
|
||||||
|
let testContract: TestEth2DaiBridgeContract;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
testContract = await TestEth2DaiBridgeContract.deployFrom0xArtifactAsync(
|
||||||
|
artifacts.TestEth2DaiBridge,
|
||||||
|
env.provider,
|
||||||
|
env.txDefaults,
|
||||||
|
artifacts,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('isValidSignature()', () => {
|
||||||
|
it('returns success bytes', async () => {
|
||||||
|
const LEGACY_WALLET_MAGIC_VALUE = '0xb0671381';
|
||||||
|
const result = await testContract
|
||||||
|
.isValidSignature(hexUtils.random(), hexUtils.random(_.random(0, 32)))
|
||||||
|
.callAsync();
|
||||||
|
expect(result).to.eq(LEGACY_WALLET_MAGIC_VALUE);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('bridgeTransferFrom()', () => {
|
||||||
|
interface WithdrawToOpts {
|
||||||
|
toTokenAddress?: string;
|
||||||
|
fromTokenAddress?: string;
|
||||||
|
toAddress: string;
|
||||||
|
amount: Numberish;
|
||||||
|
fromTokenBalance: Numberish;
|
||||||
|
revertReason: string;
|
||||||
|
fillAmount: Numberish;
|
||||||
|
toTokentransferRevertReason: string;
|
||||||
|
toTokenTransferReturnData: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface WithdrawToResult {
|
||||||
|
opts: WithdrawToOpts;
|
||||||
|
result: string;
|
||||||
|
logs: DecodedLogs;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createWithdrawToOpts(opts?: Partial<WithdrawToOpts>): WithdrawToOpts {
|
||||||
|
return {
|
||||||
|
toAddress: randomAddress(),
|
||||||
|
amount: getRandomInteger(1, 100e18),
|
||||||
|
revertReason: '',
|
||||||
|
fillAmount: getRandomInteger(1, 100e18),
|
||||||
|
fromTokenBalance: getRandomInteger(1, 100e18),
|
||||||
|
toTokentransferRevertReason: '',
|
||||||
|
toTokenTransferReturnData: hexUtils.leftPad(1),
|
||||||
|
...opts,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function withdrawToAsync(opts?: Partial<WithdrawToOpts>): Promise<WithdrawToResult> {
|
||||||
|
const _opts = createWithdrawToOpts(opts);
|
||||||
|
// Set the fill behavior.
|
||||||
|
await testContract
|
||||||
|
.setFillBehavior(_opts.revertReason, new BigNumber(_opts.fillAmount))
|
||||||
|
.awaitTransactionSuccessAsync();
|
||||||
|
// Create tokens and balances.
|
||||||
|
if (_opts.fromTokenAddress === undefined) {
|
||||||
|
const createTokenFn = testContract.createToken(new BigNumber(_opts.fromTokenBalance));
|
||||||
|
_opts.fromTokenAddress = await createTokenFn.callAsync();
|
||||||
|
await createTokenFn.awaitTransactionSuccessAsync();
|
||||||
|
}
|
||||||
|
if (_opts.toTokenAddress === undefined) {
|
||||||
|
const createTokenFn = testContract.createToken(constants.ZERO_AMOUNT);
|
||||||
|
_opts.toTokenAddress = await createTokenFn.callAsync();
|
||||||
|
await createTokenFn.awaitTransactionSuccessAsync();
|
||||||
|
}
|
||||||
|
// Set the transfer behavior of `toTokenAddress`.
|
||||||
|
await testContract
|
||||||
|
.setTransferBehavior(
|
||||||
|
_opts.toTokenAddress,
|
||||||
|
_opts.toTokentransferRevertReason,
|
||||||
|
_opts.toTokenTransferReturnData,
|
||||||
|
)
|
||||||
|
.awaitTransactionSuccessAsync();
|
||||||
|
// Call bridgeTransferFrom().
|
||||||
|
const bridgeTransferFromFn = testContract.bridgeTransferFrom(
|
||||||
|
// "to" token address
|
||||||
|
_opts.toTokenAddress,
|
||||||
|
// Random from address.
|
||||||
|
randomAddress(),
|
||||||
|
// To address.
|
||||||
|
_opts.toAddress,
|
||||||
|
new BigNumber(_opts.amount),
|
||||||
|
// ABI-encode the "from" token address as the bridge data.
|
||||||
|
hexUtils.leftPad(_opts.fromTokenAddress as string),
|
||||||
|
);
|
||||||
|
const result = await bridgeTransferFromFn.callAsync();
|
||||||
|
const { logs } = await bridgeTransferFromFn.awaitTransactionSuccessAsync();
|
||||||
|
return {
|
||||||
|
opts: _opts,
|
||||||
|
result,
|
||||||
|
logs: (logs as any) as DecodedLogs,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
it('returns magic bytes on success', async () => {
|
||||||
|
const BRIDGE_SUCCESS_RETURN_DATA = AssetProxyId.ERC20Bridge;
|
||||||
|
const { result } = await withdrawToAsync();
|
||||||
|
expect(result).to.eq(BRIDGE_SUCCESS_RETURN_DATA);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls `Eth2Dai.sellAllAmount()`', async () => {
|
||||||
|
const { opts, logs } = await withdrawToAsync();
|
||||||
|
const transfers = filterLogsToArguments<TestEth2DaiBridgeSellAllAmountEventArgs>(
|
||||||
|
logs,
|
||||||
|
TestEth2DaiBridgeEvents.SellAllAmount,
|
||||||
|
);
|
||||||
|
expect(transfers.length).to.eq(1);
|
||||||
|
expect(transfers[0].sellToken).to.eq(opts.fromTokenAddress);
|
||||||
|
expect(transfers[0].buyToken).to.eq(opts.toTokenAddress);
|
||||||
|
expect(transfers[0].sellTokenAmount).to.bignumber.eq(opts.fromTokenBalance);
|
||||||
|
expect(transfers[0].minimumFillAmount).to.bignumber.eq(opts.amount);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets an unlimited allowance on the `fromTokenAddress` token', async () => {
|
||||||
|
const { opts, logs } = await withdrawToAsync();
|
||||||
|
const approvals = filterLogsToArguments<TestEth2DaiBridgeTokenApproveEventArgs>(
|
||||||
|
logs,
|
||||||
|
TestEth2DaiBridgeEvents.TokenApprove,
|
||||||
|
);
|
||||||
|
expect(approvals.length).to.eq(1);
|
||||||
|
expect(approvals[0].token).to.eq(opts.fromTokenAddress);
|
||||||
|
expect(approvals[0].spender).to.eq(testContract.address);
|
||||||
|
expect(approvals[0].allowance).to.bignumber.eq(constants.MAX_UINT256);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('transfers filled amount to `to`', async () => {
|
||||||
|
const { opts, logs } = await withdrawToAsync();
|
||||||
|
const transfers = filterLogsToArguments<TestEth2DaiBridgeTokenTransferEventArgs>(
|
||||||
|
logs,
|
||||||
|
TestEth2DaiBridgeEvents.TokenTransfer,
|
||||||
|
);
|
||||||
|
expect(transfers.length).to.eq(1);
|
||||||
|
expect(transfers[0].token).to.eq(opts.toTokenAddress);
|
||||||
|
expect(transfers[0].from).to.eq(testContract.address);
|
||||||
|
expect(transfers[0].to).to.eq(opts.toAddress);
|
||||||
|
expect(transfers[0].amount).to.bignumber.eq(opts.fillAmount);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails if `Eth2Dai.sellAllAmount()` reverts', async () => {
|
||||||
|
const opts = createWithdrawToOpts({ revertReason: 'FOOBAR' });
|
||||||
|
const tx = withdrawToAsync(opts);
|
||||||
|
return expect(tx).to.revertWith(opts.revertReason);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails if `toTokenAddress.transfer()` reverts', async () => {
|
||||||
|
const opts = createWithdrawToOpts({ toTokentransferRevertReason: 'FOOBAR' });
|
||||||
|
const tx = withdrawToAsync(opts);
|
||||||
|
return expect(tx).to.revertWith(opts.toTokentransferRevertReason);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails if `toTokenAddress.transfer()` returns false', async () => {
|
||||||
|
const opts = createWithdrawToOpts({ toTokenTransferReturnData: hexUtils.leftPad(0) });
|
||||||
|
const tx = withdrawToAsync(opts);
|
||||||
|
return expect(tx).to.revertWith(new RawRevertError(hexUtils.leftPad(0)));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('succeeds if `toTokenAddress.transfer()` returns true', async () => {
|
||||||
|
await withdrawToAsync({ toTokenTransferReturnData: hexUtils.leftPad(1) });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
283
contracts/asset-proxy/test/kyber_bridge.ts
Normal file
283
contracts/asset-proxy/test/kyber_bridge.ts
Normal file
@@ -0,0 +1,283 @@
|
|||||||
|
import {
|
||||||
|
blockchainTests,
|
||||||
|
constants,
|
||||||
|
expect,
|
||||||
|
getRandomInteger,
|
||||||
|
getRandomPortion,
|
||||||
|
randomAddress,
|
||||||
|
verifyEventsFromLogs,
|
||||||
|
} from '@0x/contracts-test-utils';
|
||||||
|
import { AssetProxyId } from '@0x/types';
|
||||||
|
import { BigNumber, hexUtils } from '@0x/utils';
|
||||||
|
import { DecodedLogs } from 'ethereum-types';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
|
import { artifacts } from './artifacts';
|
||||||
|
|
||||||
|
import { TestKyberBridgeContract, TestKyberBridgeEvents } from './wrappers';
|
||||||
|
|
||||||
|
blockchainTests.resets('KyberBridge unit tests', env => {
|
||||||
|
const KYBER_ETH_ADDRESS = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee';
|
||||||
|
const FROM_TOKEN_DECIMALS = 6;
|
||||||
|
const TO_TOKEN_DECIMALS = 18;
|
||||||
|
const FROM_TOKEN_BASE = new BigNumber(10).pow(FROM_TOKEN_DECIMALS);
|
||||||
|
const TO_TOKEN_BASE = new BigNumber(10).pow(TO_TOKEN_DECIMALS);
|
||||||
|
const WETH_BASE = new BigNumber(10).pow(18);
|
||||||
|
const KYBER_RATE_BASE = WETH_BASE;
|
||||||
|
let testContract: TestKyberBridgeContract;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
testContract = await TestKyberBridgeContract.deployFrom0xArtifactAsync(
|
||||||
|
artifacts.TestKyberBridge,
|
||||||
|
env.provider,
|
||||||
|
env.txDefaults,
|
||||||
|
artifacts,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('isValidSignature()', () => {
|
||||||
|
it('returns success bytes', async () => {
|
||||||
|
const LEGACY_WALLET_MAGIC_VALUE = '0xb0671381';
|
||||||
|
const result = await testContract
|
||||||
|
.isValidSignature(hexUtils.random(), hexUtils.random(_.random(0, 32)))
|
||||||
|
.callAsync();
|
||||||
|
expect(result).to.eq(LEGACY_WALLET_MAGIC_VALUE);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('bridgeTransferFrom()', () => {
|
||||||
|
let fromTokenAddress: string;
|
||||||
|
let toTokenAddress: string;
|
||||||
|
let wethAddress: string;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
wethAddress = await testContract.weth().callAsync();
|
||||||
|
fromTokenAddress = await testContract.createToken(FROM_TOKEN_DECIMALS).callAsync();
|
||||||
|
await testContract.createToken(FROM_TOKEN_DECIMALS).awaitTransactionSuccessAsync();
|
||||||
|
toTokenAddress = await testContract.createToken(TO_TOKEN_DECIMALS).callAsync();
|
||||||
|
await testContract.createToken(TO_TOKEN_DECIMALS).awaitTransactionSuccessAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
const STATIC_KYBER_TRADE_ARGS = {
|
||||||
|
maxBuyTokenAmount: constants.MAX_UINT256,
|
||||||
|
walletId: constants.NULL_ADDRESS,
|
||||||
|
};
|
||||||
|
|
||||||
|
interface TransferFromOpts {
|
||||||
|
toTokenAddress: string;
|
||||||
|
fromTokenAddress: string;
|
||||||
|
toAddress: string;
|
||||||
|
// Amount to pass into `bridgeTransferFrom()`
|
||||||
|
amount: BigNumber;
|
||||||
|
// Amount to convert in `trade()`.
|
||||||
|
fillAmount: BigNumber;
|
||||||
|
// Token balance of the bridge.
|
||||||
|
fromTokenBalance: BigNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TransferFromResult {
|
||||||
|
opts: TransferFromOpts;
|
||||||
|
result: string;
|
||||||
|
logs: DecodedLogs;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createTransferFromOpts(opts?: Partial<TransferFromOpts>): TransferFromOpts {
|
||||||
|
const amount = getRandomInteger(1, TO_TOKEN_BASE.times(100));
|
||||||
|
return {
|
||||||
|
fromTokenAddress,
|
||||||
|
toTokenAddress,
|
||||||
|
amount,
|
||||||
|
toAddress: randomAddress(),
|
||||||
|
fillAmount: getRandomPortion(amount),
|
||||||
|
fromTokenBalance: getRandomInteger(1, FROM_TOKEN_BASE.times(100)),
|
||||||
|
...opts,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function withdrawToAsync(opts?: Partial<TransferFromOpts>): Promise<TransferFromResult> {
|
||||||
|
const _opts = createTransferFromOpts(opts);
|
||||||
|
// Fund the contract with input tokens.
|
||||||
|
await testContract
|
||||||
|
.grantTokensTo(_opts.fromTokenAddress, testContract.address, _opts.fromTokenBalance)
|
||||||
|
.awaitTransactionSuccessAsync({ value: _opts.fromTokenBalance });
|
||||||
|
// Fund the contract with output tokens.
|
||||||
|
await testContract.setNextFillAmount(_opts.fillAmount).awaitTransactionSuccessAsync({
|
||||||
|
value: _opts.toTokenAddress === wethAddress ? _opts.fillAmount : constants.ZERO_AMOUNT,
|
||||||
|
});
|
||||||
|
// Call bridgeTransferFrom().
|
||||||
|
const bridgeTransferFromFn = testContract.bridgeTransferFrom(
|
||||||
|
// Output token
|
||||||
|
_opts.toTokenAddress,
|
||||||
|
// Random maker address.
|
||||||
|
randomAddress(),
|
||||||
|
// Recipient address.
|
||||||
|
_opts.toAddress,
|
||||||
|
// Transfer amount.
|
||||||
|
_opts.amount,
|
||||||
|
// ABI-encode the input token address as the bridge data.
|
||||||
|
hexUtils.leftPad(_opts.fromTokenAddress),
|
||||||
|
);
|
||||||
|
const result = await bridgeTransferFromFn.callAsync();
|
||||||
|
const { logs } = await bridgeTransferFromFn.awaitTransactionSuccessAsync();
|
||||||
|
return {
|
||||||
|
opts: _opts,
|
||||||
|
result,
|
||||||
|
logs: (logs as any) as DecodedLogs,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMinimumConversionRate(opts: TransferFromOpts): BigNumber {
|
||||||
|
const fromBase = opts.fromTokenAddress === wethAddress ? WETH_BASE : FROM_TOKEN_BASE;
|
||||||
|
const toBase = opts.toTokenAddress === wethAddress ? WETH_BASE : TO_TOKEN_BASE;
|
||||||
|
return opts.amount
|
||||||
|
.div(toBase)
|
||||||
|
.div(opts.fromTokenBalance.div(fromBase))
|
||||||
|
.times(KYBER_RATE_BASE)
|
||||||
|
.integerValue(BigNumber.ROUND_DOWN);
|
||||||
|
}
|
||||||
|
|
||||||
|
it('returns magic bytes on success', async () => {
|
||||||
|
const BRIDGE_SUCCESS_RETURN_DATA = AssetProxyId.ERC20Bridge;
|
||||||
|
const { result } = await withdrawToAsync();
|
||||||
|
expect(result).to.eq(BRIDGE_SUCCESS_RETURN_DATA);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can trade token -> token', async () => {
|
||||||
|
const { opts, logs } = await withdrawToAsync();
|
||||||
|
verifyEventsFromLogs(
|
||||||
|
logs,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
sellTokenAddress: opts.fromTokenAddress,
|
||||||
|
buyTokenAddress: opts.toTokenAddress,
|
||||||
|
sellAmount: opts.fromTokenBalance,
|
||||||
|
recipientAddress: opts.toAddress,
|
||||||
|
minConversionRate: getMinimumConversionRate(opts),
|
||||||
|
msgValue: constants.ZERO_AMOUNT,
|
||||||
|
...STATIC_KYBER_TRADE_ARGS,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
TestKyberBridgeEvents.KyberBridgeTrade,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can trade token -> ETH', async () => {
|
||||||
|
const { opts, logs } = await withdrawToAsync({
|
||||||
|
toTokenAddress: wethAddress,
|
||||||
|
});
|
||||||
|
verifyEventsFromLogs(
|
||||||
|
logs,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
sellTokenAddress: opts.fromTokenAddress,
|
||||||
|
buyTokenAddress: KYBER_ETH_ADDRESS,
|
||||||
|
sellAmount: opts.fromTokenBalance,
|
||||||
|
recipientAddress: testContract.address,
|
||||||
|
minConversionRate: getMinimumConversionRate(opts),
|
||||||
|
msgValue: constants.ZERO_AMOUNT,
|
||||||
|
...STATIC_KYBER_TRADE_ARGS,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
TestKyberBridgeEvents.KyberBridgeTrade,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can trade ETH -> token', async () => {
|
||||||
|
const { opts, logs } = await withdrawToAsync({
|
||||||
|
fromTokenAddress: wethAddress,
|
||||||
|
});
|
||||||
|
verifyEventsFromLogs(
|
||||||
|
logs,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
sellTokenAddress: KYBER_ETH_ADDRESS,
|
||||||
|
buyTokenAddress: opts.toTokenAddress,
|
||||||
|
sellAmount: opts.fromTokenBalance,
|
||||||
|
recipientAddress: opts.toAddress,
|
||||||
|
minConversionRate: getMinimumConversionRate(opts),
|
||||||
|
msgValue: opts.fromTokenBalance,
|
||||||
|
...STATIC_KYBER_TRADE_ARGS,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
TestKyberBridgeEvents.KyberBridgeTrade,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does nothing if bridge has no token balance', async () => {
|
||||||
|
const { logs } = await withdrawToAsync({
|
||||||
|
fromTokenBalance: constants.ZERO_AMOUNT,
|
||||||
|
});
|
||||||
|
expect(logs).to.be.length(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('only transfers the token if trading the same token', async () => {
|
||||||
|
const { opts, logs } = await withdrawToAsync({
|
||||||
|
toTokenAddress: fromTokenAddress,
|
||||||
|
});
|
||||||
|
verifyEventsFromLogs(
|
||||||
|
logs,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
tokenAddress: fromTokenAddress,
|
||||||
|
ownerAddress: testContract.address,
|
||||||
|
recipientAddress: opts.toAddress,
|
||||||
|
amount: opts.fromTokenBalance,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
TestKyberBridgeEvents.KyberBridgeTokenTransfer,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('grants Kyber an allowance when selling non-WETH', async () => {
|
||||||
|
const { opts, logs } = await withdrawToAsync();
|
||||||
|
verifyEventsFromLogs(
|
||||||
|
logs,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
tokenAddress: opts.fromTokenAddress,
|
||||||
|
ownerAddress: testContract.address,
|
||||||
|
spenderAddress: testContract.address,
|
||||||
|
allowance: constants.MAX_UINT256,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
TestKyberBridgeEvents.KyberBridgeTokenApprove,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not grant Kyber an allowance when selling WETH', async () => {
|
||||||
|
const { logs } = await withdrawToAsync({
|
||||||
|
fromTokenAddress: wethAddress,
|
||||||
|
});
|
||||||
|
verifyEventsFromLogs(logs, [], TestKyberBridgeEvents.KyberBridgeTokenApprove);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('withdraws WETH and passes it to Kyber when selling WETH', async () => {
|
||||||
|
const { opts, logs } = await withdrawToAsync({
|
||||||
|
fromTokenAddress: wethAddress,
|
||||||
|
});
|
||||||
|
expect(logs[0].event).to.eq(TestKyberBridgeEvents.KyberBridgeWethWithdraw);
|
||||||
|
expect(logs[0].args).to.deep.eq({
|
||||||
|
ownerAddress: testContract.address,
|
||||||
|
amount: opts.fromTokenBalance,
|
||||||
|
});
|
||||||
|
expect(logs[1].event).to.eq(TestKyberBridgeEvents.KyberBridgeTrade);
|
||||||
|
expect(logs[1].args.msgValue).to.bignumber.eq(opts.fromTokenBalance);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('wraps WETH and transfers it to the recipient when buyng WETH', async () => {
|
||||||
|
const { opts, logs } = await withdrawToAsync({
|
||||||
|
toTokenAddress: wethAddress,
|
||||||
|
});
|
||||||
|
expect(logs[0].event).to.eq(TestKyberBridgeEvents.KyberBridgeTokenApprove);
|
||||||
|
expect(logs[0].args.tokenAddress).to.eq(opts.fromTokenAddress);
|
||||||
|
expect(logs[1].event).to.eq(TestKyberBridgeEvents.KyberBridgeTrade);
|
||||||
|
expect(logs[1].args.recipientAddress).to.eq(testContract.address);
|
||||||
|
expect(logs[2].event).to.eq(TestKyberBridgeEvents.KyberBridgeWethDeposit);
|
||||||
|
expect(logs[2].args).to.deep.eq({
|
||||||
|
msgValue: opts.fillAmount,
|
||||||
|
ownerAddress: testContract.address,
|
||||||
|
amount: opts.fillAmount,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,20 +1,25 @@
|
|||||||
import {
|
import {
|
||||||
chaiSetup,
|
chaiSetup,
|
||||||
constants,
|
constants,
|
||||||
expectTransactionFailedAsync,
|
|
||||||
expectTransactionFailedWithoutReasonAsync,
|
expectTransactionFailedWithoutReasonAsync,
|
||||||
provider,
|
provider,
|
||||||
txDefaults,
|
txDefaults,
|
||||||
web3Wrapper,
|
web3Wrapper,
|
||||||
} 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 { AssetProxyId, RevertReason } from '@0x/types';
|
import { AssetProxyId, RevertReason } from '@0x/types';
|
||||||
import { AbiEncoder, BigNumber } from '@0x/utils';
|
import { AbiEncoder, BigNumber } from '@0x/utils';
|
||||||
import * as chai from 'chai';
|
import * as chai from 'chai';
|
||||||
import * as ethUtil from 'ethereumjs-util';
|
import * as ethUtil from 'ethereumjs-util';
|
||||||
|
|
||||||
import { artifacts, IAssetProxyContract, StaticCallProxyContract, TestStaticCallTargetContract } from '../src';
|
import { artifacts } from './artifacts';
|
||||||
|
|
||||||
|
import {
|
||||||
|
IAssetDataContract,
|
||||||
|
IAssetProxyContract,
|
||||||
|
StaticCallProxyContract,
|
||||||
|
TestStaticCallTargetContract,
|
||||||
|
} from './wrappers';
|
||||||
|
|
||||||
chaiSetup.configure();
|
chaiSetup.configure();
|
||||||
const expect = chai.expect;
|
const expect = chai.expect;
|
||||||
@@ -25,6 +30,7 @@ describe('StaticCallProxy', () => {
|
|||||||
let fromAddress: string;
|
let fromAddress: string;
|
||||||
let toAddress: string;
|
let toAddress: string;
|
||||||
|
|
||||||
|
let assetDataInterface: IAssetDataContract;
|
||||||
let staticCallProxy: IAssetProxyContract;
|
let staticCallProxy: IAssetProxyContract;
|
||||||
let staticCallTarget: TestStaticCallTargetContract;
|
let staticCallTarget: TestStaticCallTargetContract;
|
||||||
|
|
||||||
@@ -43,7 +49,14 @@ describe('StaticCallProxy', () => {
|
|||||||
txDefaults,
|
txDefaults,
|
||||||
artifacts,
|
artifacts,
|
||||||
);
|
);
|
||||||
staticCallProxy = new IAssetProxyContract(staticCallProxyWithoutTransferFrom.address, provider, txDefaults);
|
assetDataInterface = new IAssetDataContract(constants.NULL_ADDRESS, provider);
|
||||||
|
staticCallProxy = new IAssetProxyContract(
|
||||||
|
staticCallProxyWithoutTransferFrom.address,
|
||||||
|
provider,
|
||||||
|
txDefaults,
|
||||||
|
{},
|
||||||
|
StaticCallProxyContract.deployedBytecode,
|
||||||
|
);
|
||||||
staticCallTarget = await TestStaticCallTargetContract.deployFrom0xArtifactAsync(
|
staticCallTarget = await TestStaticCallTargetContract.deployFrom0xArtifactAsync(
|
||||||
artifacts.TestStaticCallTarget,
|
artifacts.TestStaticCallTarget,
|
||||||
provider,
|
provider,
|
||||||
@@ -71,26 +84,21 @@ describe('StaticCallProxy', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('should have an id of 0xc339d10a', async () => {
|
it('should have an id of 0xc339d10a', async () => {
|
||||||
const proxyId = await staticCallProxy.getProxyId.callAsync();
|
const proxyId = await staticCallProxy.getProxyId().callAsync();
|
||||||
const expectedProxyId = AssetProxyId.StaticCall;
|
const expectedProxyId = AssetProxyId.StaticCall;
|
||||||
expect(proxyId).to.equal(expectedProxyId);
|
expect(proxyId).to.equal(expectedProxyId);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('transferFrom', () => {
|
describe('transferFrom', () => {
|
||||||
it('should revert if assetData lies outside the bounds of calldata', async () => {
|
it('should revert if assetData lies outside the bounds of calldata', async () => {
|
||||||
const staticCallData = staticCallTarget.noInputFunction.getABIEncodedTransactionData();
|
const staticCallData = staticCallTarget.noInputFunction().getABIEncodedTransactionData();
|
||||||
const expectedResultHash = constants.KECCAK256_NULL;
|
const expectedResultHash = constants.KECCAK256_NULL;
|
||||||
const assetData = assetDataUtils.encodeStaticCallAssetData(
|
const assetData = assetDataInterface
|
||||||
staticCallTarget.address,
|
.StaticCall(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||||
staticCallData,
|
.getABIEncodedTransactionData();
|
||||||
expectedResultHash,
|
const txData = staticCallProxy
|
||||||
);
|
.transferFrom(assetData, fromAddress, toAddress, amount)
|
||||||
const txData = staticCallProxy.transferFrom.getABIEncodedTransactionData(
|
.getABIEncodedTransactionData();
|
||||||
assetData,
|
|
||||||
fromAddress,
|
|
||||||
toAddress,
|
|
||||||
amount,
|
|
||||||
);
|
|
||||||
const offsetToAssetData = '0000000000000000000000000000000000000000000000000000000000000080';
|
const offsetToAssetData = '0000000000000000000000000000000000000000000000000000000000000080';
|
||||||
const txDataEndBuffer = ethUtil.toBuffer((txData.length - 2) / 2 - 4);
|
const txDataEndBuffer = ethUtil.toBuffer((txData.length - 2) / 2 - 4);
|
||||||
const paddedTxDataEndBuffer = ethUtil.setLengthLeft(txDataEndBuffer, 32);
|
const paddedTxDataEndBuffer = ethUtil.setLengthLeft(txDataEndBuffer, 32);
|
||||||
@@ -108,23 +116,22 @@ describe('StaticCallProxy', () => {
|
|||||||
it('should revert if the length of assetData is less than 100 bytes', async () => {
|
it('should revert if the length of assetData is less than 100 bytes', async () => {
|
||||||
const staticCallData = constants.NULL_BYTES;
|
const staticCallData = constants.NULL_BYTES;
|
||||||
const expectedResultHash = constants.KECCAK256_NULL;
|
const expectedResultHash = constants.KECCAK256_NULL;
|
||||||
const assetData = assetDataUtils
|
const assetData = assetDataInterface
|
||||||
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash)
|
.StaticCall(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||||
|
.getABIEncodedTransactionData()
|
||||||
.slice(0, -128);
|
.slice(0, -128);
|
||||||
const assetDataByteLen = (assetData.length - 2) / 2;
|
const assetDataByteLen = (assetData.length - 2) / 2;
|
||||||
expect((assetDataByteLen - 4) % 32).to.equal(0);
|
expect((assetDataByteLen - 4) % 32).to.equal(0);
|
||||||
await expectTransactionFailedWithoutReasonAsync(
|
await expectTransactionFailedWithoutReasonAsync(
|
||||||
staticCallProxy.transferFrom.sendTransactionAsync(assetData, fromAddress, toAddress, amount),
|
staticCallProxy.transferFrom(assetData, fromAddress, toAddress, amount).sendTransactionAsync(),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('should revert if the offset to `staticCallData` points to outside of assetData', async () => {
|
it('should revert if the offset to `staticCallData` points to outside of assetData', async () => {
|
||||||
const staticCallData = staticCallTarget.noInputFunction.getABIEncodedTransactionData();
|
const staticCallData = staticCallTarget.noInputFunction().getABIEncodedTransactionData();
|
||||||
const expectedResultHash = constants.KECCAK256_NULL;
|
const expectedResultHash = constants.KECCAK256_NULL;
|
||||||
const assetData = assetDataUtils.encodeStaticCallAssetData(
|
const assetData = assetDataInterface
|
||||||
staticCallTarget.address,
|
.StaticCall(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||||
staticCallData,
|
.getABIEncodedTransactionData();
|
||||||
expectedResultHash,
|
|
||||||
);
|
|
||||||
const offsetToStaticCallData = '0000000000000000000000000000000000000000000000000000000000000060';
|
const offsetToStaticCallData = '0000000000000000000000000000000000000000000000000000000000000060';
|
||||||
const assetDataEndBuffer = ethUtil.toBuffer((assetData.length - 2) / 2 - 4);
|
const assetDataEndBuffer = ethUtil.toBuffer((assetData.length - 2) / 2 - 4);
|
||||||
const paddedAssetDataEndBuffer = ethUtil.setLengthLeft(assetDataEndBuffer, 32);
|
const paddedAssetDataEndBuffer = ethUtil.setLengthLeft(assetDataEndBuffer, 32);
|
||||||
@@ -135,90 +142,86 @@ describe('StaticCallProxy', () => {
|
|||||||
invalidOffsetToStaticCallData,
|
invalidOffsetToStaticCallData,
|
||||||
)}${newStaticCallData}`;
|
)}${newStaticCallData}`;
|
||||||
await expectTransactionFailedWithoutReasonAsync(
|
await expectTransactionFailedWithoutReasonAsync(
|
||||||
staticCallProxy.transferFrom.sendTransactionAsync(badAssetData, fromAddress, toAddress, amount),
|
staticCallProxy.transferFrom(badAssetData, fromAddress, toAddress, amount).sendTransactionAsync(),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('should revert if the callTarget attempts to write to state', async () => {
|
it('should revert if the callTarget attempts to write to state', async () => {
|
||||||
const staticCallData = staticCallTarget.updateState.getABIEncodedTransactionData();
|
const staticCallData = staticCallTarget.updateState().getABIEncodedTransactionData();
|
||||||
const expectedResultHash = constants.KECCAK256_NULL;
|
const expectedResultHash = constants.KECCAK256_NULL;
|
||||||
const assetData = assetDataUtils.encodeStaticCallAssetData(
|
const assetData = assetDataInterface
|
||||||
staticCallTarget.address,
|
.StaticCall(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||||
staticCallData,
|
.getABIEncodedTransactionData();
|
||||||
expectedResultHash,
|
|
||||||
);
|
|
||||||
await expectTransactionFailedWithoutReasonAsync(
|
await expectTransactionFailedWithoutReasonAsync(
|
||||||
staticCallProxy.transferFrom.sendTransactionAsync(assetData, fromAddress, toAddress, amount),
|
staticCallProxy.transferFrom(assetData, fromAddress, toAddress, amount).sendTransactionAsync(),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('should revert with data provided by the callTarget if the staticcall reverts', async () => {
|
it('should revert with data provided by the callTarget if the staticcall reverts', async () => {
|
||||||
const staticCallData = staticCallTarget.assertEvenNumber.getABIEncodedTransactionData(new BigNumber(1));
|
const staticCallData = staticCallTarget.assertEvenNumber(new BigNumber(1)).getABIEncodedTransactionData();
|
||||||
const expectedResultHash = constants.KECCAK256_NULL;
|
const expectedResultHash = constants.KECCAK256_NULL;
|
||||||
const assetData = assetDataUtils.encodeStaticCallAssetData(
|
const assetData = assetDataInterface
|
||||||
staticCallTarget.address,
|
.StaticCall(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||||
staticCallData,
|
.getABIEncodedTransactionData();
|
||||||
expectedResultHash,
|
return expect(
|
||||||
);
|
staticCallProxy.transferFrom(assetData, fromAddress, toAddress, amount).awaitTransactionSuccessAsync(),
|
||||||
await expectTransactionFailedAsync(
|
).to.revertWith(RevertReason.TargetNotEven);
|
||||||
staticCallProxy.transferFrom.sendTransactionAsync(assetData, fromAddress, toAddress, amount),
|
|
||||||
RevertReason.TargetNotEven,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
it('should revert if the hash of the output is different than expected expected', async () => {
|
it('should revert if the hash of the output is different than expected expected', async () => {
|
||||||
const staticCallData = staticCallTarget.isOddNumber.getABIEncodedTransactionData(new BigNumber(0));
|
const staticCallData = staticCallTarget.isOddNumber(new BigNumber(0)).getABIEncodedTransactionData();
|
||||||
const trueAsBuffer = ethUtil.toBuffer('0x0000000000000000000000000000000000000000000000000000000000000001');
|
const trueAsBuffer = ethUtil.toBuffer('0x0000000000000000000000000000000000000000000000000000000000000001');
|
||||||
const expectedResultHash = ethUtil.bufferToHex(ethUtil.sha3(trueAsBuffer));
|
const expectedResultHash = ethUtil.bufferToHex(ethUtil.sha3(trueAsBuffer));
|
||||||
const assetData = assetDataUtils.encodeStaticCallAssetData(
|
const assetData = assetDataInterface
|
||||||
staticCallTarget.address,
|
.StaticCall(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||||
staticCallData,
|
.getABIEncodedTransactionData();
|
||||||
expectedResultHash,
|
return expect(
|
||||||
);
|
staticCallProxy.transferFrom(assetData, fromAddress, toAddress, amount).awaitTransactionSuccessAsync(),
|
||||||
await expectTransactionFailedAsync(
|
).to.revertWith(RevertReason.UnexpectedStaticCallResult);
|
||||||
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 () => {
|
it('should be successful if a function call with no inputs and no outputs is successful', async () => {
|
||||||
const staticCallData = staticCallTarget.noInputFunction.getABIEncodedTransactionData();
|
const staticCallData = staticCallTarget.noInputFunction().getABIEncodedTransactionData();
|
||||||
const expectedResultHash = constants.KECCAK256_NULL;
|
const expectedResultHash = constants.KECCAK256_NULL;
|
||||||
const assetData = assetDataUtils.encodeStaticCallAssetData(
|
const assetData = assetDataInterface
|
||||||
staticCallTarget.address,
|
.StaticCall(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||||
staticCallData,
|
.getABIEncodedTransactionData();
|
||||||
expectedResultHash,
|
await staticCallProxy
|
||||||
);
|
.transferFrom(assetData, fromAddress, toAddress, amount)
|
||||||
await staticCallProxy.transferFrom.awaitTransactionSuccessAsync(assetData, fromAddress, toAddress, amount);
|
.awaitTransactionSuccessAsync();
|
||||||
});
|
});
|
||||||
it('should be successful if the staticCallTarget is not a contract and no return value is expected', async () => {
|
it('should be successful if the staticCallTarget is not a contract and no return value is expected', async () => {
|
||||||
const staticCallData = '0x0102030405060708';
|
const staticCallData = '0x0102030405060708';
|
||||||
const expectedResultHash = constants.KECCAK256_NULL;
|
const expectedResultHash = constants.KECCAK256_NULL;
|
||||||
const assetData = assetDataUtils.encodeStaticCallAssetData(toAddress, staticCallData, expectedResultHash);
|
const assetData = assetDataInterface
|
||||||
await staticCallProxy.transferFrom.awaitTransactionSuccessAsync(assetData, fromAddress, toAddress, amount);
|
.StaticCall(toAddress, staticCallData, expectedResultHash)
|
||||||
|
.getABIEncodedTransactionData();
|
||||||
|
await staticCallProxy
|
||||||
|
.transferFrom(assetData, fromAddress, toAddress, amount)
|
||||||
|
.awaitTransactionSuccessAsync();
|
||||||
});
|
});
|
||||||
it('should be successful if a function call with one static input returns the correct value', async () => {
|
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 staticCallData = staticCallTarget.isOddNumber(new BigNumber(1)).getABIEncodedTransactionData();
|
||||||
const trueAsBuffer = ethUtil.toBuffer('0x0000000000000000000000000000000000000000000000000000000000000001');
|
const trueAsBuffer = ethUtil.toBuffer('0x0000000000000000000000000000000000000000000000000000000000000001');
|
||||||
const expectedResultHash = ethUtil.bufferToHex(ethUtil.sha3(trueAsBuffer));
|
const expectedResultHash = ethUtil.bufferToHex(ethUtil.sha3(trueAsBuffer));
|
||||||
const assetData = assetDataUtils.encodeStaticCallAssetData(
|
const assetData = assetDataInterface
|
||||||
staticCallTarget.address,
|
.StaticCall(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||||
staticCallData,
|
.getABIEncodedTransactionData();
|
||||||
expectedResultHash,
|
await staticCallProxy
|
||||||
);
|
.transferFrom(assetData, fromAddress, toAddress, amount)
|
||||||
await staticCallProxy.transferFrom.awaitTransactionSuccessAsync(assetData, fromAddress, toAddress, amount);
|
.awaitTransactionSuccessAsync();
|
||||||
});
|
});
|
||||||
it('should be successful if a function with one dynamic input is successful', async () => {
|
it('should be successful if a function with one dynamic input is successful', async () => {
|
||||||
const dynamicInput = '0x0102030405060708';
|
const dynamicInput = '0x0102030405060708';
|
||||||
const staticCallData = staticCallTarget.dynamicInputFunction.getABIEncodedTransactionData(dynamicInput);
|
const staticCallData = staticCallTarget.dynamicInputFunction(dynamicInput).getABIEncodedTransactionData();
|
||||||
const expectedResultHash = constants.KECCAK256_NULL;
|
const expectedResultHash = constants.KECCAK256_NULL;
|
||||||
const assetData = assetDataUtils.encodeStaticCallAssetData(
|
const assetData = assetDataInterface
|
||||||
staticCallTarget.address,
|
.StaticCall(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||||
staticCallData,
|
.getABIEncodedTransactionData();
|
||||||
expectedResultHash,
|
await staticCallProxy
|
||||||
);
|
.transferFrom(assetData, fromAddress, toAddress, amount)
|
||||||
await staticCallProxy.transferFrom.awaitTransactionSuccessAsync(assetData, fromAddress, toAddress, amount);
|
.awaitTransactionSuccessAsync();
|
||||||
});
|
});
|
||||||
it('should be successful if a function call returns a complex type', async () => {
|
it('should be successful if a function call returns a complex type', async () => {
|
||||||
const a = new BigNumber(1);
|
const a = new BigNumber(1);
|
||||||
const b = new BigNumber(2);
|
const b = new BigNumber(2);
|
||||||
const staticCallData = staticCallTarget.returnComplexType.getABIEncodedTransactionData(a, b);
|
const staticCallData = staticCallTarget.returnComplexType(a, b).getABIEncodedTransactionData();
|
||||||
const abiEncoder = new AbiEncoder.DynamicBytes({
|
const abiEncoder = new AbiEncoder.DynamicBytes({
|
||||||
name: '',
|
name: '',
|
||||||
type: 'bytes',
|
type: 'bytes',
|
||||||
@@ -231,12 +234,12 @@ describe('StaticCallProxy', () => {
|
|||||||
const expectedResultHash = ethUtil.bufferToHex(
|
const expectedResultHash = ethUtil.bufferToHex(
|
||||||
ethUtil.sha3(ethUtil.toBuffer(encodedExpectedResultWithOffset)),
|
ethUtil.sha3(ethUtil.toBuffer(encodedExpectedResultWithOffset)),
|
||||||
);
|
);
|
||||||
const assetData = assetDataUtils.encodeStaticCallAssetData(
|
const assetData = assetDataInterface
|
||||||
staticCallTarget.address,
|
.StaticCall(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||||
staticCallData,
|
.getABIEncodedTransactionData();
|
||||||
expectedResultHash,
|
await staticCallProxy
|
||||||
);
|
.transferFrom(assetData, fromAddress, toAddress, amount)
|
||||||
await staticCallProxy.transferFrom.awaitTransactionSuccessAsync(assetData, fromAddress, toAddress, amount);
|
.awaitTransactionSuccessAsync();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
370
contracts/asset-proxy/test/uniswap_bridge.ts
Normal file
370
contracts/asset-proxy/test/uniswap_bridge.ts
Normal file
@@ -0,0 +1,370 @@
|
|||||||
|
import {
|
||||||
|
blockchainTests,
|
||||||
|
constants,
|
||||||
|
expect,
|
||||||
|
filterLogs,
|
||||||
|
filterLogsToArguments,
|
||||||
|
getRandomInteger,
|
||||||
|
Numberish,
|
||||||
|
randomAddress,
|
||||||
|
} from '@0x/contracts-test-utils';
|
||||||
|
import { AssetProxyId } from '@0x/types';
|
||||||
|
import { BigNumber, hexUtils } from '@0x/utils';
|
||||||
|
import { DecodedLogs } from 'ethereum-types';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
|
import { artifacts } from './artifacts';
|
||||||
|
|
||||||
|
import {
|
||||||
|
TestUniswapBridgeContract,
|
||||||
|
TestUniswapBridgeEthToTokenTransferInputEventArgs as EthToTokenTransferInputArgs,
|
||||||
|
TestUniswapBridgeEvents as ContractEvents,
|
||||||
|
TestUniswapBridgeTokenApproveEventArgs as TokenApproveArgs,
|
||||||
|
TestUniswapBridgeTokenToEthSwapInputEventArgs as TokenToEthSwapInputArgs,
|
||||||
|
TestUniswapBridgeTokenToTokenTransferInputEventArgs as TokenToTokenTransferInputArgs,
|
||||||
|
TestUniswapBridgeTokenTransferEventArgs as TokenTransferArgs,
|
||||||
|
TestUniswapBridgeWethDepositEventArgs as WethDepositArgs,
|
||||||
|
TestUniswapBridgeWethWithdrawEventArgs as WethWithdrawArgs,
|
||||||
|
} from './wrappers';
|
||||||
|
|
||||||
|
blockchainTests.resets('UniswapBridge unit tests', env => {
|
||||||
|
let testContract: TestUniswapBridgeContract;
|
||||||
|
let wethTokenAddress: string;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
testContract = await TestUniswapBridgeContract.deployFrom0xArtifactAsync(
|
||||||
|
artifacts.TestUniswapBridge,
|
||||||
|
env.provider,
|
||||||
|
env.txDefaults,
|
||||||
|
artifacts,
|
||||||
|
);
|
||||||
|
wethTokenAddress = await testContract.wethToken().callAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('isValidSignature()', () => {
|
||||||
|
it('returns success bytes', async () => {
|
||||||
|
const LEGACY_WALLET_MAGIC_VALUE = '0xb0671381';
|
||||||
|
const result = await testContract
|
||||||
|
.isValidSignature(hexUtils.random(), hexUtils.random(_.random(0, 32)))
|
||||||
|
.callAsync();
|
||||||
|
expect(result).to.eq(LEGACY_WALLET_MAGIC_VALUE);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('bridgeTransferFrom()', () => {
|
||||||
|
interface WithdrawToOpts {
|
||||||
|
fromTokenAddress: string;
|
||||||
|
toTokenAddress: string;
|
||||||
|
fromTokenBalance: Numberish;
|
||||||
|
toAddress: string;
|
||||||
|
amount: Numberish;
|
||||||
|
exchangeRevertReason: string;
|
||||||
|
exchangeFillAmount: Numberish;
|
||||||
|
toTokenRevertReason: string;
|
||||||
|
fromTokenRevertReason: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createWithdrawToOpts(opts?: Partial<WithdrawToOpts>): WithdrawToOpts {
|
||||||
|
return {
|
||||||
|
fromTokenAddress: constants.NULL_ADDRESS,
|
||||||
|
toTokenAddress: constants.NULL_ADDRESS,
|
||||||
|
fromTokenBalance: getRandomInteger(1, 1e18),
|
||||||
|
toAddress: randomAddress(),
|
||||||
|
amount: getRandomInteger(1, 1e18),
|
||||||
|
exchangeRevertReason: '',
|
||||||
|
exchangeFillAmount: getRandomInteger(1, 1e18),
|
||||||
|
toTokenRevertReason: '',
|
||||||
|
fromTokenRevertReason: '',
|
||||||
|
...opts,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface WithdrawToResult {
|
||||||
|
opts: WithdrawToOpts;
|
||||||
|
result: string;
|
||||||
|
logs: DecodedLogs;
|
||||||
|
blockTime: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function withdrawToAsync(opts?: Partial<WithdrawToOpts>): Promise<WithdrawToResult> {
|
||||||
|
const _opts = createWithdrawToOpts(opts);
|
||||||
|
const callData = { value: new BigNumber(_opts.exchangeFillAmount) };
|
||||||
|
// Create the "from" token and exchange.
|
||||||
|
const createFromTokenFn = testContract.createTokenAndExchange(
|
||||||
|
_opts.fromTokenAddress,
|
||||||
|
_opts.exchangeRevertReason,
|
||||||
|
);
|
||||||
|
[_opts.fromTokenAddress] = await createFromTokenFn.callAsync(callData);
|
||||||
|
await createFromTokenFn.awaitTransactionSuccessAsync(callData);
|
||||||
|
|
||||||
|
// Create the "to" token and exchange.
|
||||||
|
const createToTokenFn = testContract.createTokenAndExchange(
|
||||||
|
_opts.toTokenAddress,
|
||||||
|
_opts.exchangeRevertReason,
|
||||||
|
);
|
||||||
|
[_opts.toTokenAddress] = await createToTokenFn.callAsync(callData);
|
||||||
|
await createToTokenFn.awaitTransactionSuccessAsync(callData);
|
||||||
|
|
||||||
|
await testContract
|
||||||
|
.setTokenRevertReason(_opts.toTokenAddress, _opts.toTokenRevertReason)
|
||||||
|
.awaitTransactionSuccessAsync();
|
||||||
|
await testContract
|
||||||
|
.setTokenRevertReason(_opts.fromTokenAddress, _opts.fromTokenRevertReason)
|
||||||
|
.awaitTransactionSuccessAsync();
|
||||||
|
// Set the token balance for the token we're converting from.
|
||||||
|
await testContract.setTokenBalance(_opts.fromTokenAddress).awaitTransactionSuccessAsync({
|
||||||
|
value: new BigNumber(_opts.fromTokenBalance),
|
||||||
|
});
|
||||||
|
// Call bridgeTransferFrom().
|
||||||
|
const bridgeTransferFromFn = testContract.bridgeTransferFrom(
|
||||||
|
// The "to" token address.
|
||||||
|
_opts.toTokenAddress,
|
||||||
|
// The "from" address.
|
||||||
|
randomAddress(),
|
||||||
|
// The "to" address.
|
||||||
|
_opts.toAddress,
|
||||||
|
// The amount to transfer to "to"
|
||||||
|
new BigNumber(_opts.amount),
|
||||||
|
// ABI-encoded "from" token address.
|
||||||
|
hexUtils.leftPad(_opts.fromTokenAddress),
|
||||||
|
);
|
||||||
|
const result = await bridgeTransferFromFn.callAsync();
|
||||||
|
const receipt = await bridgeTransferFromFn.awaitTransactionSuccessAsync();
|
||||||
|
return {
|
||||||
|
opts: _opts,
|
||||||
|
result,
|
||||||
|
logs: (receipt.logs as any) as DecodedLogs,
|
||||||
|
blockTime: await env.web3Wrapper.getBlockTimestampAsync(receipt.blockNumber),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getExchangeForTokenAsync(tokenAddress: string): Promise<string> {
|
||||||
|
return testContract.getExchange(tokenAddress).callAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
it('returns magic bytes on success', async () => {
|
||||||
|
const { result } = await withdrawToAsync();
|
||||||
|
expect(result).to.eq(AssetProxyId.ERC20Bridge);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('just transfers tokens to `to` if the same tokens are in play', async () => {
|
||||||
|
const createTokenFn = await testContract.createTokenAndExchange(constants.NULL_ADDRESS, '');
|
||||||
|
const [tokenAddress] = await createTokenFn.callAsync();
|
||||||
|
await createTokenFn.awaitTransactionSuccessAsync();
|
||||||
|
const { opts, result, logs } = await withdrawToAsync({
|
||||||
|
fromTokenAddress: tokenAddress,
|
||||||
|
toTokenAddress: tokenAddress,
|
||||||
|
});
|
||||||
|
expect(result).to.eq(AssetProxyId.ERC20Bridge);
|
||||||
|
const transfers = filterLogsToArguments<TokenTransferArgs>(logs, ContractEvents.TokenTransfer);
|
||||||
|
expect(transfers.length).to.eq(1);
|
||||||
|
expect(transfers[0].token).to.eq(tokenAddress);
|
||||||
|
expect(transfers[0].from).to.eq(testContract.address);
|
||||||
|
expect(transfers[0].to).to.eq(opts.toAddress);
|
||||||
|
expect(transfers[0].amount).to.bignumber.eq(opts.amount);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('token -> token', () => {
|
||||||
|
it('calls `IUniswapExchange.tokenToTokenTransferInput()', async () => {
|
||||||
|
const { opts, logs, blockTime } = await withdrawToAsync();
|
||||||
|
const exchangeAddress = await getExchangeForTokenAsync(opts.fromTokenAddress);
|
||||||
|
const calls = filterLogsToArguments<TokenToTokenTransferInputArgs>(
|
||||||
|
logs,
|
||||||
|
ContractEvents.TokenToTokenTransferInput,
|
||||||
|
);
|
||||||
|
expect(calls.length).to.eq(1);
|
||||||
|
expect(calls[0].exchange).to.eq(exchangeAddress);
|
||||||
|
expect(calls[0].tokensSold).to.bignumber.eq(opts.fromTokenBalance);
|
||||||
|
expect(calls[0].minTokensBought).to.bignumber.eq(opts.amount);
|
||||||
|
expect(calls[0].minEthBought).to.bignumber.eq(1);
|
||||||
|
expect(calls[0].deadline).to.bignumber.eq(blockTime);
|
||||||
|
expect(calls[0].recipient).to.eq(opts.toAddress);
|
||||||
|
expect(calls[0].toTokenAddress).to.eq(opts.toTokenAddress);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets allowance for "from" token', async () => {
|
||||||
|
const { opts, logs } = await withdrawToAsync();
|
||||||
|
const approvals = filterLogsToArguments<TokenApproveArgs>(logs, ContractEvents.TokenApprove);
|
||||||
|
const exchangeAddress = await getExchangeForTokenAsync(opts.fromTokenAddress);
|
||||||
|
expect(approvals.length).to.eq(1);
|
||||||
|
expect(approvals[0].spender).to.eq(exchangeAddress);
|
||||||
|
expect(approvals[0].allowance).to.bignumber.eq(constants.MAX_UINT256);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets allowance for "from" token on subsequent calls', async () => {
|
||||||
|
const { opts } = await withdrawToAsync();
|
||||||
|
const { logs } = await withdrawToAsync(opts);
|
||||||
|
const approvals = filterLogsToArguments<TokenApproveArgs>(logs, ContractEvents.TokenApprove);
|
||||||
|
const exchangeAddress = await getExchangeForTokenAsync(opts.fromTokenAddress);
|
||||||
|
expect(approvals.length).to.eq(1);
|
||||||
|
expect(approvals[0].spender).to.eq(exchangeAddress);
|
||||||
|
expect(approvals[0].allowance).to.bignumber.eq(constants.MAX_UINT256);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails if "from" token does not exist', async () => {
|
||||||
|
const tx = testContract
|
||||||
|
.bridgeTransferFrom(
|
||||||
|
randomAddress(),
|
||||||
|
randomAddress(),
|
||||||
|
randomAddress(),
|
||||||
|
getRandomInteger(1, 1e18),
|
||||||
|
hexUtils.leftPad(randomAddress()),
|
||||||
|
)
|
||||||
|
.awaitTransactionSuccessAsync();
|
||||||
|
return expect(tx).to.eventually.be.rejectedWith('NO_UNISWAP_EXCHANGE_FOR_TOKEN');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails if the exchange fails', async () => {
|
||||||
|
const revertReason = 'FOOBAR';
|
||||||
|
const tx = withdrawToAsync({
|
||||||
|
exchangeRevertReason: revertReason,
|
||||||
|
});
|
||||||
|
return expect(tx).to.eventually.be.rejectedWith(revertReason);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('token -> ETH', () => {
|
||||||
|
it('calls `IUniswapExchange.tokenToEthSwapInput()`, `WETH.deposit()`, then `transfer()`', async () => {
|
||||||
|
const { opts, logs, blockTime } = await withdrawToAsync({
|
||||||
|
toTokenAddress: wethTokenAddress,
|
||||||
|
});
|
||||||
|
const exchangeAddress = await getExchangeForTokenAsync(opts.fromTokenAddress);
|
||||||
|
let calls: any = filterLogs<TokenToEthSwapInputArgs>(logs, ContractEvents.TokenToEthSwapInput);
|
||||||
|
expect(calls.length).to.eq(1);
|
||||||
|
expect(calls[0].args.exchange).to.eq(exchangeAddress);
|
||||||
|
expect(calls[0].args.tokensSold).to.bignumber.eq(opts.fromTokenBalance);
|
||||||
|
expect(calls[0].args.minEthBought).to.bignumber.eq(opts.amount);
|
||||||
|
expect(calls[0].args.deadline).to.bignumber.eq(blockTime);
|
||||||
|
calls = filterLogs<WethDepositArgs>(
|
||||||
|
logs.slice(calls[0].logIndex as number),
|
||||||
|
ContractEvents.WethDeposit,
|
||||||
|
);
|
||||||
|
expect(calls.length).to.eq(1);
|
||||||
|
expect(calls[0].args.amount).to.bignumber.eq(opts.exchangeFillAmount);
|
||||||
|
calls = filterLogs<TokenTransferArgs>(
|
||||||
|
logs.slice(calls[0].logIndex as number),
|
||||||
|
ContractEvents.TokenTransfer,
|
||||||
|
);
|
||||||
|
expect(calls.length).to.eq(1);
|
||||||
|
expect(calls[0].args.token).to.eq(opts.toTokenAddress);
|
||||||
|
expect(calls[0].args.from).to.eq(testContract.address);
|
||||||
|
expect(calls[0].args.to).to.eq(opts.toAddress);
|
||||||
|
expect(calls[0].args.amount).to.bignumber.eq(opts.exchangeFillAmount);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets allowance for "from" token', async () => {
|
||||||
|
const { opts, logs } = await withdrawToAsync({
|
||||||
|
toTokenAddress: wethTokenAddress,
|
||||||
|
});
|
||||||
|
const transfers = filterLogsToArguments<TokenApproveArgs>(logs, ContractEvents.TokenApprove);
|
||||||
|
const exchangeAddress = await getExchangeForTokenAsync(opts.fromTokenAddress);
|
||||||
|
expect(transfers.length).to.eq(1);
|
||||||
|
expect(transfers[0].spender).to.eq(exchangeAddress);
|
||||||
|
expect(transfers[0].allowance).to.bignumber.eq(constants.MAX_UINT256);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets allowance for "from" token on subsequent calls', async () => {
|
||||||
|
const { opts } = await withdrawToAsync({
|
||||||
|
toTokenAddress: wethTokenAddress,
|
||||||
|
});
|
||||||
|
const { logs } = await withdrawToAsync(opts);
|
||||||
|
const approvals = filterLogsToArguments<TokenApproveArgs>(logs, ContractEvents.TokenApprove);
|
||||||
|
const exchangeAddress = await getExchangeForTokenAsync(opts.fromTokenAddress);
|
||||||
|
expect(approvals.length).to.eq(1);
|
||||||
|
expect(approvals[0].spender).to.eq(exchangeAddress);
|
||||||
|
expect(approvals[0].allowance).to.bignumber.eq(constants.MAX_UINT256);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails if "from" token does not exist', async () => {
|
||||||
|
const tx = testContract
|
||||||
|
.bridgeTransferFrom(
|
||||||
|
randomAddress(),
|
||||||
|
randomAddress(),
|
||||||
|
randomAddress(),
|
||||||
|
getRandomInteger(1, 1e18),
|
||||||
|
hexUtils.leftPad(wethTokenAddress),
|
||||||
|
)
|
||||||
|
.awaitTransactionSuccessAsync();
|
||||||
|
return expect(tx).to.eventually.be.rejectedWith('NO_UNISWAP_EXCHANGE_FOR_TOKEN');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails if `WETH.deposit()` fails', async () => {
|
||||||
|
const revertReason = 'FOOBAR';
|
||||||
|
const tx = withdrawToAsync({
|
||||||
|
toTokenAddress: wethTokenAddress,
|
||||||
|
toTokenRevertReason: revertReason,
|
||||||
|
});
|
||||||
|
return expect(tx).to.eventually.be.rejectedWith(revertReason);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails if the exchange fails', async () => {
|
||||||
|
const revertReason = 'FOOBAR';
|
||||||
|
const tx = withdrawToAsync({
|
||||||
|
toTokenAddress: wethTokenAddress,
|
||||||
|
exchangeRevertReason: revertReason,
|
||||||
|
});
|
||||||
|
return expect(tx).to.eventually.be.rejectedWith(revertReason);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('ETH -> token', () => {
|
||||||
|
it('calls `WETH.withdraw()`, then `IUniswapExchange.ethToTokenTransferInput()`', async () => {
|
||||||
|
const { opts, logs, blockTime } = await withdrawToAsync({
|
||||||
|
fromTokenAddress: wethTokenAddress,
|
||||||
|
});
|
||||||
|
const exchangeAddress = await getExchangeForTokenAsync(opts.toTokenAddress);
|
||||||
|
let calls: any = filterLogs<WethWithdrawArgs>(logs, ContractEvents.WethWithdraw);
|
||||||
|
expect(calls.length).to.eq(1);
|
||||||
|
expect(calls[0].args.amount).to.bignumber.eq(opts.fromTokenBalance);
|
||||||
|
calls = filterLogs<EthToTokenTransferInputArgs>(
|
||||||
|
logs.slice(calls[0].logIndex as number),
|
||||||
|
ContractEvents.EthToTokenTransferInput,
|
||||||
|
);
|
||||||
|
expect(calls.length).to.eq(1);
|
||||||
|
expect(calls[0].args.exchange).to.eq(exchangeAddress);
|
||||||
|
expect(calls[0].args.minTokensBought).to.bignumber.eq(opts.amount);
|
||||||
|
expect(calls[0].args.deadline).to.bignumber.eq(blockTime);
|
||||||
|
expect(calls[0].args.recipient).to.eq(opts.toAddress);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not set any allowance', async () => {
|
||||||
|
const { logs } = await withdrawToAsync({
|
||||||
|
fromTokenAddress: wethTokenAddress,
|
||||||
|
});
|
||||||
|
const approvals = filterLogsToArguments<TokenApproveArgs>(logs, ContractEvents.TokenApprove);
|
||||||
|
expect(approvals).to.be.empty('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails if "to" token does not exist', async () => {
|
||||||
|
const tx = testContract
|
||||||
|
.bridgeTransferFrom(
|
||||||
|
wethTokenAddress,
|
||||||
|
randomAddress(),
|
||||||
|
randomAddress(),
|
||||||
|
getRandomInteger(1, 1e18),
|
||||||
|
hexUtils.leftPad(randomAddress()),
|
||||||
|
)
|
||||||
|
.awaitTransactionSuccessAsync();
|
||||||
|
return expect(tx).to.eventually.be.rejectedWith('NO_UNISWAP_EXCHANGE_FOR_TOKEN');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails if the `WETH.withdraw()` fails', async () => {
|
||||||
|
const revertReason = 'FOOBAR';
|
||||||
|
const tx = withdrawToAsync({
|
||||||
|
fromTokenAddress: wethTokenAddress,
|
||||||
|
fromTokenRevertReason: revertReason,
|
||||||
|
});
|
||||||
|
return expect(tx).to.eventually.be.rejectedWith(revertReason);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails if the exchange fails', async () => {
|
||||||
|
const revertReason = 'FOOBAR';
|
||||||
|
const tx = withdrawToAsync({
|
||||||
|
fromTokenAddress: wethTokenAddress,
|
||||||
|
exchangeRevertReason: revertReason,
|
||||||
|
});
|
||||||
|
return expect(tx).to.eventually.be.rejectedWith(revertReason);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
export * from './erc20_wrapper';
|
|
||||||
export * from './erc721_wrapper';
|
|
||||||
export * from './erc1155_proxy_wrapper';
|
|
||||||
40
contracts/asset-proxy/test/wrappers.ts
Normal file
40
contracts/asset-proxy/test/wrappers.ts
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* -----------------------------------------------------------------------------
|
||||||
|
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||||
|
* -----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
export * from '../test/generated-wrappers/chai_bridge';
|
||||||
|
export * from '../test/generated-wrappers/curve_bridge';
|
||||||
|
export * from '../test/generated-wrappers/dydx_bridge';
|
||||||
|
export * from '../test/generated-wrappers/erc1155_proxy';
|
||||||
|
export * from '../test/generated-wrappers/erc20_bridge_proxy';
|
||||||
|
export * from '../test/generated-wrappers/erc20_proxy';
|
||||||
|
export * from '../test/generated-wrappers/erc721_proxy';
|
||||||
|
export * from '../test/generated-wrappers/eth2_dai_bridge';
|
||||||
|
export * from '../test/generated-wrappers/i_asset_data';
|
||||||
|
export * from '../test/generated-wrappers/i_asset_proxy';
|
||||||
|
export * from '../test/generated-wrappers/i_asset_proxy_dispatcher';
|
||||||
|
export * from '../test/generated-wrappers/i_authorizable';
|
||||||
|
export * from '../test/generated-wrappers/i_chai';
|
||||||
|
export * from '../test/generated-wrappers/i_curve';
|
||||||
|
export * from '../test/generated-wrappers/i_dydx';
|
||||||
|
export * from '../test/generated-wrappers/i_dydx_bridge';
|
||||||
|
export * from '../test/generated-wrappers/i_erc20_bridge';
|
||||||
|
export * from '../test/generated-wrappers/i_eth2_dai';
|
||||||
|
export * from '../test/generated-wrappers/i_kyber_network_proxy';
|
||||||
|
export * from '../test/generated-wrappers/i_uniswap_exchange';
|
||||||
|
export * from '../test/generated-wrappers/i_uniswap_exchange_factory';
|
||||||
|
export * from '../test/generated-wrappers/kyber_bridge';
|
||||||
|
export * from '../test/generated-wrappers/mixin_asset_proxy_dispatcher';
|
||||||
|
export * from '../test/generated-wrappers/mixin_authorizable';
|
||||||
|
export * from '../test/generated-wrappers/multi_asset_proxy';
|
||||||
|
export * from '../test/generated-wrappers/ownable';
|
||||||
|
export * from '../test/generated-wrappers/static_call_proxy';
|
||||||
|
export * from '../test/generated-wrappers/test_chai_bridge';
|
||||||
|
export * from '../test/generated-wrappers/test_dydx_bridge';
|
||||||
|
export * from '../test/generated-wrappers/test_erc20_bridge';
|
||||||
|
export * from '../test/generated-wrappers/test_eth2_dai_bridge';
|
||||||
|
export * from '../test/generated-wrappers/test_kyber_bridge';
|
||||||
|
export * from '../test/generated-wrappers/test_static_call_target';
|
||||||
|
export * from '../test/generated-wrappers/test_uniswap_bridge';
|
||||||
|
export * from '../test/generated-wrappers/uniswap_bridge';
|
||||||
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: 'istanbul',
|
||||||
|
optimizer: {
|
||||||
|
enabled: true,
|
||||||
|
runs: 1000000,
|
||||||
|
details: { yul: true, deduplicate: true, cse: true, constantOptimizer: true },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -3,16 +3,76 @@
|
|||||||
"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/ChaiBridge.json",
|
||||||
|
"generated-artifacts/CurveBridge.json",
|
||||||
|
"generated-artifacts/DydxBridge.json",
|
||||||
"generated-artifacts/ERC1155Proxy.json",
|
"generated-artifacts/ERC1155Proxy.json",
|
||||||
|
"generated-artifacts/ERC20BridgeProxy.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/IChai.json",
|
||||||
|
"generated-artifacts/ICurve.json",
|
||||||
|
"generated-artifacts/IDydx.json",
|
||||||
|
"generated-artifacts/IDydxBridge.json",
|
||||||
|
"generated-artifacts/IERC20Bridge.json",
|
||||||
|
"generated-artifacts/IEth2Dai.json",
|
||||||
|
"generated-artifacts/IKyberNetworkProxy.json",
|
||||||
|
"generated-artifacts/IUniswapExchange.json",
|
||||||
|
"generated-artifacts/IUniswapExchangeFactory.json",
|
||||||
|
"generated-artifacts/KyberBridge.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/StaticCallProxy.json",
|
||||||
"generated-artifacts/TestStaticCallTarget.json"
|
"generated-artifacts/TestChaiBridge.json",
|
||||||
|
"generated-artifacts/TestDydxBridge.json",
|
||||||
|
"generated-artifacts/TestERC20Bridge.json",
|
||||||
|
"generated-artifacts/TestEth2DaiBridge.json",
|
||||||
|
"generated-artifacts/TestKyberBridge.json",
|
||||||
|
"generated-artifacts/TestStaticCallTarget.json",
|
||||||
|
"generated-artifacts/TestUniswapBridge.json",
|
||||||
|
"generated-artifacts/UniswapBridge.json",
|
||||||
|
"test/generated-artifacts/ChaiBridge.json",
|
||||||
|
"test/generated-artifacts/CurveBridge.json",
|
||||||
|
"test/generated-artifacts/DydxBridge.json",
|
||||||
|
"test/generated-artifacts/ERC1155Proxy.json",
|
||||||
|
"test/generated-artifacts/ERC20BridgeProxy.json",
|
||||||
|
"test/generated-artifacts/ERC20Proxy.json",
|
||||||
|
"test/generated-artifacts/ERC721Proxy.json",
|
||||||
|
"test/generated-artifacts/Eth2DaiBridge.json",
|
||||||
|
"test/generated-artifacts/IAssetData.json",
|
||||||
|
"test/generated-artifacts/IAssetProxy.json",
|
||||||
|
"test/generated-artifacts/IAssetProxyDispatcher.json",
|
||||||
|
"test/generated-artifacts/IAuthorizable.json",
|
||||||
|
"test/generated-artifacts/IChai.json",
|
||||||
|
"test/generated-artifacts/ICurve.json",
|
||||||
|
"test/generated-artifacts/IDydx.json",
|
||||||
|
"test/generated-artifacts/IDydxBridge.json",
|
||||||
|
"test/generated-artifacts/IERC20Bridge.json",
|
||||||
|
"test/generated-artifacts/IEth2Dai.json",
|
||||||
|
"test/generated-artifacts/IKyberNetworkProxy.json",
|
||||||
|
"test/generated-artifacts/IUniswapExchange.json",
|
||||||
|
"test/generated-artifacts/IUniswapExchangeFactory.json",
|
||||||
|
"test/generated-artifacts/KyberBridge.json",
|
||||||
|
"test/generated-artifacts/MixinAssetProxyDispatcher.json",
|
||||||
|
"test/generated-artifacts/MixinAuthorizable.json",
|
||||||
|
"test/generated-artifacts/MultiAssetProxy.json",
|
||||||
|
"test/generated-artifacts/Ownable.json",
|
||||||
|
"test/generated-artifacts/StaticCallProxy.json",
|
||||||
|
"test/generated-artifacts/TestChaiBridge.json",
|
||||||
|
"test/generated-artifacts/TestDydxBridge.json",
|
||||||
|
"test/generated-artifacts/TestERC20Bridge.json",
|
||||||
|
"test/generated-artifacts/TestEth2DaiBridge.json",
|
||||||
|
"test/generated-artifacts/TestKyberBridge.json",
|
||||||
|
"test/generated-artifacts/TestStaticCallTarget.json",
|
||||||
|
"test/generated-artifacts/TestUniswapBridge.json",
|
||||||
|
"test/generated-artifacts/UniswapBridge.json"
|
||||||
],
|
],
|
||||||
"exclude": ["./deploy/solc/solc_bin"]
|
"exclude": ["./deploy/solc/solc_bin"]
|
||||||
}
|
}
|
||||||
|
|||||||
10
contracts/broker/.npmignore
Normal file
10
contracts/broker/.npmignore
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# Blacklist all files
|
||||||
|
.*
|
||||||
|
*
|
||||||
|
# Whitelist lib
|
||||||
|
!lib/**/*
|
||||||
|
# Whitelist Solidity contracts
|
||||||
|
!contracts/src/**/*
|
||||||
|
# Blacklist tests in lib
|
||||||
|
/lib/test/*
|
||||||
|
# Package specific ignore
|
||||||
39
contracts/broker/CHANGELOG.json
Normal file
39
contracts/broker/CHANGELOG.json
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"version": "1.1.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Added decoders for broker data",
|
||||||
|
"pr": 2484
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1581748629
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1581204851,
|
||||||
|
"version": "1.0.2",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1580988106,
|
||||||
|
"version": "1.0.1",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.0.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Created package",
|
||||||
|
"pr": "2455"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
22
contracts/broker/CHANGELOG.md
Normal file
22
contracts/broker/CHANGELOG.md
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<!--
|
||||||
|
changelogUtils.file is auto-generated using the monorepo-scripts package. Don't edit directly.
|
||||||
|
Edit the package's CHANGELOG.json file only.
|
||||||
|
-->
|
||||||
|
|
||||||
|
CHANGELOG
|
||||||
|
|
||||||
|
## v1.1.0 - _February 15, 2020_
|
||||||
|
|
||||||
|
* Added decoders for broker data (#2484)
|
||||||
|
|
||||||
|
## v1.0.2 - _February 8, 2020_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v1.0.1 - _February 6, 2020_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v1.0.0 - _Invalid date_
|
||||||
|
|
||||||
|
* Created package (#2455)
|
||||||
1
contracts/broker/DEPLOYS.json
Normal file
1
contracts/broker/DEPLOYS.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
[]
|
||||||
73
contracts/broker/README.md
Normal file
73
contracts/broker/README.md
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
## Broker
|
||||||
|
|
||||||
|
This package contains the implementation of the [`Broker` contract](https://github.com/0xProject/ZEIPs/issues/75). This contract serves as an entry-point to the 0x Exchange for the filling of property-based orders. 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-broker --save
|
||||||
|
```
|
||||||
|
|
||||||
|
## Bug bounty
|
||||||
|
|
||||||
|
A bug bounty for the 3.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-broker yarn build
|
||||||
|
```
|
||||||
|
|
||||||
|
Or continuously rebuild on change:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
PKG=@0x/contracts-broker 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/broker/compiler.json
Normal file
26
contracts/broker/compiler.json
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"artifactsDir": "./test/generated-artifacts",
|
||||||
|
"contractsDir": "./contracts",
|
||||||
|
"useDockerisedSolc": false,
|
||||||
|
"isOfflineMode": false,
|
||||||
|
"compilerSettings": {
|
||||||
|
"evmVersion": "istanbul",
|
||||||
|
"optimizer": {
|
||||||
|
"enabled": true,
|
||||||
|
"runs": 1000000,
|
||||||
|
"details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
|
||||||
|
},
|
||||||
|
"outputSelection": {
|
||||||
|
"*": {
|
||||||
|
"*": [
|
||||||
|
"abi",
|
||||||
|
"devdoc",
|
||||||
|
"evm.bytecode.object",
|
||||||
|
"evm.bytecode.sourceMap",
|
||||||
|
"evm.deployedBytecode.object",
|
||||||
|
"evm.deployedBytecode.sourceMap"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
314
contracts/broker/contracts/src/Broker.sol
Normal file
314
contracts/broker/contracts/src/Broker.sol
Normal file
@@ -0,0 +1,314 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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-asset-proxy/contracts/src/interfaces/IAssetData.sol";
|
||||||
|
import "@0x/contracts-erc20/contracts/src/interfaces/IEtherToken.sol";
|
||||||
|
import "@0x/contracts-erc721/contracts/src/interfaces/IERC721Token.sol";
|
||||||
|
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibFillResults.sol";
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||||
|
import "@0x/contracts-extensions/contracts/src/LibAssetDataTransfer.sol";
|
||||||
|
import "@0x/contracts-extensions/contracts/src/MixinWethUtils.sol";
|
||||||
|
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||||
|
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
|
||||||
|
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
||||||
|
import "./interfaces/IBroker.sol";
|
||||||
|
import "./interfaces/IPropertyValidator.sol";
|
||||||
|
import "./libs/LibBrokerRichErrors.sol";
|
||||||
|
|
||||||
|
|
||||||
|
// solhint-disable space-after-comma, var-name-mixedcase
|
||||||
|
contract Broker is
|
||||||
|
IBroker,
|
||||||
|
MixinWethUtils
|
||||||
|
{
|
||||||
|
// Contract addresses
|
||||||
|
|
||||||
|
// Address of the 0x Exchange contract
|
||||||
|
address internal EXCHANGE;
|
||||||
|
// Address of the 0x ERC1155 Asset Proxy contract
|
||||||
|
address internal ERC1155_PROXY;
|
||||||
|
|
||||||
|
// The following storage variables are used to cache data for the duration of the transcation.
|
||||||
|
// They should always cleared at the end of the transaction.
|
||||||
|
|
||||||
|
// Token IDs specified by the taker to be used to fill property-based orders.
|
||||||
|
uint256[] internal _cachedTokenIds;
|
||||||
|
// An index to the above array keeping track of which assets have been transferred.
|
||||||
|
uint256 internal _cacheIndex;
|
||||||
|
// The address that called `brokerTrade` or `batchBrokerTrade`. Assets will be transferred to
|
||||||
|
// and from this address as the effectual taker of the orders.
|
||||||
|
address internal _sender;
|
||||||
|
|
||||||
|
using LibSafeMath for uint256;
|
||||||
|
using LibBytes for bytes;
|
||||||
|
using LibAssetDataTransfer for bytes;
|
||||||
|
|
||||||
|
/// @param exchange Address of the 0x Exchange contract.
|
||||||
|
/// @param exchange Address of the Wrapped Ether contract.
|
||||||
|
/// @param exchange Address of the 0x ERC1155 Asset Proxy contract.
|
||||||
|
constructor (
|
||||||
|
address exchange,
|
||||||
|
address weth
|
||||||
|
)
|
||||||
|
public
|
||||||
|
MixinWethUtils(
|
||||||
|
exchange,
|
||||||
|
weth
|
||||||
|
)
|
||||||
|
{
|
||||||
|
EXCHANGE = exchange;
|
||||||
|
ERC1155_PROXY = IExchange(EXCHANGE).getAssetProxy(IAssetData(address(0)).ERC1155Assets.selector);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev The Broker implements the ERC1155 transfer function to be compatible with the ERC1155 asset proxy
|
||||||
|
/// @param from Since the Broker serves as the taker of the order, this should equal `address(this)`
|
||||||
|
/// @param to This should be the maker of the order.
|
||||||
|
/// @param amounts Should be an array of just one `uint256`, specifying the amount of the brokered assets to transfer.
|
||||||
|
/// @param data Encodes the validator contract address and any auxiliary data it needs for property validation.
|
||||||
|
function safeBatchTransferFrom(
|
||||||
|
address from,
|
||||||
|
address to,
|
||||||
|
uint256[] calldata /* ids */,
|
||||||
|
uint256[] calldata amounts,
|
||||||
|
bytes calldata data
|
||||||
|
)
|
||||||
|
external
|
||||||
|
{
|
||||||
|
// Only the ERC1155 asset proxy contract should be calling this function.
|
||||||
|
if (msg.sender != ERC1155_PROXY) {
|
||||||
|
LibRichErrors.rrevert(LibBrokerRichErrors.OnlyERC1155ProxyError(
|
||||||
|
msg.sender
|
||||||
|
));
|
||||||
|
}
|
||||||
|
// Only `takerAssetData` should be using Broker assets
|
||||||
|
if (from != address(this)) {
|
||||||
|
LibRichErrors.rrevert(
|
||||||
|
LibBrokerRichErrors.InvalidFromAddressError(from)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Only one asset amount should be specified.
|
||||||
|
if (amounts.length != 1) {
|
||||||
|
LibRichErrors.rrevert(
|
||||||
|
LibBrokerRichErrors.AmountsLengthMustEqualOneError(amounts.length)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint256 cacheIndex = _cacheIndex;
|
||||||
|
uint256 remainingAmount = amounts[0];
|
||||||
|
|
||||||
|
// Verify that there are enough broker assets to transfer
|
||||||
|
if (_cachedTokenIds.length.safeSub(cacheIndex) < remainingAmount) {
|
||||||
|
LibRichErrors.rrevert(
|
||||||
|
LibBrokerRichErrors.TooFewBrokerAssetsProvidedError(_cachedTokenIds.length)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode validator and params from `data`
|
||||||
|
(address tokenAddress, address validator, bytes memory propertyData) = abi.decode(
|
||||||
|
data,
|
||||||
|
(address, address, bytes)
|
||||||
|
);
|
||||||
|
|
||||||
|
while (remainingAmount != 0) {
|
||||||
|
uint256 tokenId = _cachedTokenIds[cacheIndex];
|
||||||
|
cacheIndex++;
|
||||||
|
|
||||||
|
// Validate asset properties
|
||||||
|
IPropertyValidator(validator).checkBrokerAsset(
|
||||||
|
tokenId,
|
||||||
|
propertyData
|
||||||
|
);
|
||||||
|
|
||||||
|
// Perform the transfer
|
||||||
|
IERC721Token(tokenAddress).transferFrom(
|
||||||
|
_sender,
|
||||||
|
to,
|
||||||
|
tokenId
|
||||||
|
);
|
||||||
|
|
||||||
|
remainingAmount--;
|
||||||
|
}
|
||||||
|
// Update cache index in storage
|
||||||
|
_cacheIndex = cacheIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Fills a single property-based order by the given amount using the given assets.
|
||||||
|
/// Pays protocol fees using either the ETH supplied by the taker to the transaction or
|
||||||
|
/// WETH acquired from the maker during settlement. The final WETH balance is sent to the taker.
|
||||||
|
/// @param brokeredTokenIds Token IDs specified by the taker to be used to fill the orders.
|
||||||
|
/// @param order The property-based order to fill. The format of a property-based order is the
|
||||||
|
/// same as that of a normal order, except the takerAssetData. Instaed of specifying a
|
||||||
|
/// specific ERC721 asset, the takerAssetData should be ERC1155 assetData where the
|
||||||
|
/// underlying tokenAddress is this contract's address and the desired properties are
|
||||||
|
/// encoded in the extra data field. Also note that takerFees must be denominated in
|
||||||
|
/// WETH (or zero).
|
||||||
|
/// @param takerAssetFillAmount The amount to fill the order by.
|
||||||
|
/// @param signature The maker's signature of the given order.
|
||||||
|
/// @param fillFunctionSelector The selector for either `fillOrder` or `fillOrKillOrder`.
|
||||||
|
/// @param ethFeeAmounts Amounts of ETH, denominated in Wei, that are paid to corresponding feeRecipients.
|
||||||
|
/// @param feeRecipients Addresses that will receive ETH when orders are filled.
|
||||||
|
/// @return fillResults Amounts filled and fees paid by the maker and taker.
|
||||||
|
function brokerTrade(
|
||||||
|
uint256[] memory brokeredTokenIds,
|
||||||
|
LibOrder.Order memory order,
|
||||||
|
uint256 takerAssetFillAmount,
|
||||||
|
bytes memory signature,
|
||||||
|
bytes4 fillFunctionSelector,
|
||||||
|
uint256[] memory ethFeeAmounts,
|
||||||
|
address payable[] memory feeRecipients
|
||||||
|
)
|
||||||
|
public
|
||||||
|
payable
|
||||||
|
returns (LibFillResults.FillResults memory fillResults)
|
||||||
|
{
|
||||||
|
// Cache the taker-supplied asset data
|
||||||
|
_cachedTokenIds = brokeredTokenIds;
|
||||||
|
// Cache the sender's address
|
||||||
|
_sender = msg.sender;
|
||||||
|
|
||||||
|
// Sanity-check the provided function selector
|
||||||
|
if (
|
||||||
|
fillFunctionSelector != IExchange(address(0)).fillOrder.selector &&
|
||||||
|
fillFunctionSelector != IExchange(address(0)).fillOrKillOrder.selector
|
||||||
|
) {
|
||||||
|
LibBrokerRichErrors.InvalidFunctionSelectorError(fillFunctionSelector);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pay ETH affiliate fees to all feeRecipient addresses
|
||||||
|
_transferEthFeesAndWrapRemaining(ethFeeAmounts, feeRecipients);
|
||||||
|
|
||||||
|
// Perform the fill
|
||||||
|
bytes memory fillCalldata = abi.encodeWithSelector(
|
||||||
|
fillFunctionSelector,
|
||||||
|
order,
|
||||||
|
takerAssetFillAmount,
|
||||||
|
signature
|
||||||
|
);
|
||||||
|
// solhint-disable-next-line avoid-call-value
|
||||||
|
(bool didSucceed, bytes memory returnData) = EXCHANGE.call(fillCalldata);
|
||||||
|
if (didSucceed) {
|
||||||
|
fillResults = abi.decode(returnData, (LibFillResults.FillResults));
|
||||||
|
} else {
|
||||||
|
// Re-throw error
|
||||||
|
LibRichErrors.rrevert(returnData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transfer maker asset to taker
|
||||||
|
if (!order.makerAssetData.equals(WETH_ASSET_DATA)) {
|
||||||
|
order.makerAssetData.transferOut(fillResults.makerAssetFilledAmount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refund remaining ETH to msg.sender.
|
||||||
|
_unwrapAndTransferEth(WETH.balanceOf(address(this)));
|
||||||
|
|
||||||
|
_clearStorage();
|
||||||
|
|
||||||
|
return fillResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Fills multiple property-based orders by the given amounts using the given assets.
|
||||||
|
/// Pays protocol fees using either the ETH supplied by the taker to the transaction or
|
||||||
|
/// WETH acquired from the maker during settlement. The final WETH balance is sent to the taker.
|
||||||
|
/// @param brokeredTokenIds Token IDs specified by the taker to be used to fill the orders.
|
||||||
|
/// @param orders The property-based orders to fill. The format of a property-based order is the
|
||||||
|
/// same as that of a normal order, except the takerAssetData. Instaed of specifying a
|
||||||
|
/// specific ERC721 asset, the takerAssetData should be ERC1155 assetData where the
|
||||||
|
/// underlying tokenAddress is this contract's address and the desired properties are
|
||||||
|
/// encoded in the extra data field. Also note that takerFees must be denominated in
|
||||||
|
/// WETH (or zero).
|
||||||
|
/// @param takerAssetFillAmounts The amounts to fill the orders by.
|
||||||
|
/// @param signatures The makers' signatures for the given orders.
|
||||||
|
/// @param batchFillFunctionSelector The selector for either `batchFillOrders`,
|
||||||
|
/// `batchFillOrKillOrders`, or `batchFillOrdersNoThrow`.
|
||||||
|
/// @param ethFeeAmounts Amounts of ETH, denominated in Wei, that are paid to corresponding feeRecipients.
|
||||||
|
/// @param feeRecipients Addresses that will receive ETH when orders are filled.
|
||||||
|
/// @return fillResults Amounts filled and fees paid by the makers and taker.
|
||||||
|
function batchBrokerTrade(
|
||||||
|
uint256[] memory brokeredTokenIds,
|
||||||
|
LibOrder.Order[] memory orders,
|
||||||
|
uint256[] memory takerAssetFillAmounts,
|
||||||
|
bytes[] memory signatures,
|
||||||
|
bytes4 batchFillFunctionSelector,
|
||||||
|
uint256[] memory ethFeeAmounts,
|
||||||
|
address payable[] memory feeRecipients
|
||||||
|
)
|
||||||
|
public
|
||||||
|
payable
|
||||||
|
returns (LibFillResults.FillResults[] memory fillResults)
|
||||||
|
{
|
||||||
|
// Cache the taker-supplied asset data
|
||||||
|
_cachedTokenIds = brokeredTokenIds;
|
||||||
|
// Cache the sender's address
|
||||||
|
_sender = msg.sender;
|
||||||
|
|
||||||
|
// Sanity-check the provided function selector
|
||||||
|
if (
|
||||||
|
batchFillFunctionSelector != IExchange(address(0)).batchFillOrders.selector &&
|
||||||
|
batchFillFunctionSelector != IExchange(address(0)).batchFillOrKillOrders.selector &&
|
||||||
|
batchFillFunctionSelector != IExchange(address(0)).batchFillOrdersNoThrow.selector
|
||||||
|
) {
|
||||||
|
LibBrokerRichErrors.InvalidFunctionSelectorError(batchFillFunctionSelector);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pay ETH affiliate fees to all feeRecipient addresses
|
||||||
|
_transferEthFeesAndWrapRemaining(ethFeeAmounts, feeRecipients);
|
||||||
|
|
||||||
|
// Perform the batch fill
|
||||||
|
bytes memory batchFillCalldata = abi.encodeWithSelector(
|
||||||
|
batchFillFunctionSelector,
|
||||||
|
orders,
|
||||||
|
takerAssetFillAmounts,
|
||||||
|
signatures
|
||||||
|
);
|
||||||
|
// solhint-disable-next-line avoid-call-value
|
||||||
|
(bool didSucceed, bytes memory returnData) = EXCHANGE.call(batchFillCalldata);
|
||||||
|
if (didSucceed) {
|
||||||
|
// solhint-disable-next-line indent
|
||||||
|
fillResults = abi.decode(returnData, (LibFillResults.FillResults[]));
|
||||||
|
} else {
|
||||||
|
// Re-throw error
|
||||||
|
LibRichErrors.rrevert(returnData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transfer maker assets to taker
|
||||||
|
for (uint256 i = 0; i < orders.length; i++) {
|
||||||
|
if (!orders[i].makerAssetData.equals(WETH_ASSET_DATA)) {
|
||||||
|
orders[i].makerAssetData.transferOut(fillResults[i].makerAssetFilledAmount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refund remaining ETH to msg.sender.
|
||||||
|
_unwrapAndTransferEth(WETH.balanceOf(address(this)));
|
||||||
|
|
||||||
|
_clearStorage();
|
||||||
|
|
||||||
|
return fillResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _clearStorage()
|
||||||
|
private
|
||||||
|
{
|
||||||
|
delete _cachedTokenIds;
|
||||||
|
_cacheIndex = 0;
|
||||||
|
_sender = address(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
101
contracts/broker/contracts/src/interfaces/IBroker.sol
Normal file
101
contracts/broker/contracts/src/interfaces/IBroker.sol
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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/LibFillResults.sol";
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||||
|
|
||||||
|
|
||||||
|
// solhint-disable space-after-comma
|
||||||
|
interface IBroker {
|
||||||
|
|
||||||
|
/// @dev Fills a single property-based order by the given amount using the given assets.
|
||||||
|
/// Pays protocol fees using either the ETH supplied by the taker to the transaction or
|
||||||
|
/// WETH acquired from the maker during settlement. The final WETH balance is sent to the taker.
|
||||||
|
/// @param brokeredTokenIds Token IDs specified by the taker to be used to fill the orders.
|
||||||
|
/// @param order The property-based order to fill. The format of a property-based order is the
|
||||||
|
/// same as that of a normal order, except the takerAssetData. Instaed of specifying a
|
||||||
|
/// specific ERC721 asset, the takerAssetData should be ERC1155 assetData where the
|
||||||
|
/// underlying tokenAddress is this contract's address and the desired properties are
|
||||||
|
/// encoded in the extra data field. Also note that takerFees must be denominated in
|
||||||
|
/// WETH (or zero).
|
||||||
|
/// @param takerAssetFillAmount The amount to fill the order by.
|
||||||
|
/// @param signature The maker's signature of the given order.
|
||||||
|
/// @param fillFunctionSelector The selector for either `fillOrder` or `fillOrKillOrder`.
|
||||||
|
/// @param ethFeeAmounts Amounts of ETH, denominated in Wei, that are paid to corresponding feeRecipients.
|
||||||
|
/// @param feeRecipients Addresses that will receive ETH when orders are filled.
|
||||||
|
/// @return fillResults Amounts filled and fees paid by the maker and taker.
|
||||||
|
function brokerTrade(
|
||||||
|
uint256[] calldata brokeredTokenIds,
|
||||||
|
LibOrder.Order calldata order,
|
||||||
|
uint256 takerAssetFillAmount,
|
||||||
|
bytes calldata signature,
|
||||||
|
bytes4 fillFunctionSelector,
|
||||||
|
uint256[] calldata ethFeeAmounts,
|
||||||
|
address payable[] calldata feeRecipients
|
||||||
|
)
|
||||||
|
external
|
||||||
|
payable
|
||||||
|
returns (LibFillResults.FillResults memory fillResults);
|
||||||
|
|
||||||
|
/// @dev Fills multiple property-based orders by the given amounts using the given assets.
|
||||||
|
/// Pays protocol fees using either the ETH supplied by the taker to the transaction or
|
||||||
|
/// WETH acquired from the maker during settlement. The final WETH balance is sent to the taker.
|
||||||
|
/// @param brokeredTokenIds Token IDs specified by the taker to be used to fill the orders.
|
||||||
|
/// @param orders The property-based orders to fill. The format of a property-based order is the
|
||||||
|
/// same as that of a normal order, except the takerAssetData. Instaed of specifying a
|
||||||
|
/// specific ERC721 asset, the takerAssetData should be ERC1155 assetData where the
|
||||||
|
/// underlying tokenAddress is this contract's address and the desired properties are
|
||||||
|
/// encoded in the extra data field. Also note that takerFees must be denominated in
|
||||||
|
/// WETH (or zero).
|
||||||
|
/// @param takerAssetFillAmounts The amounts to fill the orders by.
|
||||||
|
/// @param signatures The makers' signatures for the given orders.
|
||||||
|
/// @param batchFillFunctionSelector The selector for either `batchFillOrders`,
|
||||||
|
/// `batchFillOrKillOrders`, or `batchFillOrdersNoThrow`.
|
||||||
|
/// @param ethFeeAmounts Amounts of ETH, denominated in Wei, that are paid to corresponding feeRecipients.
|
||||||
|
/// @param feeRecipients Addresses that will receive ETH when orders are filled.
|
||||||
|
/// @return fillResults Amounts filled and fees paid by the makers and taker.
|
||||||
|
function batchBrokerTrade(
|
||||||
|
uint256[] calldata brokeredTokenIds,
|
||||||
|
LibOrder.Order[] calldata orders,
|
||||||
|
uint256[] calldata takerAssetFillAmounts,
|
||||||
|
bytes[] calldata signatures,
|
||||||
|
bytes4 batchFillFunctionSelector,
|
||||||
|
uint256[] calldata ethFeeAmounts,
|
||||||
|
address payable[] calldata feeRecipients
|
||||||
|
)
|
||||||
|
external
|
||||||
|
payable
|
||||||
|
returns (LibFillResults.FillResults[] memory fillResults);
|
||||||
|
|
||||||
|
/// @dev The Broker implements the ERC1155 transfer function to be compatible with the ERC1155 asset proxy
|
||||||
|
/// @param from Since the Broker serves as the taker of the order, this should equal `address(this)`
|
||||||
|
/// @param to This should be the maker of the order.
|
||||||
|
/// @param amounts Should be an array of just one `uint256`, specifying the amount of the brokered assets to transfer.
|
||||||
|
/// @param data Encodes the validator contract address and any auxiliary data it needs for property validation.
|
||||||
|
function safeBatchTransferFrom(
|
||||||
|
address from,
|
||||||
|
address to,
|
||||||
|
uint256[] calldata /* ids */,
|
||||||
|
uint256[] calldata amounts,
|
||||||
|
bytes calldata data
|
||||||
|
)
|
||||||
|
external;
|
||||||
|
}
|
||||||
33
contracts/broker/contracts/src/interfaces/IGodsUnchained.sol
Normal file
33
contracts/broker/contracts/src/interfaces/IGodsUnchained.sol
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.5.9;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
|
||||||
|
interface IGodsUnchained {
|
||||||
|
|
||||||
|
/// @dev Returns the proto and quality for a particular card given its token id
|
||||||
|
/// @param tokenId The id of the card to query.
|
||||||
|
/// @return proto The proto of the given card.
|
||||||
|
/// @return quality The quality of the given card
|
||||||
|
function getDetails(uint256 tokenId)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (uint16 proto, uint8 quality);
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.5.9;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
|
||||||
|
interface IPropertyValidator {
|
||||||
|
|
||||||
|
/// @dev Checks that the given asset data satisfies the properties encoded in `propertyData`.
|
||||||
|
/// Should revert if the asset does not satisfy the specified properties.
|
||||||
|
/// @param tokenId The ERC721 tokenId of the asset to check.
|
||||||
|
/// @param propertyData Encoded properties or auxiliary data needed to perform the check.
|
||||||
|
function checkBrokerAsset(
|
||||||
|
uint256 tokenId,
|
||||||
|
bytes calldata propertyData
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view;
|
||||||
|
}
|
||||||
109
contracts/broker/contracts/src/libs/LibBrokerRichErrors.sol
Normal file
109
contracts/broker/contracts/src/libs/LibBrokerRichErrors.sol
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.5.9;
|
||||||
|
|
||||||
|
|
||||||
|
library LibBrokerRichErrors {
|
||||||
|
|
||||||
|
// bytes4(keccak256("InvalidFromAddressError(address)"))
|
||||||
|
bytes4 internal constant INVALID_FROM_ADDRESS_ERROR_SELECTOR =
|
||||||
|
0x906bfb3c;
|
||||||
|
|
||||||
|
// bytes4(keccak256("AmountsLengthMustEqualOneError(uint256)"))
|
||||||
|
bytes4 internal constant AMOUNTS_LENGTH_MUST_EQUAL_ONE_ERROR_SELECTOR =
|
||||||
|
0xba9be200;
|
||||||
|
|
||||||
|
// bytes4(keccak256("TooFewBrokerAssetsProvidedError(uint256)"))
|
||||||
|
bytes4 internal constant TOO_FEW_BROKER_ASSETS_PROVIDED_ERROR_SELECTOR =
|
||||||
|
0x55272586;
|
||||||
|
|
||||||
|
// bytes4(keccak256("InvalidFunctionSelectorError(bytes4)"))
|
||||||
|
bytes4 internal constant INVALID_FUNCTION_SELECTOR_ERROR_SELECTOR =
|
||||||
|
0x540943f1;
|
||||||
|
|
||||||
|
// bytes4(keccak256("OnlyERC1155ProxyError(address)"))
|
||||||
|
bytes4 internal constant ONLY_ERC_1155_PROXY_ERROR_SELECTOR =
|
||||||
|
0xccc529af;
|
||||||
|
|
||||||
|
// solhint-disable func-name-mixedcase
|
||||||
|
function InvalidFromAddressError(
|
||||||
|
address from
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (bytes memory)
|
||||||
|
{
|
||||||
|
return abi.encodeWithSelector(
|
||||||
|
INVALID_FROM_ADDRESS_ERROR_SELECTOR,
|
||||||
|
from
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function AmountsLengthMustEqualOneError(
|
||||||
|
uint256 amountsLength
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (bytes memory)
|
||||||
|
{
|
||||||
|
return abi.encodeWithSelector(
|
||||||
|
AMOUNTS_LENGTH_MUST_EQUAL_ONE_ERROR_SELECTOR,
|
||||||
|
amountsLength
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function TooFewBrokerAssetsProvidedError(
|
||||||
|
uint256 numBrokeredAssets
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (bytes memory)
|
||||||
|
{
|
||||||
|
return abi.encodeWithSelector(
|
||||||
|
TOO_FEW_BROKER_ASSETS_PROVIDED_ERROR_SELECTOR,
|
||||||
|
numBrokeredAssets
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function InvalidFunctionSelectorError(
|
||||||
|
bytes4 selector
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (bytes memory)
|
||||||
|
{
|
||||||
|
return abi.encodeWithSelector(
|
||||||
|
INVALID_FUNCTION_SELECTOR_ERROR_SELECTOR,
|
||||||
|
selector
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function OnlyERC1155ProxyError(
|
||||||
|
address sender
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (bytes memory)
|
||||||
|
{
|
||||||
|
return abi.encodeWithSelector(
|
||||||
|
ONLY_ERC_1155_PROXY_ERROR_SELECTOR,
|
||||||
|
sender
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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 "../interfaces/IGodsUnchained.sol";
|
||||||
|
import "../interfaces/IPropertyValidator.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract GodsUnchainedValidator is
|
||||||
|
IPropertyValidator
|
||||||
|
{
|
||||||
|
IGodsUnchained internal GODS_UNCHAINED; // solhint-disable-line var-name-mixedcase
|
||||||
|
|
||||||
|
using LibBytes for bytes;
|
||||||
|
|
||||||
|
constructor(address _godsUnchained)
|
||||||
|
public
|
||||||
|
{
|
||||||
|
GODS_UNCHAINED = IGodsUnchained(_godsUnchained);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Checks that the given card (encoded as assetData) has the proto and quality encoded in `propertyData`.
|
||||||
|
/// Reverts if the card doesn't match the specified proto and quality.
|
||||||
|
/// @param tokenId The ERC721 tokenId of the card to check.
|
||||||
|
/// @param propertyData Encoded proto and quality that the card is expected to have.
|
||||||
|
function checkBrokerAsset(
|
||||||
|
uint256 tokenId,
|
||||||
|
bytes calldata propertyData
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
{
|
||||||
|
(uint16 expectedProto, uint8 expectedQuality) = abi.decode(
|
||||||
|
propertyData,
|
||||||
|
(uint16, uint8)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Validate card properties.
|
||||||
|
(uint16 proto, uint8 quality) = GODS_UNCHAINED.getDetails(tokenId);
|
||||||
|
require(proto == expectedProto, "GodsUnchainedValidator/PROTO_MISMATCH");
|
||||||
|
require(quality == expectedQuality, "GodsUnchainedValidator/QUALITY_MISMATCH");
|
||||||
|
}
|
||||||
|
}
|
||||||
55
contracts/broker/contracts/test/TestGodsUnchained.sol
Normal file
55
contracts/broker/contracts/test/TestGodsUnchained.sol
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.5.9;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-erc721/contracts/test/DummyERC721Token.sol";
|
||||||
|
import "../src/interfaces/IGodsUnchained.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract TestGodsUnchained is
|
||||||
|
IGodsUnchained,
|
||||||
|
DummyERC721Token
|
||||||
|
{
|
||||||
|
mapping (uint256 => uint16) internal _protoByTokenId;
|
||||||
|
mapping (uint256 => uint8) internal _qualityByTokenId;
|
||||||
|
|
||||||
|
constructor (
|
||||||
|
string memory _name,
|
||||||
|
string memory _symbol
|
||||||
|
)
|
||||||
|
public
|
||||||
|
DummyERC721Token(_name, _symbol)
|
||||||
|
{} // solhint-disable-line no-empty-blocks
|
||||||
|
|
||||||
|
function setTokenProperties(uint256 tokenId, uint16 proto, uint8 quality)
|
||||||
|
external
|
||||||
|
{
|
||||||
|
_protoByTokenId[tokenId] = proto;
|
||||||
|
_qualityByTokenId[tokenId] = quality;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDetails(uint256 tokenId)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (uint16 proto, uint8 quality)
|
||||||
|
{
|
||||||
|
return (_protoByTokenId[tokenId], _qualityByTokenId[tokenId]);
|
||||||
|
}
|
||||||
|
}
|
||||||
96
contracts/broker/package.json
Normal file
96
contracts/broker/package.json
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
{
|
||||||
|
"name": "@0x/contracts-broker",
|
||||||
|
"version": "1.1.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.12"
|
||||||
|
},
|
||||||
|
"description": "Extension of 0x protocol for property-based orders",
|
||||||
|
"main": "lib/src/index.js",
|
||||||
|
"directories": {
|
||||||
|
"test": "test"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "yarn build:contracts && yarn build:ts",
|
||||||
|
"build:contracts": "run-s compile contracts:gen generate_contract_wrappers contracts:copy",
|
||||||
|
"build:ts": "tsc -b",
|
||||||
|
"build:ci": "yarn build",
|
||||||
|
"test": "yarn run_mocha",
|
||||||
|
"rebuild_and_test": "run-s build test",
|
||||||
|
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
||||||
|
"test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html",
|
||||||
|
"test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha",
|
||||||
|
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
|
||||||
|
"compile": "sol-compiler",
|
||||||
|
"watch": "sol-compiler -w",
|
||||||
|
"clean": "shx rm -rf lib test/generated-artifacts test/generated-wrappers generated-artifacts generated-wrappers",
|
||||||
|
"generate_contract_wrappers": "abi-gen --debug --abis ${npm_package_config_abis} --output test/generated-wrappers --backend ethers",
|
||||||
|
"lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./test/generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude ./test/generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||||
|
"coverage:report:text": "istanbul report text",
|
||||||
|
"coverage:report:html": "istanbul report html && open coverage/index.html",
|
||||||
|
"profiler:report:html": "istanbul report html && open coverage/index.html",
|
||||||
|
"coverage:report:lcov": "istanbul report lcov",
|
||||||
|
"test:circleci": "yarn test",
|
||||||
|
"contracts:gen": "contracts-gen generate",
|
||||||
|
"contracts:copy": "contracts-gen copy",
|
||||||
|
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol",
|
||||||
|
"compile:truffle": "truffle compile",
|
||||||
|
"docs:md": "ts-doc-gen --sourceDir='$PROJECT_FILES' --output=$MD_FILE_DIR --fileExtension=mdx --tsconfig=./typedoc-tsconfig.json",
|
||||||
|
"docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES"
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
|
||||||
|
"abis": "./test/generated-artifacts/@(Broker|GodsUnchainedValidator|IBroker|IGodsUnchained|IPropertyValidator|LibBrokerRichErrors|TestGodsUnchained).json"
|
||||||
|
},
|
||||||
|
"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": "^5.2.0",
|
||||||
|
"@0x/contracts-asset-proxy": "^3.2.1",
|
||||||
|
"@0x/contracts-erc20": "^3.1.1",
|
||||||
|
"@0x/contracts-erc721": "^3.1.1",
|
||||||
|
"@0x/contracts-exchange": "^3.2.1",
|
||||||
|
"@0x/contracts-exchange-libs": "^4.3.1",
|
||||||
|
"@0x/contracts-gen": "^2.0.7",
|
||||||
|
"@0x/contracts-test-utils": "^5.1.5",
|
||||||
|
"@0x/contracts-utils": "^4.3.1",
|
||||||
|
"@0x/sol-compiler": "^4.0.7",
|
||||||
|
"@0x/ts-doc-gen": "^0.0.22",
|
||||||
|
"@0x/tslint-config": "^4.0.0",
|
||||||
|
"@0x/types": "^3.1.2",
|
||||||
|
"@0x/web3-wrapper": "^7.0.6",
|
||||||
|
"@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",
|
||||||
|
"lodash": "^4.17.11",
|
||||||
|
"make-promises-safe": "^1.1.0",
|
||||||
|
"mocha": "^6.2.0",
|
||||||
|
"npm-run-all": "^4.1.2",
|
||||||
|
"shx": "^0.2.2",
|
||||||
|
"solhint": "^1.4.1",
|
||||||
|
"truffle": "^5.0.32",
|
||||||
|
"tslint": "5.11.0",
|
||||||
|
"typedoc": "^0.15.0",
|
||||||
|
"typescript": "3.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@0x/base-contract": "^6.2.0",
|
||||||
|
"@0x/order-utils": "^10.2.1",
|
||||||
|
"@0x/typescript-typings": "^5.0.2",
|
||||||
|
"@0x/utils": "^5.4.0",
|
||||||
|
"ethereum-types": "^3.1.0"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
}
|
||||||
|
}
|
||||||
23
contracts/broker/src/artifacts.ts
Normal file
23
contracts/broker/src/artifacts.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* -----------------------------------------------------------------------------
|
||||||
|
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||||
|
* -----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
import { ContractArtifact } from 'ethereum-types';
|
||||||
|
|
||||||
|
import * as Broker from '../generated-artifacts/Broker.json';
|
||||||
|
import * as GodsUnchainedValidator from '../generated-artifacts/GodsUnchainedValidator.json';
|
||||||
|
import * as IBroker from '../generated-artifacts/IBroker.json';
|
||||||
|
import * as IGodsUnchained from '../generated-artifacts/IGodsUnchained.json';
|
||||||
|
import * as IPropertyValidator from '../generated-artifacts/IPropertyValidator.json';
|
||||||
|
import * as LibBrokerRichErrors from '../generated-artifacts/LibBrokerRichErrors.json';
|
||||||
|
import * as TestGodsUnchained from '../generated-artifacts/TestGodsUnchained.json';
|
||||||
|
export const artifacts = {
|
||||||
|
Broker: Broker as ContractArtifact,
|
||||||
|
IBroker: IBroker as ContractArtifact,
|
||||||
|
IGodsUnchained: IGodsUnchained as ContractArtifact,
|
||||||
|
IPropertyValidator: IPropertyValidator as ContractArtifact,
|
||||||
|
LibBrokerRichErrors: LibBrokerRichErrors as ContractArtifact,
|
||||||
|
GodsUnchainedValidator: GodsUnchainedValidator as ContractArtifact,
|
||||||
|
TestGodsUnchained: TestGodsUnchained as ContractArtifact,
|
||||||
|
};
|
||||||
59
contracts/broker/src/gods_unchained_utils.ts
Normal file
59
contracts/broker/src/gods_unchained_utils.ts
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import { assetDataUtils } from '@0x/order-utils';
|
||||||
|
import { ERC1155AssetData } from '@0x/types';
|
||||||
|
import { AbiEncoder, BigNumber } from '@0x/utils';
|
||||||
|
|
||||||
|
export interface GodsUnchainedProperties {
|
||||||
|
proto: BigNumber | number;
|
||||||
|
quality: BigNumber | number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const propertyDataEncoder = AbiEncoder.create([{ name: 'proto', type: 'uint16' }, { name: 'quality', type: 'uint8' }]);
|
||||||
|
const brokerDataEncoder = AbiEncoder.create([
|
||||||
|
{ name: 'godsUnchainedAddress', type: 'address' },
|
||||||
|
{ name: 'validatorAddress', type: 'address' },
|
||||||
|
{ name: 'propertyData', type: 'bytes' },
|
||||||
|
]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes the given proto and quality into the bytes format expected by the GodsUnchainedValidator.
|
||||||
|
*/
|
||||||
|
export function encodePropertyData(properties: GodsUnchainedProperties): string {
|
||||||
|
return propertyDataEncoder.encode(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes the given proto and quality into ERC1155 asset data to be used as the takerAssetData
|
||||||
|
* of a property-based GodsUnchained order. Must also provide the addresses of the Broker,
|
||||||
|
* GodsUnchained, and GodsUnchainedValidator contracts. The optional bundleSize parameter specifies
|
||||||
|
* how many cards are expected for each "unit" of the takerAssetAmount. For example, If the
|
||||||
|
* takerAssetAmount is 3 and the bundleSize is 2, the taker must provide 2, 4, or 6 cards
|
||||||
|
* with the given proto and quality to fill the order. If an odd number is provided, the fill fails.
|
||||||
|
*/
|
||||||
|
export function encodeBrokerAssetData(
|
||||||
|
brokerAddress: string,
|
||||||
|
godsUnchainedAddress: string,
|
||||||
|
validatorAddress: string,
|
||||||
|
properties: GodsUnchainedProperties,
|
||||||
|
bundleSize: number = 1,
|
||||||
|
): string {
|
||||||
|
const propertyData = propertyDataEncoder.encode(properties);
|
||||||
|
const brokerData = brokerDataEncoder.encode({ godsUnchainedAddress, validatorAddress, propertyData });
|
||||||
|
return assetDataUtils.encodeERC1155AssetData(brokerAddress, [], [new BigNumber(bundleSize)], brokerData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes proto and quality from the bytes format expected by the GodsUnchainedValidator.
|
||||||
|
*/
|
||||||
|
export function decodePropertyData(propertyData: string): GodsUnchainedProperties {
|
||||||
|
return propertyDataEncoder.decode(propertyData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes proto and quality from the ERC1155 takerAssetData of a property-based GodsUnchained order.
|
||||||
|
*/
|
||||||
|
export function decodeBrokerAssetData(brokerAssetData: string): GodsUnchainedProperties {
|
||||||
|
// tslint:disable-next-line:no-unnecessary-type-assertion
|
||||||
|
const { callbackData: brokerData } = assetDataUtils.decodeAssetDataOrThrow(brokerAssetData) as ERC1155AssetData;
|
||||||
|
const { propertyData } = brokerDataEncoder.decode(brokerData);
|
||||||
|
return decodePropertyData(propertyData);
|
||||||
|
}
|
||||||
32
contracts/broker/src/index.ts
Normal file
32
contracts/broker/src/index.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
export { artifacts } from './artifacts';
|
||||||
|
export { BrokerContract, GodsUnchainedValidatorContract, TestGodsUnchainedContract } from './wrappers';
|
||||||
|
export * from './gods_unchained_utils';
|
||||||
|
export { BrokerRevertErrors } from '@0x/utils';
|
||||||
|
export {
|
||||||
|
ContractArtifact,
|
||||||
|
ContractChains,
|
||||||
|
CompilerOpts,
|
||||||
|
StandardContractOutput,
|
||||||
|
CompilerSettings,
|
||||||
|
ContractChainData,
|
||||||
|
ContractAbi,
|
||||||
|
DevdocOutput,
|
||||||
|
EvmOutput,
|
||||||
|
CompilerSettingsMetadata,
|
||||||
|
OptimizerSettings,
|
||||||
|
OutputField,
|
||||||
|
ParamDescription,
|
||||||
|
EvmBytecodeOutput,
|
||||||
|
AbiDefinition,
|
||||||
|
FunctionAbi,
|
||||||
|
EventAbi,
|
||||||
|
RevertErrorAbi,
|
||||||
|
EventParameter,
|
||||||
|
DataItem,
|
||||||
|
MethodAbi,
|
||||||
|
ConstructorAbi,
|
||||||
|
FallbackAbi,
|
||||||
|
ConstructorStateMutability,
|
||||||
|
TupleDataItem,
|
||||||
|
StateMutability,
|
||||||
|
} from 'ethereum-types';
|
||||||
12
contracts/broker/src/wrappers.ts
Normal file
12
contracts/broker/src/wrappers.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/*
|
||||||
|
* -----------------------------------------------------------------------------
|
||||||
|
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||||
|
* -----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
export * from '../generated-wrappers/broker';
|
||||||
|
export * from '../generated-wrappers/gods_unchained_validator';
|
||||||
|
export * from '../generated-wrappers/i_broker';
|
||||||
|
export * from '../generated-wrappers/i_gods_unchained';
|
||||||
|
export * from '../generated-wrappers/i_property_validator';
|
||||||
|
export * from '../generated-wrappers/lib_broker_rich_errors';
|
||||||
|
export * from '../generated-wrappers/test_gods_unchained';
|
||||||
23
contracts/broker/test/artifacts.ts
Normal file
23
contracts/broker/test/artifacts.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* -----------------------------------------------------------------------------
|
||||||
|
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||||
|
* -----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
import { ContractArtifact } from 'ethereum-types';
|
||||||
|
|
||||||
|
import * as Broker from '../test/generated-artifacts/Broker.json';
|
||||||
|
import * as GodsUnchainedValidator from '../test/generated-artifacts/GodsUnchainedValidator.json';
|
||||||
|
import * as IBroker from '../test/generated-artifacts/IBroker.json';
|
||||||
|
import * as IGodsUnchained from '../test/generated-artifacts/IGodsUnchained.json';
|
||||||
|
import * as IPropertyValidator from '../test/generated-artifacts/IPropertyValidator.json';
|
||||||
|
import * as LibBrokerRichErrors from '../test/generated-artifacts/LibBrokerRichErrors.json';
|
||||||
|
import * as TestGodsUnchained from '../test/generated-artifacts/TestGodsUnchained.json';
|
||||||
|
export const artifacts = {
|
||||||
|
Broker: Broker as ContractArtifact,
|
||||||
|
IBroker: IBroker as ContractArtifact,
|
||||||
|
IGodsUnchained: IGodsUnchained as ContractArtifact,
|
||||||
|
IPropertyValidator: IPropertyValidator as ContractArtifact,
|
||||||
|
LibBrokerRichErrors: LibBrokerRichErrors as ContractArtifact,
|
||||||
|
GodsUnchainedValidator: GodsUnchainedValidator as ContractArtifact,
|
||||||
|
TestGodsUnchained: TestGodsUnchained as ContractArtifact,
|
||||||
|
};
|
||||||
56
contracts/broker/test/gods_unchained_validator_test.ts
Normal file
56
contracts/broker/test/gods_unchained_validator_test.ts
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import { blockchainTests, constants, expect, getRandomInteger } from '@0x/contracts-test-utils';
|
||||||
|
import { BigNumber } from '@0x/utils';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
|
import { encodePropertyData } from '../src/gods_unchained_utils';
|
||||||
|
|
||||||
|
import { artifacts } from './artifacts';
|
||||||
|
import { GodsUnchainedValidatorContract, TestGodsUnchainedContract } from './wrappers';
|
||||||
|
|
||||||
|
blockchainTests.resets('GodsUnchainedValidator unit tests', env => {
|
||||||
|
let godsUnchained: TestGodsUnchainedContract;
|
||||||
|
let validator: GodsUnchainedValidatorContract;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
godsUnchained = await TestGodsUnchainedContract.deployFrom0xArtifactAsync(
|
||||||
|
artifacts.TestGodsUnchained,
|
||||||
|
env.provider,
|
||||||
|
env.txDefaults,
|
||||||
|
artifacts,
|
||||||
|
'Gods Unchained Cards',
|
||||||
|
'GU',
|
||||||
|
);
|
||||||
|
|
||||||
|
validator = await GodsUnchainedValidatorContract.deployFrom0xArtifactAsync(
|
||||||
|
artifacts.GodsUnchainedValidator,
|
||||||
|
env.provider,
|
||||||
|
env.txDefaults,
|
||||||
|
artifacts,
|
||||||
|
godsUnchained.address,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('checkBrokerAsset', () => {
|
||||||
|
const proto = new BigNumber(42);
|
||||||
|
const quality = new BigNumber(7);
|
||||||
|
const propertyData = encodePropertyData({ proto, quality });
|
||||||
|
|
||||||
|
it('succeeds if assetData proto and quality match propertyData', async () => {
|
||||||
|
const tokenId = getRandomInteger(0, constants.MAX_UINT256);
|
||||||
|
await godsUnchained.setTokenProperties(tokenId, proto, quality).awaitTransactionSuccessAsync();
|
||||||
|
await validator.checkBrokerAsset(tokenId, propertyData).callAsync();
|
||||||
|
});
|
||||||
|
it("reverts if assetData proto doesn't match propertyData", async () => {
|
||||||
|
const tokenId = getRandomInteger(0, constants.MAX_UINT256);
|
||||||
|
await godsUnchained.setTokenProperties(tokenId, proto.plus(1), quality).awaitTransactionSuccessAsync();
|
||||||
|
const tx = validator.checkBrokerAsset(tokenId, propertyData).callAsync();
|
||||||
|
expect(tx).to.revertWith('PROTO_MISMATCH');
|
||||||
|
});
|
||||||
|
it("reverts if assetData quality doesn't match proeprtyData", async () => {
|
||||||
|
const tokenId = getRandomInteger(0, constants.MAX_UINT256);
|
||||||
|
await godsUnchained.setTokenProperties(tokenId, proto, quality.plus(1)).awaitTransactionSuccessAsync();
|
||||||
|
const tx = validator.checkBrokerAsset(tokenId, propertyData).callAsync();
|
||||||
|
expect(tx).to.revertWith('QUALITY_MISMATCH');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
12
contracts/broker/test/wrappers.ts
Normal file
12
contracts/broker/test/wrappers.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/*
|
||||||
|
* -----------------------------------------------------------------------------
|
||||||
|
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||||
|
* -----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
export * from '../test/generated-wrappers/broker';
|
||||||
|
export * from '../test/generated-wrappers/gods_unchained_validator';
|
||||||
|
export * from '../test/generated-wrappers/i_broker';
|
||||||
|
export * from '../test/generated-wrappers/i_gods_unchained';
|
||||||
|
export * from '../test/generated-wrappers/i_property_validator';
|
||||||
|
export * from '../test/generated-wrappers/lib_broker_rich_errors';
|
||||||
|
export * from '../test/generated-wrappers/test_gods_unchained';
|
||||||
96
contracts/broker/truffle-config.js
Normal file
96
contracts/broker/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: 'istanbul',
|
||||||
|
optimizer: {
|
||||||
|
enabled: true,
|
||||||
|
runs: 1000000,
|
||||||
|
details: { yul: true, deduplicate: true, cse: true, constantOptimizer: true },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user