Compare commits
703 Commits
@0x/contra
...
@0x/contra
Author | SHA1 | Date | |
---|---|---|---|
|
2dd47a2103 | ||
|
f31e530b6e | ||
|
eb50f3fa9c | ||
|
8417fe4fdb | ||
|
0f1c15a6ca | ||
|
9345c4fb7f | ||
|
5e0758917b | ||
|
cbb23a42e2 | ||
|
5a59c286d0 | ||
|
2e8c600d7a | ||
|
e851cb1cbc | ||
|
3da03da32a | ||
|
3ec8924e7f | ||
|
a04722b612 | ||
|
f9a7857a90 | ||
|
58f772c74e | ||
|
a425c7e260 | ||
|
809885afd0 | ||
|
bf9b4b993f | ||
|
d94a26f0f4 | ||
|
bd9c9cedca | ||
|
651e94bd94 | ||
|
162b6f1a74 | ||
|
865a253eb5 | ||
|
9b3781abf1 | ||
|
d89243a0d3 | ||
|
b4cefc64b4 | ||
|
2fd26587e5 | ||
|
282a351859 | ||
|
a84b848ea9 | ||
|
d329320fc2 | ||
|
bc096554b5 | ||
|
5902d878d8 | ||
|
4a133ca36f | ||
|
f7252f919a | ||
|
e05a03a842 | ||
|
dcce8276b8 | ||
|
fd47947e55 | ||
|
ae151df2eb | ||
|
79de188683 | ||
|
6e5c788e13 | ||
|
f53606007d | ||
|
a4ac418bc9 | ||
|
a8c09d0bdb | ||
|
871105a48a | ||
|
3b61129ade | ||
|
f471c79b59 | ||
|
dfd9443f74 | ||
|
a36ff9e365 | ||
|
12e65bbf26 | ||
|
ab9841e60b | ||
|
7a52f12e57 | ||
|
11fd4506ac | ||
|
0c9c68030e | ||
|
55d6eddbb2 | ||
|
8341e60edb | ||
|
6273a1ca73 | ||
|
1b83ebdf89 | ||
|
fef7f0506f | ||
|
f44eb4e383 | ||
|
05df485c4a | ||
|
44857c526b | ||
|
b8ad5d5d32 | ||
|
e3e0d00e21 | ||
|
a9b1ea9690 | ||
|
8e5dd0f8d9 | ||
|
3b0c8f6d92 | ||
|
21058c2227 | ||
|
f3b8ae0781 | ||
|
d590b004c1 | ||
|
02e21141c6 | ||
|
f839a3087d | ||
|
f458815541 | ||
|
7ba754d2a4 | ||
|
09706e4ae2 | ||
|
d18de4c541 | ||
|
001c7bfdbc | ||
|
d4e04dc712 | ||
|
8650cb5217 | ||
|
0eaaddeb95 | ||
|
1cf8663f20 | ||
|
5130259552 | ||
|
ba5e19a015 | ||
|
c0400fa986 | ||
|
51179d10ce | ||
|
78752f9178 | ||
|
762db417d7 | ||
|
85bdccbc06 | ||
|
9f8cb99340 | ||
|
87b90bb04b | ||
|
e961d88277 | ||
|
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 |
@@ -23,7 +23,7 @@ jobs:
|
||||
# command: npm set prefix=/home/circleci/npm && echo 'export PATH=$HOME/circleci/npm/bin:$PATH' >> /home/circleci/.bashrc
|
||||
- run:
|
||||
name: install-yarn
|
||||
command: npm install --global yarn@1.17.0
|
||||
command: npm install --force --global yarn@1.17.0
|
||||
- run:
|
||||
name: yarn
|
||||
command: yarn --frozen-lockfile --ignore-engines install || yarn --frozen-lockfile --ignore-engines install
|
||||
@@ -37,7 +37,7 @@ jobs:
|
||||
- store_artifacts:
|
||||
path: ~/repo/packages/abi-gen/test-cli/output
|
||||
- store_artifacts:
|
||||
path: ~/repo/packages/abi-gen-wrappers/generated_docs
|
||||
path: ~/repo/packages/contract-wrappers/generated_docs
|
||||
test-contracts-ganache:
|
||||
resource_class: medium+
|
||||
docker:
|
||||
@@ -47,7 +47,7 @@ jobs:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: yarn wsrun test:circleci @0x/contracts-multisig @0x/contracts-utils @0x/contracts-exchange-libs @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-extensions @0x/contracts-asset-proxy @0x/contracts-exchange @0x/contracts-exchange-forwarder @0x/contracts-coordinator @0x/contracts-tests @0x/contracts-staking
|
||||
- run: yarn wsrun test:circleci @0x/contracts-multisig @0x/contracts-utils @0x/contracts-exchange-libs @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-extensions @0x/contracts-asset-proxy @0x/contracts-exchange @0x/contracts-exchange-forwarder @0x/contracts-coordinator @0x/contracts-staking
|
||||
test-exchange-ganache-3.0:
|
||||
resource_class: medium+
|
||||
docker:
|
||||
@@ -77,7 +77,7 @@ jobs:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: yarn wsrun test:circleci @0x/contracts-multisig @0x/contracts-utils @0x/contracts-exchange-libs @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-asset-proxy @0x/contracts-exchange-forwarder @0x/contracts-tests @0x/contracts-staking @0x/contracts-coordinator
|
||||
- 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.
|
||||
@@ -93,8 +93,8 @@ jobs:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run:
|
||||
command: yarn test:publish:circleci
|
||||
no_output_timeout: 1800
|
||||
command: yarn test:publish:circleci
|
||||
no_output_timeout: 1800
|
||||
test-doc-generation:
|
||||
docker:
|
||||
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||
@@ -104,8 +104,8 @@ jobs:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run:
|
||||
command: yarn test:generate_docs:circleci
|
||||
no_output_timeout: 1200
|
||||
command: yarn test:generate_docs:circleci
|
||||
no_output_timeout: 1200
|
||||
test-rest:
|
||||
docker:
|
||||
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||
@@ -116,23 +116,16 @@ jobs:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: yarn wsrun test:circleci @0x/contracts-test-utils
|
||||
- run: yarn wsrun test:circleci @0x/abi-gen
|
||||
# TODO (xianny): Needs to be updated for 3.0
|
||||
# - run: yarn wsrun test:circleci @0x/asset-buyer
|
||||
# TODO: Needs to be updated for 3.0. At that time, also remove
|
||||
# exclusion from monorepo package.json's test script.
|
||||
# - run: yarn wsrun test:circleci @0x/asset-swapper
|
||||
- run: yarn wsrun test:circleci @0x/asset-swapper
|
||||
- run: yarn wsrun test:circleci @0x/contract-artifacts
|
||||
- run: yarn wsrun test:circleci @0x/assert
|
||||
- run: yarn wsrun test:circleci @0x/base-contract
|
||||
# TODO (xianny): Needs to be updated for 3.0
|
||||
# - run: yarn wsrun test:circleci @0x/connect
|
||||
- run: yarn wsrun test:circleci @0x/contract-wrappers
|
||||
- run: yarn wsrun test:circleci @0x/connect
|
||||
- run: yarn wsrun test:circleci @0x/contract-wrappers-test
|
||||
- run: yarn wsrun test:circleci @0x/dev-utils
|
||||
- run: yarn wsrun test:circleci @0x/json-schemas
|
||||
- run: yarn wsrun test:circleci @0x/order-utils
|
||||
# TODO: Needs to be updated for 3.0. At that time, also remove
|
||||
# exclusion from monorepo package.json's test script.
|
||||
# - run: yarn wsrun test:circleci @0x/orderbook
|
||||
- run: yarn wsrun test:circleci @0x/orderbook
|
||||
- run: yarn wsrun test:circleci @0x/sol-compiler
|
||||
- run: yarn wsrun test:circleci @0x/sol-tracing-utils
|
||||
- run: yarn wsrun test:circleci @0x/sol-doc
|
||||
@@ -149,9 +142,9 @@ jobs:
|
||||
paths:
|
||||
- ~/repo/packages/assert/coverage/lcov.info
|
||||
- save_cache:
|
||||
key: coverage-asset-buyer-{{ .Environment.CIRCLE_SHA1 }}
|
||||
key: coverage-asset-swapper-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/asset-buyer/coverage/lcov.info
|
||||
- ~/repo/packages/asset-swapper/coverage/lcov.info
|
||||
- save_cache:
|
||||
key: coverage-base-contract-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
@@ -161,9 +154,9 @@ jobs:
|
||||
paths:
|
||||
- ~/repo/packages/connect/coverage/lcov.info
|
||||
- save_cache:
|
||||
key: coverage-contract-wrappers-{{ .Environment.CIRCLE_SHA1 }}
|
||||
key: coverage-contract-wrappers-test-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/contract-wrappers/coverage/lcov.info
|
||||
- ~/repo/packages/contract-wrappers-test/coverage/lcov.info
|
||||
- save_cache:
|
||||
key: coverage-dev-utils-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
@@ -200,33 +193,27 @@ jobs:
|
||||
working_directory: ~/repo
|
||||
docker:
|
||||
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||
- image: 0xorg/ganache-cli:4.4.0-beta.1
|
||||
environment:
|
||||
VERSION: 4.4.0-beta.1
|
||||
SNAPSHOT_NAME: 0x_ganache_snapshot-v3-beta
|
||||
- image: 0xorg/mesh:6.0.0-beta-0xv3
|
||||
- image: 0xorg/ganache-cli:6.0.0
|
||||
- image: 0xorg/mesh:0xV3
|
||||
environment:
|
||||
ETHEREUM_RPC_URL: 'http://localhost:8545'
|
||||
ETHEREUM_NETWORK_ID: '50'
|
||||
ETHEREUM_CHAIN_ID: '1337'
|
||||
USE_BOOTSTRAP_LIST: 'true'
|
||||
VERBOSITY: 3
|
||||
PRIVATE_KEY_PATH: ''
|
||||
BLOCK_POLLING_INTERVAL: '5s'
|
||||
P2P_LISTEN_PORT: '60557'
|
||||
VERBOSITY: 5
|
||||
BLOCK_POLLING_INTERVAL: '50ms'
|
||||
ETHEREUM_RPC_MAX_REQUESTS_PER_24_HR_UTC: '1778000'
|
||||
command: |
|
||||
sh -c "waitForGanache () { until printf 'POST /\r\nContent-Length: 26\r\n\r\n{\"method\":\"net_listening\"}' | nc localhost 8545 | grep true; do continue; done }; waitForGanache && ./mesh"
|
||||
- image: 0xorg/launch-kit-backend:v3
|
||||
environment:
|
||||
RPC_URL: 'http://localhost:8545'
|
||||
NETWORK_ID: 50
|
||||
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 5; }; waitForMesh && node_modules/.bin/forever ts/lib/index.js"
|
||||
sh -c "waitForMesh () { sleep 30; }; waitForMesh && node_modules/.bin/forever ts/lib/index.js"
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
@@ -369,7 +356,7 @@ jobs:
|
||||
- coverage-assert-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-asset-buyer-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- coverage-asset-swapper-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-base-contract-{{ .Environment.CIRCLE_SHA1 }}
|
||||
@@ -378,7 +365,7 @@ jobs:
|
||||
- coverage-connect-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-contract-wrappers-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- coverage-contract-wrappers-test-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-dev-utils-{{ .Environment.CIRCLE_SHA1 }}
|
||||
|
5
.github/autolabeler.yml
vendored
5
.github/autolabeler.yml
vendored
@@ -19,18 +19,15 @@ contracts: ['contracts']
|
||||
@0x/sol-tracing-utils: ['packages/sol-tracing-utils']
|
||||
@0x/utils: ['packages/utils']
|
||||
@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/assert: ['packages/assert']
|
||||
@0x/base-contract: ['packages/base-contract']
|
||||
@0x/typescript-typings: ['packages/typescript-typings']
|
||||
0x.js: ['packages/0x.js']
|
||||
@0x/abi-gen-wrappers: ['packages/abi-gen-wrappers']
|
||||
@0x/contract-artifacts: ['packages/contract-artifacts']
|
||||
@0x/dev-utils: ['packages/dev-utils']
|
||||
@0x/contract-wrappers: ['packages/contract-wrappers']
|
||||
@0x/json-schemas: ['packages/json-schemas']
|
||||
@0x/ethereum-types: ['ethereum-types']
|
||||
@0x/connect: ['packages/connect']
|
||||
@0x/testnet-faucets: ['packages/testnet-faucets']
|
||||
@0x/monorepo-scripts: ['packages/monorepo-scripts']
|
||||
|
66
.gitignore
vendored
66
.gitignore
vendored
@@ -78,28 +78,45 @@ TODO.md
|
||||
# VSCode file
|
||||
.vscode
|
||||
|
||||
# server cli
|
||||
packages/testnet-faucets/server/
|
||||
|
||||
# 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/test/generated-artifacts/
|
||||
contracts/exchange/generated-artifacts/
|
||||
contracts/exchange/test/generated-artifacts/
|
||||
contracts/asset-proxy/generated-artifacts/
|
||||
contracts/asset-proxy/test/generated-artifacts/
|
||||
contracts/multisig/generated-artifacts/
|
||||
contracts/multisig/test/generated-artifacts/
|
||||
contracts/utils/generated-artifacts/
|
||||
contracts/utils/test/generated-artifacts/
|
||||
contracts/exchange-libs/generated-artifacts/
|
||||
contracts/exchange-libs/test/generated-artifacts/
|
||||
contracts/erc20/generated-artifacts/
|
||||
contracts/erc20/test/generated-artifacts/
|
||||
contracts/erc721/generated-artifacts/
|
||||
contracts/erc721/test/generated-artifacts/
|
||||
contracts/erc1155/generated-artifacts/
|
||||
contracts/erc1155/test/generated-artifacts/
|
||||
contracts/extensions/generated-artifacts/
|
||||
contracts/extensions/test/generated-artifacts/
|
||||
contracts/exchange-forwarder/generated-artifacts/
|
||||
contracts/exchange-forwarder/test/generated-artifacts/
|
||||
contracts/dev-utils/generated-artifacts/
|
||||
contracts/dev-utils/test/generated-artifacts/
|
||||
packages/sol-tracing-utils/test/fixtures/artifacts/
|
||||
python-packages/contract_artifacts/src/zero_ex/contract_artifacts/artifacts/
|
||||
|
||||
# generated truffle contract artifacts/
|
||||
contracts/broker/build/
|
||||
contracts/erc20-bridge-sampler/build/
|
||||
contracts/staking/build/
|
||||
contracts/coordinator/build/
|
||||
contracts/exchange/build/
|
||||
@@ -115,45 +132,40 @@ contracts/exchange-forwarder/build/
|
||||
contracts/dev-utils/build/
|
||||
|
||||
# generated contract wrappers
|
||||
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/
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/dev_utils/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc20_token/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/asset_proxy_owner/__init__.py
|
||||
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/erc1155_mintable/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc1155_proxy/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc20_proxy/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc721_proxy/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc721_token/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/eth_balance_checker/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/forwarder/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/i_asset_proxy/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/i_validator/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/i_wallet/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/multi_asset_proxy/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/order_validator/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/static_call_proxy/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/weth9/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/zrx_token/__init__.py
|
||||
contracts/dev-utils/test/generated-wrappers/
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/*/__init__.py
|
||||
|
||||
# solc-bin in sol-compiler
|
||||
packages/sol-compiler/solc_bin/
|
||||
|
@@ -1,33 +1,69 @@
|
||||
lib
|
||||
.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/test/generated-wrappers
|
||||
/contracts/coordinator/generated-artifacts
|
||||
/contracts/coordinator/test/generated-artifacts
|
||||
/contracts/exchange/generated-wrappers
|
||||
/contracts/exchange/test/generated-wrappers
|
||||
/contracts/exchange/generated-artifacts
|
||||
/contracts/exchange/test/generated-artifacts
|
||||
/contracts/asset-proxy/generated-wrappers
|
||||
/contracts/asset-proxy/test/generated-wrappers
|
||||
/contracts/asset-proxy/generated-artifacts
|
||||
/contracts/asset-proxy/test/generated-artifacts
|
||||
/contracts/multisig/generated-wrappers
|
||||
/contracts/multisig/test/generated-wrappers
|
||||
/contracts/multisig/generated-artifacts
|
||||
/contracts/multisig/test/generated-artifacts
|
||||
/contracts/utils/generated-wrappers
|
||||
/contracts/utils/test/generated-wrappers
|
||||
/contracts/utils/generated-artifacts
|
||||
/contracts/utils/test/generated-artifacts
|
||||
/contracts/exchange-libs/generated-wrappers
|
||||
/contracts/exchange-libs/test/generated-wrappers
|
||||
/contracts/exchange-libs/generated-artifacts
|
||||
/contracts/exchange-libs/test/generated-artifacts
|
||||
/contracts/erc20/generated-wrappers
|
||||
/contracts/erc20/test/generated-wrappers
|
||||
/contracts/erc20/generated-artifacts
|
||||
/contracts/erc20/test/generated-artifacts
|
||||
/contracts/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/test/generated-wrappers
|
||||
/contracts/erc721/generated-artifacts
|
||||
/contracts/erc721/test/generated-artifacts
|
||||
/contracts/erc1155/generated-wrappers
|
||||
/contracts/erc1155/test/generated-wrappers
|
||||
/contracts/erc1155/generated-artifacts
|
||||
/contracts/erc1155/test/generated-artifacts
|
||||
/contracts/extensions/generated-wrappers
|
||||
/contracts/extensions/test/generated-wrappers
|
||||
/contracts/extensions/generated-artifacts
|
||||
/contracts/extensions/test/generated-artifacts
|
||||
/contracts/exchange-forwarder/generated-wrappers
|
||||
/contracts/exchange-forwarder/test/generated-wrappers
|
||||
/contracts/exchange-forwarder/generated-artifacts
|
||||
/contracts/exchange-forwarder/test/generated-artifacts
|
||||
/contracts/dev-utils/generated-wrappers
|
||||
/contracts/dev-utils/test/generated-wrappers
|
||||
/contracts/dev-utils/generated-artifacts
|
||||
/contracts/dev-utils/test/generated-artifacts
|
||||
/contracts/staking/build/
|
||||
/contracts/coordinator/build/
|
||||
/contracts/exchange/build/
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"tabWidth": 4,
|
||||
"printWidth": 120,
|
||||
"trailingComma": all,
|
||||
"singleQuote": true
|
||||
"tabWidth": 4,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all"
|
||||
}
|
||||
|
@@ -5,8 +5,8 @@
|
||||
# https://git-scm.com/docs/gitignore#_pattern_format
|
||||
|
||||
# Website
|
||||
packages/asset-buyer/ @BMillman19 @fragosti @steveklebanoff
|
||||
packages/instant/ @BMillman19 @fragosti @steveklebanoff
|
||||
packages/asset-swapper/ @BMillman19 @fragosti @dave4506
|
||||
packages/instant/ @BMillman19 @fragosti @dave4506
|
||||
|
||||
# Dev tools & setup
|
||||
.circleci/ @LogvinovLeon
|
||||
@@ -14,8 +14,8 @@ packages/abi-gen/ @feuGeneA
|
||||
packages/base-contract/ @xianny
|
||||
packages/connect/ @fragosti
|
||||
packages/abi-gen-templates/ @feuGeneA @xianny
|
||||
packages/contract-addresses/ @albrow
|
||||
packages/contract-artifacts/ @albrow
|
||||
packages/contract-addresses/ @abandeali1
|
||||
packages/contract-artifacts/ @abandeali1
|
||||
packages/dev-utils/ @LogvinovLeon @fabioberger
|
||||
packages/devnet/ @albrow
|
||||
packages/ethereum-types/ @LogvinovLeon
|
||||
|
11
README.md
11
README.md
@@ -61,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/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/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/contract-artifacts`](/packages/contract-artifacts) | [](https://www.npmjs.com/package/@0x/contract-artifacts) | 0x smart contract compilation artifacts | |
|
||||
| [`@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/asset-buyer`](/packages/asset-buyer) | [](https://www.npmjs.com/package/@0x/asset-buyer) | Convenience package for discovering and buying assets with Ether |
|
||||
| [`@0x/asset-swapper`](/packages/asset-swapper) | [](https://www.npmjs.com/package/@0x/asset-swapper) | Convenience package for discovering and performing swaps for any ERC20 Assets |
|
||||
|
||||
#### Ethereum tooling
|
||||
@@ -96,10 +94,9 @@ These packages are all under development. See [/contracts/README.md](/contracts/
|
||||
|
||||
#### Private Packages
|
||||
|
||||
| Package | Description |
|
||||
| -------------------------------------------------- | -------------------------------------------------------------------------------- |
|
||||
| [`@0x/instant`](/packages/instant) | A free and flexible way to offer simple crypto purchasing in any app or website. |
|
||||
| [`@0x/testnet-faucets`](/packages/testnet-faucets) | A faucet micro-service that dispenses test ERC20 tokens or Ether |
|
||||
| Package | Description |
|
||||
| ---------------------------------- | -------------------------------------------------------------------------------- |
|
||||
| [`@0x/instant`](/packages/instant) | A free and flexible way to offer simple crypto purchasing in any app or website. |
|
||||
|
||||
## Usage
|
||||
|
||||
|
@@ -13,4 +13,4 @@
|
||||
|
||||
#### 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
|
@@ -1,4 +1,198 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1582623685,
|
||||
"version": "3.2.2",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1581748629,
|
||||
"version": "3.2.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "3.2.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add more types and functions to `IDydx`",
|
||||
"pr": 2466
|
||||
},
|
||||
{
|
||||
"note": "Rename `DydxBrigeAction.accountId` to `DydxBridgeAction.accountIdx`",
|
||||
"pr": 2466
|
||||
},
|
||||
{
|
||||
"note": "Fix broken tests.",
|
||||
"pr": 2462
|
||||
},
|
||||
{
|
||||
"note": "Remove dependency on `@0x/contracts-dev-utils`",
|
||||
"pr": 2462
|
||||
},
|
||||
{
|
||||
"note": "Add asset data decoding functions",
|
||||
"pr": 2462
|
||||
},
|
||||
{
|
||||
"note": "Add `setOperators()` to `IDydx`",
|
||||
"pr": "TODO"
|
||||
}
|
||||
],
|
||||
"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": [
|
||||
|
@@ -5,6 +5,76 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v3.2.2 - _February 25, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.2.1 - _February 15, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.2.0 - _February 8, 2020_
|
||||
|
||||
* Add more types and functions to `IDydx` (#2466)
|
||||
* Rename `DydxBrigeAction.accountId` to `DydxBridgeAction.accountIdx` (#2466)
|
||||
* Fix broken tests. (#2462)
|
||||
* Remove dependency on `@0x/contracts-dev-utils` (#2462)
|
||||
* Add asset data decoding functions (#2462)
|
||||
* Add `setOperators()` to `IDydx` (#TODO)
|
||||
|
||||
## 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)
|
||||
|
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"artifactsDir": "./generated-artifacts",
|
||||
"artifactsDir": "./test/generated-artifacts",
|
||||
"contractsDir": "./contracts",
|
||||
"useDockerisedSolc": false,
|
||||
"isOfflineMode": false,
|
||||
"compilerSettings": {
|
||||
"evmVersion": "constantinople",
|
||||
"evmVersion": "istanbul",
|
||||
"optimizer": {
|
||||
"enabled": true,
|
||||
"runs": 1000000,
|
||||
|
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.
|
||||
accountIdx: bridgeAction.accountIdx, // index in the `accounts` when calling `operate`.
|
||||
primaryMarketId: bridgeAction.marketId, // indicates which token to deposit.
|
||||
otherAddress: depositFrom, // deposit from the account owner.
|
||||
// unused parameters
|
||||
secondaryMarketId: 0,
|
||||
otherAccountIdx: 0,
|
||||
data: hex''
|
||||
});
|
||||
}
|
||||
|
||||
/// @dev Returns a dydx `WithdrawAction`.
|
||||
/// @param withdrawTo Withdraw tokens to this address.
|
||||
/// @param amount of tokens to withdraw.
|
||||
/// @param bridgeAction A `BridgeAction` struct.
|
||||
/// @return withdrawAction The encoded dydx action.
|
||||
function _createWithdrawAction(
|
||||
address withdrawTo,
|
||||
uint256 amount,
|
||||
BridgeAction memory bridgeAction
|
||||
)
|
||||
internal
|
||||
pure
|
||||
returns (
|
||||
IDydx.ActionArgs memory withdrawAction
|
||||
)
|
||||
{
|
||||
// Create dydx amount.
|
||||
IDydx.AssetAmount memory amountToWithdraw = IDydx.AssetAmount({
|
||||
sign: false, // false if negative.
|
||||
denomination: IDydx.AssetDenomination.Wei, // Wei => actual token amount held in account.
|
||||
ref: IDydx.AssetReference.Delta, // Delta => a relative amount.
|
||||
value: amount // amount to withdraw.
|
||||
});
|
||||
|
||||
// Create withdraw action.
|
||||
withdrawAction = IDydx.ActionArgs({
|
||||
actionType: IDydx.ActionType.Withdraw, // withdraw tokens.
|
||||
amount: amountToWithdraw, // amount to withdraw.
|
||||
accountIdx: bridgeAction.accountIdx, // index in the `accounts` when calling `operate`.
|
||||
primaryMarketId: bridgeAction.marketId, // indicates which token to withdraw.
|
||||
otherAddress: withdrawTo, // withdraw tokens to this address.
|
||||
// unused parameters
|
||||
secondaryMarketId: 0,
|
||||
otherAccountIdx: 0,
|
||||
data: hex''
|
||||
});
|
||||
}
|
||||
}
|
@@ -22,6 +22,7 @@ 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";
|
||||
|
||||
@@ -29,11 +30,9 @@ import "../interfaces/IEth2Dai.sol";
|
||||
// solhint-disable space-after-comma
|
||||
contract Eth2DaiBridge is
|
||||
IERC20Bridge,
|
||||
IWallet
|
||||
IWallet,
|
||||
DeploymentConstants
|
||||
{
|
||||
/* Mainnet addresses */
|
||||
address constant public ETH2DAI_ADDRESS = 0x39755357759cE0d7f32dC8dC45414CCa409AE24e;
|
||||
|
||||
/// @dev Callback for `IERC20Bridge`. Tries to buy `amount` of
|
||||
/// `toTokenAddress` tokens by selling the entirety of the opposing asset
|
||||
/// (DAI or WETH) to the Eth2Dai contract, then transfers the bought
|
||||
@@ -56,13 +55,13 @@ contract Eth2DaiBridge is
|
||||
// Decode the bridge data to get the `fromTokenAddress`.
|
||||
(address fromTokenAddress) = abi.decode(bridgeData, (address));
|
||||
|
||||
IEth2Dai exchange = _getEth2DaiContract();
|
||||
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 = _getEth2DaiContract().sellAllAmount(
|
||||
address(fromTokenAddress),
|
||||
uint256 boughtAmount = exchange.sellAllAmount(
|
||||
fromTokenAddress,
|
||||
IERC20Token(fromTokenAddress).balanceOf(address(this)),
|
||||
toTokenAddress,
|
||||
amount
|
||||
@@ -85,14 +84,4 @@ contract Eth2DaiBridge is
|
||||
{
|
||||
return LEGACY_WALLET_MAGIC_VALUE;
|
||||
}
|
||||
|
||||
/// @dev Overridable way to get the eth2dai contract.
|
||||
/// @return exchange The Eth2Dai exchange contract.
|
||||
function _getEth2DaiContract()
|
||||
internal
|
||||
view
|
||||
returns (IEth2Dai exchange)
|
||||
{
|
||||
return IEth2Dai(ETH2DAI_ADDRESS);
|
||||
}
|
||||
}
|
||||
|
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;
|
||||
}
|
||||
}
|
@@ -23,6 +23,7 @@ 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";
|
||||
@@ -32,12 +33,9 @@ import "../interfaces/IERC20Bridge.sol";
|
||||
// solhint-disable not-rely-on-time
|
||||
contract UniswapBridge is
|
||||
IERC20Bridge,
|
||||
IWallet
|
||||
IWallet,
|
||||
DeploymentConstants
|
||||
{
|
||||
/* Mainnet addresses */
|
||||
address constant private UNISWAP_EXCHANGE_FACTORY_ADDRESS = 0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95;
|
||||
address constant private WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
|
||||
|
||||
// Struct to hold `bridgeTransferFrom()` local variables in memory and to avoid
|
||||
// stack overflows.
|
||||
struct WithdrawToState {
|
||||
@@ -90,7 +88,7 @@ contract UniswapBridge is
|
||||
// Get our balance of `fromTokenAddress` token.
|
||||
state.fromTokenBalance = IERC20Token(fromTokenAddress).balanceOf(address(this));
|
||||
// Get the weth contract.
|
||||
state.weth = getWethContract();
|
||||
state.weth = IEtherToken(_getWethAddress());
|
||||
|
||||
// Convert from WETH to a token.
|
||||
if (fromTokenAddress == address(state.weth)) {
|
||||
@@ -136,8 +134,8 @@ contract UniswapBridge is
|
||||
state.fromTokenBalance,
|
||||
// Minimum buy amount.
|
||||
amount,
|
||||
// No minimum intermediate ETH buy amount.
|
||||
0,
|
||||
// Must buy at least 1 intermediate ETH.
|
||||
1,
|
||||
// Expires after this block.
|
||||
block.timestamp,
|
||||
// Recipient is `to`.
|
||||
@@ -163,26 +161,6 @@ contract UniswapBridge is
|
||||
return LEGACY_WALLET_MAGIC_VALUE;
|
||||
}
|
||||
|
||||
/// @dev Overridable way to get the weth contract.
|
||||
/// @return token The WETH contract.
|
||||
function getWethContract()
|
||||
public
|
||||
view
|
||||
returns (IEtherToken token)
|
||||
{
|
||||
return IEtherToken(WETH_ADDRESS);
|
||||
}
|
||||
|
||||
/// @dev Overridable way to get the uniswap exchange factory contract.
|
||||
/// @return factory The exchange factory contract.
|
||||
function getUniswapExchangeFactoryContract()
|
||||
public
|
||||
view
|
||||
returns (IUniswapExchangeFactory factory)
|
||||
{
|
||||
return IUniswapExchangeFactory(UNISWAP_EXCHANGE_FACTORY_ADDRESS);
|
||||
}
|
||||
|
||||
/// @dev Grants an unlimited allowance to the exchange for its token
|
||||
/// on behalf of this contract.
|
||||
/// @param exchange The Uniswap token exchange.
|
||||
@@ -209,10 +187,13 @@ contract UniswapBridge is
|
||||
{
|
||||
address exchangeTokenAddress = fromTokenAddress;
|
||||
// Whichever isn't WETH is the exchange token.
|
||||
if (fromTokenAddress == address(getWethContract())) {
|
||||
if (fromTokenAddress == _getWethAddress()) {
|
||||
exchangeTokenAddress = toTokenAddress;
|
||||
}
|
||||
exchange = getUniswapExchangeFactoryContract().getExchange(exchangeTokenAddress);
|
||||
exchange = IUniswapExchange(
|
||||
IUniswapExchangeFactory(_getUniswapExchangeFactoryAddress())
|
||||
.getExchange(exchangeTokenAddress)
|
||||
);
|
||||
require(address(exchange) != address(0), "NO_UNISWAP_EXCHANGE_FOR_TOKEN");
|
||||
return exchange;
|
||||
}
|
||||
|
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);
|
||||
}
|
192
contracts/asset-proxy/contracts/src/interfaces/IDydx.sol
Normal file
192
contracts/asset-proxy/contracts/src/interfaces/IDydx.sol
Normal file
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
|
||||
interface IDydx {
|
||||
|
||||
/// @dev Represents the unique key that specifies an account
|
||||
struct AccountInfo {
|
||||
address owner; // The address that owns the account
|
||||
uint256 number; // A nonce that allows a single address to control many accounts
|
||||
}
|
||||
|
||||
enum ActionType {
|
||||
Deposit, // supply tokens
|
||||
Withdraw, // borrow tokens
|
||||
Transfer, // transfer balance between accounts
|
||||
Buy, // buy an amount of some token (externally)
|
||||
Sell, // sell an amount of some token (externally)
|
||||
Trade, // trade tokens against another account
|
||||
Liquidate, // liquidate an undercollateralized or expiring account
|
||||
Vaporize, // use excess tokens to zero-out a completely negative account
|
||||
Call // send arbitrary data to an address
|
||||
}
|
||||
|
||||
/// @dev Arguments that are passed to Solo in an ordered list as part of a single operation.
|
||||
/// Each ActionArgs has an actionType which specifies which action struct that this data will be
|
||||
/// parsed into before being processed.
|
||||
struct ActionArgs {
|
||||
ActionType actionType;
|
||||
uint256 accountIdx;
|
||||
AssetAmount amount;
|
||||
uint256 primaryMarketId;
|
||||
uint256 secondaryMarketId;
|
||||
address otherAddress;
|
||||
uint256 otherAccountIdx;
|
||||
bytes data;
|
||||
}
|
||||
|
||||
enum AssetDenomination {
|
||||
Wei, // the amount is denominated in wei
|
||||
Par // the amount is denominated in par
|
||||
}
|
||||
|
||||
enum AssetReference {
|
||||
Delta, // the amount is given as a delta from the current value
|
||||
Target // the amount is given as an exact number to end up at
|
||||
}
|
||||
|
||||
struct AssetAmount {
|
||||
bool sign; // true if positive
|
||||
AssetDenomination denomination;
|
||||
AssetReference ref;
|
||||
uint256 value;
|
||||
}
|
||||
|
||||
struct D256 {
|
||||
uint256 value;
|
||||
}
|
||||
|
||||
struct Value {
|
||||
uint256 value;
|
||||
}
|
||||
|
||||
struct Price {
|
||||
uint256 value;
|
||||
}
|
||||
|
||||
struct OperatorArg {
|
||||
address operator;
|
||||
bool trusted;
|
||||
}
|
||||
|
||||
/// @dev The global risk parameters that govern the health and security of the system
|
||||
struct RiskParams {
|
||||
// Required ratio of over-collateralization
|
||||
D256 marginRatio;
|
||||
// Percentage penalty incurred by liquidated accounts
|
||||
D256 liquidationSpread;
|
||||
// Percentage of the borrower's interest fee that gets passed to the suppliers
|
||||
D256 earningsRate;
|
||||
// The minimum absolute borrow value of an account
|
||||
// There must be sufficient incentivize to liquidate undercollateralized accounts
|
||||
Value minBorrowedValue;
|
||||
}
|
||||
|
||||
/// @dev The main entry-point to Solo that allows users and contracts to manage accounts.
|
||||
/// Take one or more actions on one or more accounts. The msg.sender must be the owner or
|
||||
/// operator of all accounts except for those being liquidated, vaporized, or traded with.
|
||||
/// One call to operate() is considered a singular "operation". Account collateralization is
|
||||
/// ensured only after the completion of the entire operation.
|
||||
/// @param accounts A list of all accounts that will be used in this operation. Cannot contain
|
||||
/// duplicates. In each action, the relevant account will be referred-to by its
|
||||
/// index in the list.
|
||||
/// @param actions An ordered list of all actions that will be taken in this operation. The
|
||||
/// actions will be processed in order.
|
||||
function operate(
|
||||
AccountInfo[] calldata accounts,
|
||||
ActionArgs[] calldata actions
|
||||
)
|
||||
external;
|
||||
|
||||
// @dev Approves/disapproves any number of operators. An operator is an external address that has the
|
||||
// same permissions to manipulate an account as the owner of the account. Operators are simply
|
||||
// addresses and therefore may either be externally-owned Ethereum accounts OR smart contracts.
|
||||
// Operators are also able to act as AutoTrader contracts on behalf of the account owner if the
|
||||
// operator is a smart contract and implements the IAutoTrader interface.
|
||||
// @param args A list of OperatorArgs which have an address and a boolean. The boolean value
|
||||
// denotes whether to approve (true) or revoke approval (false) for that address.
|
||||
function setOperators(OperatorArg[] calldata args) external;
|
||||
|
||||
/// @dev Return true if a particular address is approved as an operator for an owner's accounts.
|
||||
/// Approved operators can act on the accounts of the owner as if it were the operator's own.
|
||||
/// @param owner The owner of the accounts
|
||||
/// @param operator The possible operator
|
||||
/// @return isLocalOperator True if operator is approved for owner's accounts
|
||||
function getIsLocalOperator(
|
||||
address owner,
|
||||
address operator
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (bool isLocalOperator);
|
||||
|
||||
/// @dev Get the ERC20 token address for a market.
|
||||
/// @param marketId The market to query
|
||||
/// @return tokenAddress The token address
|
||||
function getMarketTokenAddress(
|
||||
uint256 marketId
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (address tokenAddress);
|
||||
|
||||
/// @dev Get all risk parameters in a single struct.
|
||||
/// @return riskParams All global risk parameters
|
||||
function getRiskParams()
|
||||
external
|
||||
view
|
||||
returns (RiskParams memory riskParams);
|
||||
|
||||
/// @dev Get the price of the token for a market.
|
||||
/// @param marketId The market to query
|
||||
/// @return price The price of each atomic unit of the token
|
||||
function getMarketPrice(
|
||||
uint256 marketId
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (Price memory price);
|
||||
|
||||
/// @dev Get the margin premium for a market. A margin premium makes it so that any positions that
|
||||
/// include the market require a higher collateralization to avoid being liquidated.
|
||||
/// @param marketId The market to query
|
||||
/// @return premium The market's margin premium
|
||||
function getMarketMarginPremium(uint256 marketId)
|
||||
external
|
||||
view
|
||||
returns (D256 memory premium);
|
||||
|
||||
/// @dev Get the total supplied and total borrowed values of an account adjusted by the marginPremium
|
||||
/// of each market. Supplied values are divided by (1 + marginPremium) for each market and
|
||||
/// borrowed values are multiplied by (1 + marginPremium) for each market. Comparing these
|
||||
/// adjusted values gives the margin-ratio of the account which will be compared to the global
|
||||
/// margin-ratio when determining if the account can be liquidated.
|
||||
/// @param account The account to query
|
||||
/// @return supplyValue The supplied value of the account (adjusted for marginPremium)
|
||||
/// @return borrowValue The borrowed value of the account (adjusted for marginPremium)
|
||||
function getAdjustedAccountValues(
|
||||
AccountInfo calldata account
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (Value memory supplyValue, Value memory borrowValue);
|
||||
}
|
@@ -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 accountIdx; // Index in `BridgeData.accountNumbers` for this action.
|
||||
uint256 marketId; // Market to operate on.
|
||||
uint256 conversionRateNumerator; // Optional. If set, transfer amount is scaled by (conversionRateNumerator/conversionRateDenominator).
|
||||
uint256 conversionRateDenominator; // Optional. If set, transfer amount is scaled by (conversionRateNumerator/conversionRateDenominator).
|
||||
}
|
||||
|
||||
struct BridgeData {
|
||||
uint256[] accountNumbers; // Account number used to identify the owner's specific account.
|
||||
BridgeAction[] actions; // Actions to carry out on the owner's accounts.
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
@@ -67,11 +67,4 @@ interface IUniswapExchange {
|
||||
)
|
||||
external
|
||||
returns (uint256 tokensBought);
|
||||
|
||||
/// @dev Retrieves the token that is associated with this exchange.
|
||||
/// @return tokenAddress The token address.
|
||||
function toTokenAddress()
|
||||
external
|
||||
view
|
||||
returns (address tokenAddress);
|
||||
}
|
||||
|
@@ -28,5 +28,5 @@ interface IUniswapExchangeFactory {
|
||||
function getExchange(address tokenAddress)
|
||||
external
|
||||
view
|
||||
returns (IUniswapExchange);
|
||||
returns (address);
|
||||
}
|
||||
|
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;
|
||||
}
|
||||
}
|
246
contracts/asset-proxy/contracts/test/TestDydxBridge.sol
Normal file
246
contracts/asset-proxy/contracts/test/TestDydxBridge.sol
Normal file
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||
import "../src/bridges/DydxBridge.sol";
|
||||
|
||||
|
||||
// solhint-disable no-empty-blocks
|
||||
contract TestDydxBridgeToken {
|
||||
|
||||
uint256 private constant INIT_HOLDER_BALANCE = 10 * 10**18; // 10 tokens
|
||||
mapping (address => uint256) private _balances;
|
||||
|
||||
/// @dev Sets initial balance of token holders.
|
||||
constructor(address[] memory holders)
|
||||
public
|
||||
{
|
||||
for (uint256 i = 0; i != holders.length; ++i) {
|
||||
_balances[holders[i]] = INIT_HOLDER_BALANCE;
|
||||
}
|
||||
_balances[msg.sender] = INIT_HOLDER_BALANCE;
|
||||
}
|
||||
|
||||
/// @dev Basic transferFrom implementation.
|
||||
function transferFrom(address from, address to, uint256 amount)
|
||||
external
|
||||
returns (bool)
|
||||
{
|
||||
if (_balances[from] < amount || _balances[to] + amount < _balances[to]) {
|
||||
return false;
|
||||
}
|
||||
_balances[from] -= amount;
|
||||
_balances[to] += amount;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @dev Returns balance of `holder`.
|
||||
function balanceOf(address holder)
|
||||
external
|
||||
view
|
||||
returns (uint256)
|
||||
{
|
||||
return _balances[holder];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// solhint-disable space-after-comma
|
||||
contract TestDydxBridge is
|
||||
IDydx,
|
||||
DydxBridge
|
||||
{
|
||||
|
||||
address private constant ALWAYS_REVERT_ADDRESS = address(1);
|
||||
address private _testTokenAddress;
|
||||
bool private _shouldRevertOnOperate;
|
||||
|
||||
event OperateAccount(
|
||||
address owner,
|
||||
uint256 number
|
||||
);
|
||||
|
||||
event OperateAction(
|
||||
ActionType actionType,
|
||||
uint256 accountIdx,
|
||||
bool amountSign,
|
||||
AssetDenomination amountDenomination,
|
||||
AssetReference amountRef,
|
||||
uint256 amountValue,
|
||||
uint256 primaryMarketId,
|
||||
uint256 secondaryMarketId,
|
||||
address otherAddress,
|
||||
uint256 otherAccountId,
|
||||
bytes data
|
||||
);
|
||||
|
||||
constructor(address[] memory holders)
|
||||
public
|
||||
{
|
||||
// Deploy a test token. This represents the asset being deposited/withdrawn from dydx.
|
||||
_testTokenAddress = address(new TestDydxBridgeToken(holders));
|
||||
}
|
||||
|
||||
/// @dev Simulates `operate` in dydx contract.
|
||||
/// Emits events so that arguments can be validated client-side.
|
||||
function operate(
|
||||
AccountInfo[] calldata accounts,
|
||||
ActionArgs[] calldata actions
|
||||
)
|
||||
external
|
||||
{
|
||||
if (_shouldRevertOnOperate) {
|
||||
revert("TestDydxBridge/SHOULD_REVERT_ON_OPERATE");
|
||||
}
|
||||
|
||||
for (uint i = 0; i < accounts.length; ++i) {
|
||||
emit OperateAccount(
|
||||
accounts[i].owner,
|
||||
accounts[i].number
|
||||
);
|
||||
}
|
||||
|
||||
for (uint i = 0; i < actions.length; ++i) {
|
||||
emit OperateAction(
|
||||
actions[i].actionType,
|
||||
actions[i].accountIdx,
|
||||
actions[i].amount.sign,
|
||||
actions[i].amount.denomination,
|
||||
actions[i].amount.ref,
|
||||
actions[i].amount.value,
|
||||
actions[i].primaryMarketId,
|
||||
actions[i].secondaryMarketId,
|
||||
actions[i].otherAddress,
|
||||
actions[i].otherAccountIdx,
|
||||
actions[i].data
|
||||
);
|
||||
|
||||
if (actions[i].actionType == IDydx.ActionType.Withdraw) {
|
||||
require(
|
||||
IERC20Token(_testTokenAddress).transferFrom(
|
||||
address(this),
|
||||
actions[i].otherAddress,
|
||||
actions[i].amount.value
|
||||
),
|
||||
"TestDydxBridge/WITHDRAW_FAILED"
|
||||
);
|
||||
} else if (actions[i].actionType == IDydx.ActionType.Deposit) {
|
||||
require(
|
||||
IERC20Token(_testTokenAddress).transferFrom(
|
||||
actions[i].otherAddress,
|
||||
address(this),
|
||||
actions[i].amount.value
|
||||
),
|
||||
"TestDydxBridge/DEPOSIT_FAILED"
|
||||
);
|
||||
} else {
|
||||
revert("TestDydxBridge/UNSUPPORTED_ACTION");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev If `true` then subsequent calls to `operate` will revert.
|
||||
function setRevertOnOperate(bool shouldRevert)
|
||||
external
|
||||
{
|
||||
_shouldRevertOnOperate = shouldRevert;
|
||||
}
|
||||
|
||||
/// @dev Returns test token.
|
||||
function getTestToken()
|
||||
external
|
||||
returns (address)
|
||||
{
|
||||
return _testTokenAddress;
|
||||
}
|
||||
|
||||
/// @dev Unused.
|
||||
function setOperators(OperatorArg[] calldata args) external {}
|
||||
|
||||
/// @dev Unused.
|
||||
function getIsLocalOperator(
|
||||
address owner,
|
||||
address operator
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (bool isLocalOperator)
|
||||
{}
|
||||
|
||||
/// @dev Unused.
|
||||
function getMarketTokenAddress(
|
||||
uint256 marketId
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (address tokenAddress)
|
||||
{}
|
||||
|
||||
/// @dev Unused.
|
||||
function getRiskParams()
|
||||
external
|
||||
view
|
||||
returns (RiskParams memory riskParams)
|
||||
{}
|
||||
|
||||
/// @dev Unsused.
|
||||
function getMarketPrice(
|
||||
uint256 marketId
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (Price memory price)
|
||||
{}
|
||||
|
||||
/// @dev Unsused
|
||||
function getMarketMarginPremium(uint256 marketId)
|
||||
external
|
||||
view
|
||||
returns (IDydx.D256 memory premium)
|
||||
{}
|
||||
|
||||
/// @dev Unused.
|
||||
function getAdjustedAccountValues(
|
||||
AccountInfo calldata account
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (Value memory supplyValue, Value memory borrowValue)
|
||||
{}
|
||||
|
||||
/// @dev overrides `_getDydxAddress()` from `DeploymentConstants` to return this address.
|
||||
function _getDydxAddress()
|
||||
internal
|
||||
view
|
||||
returns (address)
|
||||
{
|
||||
return address(this);
|
||||
}
|
||||
|
||||
/// @dev overrides `_getERC20BridgeProxyAddress()` from `DeploymentConstants` for testing.
|
||||
function _getERC20BridgeProxyAddress()
|
||||
internal
|
||||
view
|
||||
returns (address)
|
||||
{
|
||||
return msg.sender == ALWAYS_REVERT_ADDRESS ? address(0) : msg.sender;
|
||||
}
|
||||
}
|
@@ -192,11 +192,11 @@ contract TestEth2DaiBridge is
|
||||
}
|
||||
|
||||
// @dev This contract will double as the Eth2Dai contract.
|
||||
function _getEth2DaiContract()
|
||||
function _getEth2DaiAddress()
|
||||
internal
|
||||
view
|
||||
returns (IEth2Dai)
|
||||
returns (address)
|
||||
{
|
||||
return IEth2Dai(address(this));
|
||||
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);
|
||||
}
|
||||
}
|
@@ -407,26 +407,26 @@ contract TestUniswapBridge is
|
||||
function getExchange(address tokenAddress)
|
||||
external
|
||||
view
|
||||
returns (IUniswapExchange)
|
||||
returns (address)
|
||||
{
|
||||
return IUniswapExchange(_testExchanges[tokenAddress]);
|
||||
return address(_testExchanges[tokenAddress]);
|
||||
}
|
||||
|
||||
// @dev Use `wethToken`.
|
||||
function getWethContract()
|
||||
public
|
||||
function _getWethAddress()
|
||||
internal
|
||||
view
|
||||
returns (IEtherToken)
|
||||
returns (address)
|
||||
{
|
||||
return IEtherToken(address(wethToken));
|
||||
return address(wethToken);
|
||||
}
|
||||
|
||||
// @dev This contract will double as the Uniswap contract.
|
||||
function getUniswapExchangeFactoryContract()
|
||||
public
|
||||
function _getUniswapExchangeFactoryAddress()
|
||||
internal
|
||||
view
|
||||
returns (IUniswapExchangeFactory)
|
||||
returns (address)
|
||||
{
|
||||
return IUniswapExchangeFactory(address(this));
|
||||
return address(this);
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-asset-proxy",
|
||||
"version": "2.3.0-beta.1",
|
||||
"version": "3.2.2",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -12,7 +12,7 @@
|
||||
"scripts": {
|
||||
"build": "yarn pre_build && tsc -b",
|
||||
"build:ci": "yarn build",
|
||||
"pre_build": "run-s compile contracts:gen generate_contract_wrappers",
|
||||
"pre_build": "run-s compile contracts:gen generate_contract_wrappers contracts:copy",
|
||||
"test": "yarn run_mocha",
|
||||
"rebuild_and_test": "run-s build test",
|
||||
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
||||
@@ -21,21 +21,24 @@
|
||||
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
|
||||
"compile": "sol-compiler",
|
||||
"watch": "sol-compiler -w",
|
||||
"clean": "shx rm -rf lib generated-artifacts generated-wrappers",
|
||||
"generate_contract_wrappers": "abi-gen --debug --abis ${npm_package_config_abis} --output generated-wrappers --backend ethers",
|
||||
"lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||
"fix": "tslint --fix --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||
"clean": "shx rm -rf lib test/generated-artifacts test/generated-wrappers generated-artifacts generated-wrappers",
|
||||
"generate_contract_wrappers": "abi-gen --debug --abis ${npm_package_config_abis} --output test/generated-wrappers --backend ethers",
|
||||
"lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./test/generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude ./test/generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||
"fix": "tslint --fix --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./test/generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude ./test/generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||
"coverage:report:text": "istanbul report text",
|
||||
"coverage:report:html": "istanbul report html && open coverage/index.html",
|
||||
"profiler:report:html": "istanbul report html && open coverage/index.html",
|
||||
"coverage:report:lcov": "istanbul report lcov",
|
||||
"test:circleci": "yarn test",
|
||||
"contracts:gen": "contracts-gen",
|
||||
"contracts:gen": "contracts-gen generate",
|
||||
"contracts:copy": "contracts-gen copy",
|
||||
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol",
|
||||
"compile:truffle": "truffle compile"
|
||||
"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": "./generated-artifacts/@(ERC1155Proxy|ERC20BridgeProxy|ERC20Proxy|ERC721Proxy|Eth2DaiBridge|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|IERC20Bridge|IEth2Dai|IUniswapExchange|IUniswapExchangeFactory|MixinAssetProxyDispatcher|MixinAuthorizable|MultiAssetProxy|Ownable|StaticCallProxy|TestERC20Bridge|TestEth2DaiBridge|TestStaticCallTarget|TestUniswapBridge|UniswapBridge).json",
|
||||
"abis": "./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."
|
||||
},
|
||||
"repository": {
|
||||
@@ -48,12 +51,14 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/protocol/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^4.4.0-beta.1",
|
||||
"@0x/contracts-gen": "^1.1.0-beta.1",
|
||||
"@0x/contracts-test-utils": "^3.2.0-beta.1",
|
||||
"@0x/dev-utils": "^2.4.0-beta.1",
|
||||
"@0x/sol-compiler": "^3.2.0-beta.1",
|
||||
"@0x/tslint-config": "^3.1.0-beta.1",
|
||||
"@0x/abi-gen": "^5.2.1",
|
||||
"@0x/contracts-gen": "^2.0.8",
|
||||
"@0x/contracts-test-utils": "^5.3.0",
|
||||
"@0x/contracts-utils": "^4.4.0",
|
||||
"@0x/dev-utils": "^3.2.1",
|
||||
"@0x/sol-compiler": "^4.0.8",
|
||||
"@0x/ts-doc-gen": "^0.0.22",
|
||||
"@0x/tslint-config": "^4.0.0",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/node": "*",
|
||||
@@ -61,6 +66,7 @@
|
||||
"chai-as-promised": "^7.1.0",
|
||||
"chai-bignumber": "^3.0.0",
|
||||
"dirty-chai": "^2.0.1",
|
||||
"ethereumjs-util": "^5.1.1",
|
||||
"make-promises-safe": "^1.1.0",
|
||||
"mocha": "^6.2.0",
|
||||
"npm-run-all": "^4.1.2",
|
||||
@@ -68,22 +74,21 @@
|
||||
"solhint": "^1.4.1",
|
||||
"truffle": "^5.0.32",
|
||||
"tslint": "5.11.0",
|
||||
"typedoc": "^0.15.0",
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^5.5.0-beta.1",
|
||||
"@0x/contracts-dev-utils": "^0.1.0-beta.1",
|
||||
"@0x/contracts-erc1155": "^1.2.0-beta.1",
|
||||
"@0x/contracts-erc20": "^2.3.0-beta.1",
|
||||
"@0x/contracts-erc721": "^2.2.0-beta.1",
|
||||
"@0x/contracts-utils": "^3.3.0-beta.1",
|
||||
"@0x/order-utils": "^8.5.0-beta.1",
|
||||
"@0x/types": "^2.5.0-beta.1",
|
||||
"@0x/typescript-typings": "^4.4.0-beta.1",
|
||||
"@0x/utils": "^4.6.0-beta.1",
|
||||
"@0x/web3-wrapper": "^6.1.0-beta.1",
|
||||
"ethereum-types": "^2.2.0-beta.1",
|
||||
"ethereumjs-util": "^5.1.1",
|
||||
"@0x/base-contract": "^6.2.1",
|
||||
"@0x/contracts-erc1155": "^2.1.2",
|
||||
"@0x/contracts-erc20": "^3.1.2",
|
||||
"@0x/contracts-erc721": "^3.1.2",
|
||||
"@0x/contracts-exchange-libs": "^4.3.2",
|
||||
"@0x/order-utils": "^10.2.2",
|
||||
"@0x/types": "^3.1.2",
|
||||
"@0x/typescript-typings": "^5.0.2",
|
||||
"@0x/utils": "^5.4.1",
|
||||
"@0x/web3-wrapper": "^7.0.7",
|
||||
"ethereum-types": "^3.1.0",
|
||||
"lodash": "^4.17.11"
|
||||
},
|
||||
"publishConfig": {
|
||||
|
@@ -5,6 +5,9 @@
|
||||
*/
|
||||
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 ERC20BridgeProxy from '../generated-artifacts/ERC20BridgeProxy.json';
|
||||
import * as ERC20Proxy from '../generated-artifacts/ERC20Proxy.json';
|
||||
@@ -14,17 +17,26 @@ import * as IAssetData from '../generated-artifacts/IAssetData.json';
|
||||
import * as IAssetProxy from '../generated-artifacts/IAssetProxy.json';
|
||||
import * as IAssetProxyDispatcher from '../generated-artifacts/IAssetProxyDispatcher.json';
|
||||
import * as IAuthorizable from '../generated-artifacts/IAuthorizable.json';
|
||||
import * as 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 MultiAssetProxy from '../generated-artifacts/MultiAssetProxy.json';
|
||||
import * as Ownable from '../generated-artifacts/Ownable.json';
|
||||
import * as StaticCallProxy from '../generated-artifacts/StaticCallProxy.json';
|
||||
import * as TestChaiBridge from '../generated-artifacts/TestChaiBridge.json';
|
||||
import * as TestDydxBridge from '../generated-artifacts/TestDydxBridge.json';
|
||||
import * as TestERC20Bridge from '../generated-artifacts/TestERC20Bridge.json';
|
||||
import * as TestEth2DaiBridge from '../generated-artifacts/TestEth2DaiBridge.json';
|
||||
import * as TestKyberBridge from '../generated-artifacts/TestKyberBridge.json';
|
||||
import * as TestStaticCallTarget from '../generated-artifacts/TestStaticCallTarget.json';
|
||||
import * as TestUniswapBridge from '../generated-artifacts/TestUniswapBridge.json';
|
||||
import * as UniswapBridge from '../generated-artifacts/UniswapBridge.json';
|
||||
@@ -38,18 +50,30 @@ export const artifacts = {
|
||||
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,
|
||||
};
|
||||
|
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;
|
||||
accountIdx: BigNumber;
|
||||
marketId: BigNumber;
|
||||
conversionRateNumerator: BigNumber;
|
||||
conversionRateDenominator: BigNumber;
|
||||
}
|
||||
|
||||
export interface DydxBridgeData {
|
||||
accountNumbers: BigNumber[];
|
||||
actions: DydxBridgeAction[];
|
||||
}
|
||||
|
||||
export const dydxBridgeDataEncoder = AbiEncoder.create([
|
||||
{
|
||||
name: 'bridgeData',
|
||||
type: 'tuple',
|
||||
components: [
|
||||
{ name: 'accountNumbers', type: 'uint256[]' },
|
||||
{
|
||||
name: 'actions',
|
||||
type: 'tuple[]',
|
||||
components: [
|
||||
{ name: 'actionType', type: 'uint8' },
|
||||
{ name: 'accountIdx', type: 'uint256' },
|
||||
{ name: 'marketId', type: 'uint256' },
|
||||
{ name: 'conversionRateNumerator', type: 'uint256' },
|
||||
{ name: 'conversionRateDenominator', type: 'uint256' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
@@ -1,4 +1,3 @@
|
||||
import { DevUtilsContract } from '@0x/contracts-dev-utils';
|
||||
import { artifacts as erc1155Artifacts, ERC1155MintableContract, Erc1155Wrapper } from '@0x/contracts-erc1155';
|
||||
import {
|
||||
constants,
|
||||
@@ -13,7 +12,9 @@ import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import { Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { artifacts, ERC1155ProxyContract, IAssetProxyContract } from '../../src';
|
||||
import { artifacts } from './artifacts';
|
||||
|
||||
import { ERC1155ProxyContract, IAssetDataContract, IAssetProxyContract } from './wrappers';
|
||||
|
||||
export class ERC1155ProxyWrapper {
|
||||
private readonly _tokenOwnerAddresses: string[];
|
||||
@@ -26,7 +27,7 @@ export class ERC1155ProxyWrapper {
|
||||
private readonly _logDecoder: LogDecoder;
|
||||
private readonly _dummyTokenWrappers: Erc1155Wrapper[];
|
||||
private readonly _assetProxyInterface: IAssetProxyContract;
|
||||
private readonly _devUtils: DevUtilsContract;
|
||||
private readonly _assetDataInterface: IAssetDataContract;
|
||||
private _proxyContract?: ERC1155ProxyContract;
|
||||
private _proxyIdIfExists?: string;
|
||||
private _initialTokenIdsByOwner: ERC1155HoldingsByOwner = { fungible: {}, nonFungible: {} };
|
||||
@@ -38,7 +39,7 @@ export class ERC1155ProxyWrapper {
|
||||
this._logDecoder = new LogDecoder(this._web3Wrapper, allArtifacts);
|
||||
this._dummyTokenWrappers = [];
|
||||
this._assetProxyInterface = new IAssetProxyContract(constants.NULL_ADDRESS, provider);
|
||||
this._devUtils = new DevUtilsContract(constants.NULL_ADDRESS, provider);
|
||||
this._assetDataInterface = new IAssetDataContract(constants.NULL_ADDRESS, provider);
|
||||
this._tokenOwnerAddresses = tokenOwnerAddresses;
|
||||
this._contractOwnerAddress = contractOwnerAddress;
|
||||
this._fungibleTokenIds = [];
|
||||
@@ -58,7 +59,7 @@ export class ERC1155ProxyWrapper {
|
||||
txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
const erc1155Wrapper = new Erc1155Wrapper(erc1155Contract, this._provider, this._contractOwnerAddress);
|
||||
const erc1155Wrapper = new Erc1155Wrapper(erc1155Contract, this._contractOwnerAddress);
|
||||
this._dummyTokenWrappers.push(erc1155Wrapper);
|
||||
}
|
||||
return this._dummyTokenWrappers;
|
||||
@@ -74,7 +75,7 @@ export class ERC1155ProxyWrapper {
|
||||
txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
this._proxyIdIfExists = await this._proxyContract.getProxyId.callAsync();
|
||||
this._proxyIdIfExists = await this._proxyContract.getProxyId().callAsync();
|
||||
return this._proxyContract;
|
||||
}
|
||||
/**
|
||||
@@ -111,19 +112,13 @@ export class ERC1155ProxyWrapper {
|
||||
this._validateProxyContractExistsOrThrow();
|
||||
const assetData =
|
||||
assetData_ === undefined
|
||||
? await this._devUtils.encodeERC1155AssetData.callAsync(
|
||||
contractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
)
|
||||
? this._assetDataInterface
|
||||
.ERC1155Assets(contractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
|
||||
.getABIEncodedTransactionData()
|
||||
: assetData_;
|
||||
const data = this._assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
assetData,
|
||||
from,
|
||||
to,
|
||||
valueMultiplier,
|
||||
);
|
||||
const data = this._assetProxyInterface
|
||||
.transferFrom(assetData, from, to, valueMultiplier)
|
||||
.getABIEncodedTransactionData();
|
||||
return data;
|
||||
}
|
||||
/**
|
||||
@@ -171,19 +166,13 @@ export class ERC1155ProxyWrapper {
|
||||
this._validateProxyContractExistsOrThrow();
|
||||
const assetData =
|
||||
assetData_ === undefined
|
||||
? await this._devUtils.encodeERC1155AssetData.callAsync(
|
||||
contractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
)
|
||||
? this._assetDataInterface
|
||||
.ERC1155Assets(contractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
|
||||
.getABIEncodedTransactionData()
|
||||
: assetData_;
|
||||
const data = this._assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
assetData,
|
||||
from,
|
||||
to,
|
||||
valueMultiplier,
|
||||
);
|
||||
const data = this._assetProxyInterface
|
||||
.transferFrom(assetData, from, to, valueMultiplier)
|
||||
.getABIEncodedTransactionData();
|
||||
const txHash = await this._web3Wrapper.sendTransactionAsync({
|
||||
to: (this._proxyContract as ERC1155ProxyContract).address,
|
||||
data,
|
||||
@@ -364,7 +353,7 @@ export class ERC1155ProxyWrapper {
|
||||
this._validateProxyContractExistsOrThrow();
|
||||
const tokenContract = this._getContractFromAddress(contractAddress);
|
||||
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;
|
||||
}
|
||||
public getFungibleTokenIds(): BigNumber[] {
|
@@ -1,18 +1,19 @@
|
||||
import { DevUtilsContract } from '@0x/contracts-dev-utils';
|
||||
import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contracts-erc20';
|
||||
import { constants, ERC20BalancesByOwner, txDefaults } from '@0x/contracts-test-utils';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import { ZeroExProvider } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { artifacts, ERC20ProxyContract } from '../../src';
|
||||
import { artifacts } from './artifacts';
|
||||
|
||||
import { ERC20ProxyContract, IAssetDataContract } from './wrappers';
|
||||
|
||||
export class ERC20Wrapper {
|
||||
private readonly _tokenOwnerAddresses: string[];
|
||||
private readonly _contractOwnerAddress: string;
|
||||
private readonly _provider: ZeroExProvider;
|
||||
private readonly _dummyTokenContracts: DummyERC20TokenContract[];
|
||||
private readonly _devUtils: DevUtilsContract;
|
||||
private readonly _assetDataInterface: IAssetDataContract;
|
||||
private _proxyContract?: ERC20ProxyContract;
|
||||
private _proxyIdIfExists?: string;
|
||||
/**
|
||||
@@ -27,7 +28,7 @@ export class ERC20Wrapper {
|
||||
this._provider = provider;
|
||||
this._tokenOwnerAddresses = tokenOwnerAddresses;
|
||||
this._contractOwnerAddress = contractOwnerAddress;
|
||||
this._devUtils = new DevUtilsContract(constants.NULL_ADDRESS, provider);
|
||||
this._assetDataInterface = new IAssetDataContract(constants.NULL_ADDRESS, provider);
|
||||
}
|
||||
public async deployDummyTokensAsync(
|
||||
numberToDeploy: number,
|
||||
@@ -56,7 +57,7 @@ export class ERC20Wrapper {
|
||||
txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
this._proxyIdIfExists = await this._proxyContract.getProxyId.callAsync();
|
||||
this._proxyIdIfExists = await this._proxyContract.getProxyId().callAsync();
|
||||
return this._proxyContract;
|
||||
}
|
||||
public getProxyId(): string {
|
||||
@@ -68,43 +69,39 @@ export class ERC20Wrapper {
|
||||
this._validateProxyContractExistsOrThrow();
|
||||
for (const dummyTokenContract of this._dummyTokenContracts) {
|
||||
for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
|
||||
await dummyTokenContract.setBalance.awaitTransactionSuccessAsync(
|
||||
tokenOwnerAddress,
|
||||
constants.INITIAL_ERC20_BALANCE,
|
||||
{ from: this._contractOwnerAddress },
|
||||
);
|
||||
await dummyTokenContract.approve.awaitTransactionSuccessAsync(
|
||||
(this._proxyContract as ERC20ProxyContract).address,
|
||||
constants.INITIAL_ERC20_ALLOWANCE,
|
||||
{ from: tokenOwnerAddress },
|
||||
);
|
||||
await dummyTokenContract
|
||||
.setBalance(tokenOwnerAddress, constants.INITIAL_ERC20_BALANCE)
|
||||
.awaitTransactionSuccessAsync({ from: this._contractOwnerAddress });
|
||||
await dummyTokenContract
|
||||
.approve((this._proxyContract as ERC20ProxyContract).address, constants.INITIAL_ERC20_ALLOWANCE)
|
||||
.awaitTransactionSuccessAsync({ from: tokenOwnerAddress });
|
||||
}
|
||||
}
|
||||
}
|
||||
public async getBalanceAsync(userAddress: string, assetData: string): Promise<BigNumber> {
|
||||
const tokenContract = await this._getTokenContractFromAssetDataAsync(assetData);
|
||||
const balance = new BigNumber(await tokenContract.balanceOf.callAsync(userAddress));
|
||||
const balance = new BigNumber(await tokenContract.balanceOf(userAddress).callAsync());
|
||||
return balance;
|
||||
}
|
||||
public async setBalanceAsync(userAddress: string, assetData: string, amount: BigNumber): Promise<void> {
|
||||
const tokenContract = await this._getTokenContractFromAssetDataAsync(assetData);
|
||||
await tokenContract.setBalance.awaitTransactionSuccessAsync(
|
||||
userAddress,
|
||||
amount,
|
||||
{ from: this._contractOwnerAddress },
|
||||
{ pollingIntervalMs: constants.AWAIT_TRANSACTION_MINED_MS },
|
||||
);
|
||||
await tokenContract
|
||||
.setBalance(userAddress, amount)
|
||||
.awaitTransactionSuccessAsync(
|
||||
{ from: this._contractOwnerAddress },
|
||||
{ pollingIntervalMs: constants.AWAIT_TRANSACTION_MINED_MS },
|
||||
);
|
||||
}
|
||||
public async getProxyAllowanceAsync(userAddress: string, assetData: string): Promise<BigNumber> {
|
||||
const tokenContract = await this._getTokenContractFromAssetDataAsync(assetData);
|
||||
const proxyAddress = (this._proxyContract as ERC20ProxyContract).address;
|
||||
const allowance = new BigNumber(await tokenContract.allowance.callAsync(userAddress, proxyAddress));
|
||||
const allowance = new BigNumber(await tokenContract.allowance(userAddress, proxyAddress).callAsync());
|
||||
return allowance;
|
||||
}
|
||||
public async setAllowanceAsync(userAddress: string, assetData: string, amount: BigNumber): Promise<void> {
|
||||
const tokenContract = await this._getTokenContractFromAssetDataAsync(assetData);
|
||||
const proxyAddress = (this._proxyContract as ERC20ProxyContract).address;
|
||||
await tokenContract.approve.awaitTransactionSuccessAsync(proxyAddress, amount, { from: userAddress });
|
||||
await tokenContract.approve(proxyAddress, amount).awaitTransactionSuccessAsync({ from: userAddress });
|
||||
}
|
||||
public async getBalancesAsync(): Promise<ERC20BalancesByOwner> {
|
||||
this._validateDummyTokenContractsExistOrThrow();
|
||||
@@ -113,7 +110,7 @@ export class ERC20Wrapper {
|
||||
const balanceInfo: Array<{ tokenOwnerAddress: string; tokenAddress: string }> = [];
|
||||
for (const dummyTokenContract of this._dummyTokenContracts) {
|
||||
for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
|
||||
balances.push(await dummyTokenContract.balanceOf.callAsync(tokenOwnerAddress));
|
||||
balances.push(await dummyTokenContract.balanceOf(tokenOwnerAddress).callAsync());
|
||||
balanceInfo.push({
|
||||
tokenOwnerAddress,
|
||||
tokenAddress: dummyTokenContract.address,
|
||||
@@ -147,7 +144,7 @@ export class ERC20Wrapper {
|
||||
return tokenAddresses;
|
||||
}
|
||||
private async _getTokenContractFromAssetDataAsync(assetData: string): Promise<DummyERC20TokenContract> {
|
||||
const [proxyId, tokenAddress] = await this._devUtils.decodeERC20AssetData.callAsync(assetData); // tslint:disable-line:no-unused-variable
|
||||
const tokenAddress = this._assetDataInterface.getABIDecodedTransactionData<string>('ERC20Token', assetData); // tslint:disable-line:no-unused-variable
|
||||
const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === tokenAddress);
|
||||
if (tokenContractIfExists === undefined) {
|
||||
throw new Error(`Token: ${tokenAddress} was not deployed through ERC20Wrapper`);
|
@@ -5,7 +5,9 @@ import { BigNumber } from '@0x/utils';
|
||||
import { ZeroExProvider } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { artifacts, ERC721ProxyContract } from '../../src';
|
||||
import { artifacts } from './artifacts';
|
||||
|
||||
import { ERC721ProxyContract } from './wrappers';
|
||||
|
||||
export class ERC721Wrapper {
|
||||
private readonly _tokenOwnerAddresses: string[];
|
||||
@@ -44,7 +46,7 @@ export class ERC721Wrapper {
|
||||
txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
this._proxyIdIfExists = await this._proxyContract.getProxyId.callAsync();
|
||||
this._proxyIdIfExists = await this._proxyContract.getProxyId().callAsync();
|
||||
return this._proxyContract;
|
||||
}
|
||||
public getProxyId(): string {
|
||||
@@ -78,7 +80,7 @@ export class ERC721Wrapper {
|
||||
}
|
||||
public async doesTokenExistAsync(tokenAddress: string, tokenId: BigNumber): Promise<boolean> {
|
||||
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;
|
||||
return doesExist;
|
||||
}
|
||||
@@ -93,14 +95,14 @@ export class ERC721Wrapper {
|
||||
): Promise<void> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||
const proxyAddress = (this._proxyContract as ERC721ProxyContract).address;
|
||||
await tokenContract.setApprovalForAll.awaitTransactionSuccessAsync(proxyAddress, isApproved, {
|
||||
await tokenContract.setApprovalForAll(proxyAddress, isApproved).awaitTransactionSuccessAsync({
|
||||
from: ownerAddress,
|
||||
});
|
||||
}
|
||||
public async approveAsync(to: string, tokenAddress: string, tokenId: BigNumber): Promise<void> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||
const tokenOwner = await this.ownerOfAsync(tokenAddress, tokenId);
|
||||
await tokenContract.approve.awaitTransactionSuccessAsync(to, tokenId, { from: tokenOwner });
|
||||
await tokenContract.approve(to, tokenId).awaitTransactionSuccessAsync({ from: tokenOwner });
|
||||
}
|
||||
public async transferFromAsync(
|
||||
tokenAddress: string,
|
||||
@@ -109,28 +111,28 @@ export class ERC721Wrapper {
|
||||
userAddress: string,
|
||||
): Promise<void> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||
await tokenContract.transferFrom.awaitTransactionSuccessAsync(currentOwner, userAddress, tokenId, {
|
||||
await tokenContract.transferFrom(currentOwner, userAddress, tokenId).awaitTransactionSuccessAsync({
|
||||
from: currentOwner,
|
||||
});
|
||||
}
|
||||
public async mintAsync(tokenAddress: string, tokenId: BigNumber, userAddress: string): Promise<void> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||
await tokenContract.mint.awaitTransactionSuccessAsync(userAddress, tokenId, {
|
||||
await tokenContract.mint(userAddress, tokenId).awaitTransactionSuccessAsync({
|
||||
from: this._contractOwnerAddress,
|
||||
});
|
||||
}
|
||||
public async burnAsync(tokenAddress: string, tokenId: BigNumber, owner: string): Promise<void> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||
await tokenContract.burn.awaitTransactionSuccessAsync(owner, tokenId, { from: this._contractOwnerAddress });
|
||||
await tokenContract.burn(owner, tokenId).awaitTransactionSuccessAsync({ from: this._contractOwnerAddress });
|
||||
}
|
||||
public async ownerOfAsync(tokenAddress: string, tokenId: BigNumber): Promise<string> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||
const owner = await tokenContract.ownerOf.callAsync(tokenId);
|
||||
const owner = await tokenContract.ownerOf(tokenId).callAsync();
|
||||
return owner;
|
||||
}
|
||||
public async isOwnerAsync(userAddress: string, tokenAddress: string, tokenId: BigNumber): Promise<boolean> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||
const tokenOwner = await tokenContract.ownerOf.callAsync(tokenId);
|
||||
const tokenOwner = await tokenContract.ownerOf(tokenId).callAsync();
|
||||
const isOwner = tokenOwner === userAddress;
|
||||
return isOwner;
|
||||
}
|
||||
@@ -138,13 +140,13 @@ export class ERC721Wrapper {
|
||||
this._validateProxyContractExistsOrThrow();
|
||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||
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;
|
||||
}
|
||||
public async isProxyApprovedAsync(tokenAddress: string, tokenId: BigNumber): Promise<boolean> {
|
||||
this._validateProxyContractExistsOrThrow();
|
||||
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 isProxyAnApprovedOperator = approvedAddress === proxyAddress;
|
||||
return isProxyAnApprovedOperator;
|
||||
@@ -161,7 +163,7 @@ export class ERC721Wrapper {
|
||||
dummyTokenContract.address
|
||||
];
|
||||
for (const tokenId of initialTokenOwnerIds) {
|
||||
tokenOwnerAddresses.push(await dummyTokenContract.ownerOf.callAsync(tokenId));
|
||||
tokenOwnerAddresses.push(await dummyTokenContract.ownerOf(tokenId).callAsync());
|
||||
tokenInfo.push({
|
||||
tokenId,
|
||||
tokenAddress: dummyTokenContract.address,
|
@@ -1,3 +1,90 @@
|
||||
export * from './artifacts';
|
||||
export * from './wrappers';
|
||||
export * from '../test/utils';
|
||||
export { artifacts } from './artifacts';
|
||||
export {
|
||||
ChaiBridgeContract,
|
||||
ERC1155ProxyContract,
|
||||
ERC20BridgeProxyContract,
|
||||
ERC20ProxyContract,
|
||||
ERC721ProxyContract,
|
||||
Eth2DaiBridgeContract,
|
||||
DydxBridgeContract,
|
||||
IAssetDataContract,
|
||||
IAssetProxyContract,
|
||||
IChaiContract,
|
||||
IDydxContract,
|
||||
KyberBridgeContract,
|
||||
MultiAssetProxyContract,
|
||||
StaticCallProxyContract,
|
||||
TestDydxBridgeContract,
|
||||
TestStaticCallTargetContract,
|
||||
UniswapBridgeContract,
|
||||
} 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,6 +3,9 @@
|
||||
* 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/erc20_bridge_proxy';
|
||||
export * from '../generated-wrappers/erc20_proxy';
|
||||
@@ -12,17 +15,26 @@ export * from '../generated-wrappers/i_asset_data';
|
||||
export * from '../generated-wrappers/i_asset_proxy';
|
||||
export * from '../generated-wrappers/i_asset_proxy_dispatcher';
|
||||
export * from '../generated-wrappers/i_authorizable';
|
||||
export * from '../generated-wrappers/i_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/multi_asset_proxy';
|
||||
export * from '../generated-wrappers/ownable';
|
||||
export * from '../generated-wrappers/static_call_proxy';
|
||||
export * from '../generated-wrappers/test_chai_bridge';
|
||||
export * from '../generated-wrappers/test_dydx_bridge';
|
||||
export * from '../generated-wrappers/test_erc20_bridge';
|
||||
export * from '../generated-wrappers/test_eth2_dai_bridge';
|
||||
export * from '../generated-wrappers/test_kyber_bridge';
|
||||
export * from '../generated-wrappers/test_static_call_target';
|
||||
export * from '../generated-wrappers/test_uniswap_bridge';
|
||||
export * from '../generated-wrappers/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,33 +1,20 @@
|
||||
import { chaiSetup, expectTransactionFailedAsync, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils';
|
||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||
import { blockchainTests, expect, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils';
|
||||
import { RevertReason } from '@0x/types';
|
||||
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();
|
||||
const expect = chai.expect;
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
import { MixinAuthorizableContract } from './wrappers';
|
||||
|
||||
describe('Authorizable', () => {
|
||||
blockchainTests.resets('Authorizable', () => {
|
||||
let owner: string;
|
||||
let notOwner: string;
|
||||
let address: string;
|
||||
let authorizable: MixinAuthorizableContract;
|
||||
|
||||
before(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
|
||||
before(async () => {
|
||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
||||
[owner, address, notOwner] = _.slice(accounts, 0, 3);
|
||||
[owner, address, notOwner] = accounts.slice(0, 3);
|
||||
authorizable = await MixinAuthorizableContract.deployFrom0xArtifactAsync(
|
||||
artifacts.MixinAuthorizable,
|
||||
provider,
|
||||
@@ -36,131 +23,105 @@ describe('Authorizable', () => {
|
||||
);
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
|
||||
describe('addAuthorizedAddress', () => {
|
||||
it('should revert if not called by owner', async () => {
|
||||
await expectTransactionFailedAsync(
|
||||
authorizable.addAuthorizedAddress.sendTransactionAsync(notOwner, { from: notOwner }),
|
||||
RevertReason.OnlyContractOwner,
|
||||
);
|
||||
const tx = authorizable.addAuthorizedAddress(notOwner).sendTransactionAsync({ from: notOwner });
|
||||
return expect(tx).to.revertWith(RevertReason.OnlyContractOwner);
|
||||
});
|
||||
|
||||
it('should allow owner to add an authorized address', async () => {
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(address, { from: owner });
|
||||
const isAuthorized = await authorizable.authorized.callAsync(address);
|
||||
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
||||
const isAuthorized = await authorizable.authorized(address).callAsync();
|
||||
expect(isAuthorized).to.be.true();
|
||||
});
|
||||
|
||||
it('should revert if owner attempts to authorize a duplicate address', async () => {
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(address, { from: owner });
|
||||
return expectTransactionFailedAsync(
|
||||
authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }),
|
||||
RevertReason.TargetAlreadyAuthorized,
|
||||
);
|
||||
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
||||
const tx = authorizable.addAuthorizedAddress(address).sendTransactionAsync({ from: owner });
|
||||
return expect(tx).to.revertWith(RevertReason.TargetAlreadyAuthorized);
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeAuthorizedAddress', () => {
|
||||
it('should revert if not called by owner', async () => {
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(address, { from: owner });
|
||||
await expectTransactionFailedAsync(
|
||||
authorizable.removeAuthorizedAddress.sendTransactionAsync(address, { from: notOwner }),
|
||||
RevertReason.OnlyContractOwner,
|
||||
);
|
||||
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
||||
const tx = authorizable.removeAuthorizedAddress(address).sendTransactionAsync({ from: notOwner });
|
||||
return expect(tx).to.revertWith(RevertReason.OnlyContractOwner);
|
||||
});
|
||||
|
||||
it('should allow owner to remove an authorized address', async () => {
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(address, { from: owner });
|
||||
await authorizable.removeAuthorizedAddress.awaitTransactionSuccessAsync(address, { from: owner });
|
||||
const isAuthorized = await authorizable.authorized.callAsync(address);
|
||||
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
||||
await authorizable.removeAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
||||
const isAuthorized = await authorizable.authorized(address).callAsync();
|
||||
expect(isAuthorized).to.be.false();
|
||||
});
|
||||
|
||||
it('should revert if owner attempts to remove an address that is not authorized', async () => {
|
||||
return expectTransactionFailedAsync(
|
||||
authorizable.removeAuthorizedAddress.sendTransactionAsync(address, {
|
||||
from: owner,
|
||||
}),
|
||||
RevertReason.TargetNotAuthorized,
|
||||
);
|
||||
const tx = authorizable.removeAuthorizedAddress(address).sendTransactionAsync({ from: owner });
|
||||
return expect(tx).to.revertWith(RevertReason.TargetNotAuthorized);
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeAuthorizedAddressAtIndex', () => {
|
||||
it('should revert if not called by owner', async () => {
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(address, { from: owner });
|
||||
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
||||
const index = new BigNumber(0);
|
||||
await expectTransactionFailedAsync(
|
||||
authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, {
|
||||
from: notOwner,
|
||||
}),
|
||||
RevertReason.OnlyContractOwner,
|
||||
);
|
||||
const tx = authorizable
|
||||
.removeAuthorizedAddressAtIndex(address, index)
|
||||
.sendTransactionAsync({ from: notOwner });
|
||||
return expect(tx).to.revertWith(RevertReason.OnlyContractOwner);
|
||||
});
|
||||
|
||||
it('should revert if index is >= authorities.length', async () => {
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(address, { from: owner });
|
||||
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
||||
const index = new BigNumber(1);
|
||||
return expectTransactionFailedAsync(
|
||||
authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, {
|
||||
from: owner,
|
||||
}),
|
||||
RevertReason.IndexOutOfBounds,
|
||||
);
|
||||
const tx = authorizable
|
||||
.removeAuthorizedAddressAtIndex(address, index)
|
||||
.sendTransactionAsync({ from: owner });
|
||||
return expect(tx).to.revertWith(RevertReason.IndexOutOfBounds);
|
||||
});
|
||||
|
||||
it('should revert if owner attempts to remove an address that is not authorized', async () => {
|
||||
const index = new BigNumber(0);
|
||||
return expectTransactionFailedAsync(
|
||||
authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, {
|
||||
from: owner,
|
||||
}),
|
||||
RevertReason.TargetNotAuthorized,
|
||||
);
|
||||
const tx = authorizable
|
||||
.removeAuthorizedAddressAtIndex(address, index)
|
||||
.sendTransactionAsync({ from: owner });
|
||||
return expect(tx).to.revertWith(RevertReason.TargetNotAuthorized);
|
||||
});
|
||||
|
||||
it('should revert if address at index does not match target', async () => {
|
||||
const address1 = address;
|
||||
const address2 = notOwner;
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(address1, { from: owner });
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(address2, { from: owner });
|
||||
await authorizable.addAuthorizedAddress(address1).awaitTransactionSuccessAsync({ from: owner });
|
||||
await authorizable.addAuthorizedAddress(address2).awaitTransactionSuccessAsync({ from: owner });
|
||||
const address1Index = new BigNumber(0);
|
||||
return expectTransactionFailedAsync(
|
||||
authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address2, address1Index, {
|
||||
from: owner,
|
||||
}),
|
||||
RevertReason.AuthorizedAddressMismatch,
|
||||
);
|
||||
const tx = authorizable
|
||||
.removeAuthorizedAddressAtIndex(address2, address1Index)
|
||||
.sendTransactionAsync({ from: owner });
|
||||
return expect(tx).to.revertWith(RevertReason.AuthorizedAddressMismatch);
|
||||
});
|
||||
|
||||
it('should allow owner to remove an authorized address', async () => {
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(address, { from: owner });
|
||||
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
||||
const index = new BigNumber(0);
|
||||
await authorizable.removeAuthorizedAddressAtIndex.awaitTransactionSuccessAsync(address, index, {
|
||||
await authorizable.removeAuthorizedAddressAtIndex(address, index).awaitTransactionSuccessAsync({
|
||||
from: owner,
|
||||
});
|
||||
const isAuthorized = await authorizable.authorized.callAsync(address);
|
||||
const isAuthorized = await authorizable.authorized(address).callAsync();
|
||||
expect(isAuthorized).to.be.false();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAuthorizedAddresses', () => {
|
||||
it('should return all authorized addresses', async () => {
|
||||
const initial = await authorizable.getAuthorizedAddresses.callAsync();
|
||||
const initial = await authorizable.getAuthorizedAddresses().callAsync();
|
||||
expect(initial).to.have.length(0);
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(address, { from: owner });
|
||||
const afterAdd = await authorizable.getAuthorizedAddresses.callAsync();
|
||||
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
||||
const afterAdd = await authorizable.getAuthorizedAddresses().callAsync();
|
||||
expect(afterAdd).to.have.length(1);
|
||||
expect(afterAdd).to.include(address);
|
||||
await authorizable.removeAuthorizedAddress.awaitTransactionSuccessAsync(address, { from: owner });
|
||||
const afterRemove = await authorizable.getAuthorizedAddresses.callAsync();
|
||||
await authorizable.removeAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
||||
const afterRemove = await authorizable.getAuthorizedAddresses().callAsync();
|
||||
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,
|
||||
accountIdx: constants.ZERO_AMOUNT,
|
||||
marketId,
|
||||
conversionRateNumerator: constants.ZERO_AMOUNT,
|
||||
conversionRateDenominator: constants.ZERO_AMOUNT,
|
||||
};
|
||||
const defaultWithdrawAction = {
|
||||
actionType: DydxBridgeActionType.Withdraw,
|
||||
accountIdx: constants.ZERO_AMOUNT,
|
||||
marketId,
|
||||
conversionRateNumerator: constants.ZERO_AMOUNT,
|
||||
conversionRateDenominator: constants.ZERO_AMOUNT,
|
||||
};
|
||||
let testContract: TestDydxBridgeContract;
|
||||
let testProxyContract: ERC20BridgeProxyContract;
|
||||
let assetDataEncoder: IAssetDataContract;
|
||||
let owner: string;
|
||||
let authorized: string;
|
||||
let accountOwner: string;
|
||||
let receiver: string;
|
||||
|
||||
before(async () => {
|
||||
// Get accounts
|
||||
const accounts = await env.web3Wrapper.getAvailableAddressesAsync();
|
||||
[owner, authorized, accountOwner, receiver] = accounts;
|
||||
|
||||
// Deploy dydx bridge
|
||||
testContract = await TestDydxBridgeContract.deployFrom0xArtifactAsync(
|
||||
artifacts.TestDydxBridge,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
[accountOwner, receiver],
|
||||
);
|
||||
|
||||
// Deploy test erc20 bridge proxy
|
||||
testProxyContract = await ERC20BridgeProxyContract.deployFrom0xArtifactAsync(
|
||||
artifacts.ERC20BridgeProxy,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
await testProxyContract.addAuthorizedAddress(authorized).awaitTransactionSuccessAsync({ from: owner });
|
||||
|
||||
// Setup asset data encoder
|
||||
assetDataEncoder = new IAssetDataContract(constants.NULL_ADDRESS, env.provider);
|
||||
});
|
||||
|
||||
describe('bridgeTransferFrom()', () => {
|
||||
const callBridgeTransferFrom = async (
|
||||
from: string,
|
||||
to: string,
|
||||
amount: BigNumber,
|
||||
bridgeData: DydxBridgeData,
|
||||
sender: string,
|
||||
): Promise<string> => {
|
||||
const returnValue = await testContract
|
||||
.bridgeTransferFrom(
|
||||
constants.NULL_ADDRESS,
|
||||
from,
|
||||
to,
|
||||
amount,
|
||||
dydxBridgeDataEncoder.encode({ bridgeData }),
|
||||
)
|
||||
.callAsync({ from: sender });
|
||||
return returnValue;
|
||||
};
|
||||
const executeBridgeTransferFromAndVerifyEvents = async (
|
||||
from: string,
|
||||
to: string,
|
||||
amount: BigNumber,
|
||||
bridgeData: DydxBridgeData,
|
||||
sender: string,
|
||||
): Promise<void> => {
|
||||
// Execute transaction.
|
||||
const txReceipt = await testContract
|
||||
.bridgeTransferFrom(
|
||||
constants.NULL_ADDRESS,
|
||||
from,
|
||||
to,
|
||||
amount,
|
||||
dydxBridgeDataEncoder.encode({ bridgeData }),
|
||||
)
|
||||
.awaitTransactionSuccessAsync({ from: sender });
|
||||
|
||||
// Verify `OperateAccount` event.
|
||||
const expectedOperateAccountEvents = [];
|
||||
for (const accountNumber of bridgeData.accountNumbers) {
|
||||
expectedOperateAccountEvents.push({
|
||||
owner: accountOwner,
|
||||
number: accountNumber,
|
||||
});
|
||||
}
|
||||
verifyEventsFromLogs(txReceipt.logs, expectedOperateAccountEvents, TestDydxBridgeEvents.OperateAccount);
|
||||
|
||||
// Verify `OperateAction` event.
|
||||
const weiDenomination = 0;
|
||||
const deltaAmountRef = 0;
|
||||
const expectedOperateActionEvents = [];
|
||||
for (const action of bridgeData.actions) {
|
||||
expectedOperateActionEvents.push({
|
||||
actionType: action.actionType as number,
|
||||
accountIdx: action.accountIdx,
|
||||
amountSign: action.actionType === DydxBridgeActionType.Deposit ? true : false,
|
||||
amountDenomination: weiDenomination,
|
||||
amountRef: deltaAmountRef,
|
||||
amountValue: action.conversionRateDenominator.gt(0)
|
||||
? amount
|
||||
.times(action.conversionRateNumerator)
|
||||
.dividedToIntegerBy(action.conversionRateDenominator)
|
||||
: amount,
|
||||
primaryMarketId: marketId,
|
||||
secondaryMarketId: constants.ZERO_AMOUNT,
|
||||
otherAddress: action.actionType === DydxBridgeActionType.Deposit ? from : to,
|
||||
otherAccountId: constants.ZERO_AMOUNT,
|
||||
data: '0x',
|
||||
});
|
||||
}
|
||||
verifyEventsFromLogs(txReceipt.logs, expectedOperateActionEvents, TestDydxBridgeEvents.OperateAction);
|
||||
};
|
||||
it('succeeds when calling with zero amount', async () => {
|
||||
const bridgeData = {
|
||||
accountNumbers: [defaultAccountNumber],
|
||||
actions: [defaultDepositAction],
|
||||
};
|
||||
await executeBridgeTransferFromAndVerifyEvents(
|
||||
accountOwner,
|
||||
receiver,
|
||||
constants.ZERO_AMOUNT,
|
||||
bridgeData,
|
||||
authorized,
|
||||
);
|
||||
});
|
||||
it('succeeds when calling with no accounts', async () => {
|
||||
const bridgeData = {
|
||||
accountNumbers: [],
|
||||
actions: [defaultDepositAction],
|
||||
};
|
||||
await executeBridgeTransferFromAndVerifyEvents(
|
||||
accountOwner,
|
||||
receiver,
|
||||
defaultAmount,
|
||||
bridgeData,
|
||||
authorized,
|
||||
);
|
||||
});
|
||||
it('succeeds when calling with no actions', async () => {
|
||||
const bridgeData = {
|
||||
accountNumbers: [defaultAccountNumber],
|
||||
actions: [],
|
||||
};
|
||||
await executeBridgeTransferFromAndVerifyEvents(
|
||||
accountOwner,
|
||||
receiver,
|
||||
defaultAmount,
|
||||
bridgeData,
|
||||
authorized,
|
||||
);
|
||||
});
|
||||
it('succeeds when calling `operate` with the `deposit` action and a single account', async () => {
|
||||
const bridgeData = {
|
||||
accountNumbers: [defaultAccountNumber],
|
||||
actions: [defaultDepositAction],
|
||||
};
|
||||
await executeBridgeTransferFromAndVerifyEvents(
|
||||
accountOwner,
|
||||
receiver,
|
||||
defaultAmount,
|
||||
bridgeData,
|
||||
authorized,
|
||||
);
|
||||
});
|
||||
it('succeeds when calling `operate` with the `deposit` action and multiple accounts', async () => {
|
||||
const bridgeData = {
|
||||
accountNumbers: [defaultAccountNumber, defaultAccountNumber.plus(1)],
|
||||
actions: [defaultDepositAction],
|
||||
};
|
||||
await executeBridgeTransferFromAndVerifyEvents(
|
||||
accountOwner,
|
||||
receiver,
|
||||
defaultAmount,
|
||||
bridgeData,
|
||||
authorized,
|
||||
);
|
||||
});
|
||||
it('succeeds when calling `operate` with the `withdraw` action and a single account', async () => {
|
||||
const bridgeData = {
|
||||
accountNumbers: [defaultAccountNumber],
|
||||
actions: [defaultWithdrawAction],
|
||||
};
|
||||
await executeBridgeTransferFromAndVerifyEvents(
|
||||
accountOwner,
|
||||
receiver,
|
||||
defaultAmount,
|
||||
bridgeData,
|
||||
authorized,
|
||||
);
|
||||
});
|
||||
it('succeeds when calling `operate` with the `withdraw` action and multiple accounts', async () => {
|
||||
const bridgeData = {
|
||||
accountNumbers: [defaultAccountNumber, defaultAccountNumber.plus(1)],
|
||||
actions: [defaultWithdrawAction],
|
||||
};
|
||||
await executeBridgeTransferFromAndVerifyEvents(
|
||||
accountOwner,
|
||||
receiver,
|
||||
defaultAmount,
|
||||
bridgeData,
|
||||
authorized,
|
||||
);
|
||||
});
|
||||
it('succeeds when calling `operate` with the `deposit` action and multiple accounts', async () => {
|
||||
const bridgeData = {
|
||||
accountNumbers: [defaultAccountNumber, defaultAccountNumber.plus(1)],
|
||||
actions: [defaultWithdrawAction, defaultDepositAction],
|
||||
};
|
||||
await executeBridgeTransferFromAndVerifyEvents(
|
||||
accountOwner,
|
||||
receiver,
|
||||
defaultAmount,
|
||||
bridgeData,
|
||||
authorized,
|
||||
);
|
||||
});
|
||||
it('succeeds when calling `operate` with multiple actions under a single account', async () => {
|
||||
const bridgeData = {
|
||||
accountNumbers: [defaultAccountNumber],
|
||||
actions: [defaultWithdrawAction, defaultDepositAction],
|
||||
};
|
||||
await executeBridgeTransferFromAndVerifyEvents(
|
||||
accountOwner,
|
||||
receiver,
|
||||
defaultAmount,
|
||||
bridgeData,
|
||||
authorized,
|
||||
);
|
||||
});
|
||||
it('succeeds when scaling the `amount` to deposit', async () => {
|
||||
const conversionRateNumerator = new BigNumber(1);
|
||||
const conversionRateDenominator = new BigNumber(2);
|
||||
const bridgeData = {
|
||||
accountNumbers: [defaultAccountNumber],
|
||||
actions: [
|
||||
defaultWithdrawAction,
|
||||
{
|
||||
...defaultDepositAction,
|
||||
conversionRateNumerator,
|
||||
conversionRateDenominator,
|
||||
},
|
||||
],
|
||||
};
|
||||
await executeBridgeTransferFromAndVerifyEvents(
|
||||
accountOwner,
|
||||
receiver,
|
||||
defaultAmount,
|
||||
bridgeData,
|
||||
authorized,
|
||||
);
|
||||
});
|
||||
it('succeeds when scaling the `amount` to withdraw', async () => {
|
||||
const conversionRateNumerator = new BigNumber(1);
|
||||
const conversionRateDenominator = new BigNumber(2);
|
||||
const bridgeData = {
|
||||
accountNumbers: [defaultAccountNumber],
|
||||
actions: [
|
||||
defaultDepositAction,
|
||||
{
|
||||
...defaultWithdrawAction,
|
||||
conversionRateNumerator,
|
||||
conversionRateDenominator,
|
||||
},
|
||||
],
|
||||
};
|
||||
await executeBridgeTransferFromAndVerifyEvents(
|
||||
accountOwner,
|
||||
receiver,
|
||||
defaultAmount,
|
||||
bridgeData,
|
||||
authorized,
|
||||
);
|
||||
});
|
||||
it('reverts if not called by the ERC20 Bridge Proxy', async () => {
|
||||
const bridgeData = {
|
||||
accountNumbers: [defaultAccountNumber],
|
||||
actions: [defaultDepositAction],
|
||||
};
|
||||
const callBridgeTransferFromPromise = callBridgeTransferFrom(
|
||||
accountOwner,
|
||||
receiver,
|
||||
defaultAmount,
|
||||
bridgeData,
|
||||
notAuthorized,
|
||||
);
|
||||
const expectedError = RevertReason.DydxBridgeOnlyCallableByErc20BridgeProxy;
|
||||
return expect(callBridgeTransferFromPromise).to.revertWith(expectedError);
|
||||
});
|
||||
it('should return magic bytes if call succeeds', async () => {
|
||||
const bridgeData = {
|
||||
accountNumbers: [defaultAccountNumber],
|
||||
actions: [defaultDepositAction],
|
||||
};
|
||||
const returnValue = await callBridgeTransferFrom(
|
||||
accountOwner,
|
||||
receiver,
|
||||
defaultAmount,
|
||||
bridgeData,
|
||||
authorized,
|
||||
);
|
||||
expect(returnValue).to.equal(AssetProxyId.ERC20Bridge);
|
||||
});
|
||||
it('should revert when `Operate` reverts', async () => {
|
||||
// Set revert flag.
|
||||
await testContract.setRevertOnOperate(true).awaitTransactionSuccessAsync();
|
||||
|
||||
// Execute transfer.
|
||||
const bridgeData = {
|
||||
accountNumbers: [defaultAccountNumber],
|
||||
actions: [defaultDepositAction],
|
||||
};
|
||||
const tx = callBridgeTransferFrom(accountOwner, receiver, defaultAmount, bridgeData, authorized);
|
||||
const expectedError = 'TestDydxBridge/SHOULD_REVERT_ON_OPERATE';
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
it('should revert when there is a rounding error', async () => {
|
||||
// Setup a rounding error
|
||||
const conversionRateNumerator = new BigNumber(5318);
|
||||
const conversionRateDenominator = new BigNumber(47958);
|
||||
const amount = new BigNumber(9000);
|
||||
const bridgeData = {
|
||||
accountNumbers: [defaultAccountNumber],
|
||||
actions: [
|
||||
defaultDepositAction,
|
||||
{
|
||||
...defaultWithdrawAction,
|
||||
conversionRateNumerator,
|
||||
conversionRateDenominator,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// Execute transfer and assert error.
|
||||
const tx = callBridgeTransferFrom(accountOwner, receiver, amount, bridgeData, authorized);
|
||||
const expectedError = new LibMathRevertErrors.RoundingError(
|
||||
conversionRateNumerator,
|
||||
conversionRateDenominator,
|
||||
amount,
|
||||
);
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ERC20BridgeProxy.transferFrom()', () => {
|
||||
const bridgeData = {
|
||||
accountNumbers: [defaultAccountNumber],
|
||||
actions: [defaultWithdrawAction],
|
||||
};
|
||||
let assetData: string;
|
||||
|
||||
before(async () => {
|
||||
const testTokenAddress = await testContract.getTestToken().callAsync();
|
||||
assetData = assetDataEncoder
|
||||
.ERC20Bridge(testTokenAddress, testContract.address, dydxBridgeDataEncoder.encode({ bridgeData }))
|
||||
.getABIEncodedTransactionData();
|
||||
});
|
||||
|
||||
it('should succeed if `bridgeTransferFrom` succeeds', async () => {
|
||||
await testProxyContract
|
||||
.transferFrom(assetData, accountOwner, receiver, defaultAmount)
|
||||
.awaitTransactionSuccessAsync({ from: authorized });
|
||||
});
|
||||
it('should revert if `bridgeTransferFrom` reverts', async () => {
|
||||
// Set revert flag.
|
||||
await testContract.setRevertOnOperate(true).awaitTransactionSuccessAsync();
|
||||
const tx = testProxyContract
|
||||
.transferFrom(assetData, accountOwner, receiver, defaultAmount)
|
||||
.awaitTransactionSuccessAsync({ from: authorized });
|
||||
const expectedError = 'TestDydxBridge/SHOULD_REVERT_ON_OPERATE';
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
});
|
||||
});
|
@@ -1,4 +1,3 @@
|
||||
import { DevUtilsContract } from '@0x/contracts-dev-utils';
|
||||
import {
|
||||
artifacts as erc1155Artifacts,
|
||||
DummyERC1155ReceiverBatchTokenReceivedEventArgs,
|
||||
@@ -15,15 +14,19 @@ import {
|
||||
txDefaults,
|
||||
web3Wrapper,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { SafeMathRevertErrors } from '@0x/contracts-utils';
|
||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||
import { AssetProxyId, RevertReason } from '@0x/types';
|
||||
import { BigNumber, SafeMathRevertErrors } from '@0x/utils';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as chai from 'chai';
|
||||
import { LogWithDecodedArgs } from 'ethereum-types';
|
||||
import * as ethUtil from 'ethereumjs-util';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { artifacts, ERC1155ProxyContract, ERC1155ProxyWrapper, IAssetDataContract } from '../src';
|
||||
import { ERC1155ProxyWrapper } from '../src/erc1155_proxy_wrapper';
|
||||
import { ERC1155ProxyContract, IAssetDataContract } from '../src/wrappers';
|
||||
|
||||
import { artifacts } from './artifacts';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
@@ -59,8 +62,8 @@ describe('ERC1155Proxy', () => {
|
||||
// tokens
|
||||
let fungibleTokens: BigNumber[];
|
||||
let nonFungibleTokensOwnedBySpender: BigNumber[];
|
||||
// devUtils for encoding and decoding assetData
|
||||
let devUtils: DevUtilsContract;
|
||||
// IAssetData for encoding and decoding assetData
|
||||
let assetDataContract: IAssetDataContract;
|
||||
// tests
|
||||
before(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
@@ -74,8 +77,8 @@ describe('ERC1155Proxy', () => {
|
||||
const usedAddresses = ([owner, notAuthorized, authorized, spender, receiver] = _.slice(accounts, 0, 5));
|
||||
erc1155ProxyWrapper = new ERC1155ProxyWrapper(provider, usedAddresses, owner);
|
||||
erc1155Proxy = await erc1155ProxyWrapper.deployProxyAsync();
|
||||
await erc1155Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(authorized, { from: owner });
|
||||
await erc1155Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(erc1155Proxy.address, { from: owner });
|
||||
await erc1155Proxy.addAuthorizedAddress(authorized).awaitTransactionSuccessAsync({ from: owner });
|
||||
await erc1155Proxy.addAuthorizedAddress(erc1155Proxy.address).awaitTransactionSuccessAsync({ from: owner });
|
||||
// deploy & configure ERC1155 tokens and receiver
|
||||
[erc1155Wrapper] = await erc1155ProxyWrapper.deployDummyContractsAsync();
|
||||
erc1155Contract = erc1155Wrapper.getContract();
|
||||
@@ -97,8 +100,8 @@ describe('ERC1155Proxy', () => {
|
||||
tokenBalances.nonFungible[spender][erc1155Contract.address][nonFungibleTokenAsString][0];
|
||||
nonFungibleTokensOwnedBySpender.push(nonFungibleTokenHeldBySpender);
|
||||
});
|
||||
// set up devUtils
|
||||
devUtils = new DevUtilsContract(constants.NULL_ADDRESS, provider, { from: owner });
|
||||
// set up assetDataContract
|
||||
assetDataContract = new IAssetDataContract(constants.NULL_ADDRESS, provider, { from: owner });
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
@@ -119,7 +122,7 @@ describe('ERC1155Proxy', () => {
|
||||
);
|
||||
});
|
||||
it('should have an id of 0xa7cb5fb7', async () => {
|
||||
const proxyId = await erc1155Proxy.getProxyId.callAsync();
|
||||
const proxyId = await erc1155Proxy.getProxyId().callAsync();
|
||||
const expectedProxyId = AssetProxyId.ERC1155;
|
||||
expect(proxyId).to.equal(expectedProxyId);
|
||||
});
|
||||
@@ -634,12 +637,9 @@ describe('ERC1155Proxy', () => {
|
||||
return value.times(valueMultiplier);
|
||||
});
|
||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||
const assetData = await devUtils.encodeERC1155AssetData.callAsync(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
const assetData = assetDataContract
|
||||
.ERC1155Assets(erc1155ContractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
|
||||
.getABIEncodedTransactionData();
|
||||
const extraData = '0102030405060708091001020304050607080910010203040506070809100102';
|
||||
const assetDataWithExtraData = `${assetData}${extraData}`;
|
||||
// check balances before transfer
|
||||
@@ -694,14 +694,16 @@ describe('ERC1155Proxy', () => {
|
||||
// create token
|
||||
await erc1155Wrapper
|
||||
.getContract()
|
||||
.createWithType.awaitTransactionSuccessAsync(tokenToCreate, tokenUri, {
|
||||
.createWithType(tokenToCreate, tokenUri)
|
||||
.awaitTransactionSuccessAsync({
|
||||
from: owner,
|
||||
});
|
||||
|
||||
// mint balance for spender
|
||||
await erc1155Wrapper
|
||||
.getContract()
|
||||
.mintFungible.awaitTransactionSuccessAsync(tokenToCreate, [spender], [spenderInitialBalance], {
|
||||
.mintFungible(tokenToCreate, [spender], [spenderInitialBalance])
|
||||
.awaitTransactionSuccessAsync({
|
||||
from: owner,
|
||||
});
|
||||
}
|
||||
@@ -737,9 +739,8 @@ describe('ERC1155Proxy', () => {
|
||||
const valuesToTransfer = tokensToTransfer;
|
||||
const valueMultiplier = new BigNumber(2);
|
||||
|
||||
// hand encode optimized assetData because our tooling (based on LibAssetData.sol/encodeERC1155AssetData) does not use optimized encoding
|
||||
const assetDataContract = new IAssetDataContract(constants.NULL_ADDRESS, provider);
|
||||
const selector = assetDataContract.ERC1155Assets.getSelector();
|
||||
// hand encode optimized assetData because our tooling (based on LibAssetData.sol/ERC1155Assets) does not use optimized encoding
|
||||
const selector = assetDataContract.getSelector('ERC1155Assets');
|
||||
const assetDataWithoutContractAddress =
|
||||
'0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040102030400000000000000000000000000000000000000000000000000000000';
|
||||
const assetData = `${selector}000000000000000000000000${erc1155ContractAddress.substr(
|
||||
@@ -794,14 +795,16 @@ describe('ERC1155Proxy', () => {
|
||||
// create token
|
||||
await erc1155Wrapper
|
||||
.getContract()
|
||||
.createWithType.awaitTransactionSuccessAsync(tokenToCreate, tokenUri, {
|
||||
.createWithType(tokenToCreate, tokenUri)
|
||||
.awaitTransactionSuccessAsync({
|
||||
from: owner,
|
||||
});
|
||||
|
||||
// mint balance for spender
|
||||
await erc1155Wrapper
|
||||
.getContract()
|
||||
.mintFungible.awaitTransactionSuccessAsync(tokenToCreate, [spender], [spenderInitialBalance], {
|
||||
.mintFungible(tokenToCreate, [spender], [spenderInitialBalance])
|
||||
.awaitTransactionSuccessAsync({
|
||||
from: owner,
|
||||
});
|
||||
}
|
||||
@@ -847,12 +850,9 @@ describe('ERC1155Proxy', () => {
|
||||
const valuesToTransfer = [new BigNumber(2), new BigNumber(2)];
|
||||
const valueMultiplier = new BigNumber(2);
|
||||
// create callback data that is the encoded version of `valuesToTransfer`
|
||||
const generatedAssetData = await devUtils.encodeERC1155AssetData.callAsync(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
const generatedAssetData = assetDataContract
|
||||
.ERC1155Assets(erc1155ContractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
|
||||
.getABIEncodedTransactionData();
|
||||
// remove the function selector and contract address from check, as these change on each test
|
||||
const offsetToTokenIds = 74;
|
||||
const assetDataSelectorAndContractAddress = generatedAssetData.substr(0, offsetToTokenIds);
|
||||
@@ -919,14 +919,16 @@ describe('ERC1155Proxy', () => {
|
||||
// create token
|
||||
await erc1155Wrapper
|
||||
.getContract()
|
||||
.createWithType.awaitTransactionSuccessAsync(tokenToCreate, tokenUri, {
|
||||
.createWithType(tokenToCreate, tokenUri)
|
||||
.awaitTransactionSuccessAsync({
|
||||
from: owner,
|
||||
});
|
||||
|
||||
// mint balance for spender
|
||||
await erc1155Wrapper
|
||||
.getContract()
|
||||
.mintFungible.awaitTransactionSuccessAsync(tokenToCreate, [spender], [spenderInitialBalance], {
|
||||
.mintFungible(tokenToCreate, [spender], [spenderInitialBalance])
|
||||
.awaitTransactionSuccessAsync({
|
||||
from: owner,
|
||||
});
|
||||
}
|
||||
@@ -969,12 +971,9 @@ describe('ERC1155Proxy', () => {
|
||||
const valuesToTransfer = [new BigNumber(1), new BigNumber(2)];
|
||||
const valueMultiplier = new BigNumber(2);
|
||||
// create callback data that is the encoded version of `valuesToTransfer`
|
||||
const generatedAssetData = await devUtils.encodeERC1155AssetData.callAsync(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
const generatedAssetData = assetDataContract
|
||||
.ERC1155Assets(erc1155ContractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
|
||||
.getABIEncodedTransactionData();
|
||||
// remove the function selector and contract address from check, as these change on each test
|
||||
const offsetToTokenIds = 74;
|
||||
const assetDataSelectorAndContractAddress = generatedAssetData.substr(0, offsetToTokenIds);
|
||||
@@ -1032,12 +1031,9 @@ describe('ERC1155Proxy', () => {
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||
const assetData = await devUtils.encodeERC1155AssetData.callAsync(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
const assetData = assetDataContract
|
||||
.ERC1155Assets(erc1155ContractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
|
||||
.getABIEncodedTransactionData();
|
||||
// The asset data we just generated will look like this:
|
||||
// a7cb5fb7
|
||||
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
|
||||
@@ -1079,12 +1075,9 @@ describe('ERC1155Proxy', () => {
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||
const assetData = await devUtils.encodeERC1155AssetData.callAsync(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
const assetData = assetDataContract
|
||||
.ERC1155Assets(erc1155ContractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
|
||||
.getABIEncodedTransactionData();
|
||||
// The asset data we just generated will look like this:
|
||||
// a7cb5fb7
|
||||
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
|
||||
@@ -1130,12 +1123,9 @@ describe('ERC1155Proxy', () => {
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||
const assetData = await devUtils.encodeERC1155AssetData.callAsync(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
const assetData = assetDataContract
|
||||
.ERC1155Assets(erc1155ContractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
|
||||
.getABIEncodedTransactionData();
|
||||
// The asset data we just generated will look like this:
|
||||
// a7cb5fb7
|
||||
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
|
||||
@@ -1181,12 +1171,9 @@ describe('ERC1155Proxy', () => {
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||
const assetData = await devUtils.encodeERC1155AssetData.callAsync(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
const assetData = assetDataContract
|
||||
.ERC1155Assets(erc1155ContractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
|
||||
.getABIEncodedTransactionData();
|
||||
// The asset data we just generated will look like this:
|
||||
// a7cb5fb7
|
||||
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
|
||||
@@ -1232,12 +1219,9 @@ describe('ERC1155Proxy', () => {
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||
const assetData = await devUtils.encodeERC1155AssetData.callAsync(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
const assetData = assetDataContract
|
||||
.ERC1155Assets(erc1155ContractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
|
||||
.getABIEncodedTransactionData();
|
||||
// The asset data we just generated will look like this:
|
||||
// a7cb5fb7
|
||||
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
|
||||
@@ -1284,12 +1268,9 @@ describe('ERC1155Proxy', () => {
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||
const assetData = await devUtils.encodeERC1155AssetData.callAsync(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
const assetData = assetDataContract
|
||||
.ERC1155Assets(erc1155ContractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
|
||||
.getABIEncodedTransactionData();
|
||||
// The asset data we just generated will look like this:
|
||||
// a7cb5fb7
|
||||
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
|
||||
@@ -1331,12 +1312,9 @@ describe('ERC1155Proxy', () => {
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||
const assetData = await devUtils.encodeERC1155AssetData.callAsync(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
const assetData = assetDataContract
|
||||
.ERC1155Assets(erc1155ContractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
|
||||
.getABIEncodedTransactionData();
|
||||
// The asset data we just generated will look like this:
|
||||
// a7cb5fb7
|
||||
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
|
||||
@@ -1382,12 +1360,9 @@ describe('ERC1155Proxy', () => {
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||
const assetData = await devUtils.encodeERC1155AssetData.callAsync(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
const assetData = assetDataContract
|
||||
.ERC1155Assets(erc1155ContractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
|
||||
.getABIEncodedTransactionData();
|
||||
// The asset data we just generated will look like this:
|
||||
// a7cb5fb7
|
||||
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
|
||||
@@ -1429,12 +1404,9 @@ describe('ERC1155Proxy', () => {
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||
const assetData = await devUtils.encodeERC1155AssetData.callAsync(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
const assetData = assetDataContract
|
||||
.ERC1155Assets(erc1155ContractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
|
||||
.getABIEncodedTransactionData();
|
||||
// The asset data we just generated will look like this:
|
||||
// a7cb5fb7
|
||||
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
|
||||
@@ -1480,12 +1452,9 @@ describe('ERC1155Proxy', () => {
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||
const assetData = await devUtils.encodeERC1155AssetData.callAsync(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
const assetData = assetDataContract
|
||||
.ERC1155Assets(erc1155ContractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
|
||||
.getABIEncodedTransactionData();
|
||||
const txData = await erc1155ProxyWrapper.getTransferFromAbiEncodedTxDataAsync(
|
||||
spender,
|
||||
receiverContract,
|
||||
@@ -1511,12 +1480,9 @@ describe('ERC1155Proxy', () => {
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
|
||||
const assetData = await devUtils.encodeERC1155AssetData.callAsync(
|
||||
erc1155ContractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
const assetData = assetDataContract
|
||||
.ERC1155Assets(erc1155ContractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
|
||||
.getABIEncodedTransactionData();
|
||||
const txData = await erc1155ProxyWrapper.getTransferFromAbiEncodedTxDataAsync(
|
||||
spender,
|
||||
receiverContract,
|
||||
@@ -1640,7 +1606,7 @@ describe('ERC1155Proxy', () => {
|
||||
it('should propagate revert reason from erc1155 contract failure', async () => {
|
||||
// disable transfers
|
||||
const shouldRejectTransfer = true;
|
||||
await erc1155Receiver.setRejectTransferFlag.awaitTransactionSuccessAsync(shouldRejectTransfer, {
|
||||
await erc1155Receiver.setRejectTransferFlag(shouldRejectTransfer).awaitTransactionSuccessAsync({
|
||||
from: owner,
|
||||
});
|
||||
// setup test parameters
|
||||
|
@@ -3,27 +3,22 @@ import {
|
||||
constants,
|
||||
expect,
|
||||
getRandomInteger,
|
||||
hexLeftPad,
|
||||
hexRightPad,
|
||||
hexSlice,
|
||||
Numberish,
|
||||
randomAddress,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { AuthorizableRevertErrors } from '@0x/contracts-utils';
|
||||
import { AssetProxyId } from '@0x/types';
|
||||
import { AbiEncoder, AuthorizableRevertErrors, BigNumber, StringRevertError } from '@0x/utils';
|
||||
import { AbiEncoder, BigNumber, hexUtils, StringRevertError } from '@0x/utils';
|
||||
import { DecodedLogs } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import {
|
||||
artifacts,
|
||||
ERC20BridgeProxyContract,
|
||||
TestERC20BridgeBridgeWithdrawToEventArgs,
|
||||
TestERC20BridgeContract,
|
||||
} from '../src';
|
||||
import { artifacts } from './artifacts';
|
||||
|
||||
import { ERC20BridgeProxyContract, TestERC20BridgeContract } from './wrappers';
|
||||
|
||||
blockchainTests.resets('ERC20BridgeProxy unit tests', env => {
|
||||
const PROXY_ID = AssetProxyId.ERC20Bridge;
|
||||
const BRIDGE_SUCCESS_RETURN_DATA = hexRightPad(PROXY_ID);
|
||||
const BRIDGE_SUCCESS_RETURN_DATA = hexUtils.rightPad(PROXY_ID);
|
||||
let owner: string;
|
||||
let badCaller: string;
|
||||
let assetProxy: ERC20BridgeProxyContract;
|
||||
@@ -44,8 +39,8 @@ blockchainTests.resets('ERC20BridgeProxy unit tests', env => {
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
testTokenAddress = await bridgeContract.testToken.callAsync();
|
||||
await assetProxy.addAuthorizedAddress.awaitTransactionSuccessAsync(owner);
|
||||
testTokenAddress = await bridgeContract.testToken().callAsync();
|
||||
await assetProxy.addAuthorizedAddress(owner).awaitTransactionSuccessAsync();
|
||||
});
|
||||
|
||||
interface AssetDataOpts {
|
||||
@@ -102,7 +97,7 @@ blockchainTests.resets('ERC20BridgeProxy unit tests', env => {
|
||||
}
|
||||
|
||||
async function setTestTokenBalanceAsync(_owner: string, balance: Numberish): Promise<void> {
|
||||
await bridgeContract.setTestTokenBalance.awaitTransactionSuccessAsync(_owner, new BigNumber(balance));
|
||||
await bridgeContract.setTestTokenBalance(_owner, new BigNumber(balance)).awaitTransactionSuccessAsync();
|
||||
}
|
||||
|
||||
describe('transferFrom()', () => {
|
||||
@@ -132,13 +127,9 @@ blockchainTests.resets('ERC20BridgeProxy unit tests', env => {
|
||||
|
||||
async function transferFromAsync(opts?: Partial<TransferFromOpts>, caller?: string): Promise<DecodedLogs> {
|
||||
const _opts = createTransferFromOpts(opts);
|
||||
const { logs } = await assetProxy.transferFrom.awaitTransactionSuccessAsync(
|
||||
encodeAssetData(_opts.assetData),
|
||||
_opts.from,
|
||||
_opts.to,
|
||||
new BigNumber(_opts.amount),
|
||||
{ from: caller },
|
||||
);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -164,7 +155,7 @@ blockchainTests.resets('ERC20BridgeProxy unit tests', env => {
|
||||
const opts = createTransferFromOpts();
|
||||
const logs = await transferFromAsync(opts);
|
||||
expect(logs.length).to.eq(1);
|
||||
const args = logs[0].args as TestERC20BridgeBridgeWithdrawToEventArgs;
|
||||
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);
|
||||
@@ -179,13 +170,10 @@ blockchainTests.resets('ERC20BridgeProxy unit tests', env => {
|
||||
|
||||
it('fails if asset data is truncated', async () => {
|
||||
const opts = createTransferFromOpts();
|
||||
const truncatedAssetData = hexSlice(encodeAssetData(opts.assetData), 0, -1);
|
||||
const tx = assetProxy.transferFrom.awaitTransactionSuccessAsync(
|
||||
truncatedAssetData,
|
||||
opts.from,
|
||||
opts.to,
|
||||
new BigNumber(opts.amount),
|
||||
);
|
||||
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();
|
||||
});
|
||||
|
||||
@@ -206,7 +194,7 @@ blockchainTests.resets('ERC20BridgeProxy unit tests', env => {
|
||||
const tx = transferFromAsync({
|
||||
assetData: createAssetData({
|
||||
bridgeData: createBridgeData({
|
||||
returnData: hexLeftPad('0x1'),
|
||||
returnData: hexUtils.leftPad('0x1'),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
@@ -219,7 +207,7 @@ blockchainTests.resets('ERC20BridgeProxy unit tests', env => {
|
||||
const tx = transferFromAsync({
|
||||
assetData: createAssetData({
|
||||
bridgeData: createBridgeData({
|
||||
returnData: hexRightPad('0x1'),
|
||||
returnData: hexUtils.rightPad('0x1'),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
@@ -281,18 +269,18 @@ blockchainTests.resets('ERC20BridgeProxy unit tests', env => {
|
||||
it('retrieves the balance of the encoded token', async () => {
|
||||
const _owner = randomAddress();
|
||||
const balance = getRandomInteger(1, 100e18);
|
||||
await bridgeContract.setTestTokenBalance.awaitTransactionSuccessAsync(_owner, balance);
|
||||
await bridgeContract.setTestTokenBalance(_owner, balance).awaitTransactionSuccessAsync();
|
||||
const assetData = createAssetData({
|
||||
tokenAddress: testTokenAddress,
|
||||
});
|
||||
const actualBalance = await assetProxy.balanceOf.callAsync(encodeAssetData(assetData), _owner);
|
||||
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();
|
||||
const proxyId = await assetProxy.getProxyId().callAsync();
|
||||
expect(proxyId).to.eq(PROXY_ID);
|
||||
});
|
||||
});
|
||||
|
@@ -4,28 +4,25 @@ import {
|
||||
expect,
|
||||
filterLogsToArguments,
|
||||
getRandomInteger,
|
||||
hexLeftPad,
|
||||
hexRandom,
|
||||
Numberish,
|
||||
randomAddress,
|
||||
TransactionHelper,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { AssetProxyId } from '@0x/types';
|
||||
import { BigNumber, RawRevertError } from '@0x/utils';
|
||||
import { BigNumber, hexUtils, RawRevertError } from '@0x/utils';
|
||||
import { DecodedLogs } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { artifacts } from './artifacts';
|
||||
|
||||
import {
|
||||
artifacts,
|
||||
TestEth2DaiBridgeContract,
|
||||
TestEth2DaiBridgeEvents,
|
||||
TestEth2DaiBridgeSellAllAmountEventArgs,
|
||||
TestEth2DaiBridgeTokenApproveEventArgs,
|
||||
TestEth2DaiBridgeTokenTransferEventArgs,
|
||||
} from '../src';
|
||||
} from './wrappers';
|
||||
|
||||
blockchainTests.resets('Eth2DaiBridge unit tests', env => {
|
||||
const txHelper = new TransactionHelper(env.web3Wrapper, artifacts);
|
||||
let testContract: TestEth2DaiBridgeContract;
|
||||
|
||||
before(async () => {
|
||||
@@ -40,7 +37,9 @@ blockchainTests.resets('Eth2DaiBridge unit tests', env => {
|
||||
describe('isValidSignature()', () => {
|
||||
it('returns success bytes', async () => {
|
||||
const LEGACY_WALLET_MAGIC_VALUE = '0xb0671381';
|
||||
const result = await testContract.isValidSignature.callAsync(hexRandom(), hexRandom(_.random(0, 32)));
|
||||
const result = await testContract
|
||||
.isValidSignature(hexUtils.random(), hexUtils.random(_.random(0, 32)))
|
||||
.callAsync();
|
||||
expect(result).to.eq(LEGACY_WALLET_MAGIC_VALUE);
|
||||
});
|
||||
});
|
||||
@@ -72,7 +71,7 @@ blockchainTests.resets('Eth2DaiBridge unit tests', env => {
|
||||
fillAmount: getRandomInteger(1, 100e18),
|
||||
fromTokenBalance: getRandomInteger(1, 100e18),
|
||||
toTokentransferRevertReason: '',
|
||||
toTokenTransferReturnData: hexLeftPad(1),
|
||||
toTokenTransferReturnData: hexUtils.leftPad(1),
|
||||
...opts,
|
||||
};
|
||||
}
|
||||
@@ -80,32 +79,30 @@ blockchainTests.resets('Eth2DaiBridge unit tests', env => {
|
||||
async function withdrawToAsync(opts?: Partial<WithdrawToOpts>): Promise<WithdrawToResult> {
|
||||
const _opts = createWithdrawToOpts(opts);
|
||||
// Set the fill behavior.
|
||||
await testContract.setFillBehavior.awaitTransactionSuccessAsync(
|
||||
_opts.revertReason,
|
||||
new BigNumber(_opts.fillAmount),
|
||||
);
|
||||
await testContract
|
||||
.setFillBehavior(_opts.revertReason, new BigNumber(_opts.fillAmount))
|
||||
.awaitTransactionSuccessAsync();
|
||||
// Create tokens and balances.
|
||||
if (_opts.fromTokenAddress === undefined) {
|
||||
[_opts.fromTokenAddress] = await txHelper.getResultAndReceiptAsync(
|
||||
testContract.createToken,
|
||||
new BigNumber(_opts.fromTokenBalance),
|
||||
);
|
||||
const createTokenFn = testContract.createToken(new BigNumber(_opts.fromTokenBalance));
|
||||
_opts.fromTokenAddress = await createTokenFn.callAsync();
|
||||
await createTokenFn.awaitTransactionSuccessAsync();
|
||||
}
|
||||
if (_opts.toTokenAddress === undefined) {
|
||||
[_opts.toTokenAddress] = await txHelper.getResultAndReceiptAsync(
|
||||
testContract.createToken,
|
||||
constants.ZERO_AMOUNT,
|
||||
);
|
||||
const createTokenFn = testContract.createToken(constants.ZERO_AMOUNT);
|
||||
_opts.toTokenAddress = await createTokenFn.callAsync();
|
||||
await createTokenFn.awaitTransactionSuccessAsync();
|
||||
}
|
||||
// Set the transfer behavior of `toTokenAddress`.
|
||||
await testContract.setTransferBehavior.awaitTransactionSuccessAsync(
|
||||
_opts.toTokenAddress,
|
||||
_opts.toTokentransferRevertReason,
|
||||
_opts.toTokenTransferReturnData,
|
||||
);
|
||||
await testContract
|
||||
.setTransferBehavior(
|
||||
_opts.toTokenAddress,
|
||||
_opts.toTokentransferRevertReason,
|
||||
_opts.toTokenTransferReturnData,
|
||||
)
|
||||
.awaitTransactionSuccessAsync();
|
||||
// Call bridgeTransferFrom().
|
||||
const [result, { logs }] = await txHelper.getResultAndReceiptAsync(
|
||||
testContract.bridgeTransferFrom,
|
||||
const bridgeTransferFromFn = testContract.bridgeTransferFrom(
|
||||
// "to" token address
|
||||
_opts.toTokenAddress,
|
||||
// Random from address.
|
||||
@@ -114,8 +111,10 @@ blockchainTests.resets('Eth2DaiBridge unit tests', env => {
|
||||
_opts.toAddress,
|
||||
new BigNumber(_opts.amount),
|
||||
// ABI-encode the "from" token address as the bridge data.
|
||||
hexLeftPad(_opts.fromTokenAddress as string),
|
||||
hexUtils.leftPad(_opts.fromTokenAddress as string),
|
||||
);
|
||||
const result = await bridgeTransferFromFn.callAsync();
|
||||
const { logs } = await bridgeTransferFromFn.awaitTransactionSuccessAsync();
|
||||
return {
|
||||
opts: _opts,
|
||||
result,
|
||||
@@ -180,13 +179,13 @@ blockchainTests.resets('Eth2DaiBridge unit tests', env => {
|
||||
});
|
||||
|
||||
it('fails if `toTokenAddress.transfer()` returns false', async () => {
|
||||
const opts = createWithdrawToOpts({ toTokenTransferReturnData: hexLeftPad(0) });
|
||||
const opts = createWithdrawToOpts({ toTokenTransferReturnData: hexUtils.leftPad(0) });
|
||||
const tx = withdrawToAsync(opts);
|
||||
return expect(tx).to.revertWith(new RawRevertError(hexLeftPad(0)));
|
||||
return expect(tx).to.revertWith(new RawRevertError(hexUtils.leftPad(0)));
|
||||
});
|
||||
|
||||
it('succeeds if `toTokenAddress.transfer()` returns true', async () => {
|
||||
await withdrawToAsync({ toTokenTransferReturnData: hexLeftPad(1) });
|
||||
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,8 +1,6 @@
|
||||
import { DevUtilsContract } from '@0x/contracts-dev-utils';
|
||||
import {
|
||||
chaiSetup,
|
||||
constants,
|
||||
expectTransactionFailedAsync,
|
||||
expectTransactionFailedWithoutReasonAsync,
|
||||
provider,
|
||||
txDefaults,
|
||||
@@ -14,7 +12,14 @@ import { AbiEncoder, BigNumber } from '@0x/utils';
|
||||
import * as chai from 'chai';
|
||||
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();
|
||||
const expect = chai.expect;
|
||||
@@ -25,7 +30,7 @@ describe('StaticCallProxy', () => {
|
||||
let fromAddress: string;
|
||||
let toAddress: string;
|
||||
|
||||
let devUtils: DevUtilsContract;
|
||||
let assetDataInterface: IAssetDataContract;
|
||||
let staticCallProxy: IAssetProxyContract;
|
||||
let staticCallTarget: TestStaticCallTargetContract;
|
||||
|
||||
@@ -44,7 +49,7 @@ describe('StaticCallProxy', () => {
|
||||
txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
devUtils = new DevUtilsContract(constants.NULL_ADDRESS, provider);
|
||||
assetDataInterface = new IAssetDataContract(constants.NULL_ADDRESS, provider);
|
||||
staticCallProxy = new IAssetProxyContract(
|
||||
staticCallProxyWithoutTransferFrom.address,
|
||||
provider,
|
||||
@@ -79,26 +84,21 @@ describe('StaticCallProxy', () => {
|
||||
);
|
||||
});
|
||||
it('should have an id of 0xc339d10a', async () => {
|
||||
const proxyId = await staticCallProxy.getProxyId.callAsync();
|
||||
const proxyId = await staticCallProxy.getProxyId().callAsync();
|
||||
const expectedProxyId = AssetProxyId.StaticCall;
|
||||
expect(proxyId).to.equal(expectedProxyId);
|
||||
});
|
||||
});
|
||||
describe('transferFrom', () => {
|
||||
it('should revert if assetData lies outside the bounds of calldata', async () => {
|
||||
const staticCallData = staticCallTarget.noInputFunction.getABIEncodedTransactionData();
|
||||
const staticCallData = staticCallTarget.noInputFunction().getABIEncodedTransactionData();
|
||||
const expectedResultHash = constants.KECCAK256_NULL;
|
||||
const assetData = await devUtils.encodeStaticCallAssetData.callAsync(
|
||||
staticCallTarget.address,
|
||||
staticCallData,
|
||||
expectedResultHash,
|
||||
);
|
||||
const txData = staticCallProxy.transferFrom.getABIEncodedTransactionData(
|
||||
assetData,
|
||||
fromAddress,
|
||||
toAddress,
|
||||
amount,
|
||||
);
|
||||
const assetData = assetDataInterface
|
||||
.StaticCall(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||
.getABIEncodedTransactionData();
|
||||
const txData = staticCallProxy
|
||||
.transferFrom(assetData, fromAddress, toAddress, amount)
|
||||
.getABIEncodedTransactionData();
|
||||
const offsetToAssetData = '0000000000000000000000000000000000000000000000000000000000000080';
|
||||
const txDataEndBuffer = ethUtil.toBuffer((txData.length - 2) / 2 - 4);
|
||||
const paddedTxDataEndBuffer = ethUtil.setLengthLeft(txDataEndBuffer, 32);
|
||||
@@ -116,25 +116,22 @@ describe('StaticCallProxy', () => {
|
||||
it('should revert if the length of assetData is less than 100 bytes', async () => {
|
||||
const staticCallData = constants.NULL_BYTES;
|
||||
const expectedResultHash = constants.KECCAK256_NULL;
|
||||
const assetData = (await devUtils.encodeStaticCallAssetData.callAsync(
|
||||
staticCallTarget.address,
|
||||
staticCallData,
|
||||
expectedResultHash,
|
||||
)).slice(0, -128);
|
||||
const assetData = assetDataInterface
|
||||
.StaticCall(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||
.getABIEncodedTransactionData()
|
||||
.slice(0, -128);
|
||||
const assetDataByteLen = (assetData.length - 2) / 2;
|
||||
expect((assetDataByteLen - 4) % 32).to.equal(0);
|
||||
await expectTransactionFailedWithoutReasonAsync(
|
||||
staticCallProxy.transferFrom.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 () => {
|
||||
const staticCallData = staticCallTarget.noInputFunction.getABIEncodedTransactionData();
|
||||
const staticCallData = staticCallTarget.noInputFunction().getABIEncodedTransactionData();
|
||||
const expectedResultHash = constants.KECCAK256_NULL;
|
||||
const assetData = await devUtils.encodeStaticCallAssetData.callAsync(
|
||||
staticCallTarget.address,
|
||||
staticCallData,
|
||||
expectedResultHash,
|
||||
);
|
||||
const assetData = assetDataInterface
|
||||
.StaticCall(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||
.getABIEncodedTransactionData();
|
||||
const offsetToStaticCallData = '0000000000000000000000000000000000000000000000000000000000000060';
|
||||
const assetDataEndBuffer = ethUtil.toBuffer((assetData.length - 2) / 2 - 4);
|
||||
const paddedAssetDataEndBuffer = ethUtil.setLengthLeft(assetDataEndBuffer, 32);
|
||||
@@ -145,94 +142,86 @@ describe('StaticCallProxy', () => {
|
||||
invalidOffsetToStaticCallData,
|
||||
)}${newStaticCallData}`;
|
||||
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 () => {
|
||||
const staticCallData = staticCallTarget.updateState.getABIEncodedTransactionData();
|
||||
const staticCallData = staticCallTarget.updateState().getABIEncodedTransactionData();
|
||||
const expectedResultHash = constants.KECCAK256_NULL;
|
||||
const assetData = await devUtils.encodeStaticCallAssetData.callAsync(
|
||||
staticCallTarget.address,
|
||||
staticCallData,
|
||||
expectedResultHash,
|
||||
);
|
||||
const assetData = assetDataInterface
|
||||
.StaticCall(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||
.getABIEncodedTransactionData();
|
||||
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 () => {
|
||||
const staticCallData = staticCallTarget.assertEvenNumber.getABIEncodedTransactionData(new BigNumber(1));
|
||||
const staticCallData = staticCallTarget.assertEvenNumber(new BigNumber(1)).getABIEncodedTransactionData();
|
||||
const expectedResultHash = constants.KECCAK256_NULL;
|
||||
const assetData = await devUtils.encodeStaticCallAssetData.callAsync(
|
||||
staticCallTarget.address,
|
||||
staticCallData,
|
||||
expectedResultHash,
|
||||
);
|
||||
await expectTransactionFailedAsync(
|
||||
staticCallProxy.transferFrom.sendTransactionAsync(assetData, fromAddress, toAddress, amount),
|
||||
RevertReason.TargetNotEven,
|
||||
);
|
||||
const assetData = assetDataInterface
|
||||
.StaticCall(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||
.getABIEncodedTransactionData();
|
||||
return expect(
|
||||
staticCallProxy.transferFrom(assetData, fromAddress, toAddress, amount).awaitTransactionSuccessAsync(),
|
||||
).to.revertWith(RevertReason.TargetNotEven);
|
||||
});
|
||||
it('should revert if the hash of the output is different than expected expected', async () => {
|
||||
const staticCallData = staticCallTarget.isOddNumber.getABIEncodedTransactionData(new BigNumber(0));
|
||||
const staticCallData = staticCallTarget.isOddNumber(new BigNumber(0)).getABIEncodedTransactionData();
|
||||
const trueAsBuffer = ethUtil.toBuffer('0x0000000000000000000000000000000000000000000000000000000000000001');
|
||||
const expectedResultHash = ethUtil.bufferToHex(ethUtil.sha3(trueAsBuffer));
|
||||
const assetData = await devUtils.encodeStaticCallAssetData.callAsync(
|
||||
staticCallTarget.address,
|
||||
staticCallData,
|
||||
expectedResultHash,
|
||||
);
|
||||
await expectTransactionFailedAsync(
|
||||
staticCallProxy.transferFrom.sendTransactionAsync(assetData, fromAddress, toAddress, amount),
|
||||
RevertReason.UnexpectedStaticCallResult,
|
||||
);
|
||||
const assetData = assetDataInterface
|
||||
.StaticCall(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||
.getABIEncodedTransactionData();
|
||||
return expect(
|
||||
staticCallProxy.transferFrom(assetData, fromAddress, toAddress, amount).awaitTransactionSuccessAsync(),
|
||||
).to.revertWith(RevertReason.UnexpectedStaticCallResult);
|
||||
});
|
||||
it('should be successful if a function call with no inputs and no outputs is successful', async () => {
|
||||
const staticCallData = staticCallTarget.noInputFunction.getABIEncodedTransactionData();
|
||||
const staticCallData = staticCallTarget.noInputFunction().getABIEncodedTransactionData();
|
||||
const expectedResultHash = constants.KECCAK256_NULL;
|
||||
const assetData = await devUtils.encodeStaticCallAssetData.callAsync(
|
||||
staticCallTarget.address,
|
||||
staticCallData,
|
||||
expectedResultHash,
|
||||
);
|
||||
await staticCallProxy.transferFrom.awaitTransactionSuccessAsync(assetData, fromAddress, toAddress, amount);
|
||||
const assetData = assetDataInterface
|
||||
.StaticCall(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||
.getABIEncodedTransactionData();
|
||||
await staticCallProxy
|
||||
.transferFrom(assetData, fromAddress, toAddress, amount)
|
||||
.awaitTransactionSuccessAsync();
|
||||
});
|
||||
it('should be successful if the staticCallTarget is not a contract and no return value is expected', async () => {
|
||||
const staticCallData = '0x0102030405060708';
|
||||
const expectedResultHash = constants.KECCAK256_NULL;
|
||||
const assetData = await devUtils.encodeStaticCallAssetData.callAsync(
|
||||
toAddress,
|
||||
staticCallData,
|
||||
expectedResultHash,
|
||||
);
|
||||
await staticCallProxy.transferFrom.awaitTransactionSuccessAsync(assetData, fromAddress, toAddress, amount);
|
||||
const assetData = assetDataInterface
|
||||
.StaticCall(toAddress, staticCallData, expectedResultHash)
|
||||
.getABIEncodedTransactionData();
|
||||
await staticCallProxy
|
||||
.transferFrom(assetData, fromAddress, toAddress, amount)
|
||||
.awaitTransactionSuccessAsync();
|
||||
});
|
||||
it('should be successful if a function call with one static input returns the correct value', async () => {
|
||||
const staticCallData = staticCallTarget.isOddNumber.getABIEncodedTransactionData(new BigNumber(1));
|
||||
const staticCallData = staticCallTarget.isOddNumber(new BigNumber(1)).getABIEncodedTransactionData();
|
||||
const trueAsBuffer = ethUtil.toBuffer('0x0000000000000000000000000000000000000000000000000000000000000001');
|
||||
const expectedResultHash = ethUtil.bufferToHex(ethUtil.sha3(trueAsBuffer));
|
||||
const assetData = await devUtils.encodeStaticCallAssetData.callAsync(
|
||||
staticCallTarget.address,
|
||||
staticCallData,
|
||||
expectedResultHash,
|
||||
);
|
||||
await staticCallProxy.transferFrom.awaitTransactionSuccessAsync(assetData, fromAddress, toAddress, amount);
|
||||
const assetData = assetDataInterface
|
||||
.StaticCall(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||
.getABIEncodedTransactionData();
|
||||
await staticCallProxy
|
||||
.transferFrom(assetData, fromAddress, toAddress, amount)
|
||||
.awaitTransactionSuccessAsync();
|
||||
});
|
||||
it('should be successful if a function with one dynamic input is successful', async () => {
|
||||
const dynamicInput = '0x0102030405060708';
|
||||
const staticCallData = staticCallTarget.dynamicInputFunction.getABIEncodedTransactionData(dynamicInput);
|
||||
const staticCallData = staticCallTarget.dynamicInputFunction(dynamicInput).getABIEncodedTransactionData();
|
||||
const expectedResultHash = constants.KECCAK256_NULL;
|
||||
const assetData = await devUtils.encodeStaticCallAssetData.callAsync(
|
||||
staticCallTarget.address,
|
||||
staticCallData,
|
||||
expectedResultHash,
|
||||
);
|
||||
await staticCallProxy.transferFrom.awaitTransactionSuccessAsync(assetData, fromAddress, toAddress, amount);
|
||||
const assetData = assetDataInterface
|
||||
.StaticCall(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||
.getABIEncodedTransactionData();
|
||||
await staticCallProxy
|
||||
.transferFrom(assetData, fromAddress, toAddress, amount)
|
||||
.awaitTransactionSuccessAsync();
|
||||
});
|
||||
it('should be successful if a function call returns a complex type', async () => {
|
||||
const a = new BigNumber(1);
|
||||
const b = new BigNumber(2);
|
||||
const staticCallData = staticCallTarget.returnComplexType.getABIEncodedTransactionData(a, b);
|
||||
const staticCallData = staticCallTarget.returnComplexType(a, b).getABIEncodedTransactionData();
|
||||
const abiEncoder = new AbiEncoder.DynamicBytes({
|
||||
name: '',
|
||||
type: 'bytes',
|
||||
@@ -245,12 +234,12 @@ describe('StaticCallProxy', () => {
|
||||
const expectedResultHash = ethUtil.bufferToHex(
|
||||
ethUtil.sha3(ethUtil.toBuffer(encodedExpectedResultWithOffset)),
|
||||
);
|
||||
const assetData = await devUtils.encodeStaticCallAssetData.callAsync(
|
||||
staticCallTarget.address,
|
||||
staticCallData,
|
||||
expectedResultHash,
|
||||
);
|
||||
await staticCallProxy.transferFrom.awaitTransactionSuccessAsync(assetData, fromAddress, toAddress, amount);
|
||||
const assetData = assetDataInterface
|
||||
.StaticCall(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||
.getABIEncodedTransactionData();
|
||||
await staticCallProxy
|
||||
.transferFrom(assetData, fromAddress, toAddress, amount)
|
||||
.awaitTransactionSuccessAsync();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -5,19 +5,17 @@ import {
|
||||
filterLogs,
|
||||
filterLogsToArguments,
|
||||
getRandomInteger,
|
||||
hexLeftPad,
|
||||
hexRandom,
|
||||
Numberish,
|
||||
randomAddress,
|
||||
TransactionHelper,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { AssetProxyId } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import { BigNumber, hexUtils } from '@0x/utils';
|
||||
import { DecodedLogs } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { artifacts } from './artifacts';
|
||||
|
||||
import {
|
||||
artifacts,
|
||||
TestUniswapBridgeContract,
|
||||
TestUniswapBridgeEthToTokenTransferInputEventArgs as EthToTokenTransferInputArgs,
|
||||
TestUniswapBridgeEvents as ContractEvents,
|
||||
@@ -27,10 +25,9 @@ import {
|
||||
TestUniswapBridgeTokenTransferEventArgs as TokenTransferArgs,
|
||||
TestUniswapBridgeWethDepositEventArgs as WethDepositArgs,
|
||||
TestUniswapBridgeWethWithdrawEventArgs as WethWithdrawArgs,
|
||||
} from '../src';
|
||||
} from './wrappers';
|
||||
|
||||
blockchainTests.resets('UniswapBridge unit tests', env => {
|
||||
const txHelper = new TransactionHelper(env.web3Wrapper, artifacts);
|
||||
let testContract: TestUniswapBridgeContract;
|
||||
let wethTokenAddress: string;
|
||||
|
||||
@@ -41,13 +38,15 @@ blockchainTests.resets('UniswapBridge unit tests', env => {
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
wethTokenAddress = await testContract.wethToken.callAsync();
|
||||
wethTokenAddress = await testContract.wethToken().callAsync();
|
||||
});
|
||||
|
||||
describe('isValidSignature()', () => {
|
||||
it('returns success bytes', async () => {
|
||||
const LEGACY_WALLET_MAGIC_VALUE = '0xb0671381';
|
||||
const result = await testContract.isValidSignature.callAsync(hexRandom(), hexRandom(_.random(0, 32)));
|
||||
const result = await testContract
|
||||
.isValidSignature(hexUtils.random(), hexUtils.random(_.random(0, 32)))
|
||||
.callAsync();
|
||||
expect(result).to.eq(LEGACY_WALLET_MAGIC_VALUE);
|
||||
});
|
||||
});
|
||||
@@ -89,35 +88,35 @@ blockchainTests.resets('UniswapBridge unit tests', env => {
|
||||
|
||||
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.
|
||||
[[_opts.fromTokenAddress]] = await txHelper.getResultAndReceiptAsync(
|
||||
testContract.createTokenAndExchange,
|
||||
const createFromTokenFn = testContract.createTokenAndExchange(
|
||||
_opts.fromTokenAddress,
|
||||
_opts.exchangeRevertReason,
|
||||
{ value: new BigNumber(_opts.exchangeFillAmount) },
|
||||
);
|
||||
[_opts.fromTokenAddress] = await createFromTokenFn.callAsync(callData);
|
||||
await createFromTokenFn.awaitTransactionSuccessAsync(callData);
|
||||
|
||||
// Create the "to" token and exchange.
|
||||
[[_opts.toTokenAddress]] = await txHelper.getResultAndReceiptAsync(
|
||||
testContract.createTokenAndExchange,
|
||||
const createToTokenFn = testContract.createTokenAndExchange(
|
||||
_opts.toTokenAddress,
|
||||
_opts.exchangeRevertReason,
|
||||
{ value: new BigNumber(_opts.exchangeFillAmount) },
|
||||
);
|
||||
await testContract.setTokenRevertReason.awaitTransactionSuccessAsync(
|
||||
_opts.toTokenAddress,
|
||||
_opts.toTokenRevertReason,
|
||||
);
|
||||
await testContract.setTokenRevertReason.awaitTransactionSuccessAsync(
|
||||
_opts.fromTokenAddress,
|
||||
_opts.fromTokenRevertReason,
|
||||
);
|
||||
[_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.awaitTransactionSuccessAsync(_opts.fromTokenAddress, {
|
||||
await testContract.setTokenBalance(_opts.fromTokenAddress).awaitTransactionSuccessAsync({
|
||||
value: new BigNumber(_opts.fromTokenBalance),
|
||||
});
|
||||
// Call bridgeTransferFrom().
|
||||
const [result, receipt] = await txHelper.getResultAndReceiptAsync(
|
||||
testContract.bridgeTransferFrom,
|
||||
const bridgeTransferFromFn = testContract.bridgeTransferFrom(
|
||||
// The "to" token address.
|
||||
_opts.toTokenAddress,
|
||||
// The "from" address.
|
||||
@@ -127,8 +126,10 @@ blockchainTests.resets('UniswapBridge unit tests', env => {
|
||||
// The amount to transfer to "to"
|
||||
new BigNumber(_opts.amount),
|
||||
// ABI-encoded "from" token address.
|
||||
hexLeftPad(_opts.fromTokenAddress),
|
||||
hexUtils.leftPad(_opts.fromTokenAddress),
|
||||
);
|
||||
const result = await bridgeTransferFromFn.callAsync();
|
||||
const receipt = await bridgeTransferFromFn.awaitTransactionSuccessAsync();
|
||||
return {
|
||||
opts: _opts,
|
||||
result,
|
||||
@@ -138,7 +139,7 @@ blockchainTests.resets('UniswapBridge unit tests', env => {
|
||||
}
|
||||
|
||||
async function getExchangeForTokenAsync(tokenAddress: string): Promise<string> {
|
||||
return testContract.getExchange.callAsync(tokenAddress);
|
||||
return testContract.getExchange(tokenAddress).callAsync();
|
||||
}
|
||||
|
||||
it('returns magic bytes on success', async () => {
|
||||
@@ -147,11 +148,9 @@ blockchainTests.resets('UniswapBridge unit tests', env => {
|
||||
});
|
||||
|
||||
it('just transfers tokens to `to` if the same tokens are in play', async () => {
|
||||
const [[tokenAddress]] = await txHelper.getResultAndReceiptAsync(
|
||||
testContract.createTokenAndExchange,
|
||||
constants.NULL_ADDRESS,
|
||||
'',
|
||||
);
|
||||
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,
|
||||
@@ -177,7 +176,7 @@ blockchainTests.resets('UniswapBridge unit tests', env => {
|
||||
expect(calls[0].exchange).to.eq(exchangeAddress);
|
||||
expect(calls[0].tokensSold).to.bignumber.eq(opts.fromTokenBalance);
|
||||
expect(calls[0].minTokensBought).to.bignumber.eq(opts.amount);
|
||||
expect(calls[0].minEthBought).to.bignumber.eq(0);
|
||||
expect(calls[0].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);
|
||||
@@ -203,14 +202,16 @@ blockchainTests.resets('UniswapBridge unit tests', env => {
|
||||
});
|
||||
|
||||
it('fails if "from" token does not exist', async () => {
|
||||
const tx = testContract.bridgeTransferFrom.awaitTransactionSuccessAsync(
|
||||
randomAddress(),
|
||||
randomAddress(),
|
||||
randomAddress(),
|
||||
getRandomInteger(1, 1e18),
|
||||
hexLeftPad(randomAddress()),
|
||||
);
|
||||
return expect(tx).to.revertWith('NO_UNISWAP_EXCHANGE_FOR_TOKEN');
|
||||
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 () => {
|
||||
@@ -218,7 +219,7 @@ blockchainTests.resets('UniswapBridge unit tests', env => {
|
||||
const tx = withdrawToAsync({
|
||||
exchangeRevertReason: revertReason,
|
||||
});
|
||||
return expect(tx).to.revertWith(revertReason);
|
||||
return expect(tx).to.eventually.be.rejectedWith(revertReason);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -275,14 +276,16 @@ blockchainTests.resets('UniswapBridge unit tests', env => {
|
||||
});
|
||||
|
||||
it('fails if "from" token does not exist', async () => {
|
||||
const tx = testContract.bridgeTransferFrom.awaitTransactionSuccessAsync(
|
||||
randomAddress(),
|
||||
randomAddress(),
|
||||
randomAddress(),
|
||||
getRandomInteger(1, 1e18),
|
||||
hexLeftPad(wethTokenAddress),
|
||||
);
|
||||
return expect(tx).to.revertWith('NO_UNISWAP_EXCHANGE_FOR_TOKEN');
|
||||
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 () => {
|
||||
@@ -291,7 +294,7 @@ blockchainTests.resets('UniswapBridge unit tests', env => {
|
||||
toTokenAddress: wethTokenAddress,
|
||||
toTokenRevertReason: revertReason,
|
||||
});
|
||||
return expect(tx).to.revertWith(revertReason);
|
||||
return expect(tx).to.eventually.be.rejectedWith(revertReason);
|
||||
});
|
||||
|
||||
it('fails if the exchange fails', async () => {
|
||||
@@ -300,7 +303,7 @@ blockchainTests.resets('UniswapBridge unit tests', env => {
|
||||
toTokenAddress: wethTokenAddress,
|
||||
exchangeRevertReason: revertReason,
|
||||
});
|
||||
return expect(tx).to.revertWith(revertReason);
|
||||
return expect(tx).to.eventually.be.rejectedWith(revertReason);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -333,14 +336,16 @@ blockchainTests.resets('UniswapBridge unit tests', env => {
|
||||
});
|
||||
|
||||
it('fails if "to" token does not exist', async () => {
|
||||
const tx = testContract.bridgeTransferFrom.awaitTransactionSuccessAsync(
|
||||
wethTokenAddress,
|
||||
randomAddress(),
|
||||
randomAddress(),
|
||||
getRandomInteger(1, 1e18),
|
||||
hexLeftPad(randomAddress()),
|
||||
);
|
||||
return expect(tx).to.revertWith('NO_UNISWAP_EXCHANGE_FOR_TOKEN');
|
||||
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 () => {
|
||||
@@ -349,7 +354,7 @@ blockchainTests.resets('UniswapBridge unit tests', env => {
|
||||
fromTokenAddress: wethTokenAddress,
|
||||
fromTokenRevertReason: revertReason,
|
||||
});
|
||||
return expect(tx).to.revertWith(revertReason);
|
||||
return expect(tx).to.eventually.be.rejectedWith(revertReason);
|
||||
});
|
||||
|
||||
it('fails if the exchange fails', async () => {
|
||||
@@ -358,7 +363,7 @@ blockchainTests.resets('UniswapBridge unit tests', env => {
|
||||
fromTokenAddress: wethTokenAddress,
|
||||
exchangeRevertReason: revertReason,
|
||||
});
|
||||
return expect(tx).to.revertWith(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';
|
@@ -84,7 +84,7 @@ module.exports = {
|
||||
solc: {
|
||||
version: '0.5.9',
|
||||
settings: {
|
||||
evmVersion: 'constantinople',
|
||||
evmVersion: 'istanbul',
|
||||
optimizer: {
|
||||
enabled: true,
|
||||
runs: 1000000,
|
||||
|
@@ -3,6 +3,9 @@
|
||||
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
|
||||
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
|
||||
"files": [
|
||||
"generated-artifacts/ChaiBridge.json",
|
||||
"generated-artifacts/CurveBridge.json",
|
||||
"generated-artifacts/DydxBridge.json",
|
||||
"generated-artifacts/ERC1155Proxy.json",
|
||||
"generated-artifacts/ERC20BridgeProxy.json",
|
||||
"generated-artifacts/ERC20Proxy.json",
|
||||
@@ -12,20 +15,64 @@
|
||||
"generated-artifacts/IAssetProxy.json",
|
||||
"generated-artifacts/IAssetProxyDispatcher.json",
|
||||
"generated-artifacts/IAuthorizable.json",
|
||||
"generated-artifacts/IChai.json",
|
||||
"generated-artifacts/ICurve.json",
|
||||
"generated-artifacts/IDydx.json",
|
||||
"generated-artifacts/IDydxBridge.json",
|
||||
"generated-artifacts/IERC20Bridge.json",
|
||||
"generated-artifacts/IEth2Dai.json",
|
||||
"generated-artifacts/IKyberNetworkProxy.json",
|
||||
"generated-artifacts/IUniswapExchange.json",
|
||||
"generated-artifacts/IUniswapExchangeFactory.json",
|
||||
"generated-artifacts/KyberBridge.json",
|
||||
"generated-artifacts/MixinAssetProxyDispatcher.json",
|
||||
"generated-artifacts/MixinAuthorizable.json",
|
||||
"generated-artifacts/MultiAssetProxy.json",
|
||||
"generated-artifacts/Ownable.json",
|
||||
"generated-artifacts/StaticCallProxy.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"
|
||||
"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"]
|
||||
}
|
||||
|
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
|
48
contracts/broker/CHANGELOG.json
Normal file
48
contracts/broker/CHANGELOG.json
Normal file
@@ -0,0 +1,48 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1582623685,
|
||||
"version": "1.1.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "1.1.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Added decoders for broker data",
|
||||
"pr": 2484
|
||||
}
|
||||
],
|
||||
"timestamp": 1581748629
|
||||
},
|
||||
{
|
||||
"timestamp": 1581204851,
|
||||
"version": "1.0.2",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1580988106,
|
||||
"version": "1.0.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Created package",
|
||||
"pr": "2455"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
26
contracts/broker/CHANGELOG.md
Normal file
26
contracts/broker/CHANGELOG.md
Normal file
@@ -0,0 +1,26 @@
|
||||
<!--
|
||||
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.1 - _February 25, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.1.0 - _February 15, 2020_
|
||||
|
||||
* Added decoders for broker data (#2484)
|
||||
|
||||
## v1.0.2 - _February 8, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## 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 @@
|
||||
[]
|
@@ -1,6 +1,18 @@
|
||||
## Tests
|
||||
## Broker
|
||||
|
||||
This package implements unit tests against 0x's smart contracts. Its primary purpose is to help avoid circular dependencies between the contract packages.
|
||||
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
|
||||
|
||||
@@ -29,39 +41,21 @@ yarn install
|
||||
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-tests yarn build
|
||||
PKG=@0x/contracts-broker yarn build
|
||||
```
|
||||
|
||||
Or continuously rebuild on change:
|
||||
|
||||
```bash
|
||||
PKG=@0x/contracts-tests yarn watch
|
||||
PKG=@0x/contracts-broker yarn watch
|
||||
```
|
||||
|
||||
If imports are rebuilt in their source packages, they do not need to be rebuilt here.
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
// contracts/tests/test/some-new/some_new_test.ts
|
||||
import { SomeNewContract } from '@0x/contracts-some-new';
|
||||
|
||||
describe('should do its thing', () => {
|
||||
const contractInstance = new SomeNewContract();
|
||||
expect(contractInstance.someTruthyFunction.callAsync()).to.be.true();
|
||||
})
|
||||
```
|
||||
|
||||
Run `yarn watch` from `contracts/some-new`, and then running `yarn test` from this package should test the new changes.
|
||||
|
||||
### Clean
|
||||
|
||||
```bash
|
||||
yarn clean
|
||||
```
|
||||
|
||||
Since the purpose of this package is to test other packages, make sure you are running `yarn clean` as necessary in the imported packages as well.
|
||||
|
||||
### Lint
|
||||
|
||||
```bash
|
||||
@@ -73,3 +67,7 @@ yarn lint
|
||||
```bash
|
||||
yarn test
|
||||
```
|
||||
|
||||
#### Testing options
|
||||
|
||||
Contracts testing options like coverage, profiling, revert traces or backing node choosing - are described [here](../TESTING.md).
|
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"artifactsDir": "./generated-artifacts",
|
||||
"artifactsDir": "./test/generated-artifacts",
|
||||
"contractsDir": "./contracts",
|
||||
"useDockerisedSolc": false,
|
||||
"isOfflineMode": false,
|
||||
"compilerSettings": {
|
||||
"evmVersion": "constantinople",
|
||||
"evmVersion": "istanbul",
|
||||
"optimizer": {
|
||||
"enabled": true,
|
||||
"runs": 1000000,
|
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;
|
||||
}
|
@@ -16,22 +16,18 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibEIP712ExchangeDomain.sol";
|
||||
import "../src/MixinSignatureValidator.sol";
|
||||
import "../src/MixinTransactions.sol";
|
||||
|
||||
interface IGodsUnchained {
|
||||
|
||||
contract TestSignatureValidator is
|
||||
LibEIP712ExchangeDomain,
|
||||
MixinSignatureValidator
|
||||
{
|
||||
|
||||
// solhint-disable no-empty-blocks
|
||||
constructor (uint256 chainId)
|
||||
public
|
||||
LibEIP712ExchangeDomain(chainId, address(0))
|
||||
{}
|
||||
/// @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.1",
|
||||
"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.1",
|
||||
"@0x/contracts-asset-proxy": "^3.2.2",
|
||||
"@0x/contracts-erc20": "^3.1.2",
|
||||
"@0x/contracts-erc721": "^3.1.2",
|
||||
"@0x/contracts-exchange": "^3.2.2",
|
||||
"@0x/contracts-exchange-libs": "^4.3.2",
|
||||
"@0x/contracts-gen": "^2.0.8",
|
||||
"@0x/contracts-test-utils": "^5.3.0",
|
||||
"@0x/contracts-utils": "^4.4.0",
|
||||
"@0x/sol-compiler": "^4.0.8",
|
||||
"@0x/ts-doc-gen": "^0.0.22",
|
||||
"@0x/tslint-config": "^4.0.0",
|
||||
"@0x/types": "^3.1.2",
|
||||
"@0x/web3-wrapper": "^7.0.7",
|
||||
"@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.1",
|
||||
"@0x/order-utils": "^10.2.2",
|
||||
"@0x/typescript-typings": "^5.0.2",
|
||||
"@0x/utils": "^5.4.1",
|
||||
"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 },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
22
contracts/broker/tsconfig.json
Normal file
22
contracts/broker/tsconfig.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"extends": "../../tsconfig",
|
||||
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
|
||||
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
|
||||
"files": [
|
||||
"generated-artifacts/Broker.json",
|
||||
"generated-artifacts/GodsUnchainedValidator.json",
|
||||
"generated-artifacts/IBroker.json",
|
||||
"generated-artifacts/IGodsUnchained.json",
|
||||
"generated-artifacts/IPropertyValidator.json",
|
||||
"generated-artifacts/LibBrokerRichErrors.json",
|
||||
"generated-artifacts/TestGodsUnchained.json",
|
||||
"test/generated-artifacts/Broker.json",
|
||||
"test/generated-artifacts/GodsUnchainedValidator.json",
|
||||
"test/generated-artifacts/IBroker.json",
|
||||
"test/generated-artifacts/IGodsUnchained.json",
|
||||
"test/generated-artifacts/IPropertyValidator.json",
|
||||
"test/generated-artifacts/LibBrokerRichErrors.json",
|
||||
"test/generated-artifacts/TestGodsUnchained.json"
|
||||
],
|
||||
"exclude": ["./deploy/solc/solc_bin"]
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"extends": ["@0x/tslint-config"],
|
||||
"rules": {
|
||||
"max-classes-per-file": false
|
||||
"custom-no-magic-numbers": false
|
||||
}
|
||||
}
|
@@ -3,5 +3,5 @@
|
||||
"compilerOptions": {
|
||||
"outDir": "lib"
|
||||
},
|
||||
"include": ["./src/**/*"]
|
||||
"include": ["./src/**/*", "./test/**/*"]
|
||||
}
|
10
contracts/coordinator/.npmignore
Normal file
10
contracts/coordinator/.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
|
@@ -1,4 +1,180 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1582623685,
|
||||
"version": "3.1.2",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1581748629,
|
||||
"version": "3.1.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "3.1.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Update tests.",
|
||||
"pr": 2462
|
||||
}
|
||||
],
|
||||
"timestamp": 1581204851
|
||||
},
|
||||
{
|
||||
"timestamp": 1580988106,
|
||||
"version": "3.0.6",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1580811564,
|
||||
"version": "3.0.5",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1579682890,
|
||||
"version": "3.0.4",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1578272714,
|
||||
"version": "3.0.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"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": "Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils",
|
||||
"pr": 2330
|
||||
},
|
||||
{
|
||||
"note": "Introduced new export CoordinatorRevertErrors",
|
||||
"pr": 2321
|
||||
},
|
||||
{
|
||||
"note": "Added dependency on @0x/contracts-utils",
|
||||
"pr": 2321
|
||||
},
|
||||
{
|
||||
"note": "Add chainId to domain separator",
|
||||
"pr": 1742
|
||||
},
|
||||
{
|
||||
"note": "Inherit Exchange domain constants from `exchange-libs` to reduce code duplication",
|
||||
"pr": 1742
|
||||
},
|
||||
{
|
||||
"note": "Update domain separator",
|
||||
"pr": 1742
|
||||
},
|
||||
{
|
||||
"note": "Refactor contract to use new ITransactions interface",
|
||||
"pr": 1753
|
||||
},
|
||||
{
|
||||
"note": "Add verifyingContractIfExists arg to LibEIP712CoordinatorDomain constructor",
|
||||
"pr": 1753
|
||||
},
|
||||
{
|
||||
"note": "Remove LibZeroExTransaction contract",
|
||||
"pr": 1753
|
||||
},
|
||||
{
|
||||
"note": "Update tests for arbitrary fee tokens (ZEIP-28).",
|
||||
"pr": 1819
|
||||
},
|
||||
{
|
||||
"note": "Update for new `marketXOrders` consolidation.",
|
||||
"pr": 2042
|
||||
},
|
||||
{
|
||||
"note": "Use built in selectors instead of hard coded constants",
|
||||
"pr": 2055
|
||||
},
|
||||
{
|
||||
"note": "Compile and export all contracts, artifacts, and wrappers by default",
|
||||
"pr": 2055
|
||||
}
|
||||
],
|
||||
"timestamp": 1575296764
|
||||
},
|
||||
{
|
||||
"version": "2.1.0-beta.4",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
],
|
||||
"timestamp": 1575290197
|
||||
},
|
||||
{
|
||||
"version": "2.1.0-beta.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
],
|
||||
"timestamp": 1574238768
|
||||
},
|
||||
{
|
||||
"version": "2.1.0-beta.2",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils",
|
||||
"pr": 2330
|
||||
},
|
||||
{
|
||||
"note": "Introduced new export CoordinatorRevertErrors",
|
||||
"pr": 2321
|
||||
},
|
||||
{
|
||||
"note": "Added dependency on @0x/contracts-utils",
|
||||
"pr": 2321
|
||||
}
|
||||
],
|
||||
"timestamp": 1574030254
|
||||
},
|
||||
{
|
||||
"version": "2.1.0-beta.1",
|
||||
"changes": [
|
||||
|
@@ -5,6 +5,72 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v3.1.2 - _February 25, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.1 - _February 15, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.0 - _February 8, 2020_
|
||||
|
||||
* Update tests. (#2462)
|
||||
|
||||
## v3.0.6 - _February 6, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.0.5 - _February 4, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.0.4 - _January 22, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.0.3 - _January 6, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.0.2 - _December 17, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.0.1 - _December 9, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.0.0 - _December 2, 2019_
|
||||
|
||||
* Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330)
|
||||
* Introduced new export CoordinatorRevertErrors (#2321)
|
||||
* Added dependency on @0x/contracts-utils (#2321)
|
||||
* Add chainId to domain separator (#1742)
|
||||
* Inherit Exchange domain constants from `exchange-libs` to reduce code duplication (#1742)
|
||||
* Update domain separator (#1742)
|
||||
* Refactor contract to use new ITransactions interface (#1753)
|
||||
* Add verifyingContractIfExists arg to LibEIP712CoordinatorDomain constructor (#1753)
|
||||
* Remove LibZeroExTransaction contract (#1753)
|
||||
* Update tests for arbitrary fee tokens (ZEIP-28). (#1819)
|
||||
* Update for new `marketXOrders` consolidation. (#2042)
|
||||
* Use built in selectors instead of hard coded constants (#2055)
|
||||
* Compile and export all contracts, artifacts, and wrappers by default (#2055)
|
||||
|
||||
## v2.1.0-beta.4 - _December 2, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.1.0-beta.3 - _November 20, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.1.0-beta.2 - _November 17, 2019_
|
||||
|
||||
* Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330)
|
||||
* Introduced new export CoordinatorRevertErrors (#2321)
|
||||
* Added dependency on @0x/contracts-utils (#2321)
|
||||
|
||||
## v2.1.0-beta.1 - _November 7, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"artifactsDir": "./generated-artifacts",
|
||||
"artifactsDir": "./test/generated-artifacts",
|
||||
"contractsDir": "./contracts",
|
||||
"useDockerisedSolc": false,
|
||||
"compilerSettings": {
|
||||
"evmVersion": "constantinople",
|
||||
"evmVersion": "istanbul",
|
||||
"optimizer": {
|
||||
"enabled": true,
|
||||
"runs": 1000000,
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-coordinator",
|
||||
"version": "2.1.0-beta.1",
|
||||
"version": "3.1.2",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -12,7 +12,7 @@
|
||||
"scripts": {
|
||||
"build": "yarn pre_build && tsc -b",
|
||||
"build:ci": "yarn build",
|
||||
"pre_build": "run-s compile contracts:gen generate_contract_wrappers",
|
||||
"pre_build": "run-s compile contracts:gen generate_contract_wrappers contracts:copy",
|
||||
"test": "yarn run_mocha",
|
||||
"rebuild_and_test": "run-s build test",
|
||||
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
||||
@@ -21,21 +21,25 @@
|
||||
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
|
||||
"compile": "sol-compiler",
|
||||
"watch": "sol-compiler -w",
|
||||
"clean": "shx rm -rf lib generated-artifacts generated-wrappers",
|
||||
"generate_contract_wrappers": "abi-gen --debug --abis ${npm_package_config_abis} --output generated-wrappers --backend ethers",
|
||||
"lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||
"fix": "tslint --fix --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||
"clean": "shx rm -rf lib test/generated-artifacts test/generated-wrappers generated-artifacts generated-wrappers",
|
||||
"generate_contract_wrappers": "abi-gen --debug --abis ${npm_package_config_abis} --output test/generated-wrappers --backend ethers",
|
||||
"lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./test/generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude ./test/generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||
"fix": "tslint --fix --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./test/generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude ./test/generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||
"coverage:report:text": "istanbul report text",
|
||||
"coverage:report:html": "istanbul report html && open coverage/index.html",
|
||||
"profiler:report:html": "istanbul report html && open coverage/index.html",
|
||||
"coverage:report:lcov": "istanbul report lcov",
|
||||
"test:circleci": "yarn test",
|
||||
"contracts:gen": "contracts-gen",
|
||||
"contracts:gen": "contracts-gen generate",
|
||||
"contracts:copy": "contracts-gen copy",
|
||||
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol",
|
||||
"compile:truffle": "truffle compile"
|
||||
"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": "./generated-artifacts/@(Coordinator|CoordinatorRegistry|ICoordinatorApprovalVerifier|ICoordinatorCore|ICoordinatorRegistryCore|ICoordinatorSignatureValidator|LibConstants|LibCoordinatorApproval|LibCoordinatorRichErrors|LibEIP712CoordinatorDomain|MixinCoordinatorApprovalVerifier|MixinCoordinatorCore|MixinCoordinatorRegistryCore|MixinSignatureValidator).json",
|
||||
"publicInterfaceContracts": "Coordinator,CoordinatorRegistry,LibCoordinatorApproval,LibCoordinatorRichErrors,LibEIP712CoordinatorDomain,LibConstants",
|
||||
"abis": "./test/generated-artifacts/@(Coordinator|CoordinatorRegistry|ICoordinatorApprovalVerifier|ICoordinatorCore|ICoordinatorRegistryCore|ICoordinatorSignatureValidator|LibConstants|LibCoordinatorApproval|LibCoordinatorRichErrors|LibEIP712CoordinatorDomain|MixinCoordinatorApprovalVerifier|MixinCoordinatorCore|MixinCoordinatorRegistryCore|MixinSignatureValidator).json",
|
||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
|
||||
},
|
||||
"repository": {
|
||||
@@ -48,17 +52,19 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^4.4.0-beta.1",
|
||||
"@0x/contracts-asset-proxy": "^2.3.0-beta.1",
|
||||
"@0x/contracts-dev-utils": "^0.1.0-beta.1",
|
||||
"@0x/contracts-erc20": "^2.3.0-beta.1",
|
||||
"@0x/contracts-exchange": "^2.2.0-beta.1",
|
||||
"@0x/contracts-gen": "^1.1.0-beta.1",
|
||||
"@0x/contracts-test-utils": "^3.2.0-beta.1",
|
||||
"@0x/dev-utils": "^2.4.0-beta.1",
|
||||
"@0x/order-utils": "^8.5.0-beta.1",
|
||||
"@0x/sol-compiler": "^3.2.0-beta.1",
|
||||
"@0x/tslint-config": "^3.1.0-beta.1",
|
||||
"@0x/abi-gen": "^5.2.1",
|
||||
"@0x/contracts-asset-proxy": "^3.2.2",
|
||||
"@0x/contracts-dev-utils": "^1.3.0",
|
||||
"@0x/contracts-erc20": "^3.1.2",
|
||||
"@0x/contracts-exchange": "^3.2.2",
|
||||
"@0x/contracts-gen": "^2.0.8",
|
||||
"@0x/contracts-test-utils": "^5.3.0",
|
||||
"@0x/dev-utils": "^3.2.1",
|
||||
"@0x/order-utils": "^10.2.2",
|
||||
"@0x/sol-compiler": "^4.0.8",
|
||||
"@0x/ts-doc-gen": "^0.0.22",
|
||||
"@0x/tslint-config": "^4.0.0",
|
||||
"@0x/web3-wrapper": "^7.0.7",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/node": "*",
|
||||
@@ -74,16 +80,20 @@
|
||||
"solhint": "^1.4.1",
|
||||
"truffle": "^5.0.32",
|
||||
"tslint": "5.11.0",
|
||||
"typedoc": "^0.15.0",
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^5.5.0-beta.1",
|
||||
"@0x/types": "^2.5.0-beta.1",
|
||||
"@0x/typescript-typings": "^4.4.0-beta.1",
|
||||
"@0x/utils": "^4.6.0-beta.1",
|
||||
"@0x/web3-wrapper": "^6.1.0-beta.1",
|
||||
"ethereum-types": "^2.2.0-beta.1",
|
||||
"ethereumjs-util": "^5.1.1"
|
||||
"@0x/assert": "^3.0.7",
|
||||
"@0x/base-contract": "^6.2.1",
|
||||
"@0x/contract-addresses": "^4.7.0",
|
||||
"@0x/contracts-utils": "^4.4.0",
|
||||
"@0x/json-schemas": "^5.0.7",
|
||||
"@0x/types": "^3.1.2",
|
||||
"@0x/typescript-typings": "^5.0.2",
|
||||
"@0x/utils": "^5.4.1",
|
||||
"ethereum-types": "^3.1.0",
|
||||
"http-status-codes": "^1.3.2"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
@@ -1,7 +1,9 @@
|
||||
import { hexConcat, signingUtils } from '@0x/contracts-test-utils';
|
||||
import { signingUtils } from '@0x/contracts-test-utils';
|
||||
import { SignatureType, SignedZeroExTransaction } from '@0x/types';
|
||||
import { hexUtils } from '@0x/utils';
|
||||
|
||||
import { hashUtils, SignedCoordinatorApproval } from './index';
|
||||
import { hashUtils } from './hash_utils';
|
||||
import { SignedCoordinatorApproval } from './types';
|
||||
|
||||
export class ApprovalFactory {
|
||||
private readonly _privateKey: Buffer;
|
||||
@@ -22,7 +24,7 @@ export class ApprovalFactory {
|
||||
const signedApproval = {
|
||||
txOrigin,
|
||||
transaction,
|
||||
signature: hexConcat(signatureBuff),
|
||||
signature: hexUtils.concat(signatureBuff),
|
||||
};
|
||||
return signedApproval;
|
||||
}
|
@@ -7,31 +7,15 @@ import { ContractArtifact } from 'ethereum-types';
|
||||
|
||||
import * as Coordinator from '../generated-artifacts/Coordinator.json';
|
||||
import * as CoordinatorRegistry from '../generated-artifacts/CoordinatorRegistry.json';
|
||||
import * as ICoordinatorApprovalVerifier from '../generated-artifacts/ICoordinatorApprovalVerifier.json';
|
||||
import * as ICoordinatorCore from '../generated-artifacts/ICoordinatorCore.json';
|
||||
import * as ICoordinatorRegistryCore from '../generated-artifacts/ICoordinatorRegistryCore.json';
|
||||
import * as ICoordinatorSignatureValidator from '../generated-artifacts/ICoordinatorSignatureValidator.json';
|
||||
import * as LibConstants from '../generated-artifacts/LibConstants.json';
|
||||
import * as LibCoordinatorApproval from '../generated-artifacts/LibCoordinatorApproval.json';
|
||||
import * as LibCoordinatorRichErrors from '../generated-artifacts/LibCoordinatorRichErrors.json';
|
||||
import * as LibEIP712CoordinatorDomain from '../generated-artifacts/LibEIP712CoordinatorDomain.json';
|
||||
import * as MixinCoordinatorApprovalVerifier from '../generated-artifacts/MixinCoordinatorApprovalVerifier.json';
|
||||
import * as MixinCoordinatorCore from '../generated-artifacts/MixinCoordinatorCore.json';
|
||||
import * as MixinCoordinatorRegistryCore from '../generated-artifacts/MixinCoordinatorRegistryCore.json';
|
||||
import * as MixinSignatureValidator from '../generated-artifacts/MixinSignatureValidator.json';
|
||||
export const artifacts = {
|
||||
Coordinator: Coordinator as ContractArtifact,
|
||||
MixinCoordinatorApprovalVerifier: MixinCoordinatorApprovalVerifier as ContractArtifact,
|
||||
MixinCoordinatorCore: MixinCoordinatorCore as ContractArtifact,
|
||||
MixinSignatureValidator: MixinSignatureValidator as ContractArtifact,
|
||||
ICoordinatorApprovalVerifier: ICoordinatorApprovalVerifier as ContractArtifact,
|
||||
ICoordinatorCore: ICoordinatorCore as ContractArtifact,
|
||||
ICoordinatorSignatureValidator: ICoordinatorSignatureValidator as ContractArtifact,
|
||||
LibConstants: LibConstants as ContractArtifact,
|
||||
CoordinatorRegistry: CoordinatorRegistry as ContractArtifact,
|
||||
LibCoordinatorApproval: LibCoordinatorApproval as ContractArtifact,
|
||||
LibCoordinatorRichErrors: LibCoordinatorRichErrors as ContractArtifact,
|
||||
LibEIP712CoordinatorDomain: LibEIP712CoordinatorDomain as ContractArtifact,
|
||||
CoordinatorRegistry: CoordinatorRegistry as ContractArtifact,
|
||||
MixinCoordinatorRegistryCore: MixinCoordinatorRegistryCore as ContractArtifact,
|
||||
ICoordinatorRegistryCore: ICoordinatorRegistryCore as ContractArtifact,
|
||||
LibConstants: LibConstants as ContractArtifact,
|
||||
};
|
||||
|
820
contracts/coordinator/src/client/index.ts
Normal file
820
contracts/coordinator/src/client/index.ts
Normal file
@@ -0,0 +1,820 @@
|
||||
import { SendTransactionOpts } from '@0x/base-contract';
|
||||
import { getContractAddressesForChainOrThrow } from '@0x/contract-addresses';
|
||||
import { ExchangeContract } from '@0x/contracts-exchange';
|
||||
import { ExchangeFunctionName } from '@0x/contracts-test-utils';
|
||||
import { devConstants } from '@0x/dev-utils';
|
||||
import { schemas } from '@0x/json-schemas';
|
||||
import { generatePseudoRandomSalt, signatureUtils } from '@0x/order-utils';
|
||||
import { Order, SignedOrder, SignedZeroExTransaction, ZeroExTransaction } from '@0x/types';
|
||||
import { BigNumber, fetchAsync } from '@0x/utils';
|
||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import { CallData, ContractAbi, SupportedProvider, TxData } from 'ethereum-types';
|
||||
import * as HttpStatus from 'http-status-codes';
|
||||
import { flatten } from 'lodash';
|
||||
|
||||
import { artifacts } from '../artifacts';
|
||||
import { CoordinatorContract, CoordinatorRegistryContract } from '../wrappers';
|
||||
|
||||
import { assert } from './utils/assert';
|
||||
import {
|
||||
CoordinatorServerApprovalResponse,
|
||||
CoordinatorServerCancellationResponse,
|
||||
CoordinatorServerError,
|
||||
CoordinatorServerErrorMsg,
|
||||
CoordinatorServerResponse,
|
||||
} from './utils/coordinator_server_types';
|
||||
|
||||
import { decorators } from './utils/decorators';
|
||||
|
||||
export { CoordinatorServerErrorMsg, CoordinatorServerCancellationResponse };
|
||||
|
||||
const DEFAULT_TX_DATA = {
|
||||
gas: devConstants.GAS_LIMIT,
|
||||
gasPrice: new BigNumber(1),
|
||||
value: new BigNumber(150000), // DEFAULT_PROTOCOL_FEE_MULTIPLIER
|
||||
};
|
||||
|
||||
// tx expiration time will be set to (now + default_approval - time_buffer)
|
||||
const DEFAULT_APPROVAL_EXPIRATION_TIME_SECONDS = 90;
|
||||
const DEFAULT_EXPIRATION_TIME_BUFFER_SECONDS = 30;
|
||||
|
||||
/**
|
||||
* This class includes all the functionality related to filling or cancelling orders through
|
||||
* the 0x V2 Coordinator extension contract.
|
||||
*/
|
||||
export class CoordinatorClient {
|
||||
public abi: ContractAbi = artifacts.Coordinator.compilerOutput.abi;
|
||||
public chainId: number;
|
||||
public address: string;
|
||||
public exchangeAddress: string;
|
||||
public registryAddress: string;
|
||||
|
||||
private readonly _web3Wrapper: Web3Wrapper;
|
||||
private readonly _contractInstance: CoordinatorContract;
|
||||
private readonly _registryInstance: CoordinatorRegistryContract;
|
||||
private readonly _exchangeInstance: ExchangeContract;
|
||||
private readonly _feeRecipientToEndpoint: { [feeRecipient: string]: string } = {};
|
||||
private readonly _txDefaults: CallData = DEFAULT_TX_DATA;
|
||||
|
||||
/**
|
||||
* Validates that the 0x transaction has been approved by all of the feeRecipients that correspond to each order in the transaction's Exchange calldata.
|
||||
* Throws an error if the transaction approvals are not valid. Will not detect failures that would occur when the transaction is executed on the Exchange contract.
|
||||
* @param transaction 0x transaction containing salt, signerAddress, and data.
|
||||
* @param txOrigin Required signer of Ethereum transaction calling this function.
|
||||
* @param transactionSignature Proof that the transaction has been signed by the signer.
|
||||
* @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order in the transaction's Exchange calldata.
|
||||
*/
|
||||
@decorators.asyncZeroExErrorHandler
|
||||
public async assertValidCoordinatorApprovalsOrThrowAsync(
|
||||
transaction: ZeroExTransaction,
|
||||
txOrigin: string,
|
||||
transactionSignature: string,
|
||||
approvalSignatures: string[],
|
||||
): Promise<void> {
|
||||
assert.doesConformToSchema('transaction', transaction, schemas.zeroExTransactionSchema);
|
||||
assert.isETHAddressHex('txOrigin', txOrigin);
|
||||
assert.isHexString('transactionSignature', transactionSignature);
|
||||
for (const approvalSignature of approvalSignatures) {
|
||||
assert.isHexString('approvalSignature', approvalSignature);
|
||||
}
|
||||
return this._contractInstance
|
||||
.assertValidCoordinatorApprovals(transaction, txOrigin, transactionSignature, approvalSignatures)
|
||||
.callAsync();
|
||||
}
|
||||
/**
|
||||
* Instantiate CoordinatorClient
|
||||
* @param web3Wrapper Web3Wrapper instance to use.
|
||||
* @param chainId Desired chainId.
|
||||
* @param address The address of the Coordinator contract. If undefined, will
|
||||
* default to the known address corresponding to the chainId.
|
||||
* @param exchangeAddress The address of the Exchange contract. If undefined, will
|
||||
* default to the known address corresponding to the chainId.
|
||||
* @param registryAddress The address of the CoordinatorRegistry contract. If undefined, will
|
||||
* default to the known address corresponding to the chainId.
|
||||
*/
|
||||
constructor(
|
||||
address: string,
|
||||
provider: SupportedProvider,
|
||||
chainId: number,
|
||||
txDefaults?: Partial<TxData>,
|
||||
exchangeAddress?: string,
|
||||
registryAddress?: string,
|
||||
) {
|
||||
this.chainId = chainId;
|
||||
const contractAddresses = getContractAddressesForChainOrThrow(this.chainId);
|
||||
this.address = address === undefined ? contractAddresses.coordinator : address;
|
||||
this.exchangeAddress = exchangeAddress === undefined ? contractAddresses.exchange : exchangeAddress;
|
||||
this.registryAddress = registryAddress === undefined ? contractAddresses.coordinatorRegistry : registryAddress;
|
||||
this._web3Wrapper = new Web3Wrapper(provider);
|
||||
this._txDefaults = { ...txDefaults, ...DEFAULT_TX_DATA };
|
||||
this._contractInstance = new CoordinatorContract(
|
||||
this.address,
|
||||
this._web3Wrapper.getProvider(),
|
||||
this._web3Wrapper.getContractDefaults(),
|
||||
);
|
||||
this._registryInstance = new CoordinatorRegistryContract(
|
||||
this.registryAddress,
|
||||
this._web3Wrapper.getProvider(),
|
||||
this._web3Wrapper.getContractDefaults(),
|
||||
);
|
||||
this._exchangeInstance = new ExchangeContract(
|
||||
this.exchangeAddress,
|
||||
this._web3Wrapper.getProvider(),
|
||||
this._web3Wrapper.getContractDefaults(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills a signed order with an amount denominated in baseUnits of the taker asset. Under-the-hood, this
|
||||
* method uses the `feeRecipientAddress` of the order to look up the coordinator server endpoint registered in the
|
||||
* coordinator registry contract. It requests an approval from that coordinator server before
|
||||
* submitting the order and approval as a 0x transaction to the coordinator extension contract. The coordinator extension
|
||||
* contract validates approvals and then fills the order via the Exchange contract.
|
||||
* @param order An object that conforms to the Order interface.
|
||||
* @param takerAssetFillAmount The amount of the order (in taker asset baseUnits) that you wish to fill.
|
||||
* @param signature Signature corresponding to the order.
|
||||
* @param txData Transaction data. The `from` field should be the user Ethereum address who would like
|
||||
* to fill these orders. Must be available via the Provider supplied at instantiation.
|
||||
* @param sendTxOpts Optional arguments for sending the transaction.
|
||||
* @return Transaction hash.
|
||||
*/
|
||||
@decorators.asyncZeroExErrorHandler
|
||||
public async fillOrderAsync(
|
||||
order: Order,
|
||||
takerAssetFillAmount: BigNumber,
|
||||
signature: string,
|
||||
txData: TxData,
|
||||
sendTxOpts: Partial<SendTransactionOpts> = { shouldValidate: true },
|
||||
): Promise<string> {
|
||||
assert.doesConformToSchema('order', order, schemas.orderSchema);
|
||||
assert.isValidBaseUnitAmount('takerAssetFillAmount', takerAssetFillAmount);
|
||||
return this._executeTxThroughCoordinatorAsync(
|
||||
ExchangeFunctionName.FillOrder,
|
||||
txData,
|
||||
sendTxOpts,
|
||||
[order],
|
||||
order,
|
||||
takerAssetFillAmount,
|
||||
signature,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to fill a specific amount of an order. If the entire amount specified cannot be filled,
|
||||
* the fill order is abandoned.
|
||||
* @param order An object that conforms to the Order interface.
|
||||
* @param takerAssetFillAmount The amount of the order (in taker asset baseUnits) that you wish to fill.
|
||||
* @param signature Signature corresponding to the order.
|
||||
* @param txData Transaction data. The `from` field should be the user Ethereum address who would like
|
||||
* to fill these orders. Must be available via the Provider supplied at instantiation.
|
||||
* @param sendTxOpts Optional arguments for sending the transaction.
|
||||
* @return Transaction hash.
|
||||
*/
|
||||
@decorators.asyncZeroExErrorHandler
|
||||
public async fillOrKillOrderAsync(
|
||||
order: Order,
|
||||
takerAssetFillAmount: BigNumber,
|
||||
signature: string,
|
||||
txData: TxData,
|
||||
sendTxOpts: Partial<SendTransactionOpts> = { shouldValidate: true },
|
||||
): Promise<string> {
|
||||
assert.doesConformToSchema('order', order, schemas.orderSchema);
|
||||
assert.isValidBaseUnitAmount('takerAssetFillAmount', takerAssetFillAmount);
|
||||
return this._executeTxThroughCoordinatorAsync(
|
||||
ExchangeFunctionName.FillOrKillOrder,
|
||||
txData,
|
||||
sendTxOpts,
|
||||
[order],
|
||||
order,
|
||||
takerAssetFillAmount,
|
||||
signature,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch version of fillOrderAsync. Executes multiple fills atomically in a single transaction.
|
||||
* If any `feeRecipientAddress` in the batch is not registered to a coordinator server through the CoordinatorRegistryContract, the whole batch fails.
|
||||
* @param orders An array of orders to fill.
|
||||
* @param takerAssetFillAmounts The amounts of the orders (in taker asset baseUnits) that you wish to fill.
|
||||
* @param signatures Signatures corresponding to the orders.
|
||||
* @param txData Transaction data. The `from` field should be the user Ethereum address who would like
|
||||
* to fill these orders. Must be available via the Provider supplied at instantiation.
|
||||
* @param sendTxOpts Optional arguments for sending the transaction.
|
||||
* @return Transaction hash.
|
||||
*/
|
||||
@decorators.asyncZeroExErrorHandler
|
||||
public async batchFillOrdersAsync(
|
||||
orders: Order[],
|
||||
takerAssetFillAmounts: BigNumber[],
|
||||
signatures: string[],
|
||||
txData: TxData,
|
||||
sendTxOpts?: Partial<SendTransactionOpts>,
|
||||
): Promise<string> {
|
||||
return this._batchFillAsync(
|
||||
ExchangeFunctionName.BatchFillOrders,
|
||||
orders,
|
||||
takerAssetFillAmounts,
|
||||
signatures,
|
||||
txData,
|
||||
sendTxOpts,
|
||||
);
|
||||
}
|
||||
/**
|
||||
* No throw version of batchFillOrdersAsync
|
||||
* @param orders An array of orders to fill.
|
||||
* @param takerAssetFillAmounts The amounts of the orders (in taker asset baseUnits) that you wish to fill.
|
||||
* @param signatures Signatures corresponding to the orders.
|
||||
* @param txData Transaction data. The `from` field should be the user Ethereum address who would like
|
||||
* to fill these orders. Must be available via the Provider supplied at instantiation.
|
||||
* @param sendTxOpts Optional arguments for sending the transaction.
|
||||
* @return Transaction hash.
|
||||
*/
|
||||
|
||||
public async batchFillOrdersNoThrowAsync(
|
||||
orders: Order[],
|
||||
takerAssetFillAmounts: BigNumber[],
|
||||
signatures: string[],
|
||||
txData: TxData,
|
||||
sendTxOpts?: Partial<SendTransactionOpts>,
|
||||
): Promise<string> {
|
||||
return this._batchFillAsync(
|
||||
ExchangeFunctionName.BatchFillOrdersNoThrow,
|
||||
orders,
|
||||
takerAssetFillAmounts,
|
||||
signatures,
|
||||
txData,
|
||||
sendTxOpts,
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Batch version of fillOrKillOrderAsync. Executes multiple fills atomically in a single transaction.
|
||||
* @param orders An array of orders to fill.
|
||||
* @param takerAssetFillAmounts The amounts of the orders (in taker asset baseUnits) that you wish to fill.
|
||||
* @param signatures Signatures corresponding to the orders.
|
||||
* @param txData Transaction data. The `from` field should be the user Ethereum address who would like
|
||||
* to fill these orders. Must be available via the Provider supplied at instantiation.
|
||||
* @param sendTxOpts Optional arguments for sending the transaction.
|
||||
* @return Transaction hash.
|
||||
*/
|
||||
@decorators.asyncZeroExErrorHandler
|
||||
public async batchFillOrKillOrdersAsync(
|
||||
orders: Order[],
|
||||
takerAssetFillAmounts: BigNumber[],
|
||||
signatures: string[],
|
||||
txData: TxData,
|
||||
sendTxOpts?: Partial<SendTransactionOpts>,
|
||||
): Promise<string> {
|
||||
return this._batchFillAsync(
|
||||
ExchangeFunctionName.BatchFillOrKillOrders,
|
||||
orders,
|
||||
takerAssetFillAmounts,
|
||||
signatures,
|
||||
txData,
|
||||
sendTxOpts,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes multiple calls of fillOrder until total amount of makerAsset is bought by taker.
|
||||
* If any fill reverts, the error is caught and ignored. Finally, reverts if < makerAssetFillAmount has been bought.
|
||||
* NOTE: This function does not enforce that the makerAsset is the same for each order.
|
||||
* @param orders Array of order specifications.
|
||||
* @param makerAssetFillAmount Desired amount of makerAsset to buy.
|
||||
* @param signatures Proofs that orders have been signed by makers.
|
||||
* @param txData Transaction data. The `from` field should be the user Ethereum address who would like
|
||||
* to fill these orders. Must be available via the Provider supplied at instantiation.
|
||||
* @param sendTxOpts Optional arguments for sending the transaction.
|
||||
* @return Transaction hash.
|
||||
*/
|
||||
@decorators.asyncZeroExErrorHandler
|
||||
public async marketBuyOrdersFillOrKillAsync(
|
||||
orders: Order[],
|
||||
makerAssetFillAmount: BigNumber,
|
||||
signatures: string[],
|
||||
txData: TxData,
|
||||
sendTxOpts: SendTransactionOpts = { shouldValidate: true },
|
||||
): Promise<string> {
|
||||
return this._marketBuySellOrdersAsync(
|
||||
ExchangeFunctionName.MarketBuyOrdersFillOrKill,
|
||||
orders,
|
||||
makerAssetFillAmount,
|
||||
signatures,
|
||||
txData,
|
||||
sendTxOpts,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* No throw version of marketBuyOrdersFillOrKillAsync
|
||||
* @param orders An array of orders to fill.
|
||||
* @param makerAssetFillAmount Maker asset fill amount.
|
||||
* @param signatures Signatures corresponding to the orders.
|
||||
* @param txData Transaction data. The `from` field should be the user Ethereum address who would like
|
||||
* to fill these orders. Must be available via the Provider supplied at instantiation.
|
||||
* @param sendTxOpts Optional arguments for sending the transaction.
|
||||
* @return Transaction hash.
|
||||
*/
|
||||
@decorators.asyncZeroExErrorHandler
|
||||
public async marketBuyOrdersNoThrowAsync(
|
||||
orders: Order[],
|
||||
makerAssetFillAmount: BigNumber,
|
||||
signatures: string[],
|
||||
txData: TxData,
|
||||
sendTxOpts: SendTransactionOpts = { shouldValidate: true },
|
||||
): Promise<string> {
|
||||
return this._marketBuySellOrdersAsync(
|
||||
ExchangeFunctionName.MarketBuyOrdersNoThrow,
|
||||
orders,
|
||||
makerAssetFillAmount,
|
||||
signatures,
|
||||
txData,
|
||||
sendTxOpts,
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Executes multiple calls of fillOrder until total amount of takerAsset is sold by taker.
|
||||
* If any fill reverts, the error is caught and ignored. Finally, reverts if < takerAssetFillAmount has been sold.
|
||||
* NOTE: This function does not enforce that the takerAsset is the same for each order.
|
||||
* @param orders Array of order specifications.
|
||||
* @param takerAssetFillAmount Desired amount of takerAsset to sell.
|
||||
* @param signatures Proofs that orders have been signed by makers.
|
||||
* @param txData Transaction data. The `from` field should be the user Ethereum address who would like
|
||||
* to fill these orders. Must be available via the Provider supplied at instantiation.
|
||||
* @param sendTxOpts Optional arguments for sending the transaction.
|
||||
* @return Transaction hash.
|
||||
*/
|
||||
@decorators.asyncZeroExErrorHandler
|
||||
public async marketSellOrdersFillOrKillAsync(
|
||||
orders: Order[],
|
||||
takerAssetFillAmount: BigNumber,
|
||||
signatures: string[],
|
||||
txData: TxData,
|
||||
sendTxOpts: SendTransactionOpts = { shouldValidate: true },
|
||||
): Promise<string> {
|
||||
return this._marketBuySellOrdersAsync(
|
||||
ExchangeFunctionName.MarketSellOrdersFillOrKill,
|
||||
orders,
|
||||
takerAssetFillAmount,
|
||||
signatures,
|
||||
txData,
|
||||
sendTxOpts,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* No throw version of marketSellOrdersAsync
|
||||
* @param orders An array of orders to fill.
|
||||
* @param takerAssetFillAmount Taker asset fill amount.
|
||||
* @param signatures Signatures corresponding to the orders.
|
||||
* @param txData Transaction data. The `from` field should be the user Ethereum address who would like
|
||||
* to fill these orders. Must be available via the Provider supplied at instantiation.
|
||||
* @param sendTxOpts Optional arguments for sending the transaction.
|
||||
* @return Transaction hash.
|
||||
*/
|
||||
@decorators.asyncZeroExErrorHandler
|
||||
public async marketSellOrdersNoThrowAsync(
|
||||
orders: Order[],
|
||||
takerAssetFillAmount: BigNumber,
|
||||
signatures: string[],
|
||||
txData: TxData,
|
||||
sendTxOpts: SendTransactionOpts = { shouldValidate: true },
|
||||
): Promise<string> {
|
||||
return this._marketBuySellOrdersAsync(
|
||||
ExchangeFunctionName.MarketSellOrdersNoThrow,
|
||||
orders,
|
||||
takerAssetFillAmount,
|
||||
signatures,
|
||||
txData,
|
||||
sendTxOpts,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels an order on-chain by submitting an Ethereum transaction.
|
||||
* @param order An object that conforms to the Order interface. The order you would like to cancel.
|
||||
* @param txData Transaction data. The `from` field should be the maker's Ethereum address. Must be available
|
||||
* via the Provider supplied at instantiation.
|
||||
* @param sendTxOpts Optional arguments for sending the transaction.
|
||||
* @return Transaction hash.
|
||||
*/
|
||||
@decorators.asyncZeroExErrorHandler
|
||||
public async hardCancelOrderAsync(
|
||||
order: Order,
|
||||
txData: TxData,
|
||||
sendTxOpts: SendTransactionOpts = { shouldValidate: true },
|
||||
): Promise<string> {
|
||||
assert.doesConformToSchema('order', order, schemas.orderSchema);
|
||||
return this._executeTxThroughCoordinatorAsync(
|
||||
ExchangeFunctionName.CancelOrder,
|
||||
txData,
|
||||
sendTxOpts,
|
||||
[order],
|
||||
order,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch version of hardCancelOrderAsync. Cancels orders on-chain by submitting an Ethereum transaction.
|
||||
* Executes multiple cancels atomically in a single transaction.
|
||||
* @param orders An array of orders to cancel.
|
||||
* @param txData Transaction data. The `from` field should be the maker's Ethereum address. Must be available
|
||||
* via the Provider supplied at instantiation.
|
||||
* @param sendTxOpts Optional arguments for sending the transaction.
|
||||
* @return Transaction hash.
|
||||
*/
|
||||
@decorators.asyncZeroExErrorHandler
|
||||
public async batchHardCancelOrdersAsync(
|
||||
orders: Order[],
|
||||
txData: TxData,
|
||||
sendTxOpts: SendTransactionOpts = { shouldValidate: true },
|
||||
): Promise<string> {
|
||||
assert.doesConformToSchema('orders', orders, schemas.ordersSchema);
|
||||
return this._executeTxThroughCoordinatorAsync(
|
||||
ExchangeFunctionName.BatchCancelOrders,
|
||||
txData,
|
||||
sendTxOpts,
|
||||
orders,
|
||||
orders,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels orders on-chain by submitting an Ethereum transaction.
|
||||
* Cancels all orders created by makerAddress with a salt less than or equal to the targetOrderEpoch
|
||||
* and senderAddress equal to coordinator extension contract address.
|
||||
* @param targetOrderEpoch Target order epoch.
|
||||
* @param txData Transaction data. The `from` field should be the maker's Ethereum address. Must be available
|
||||
* via the Provider supplied at instantiation.
|
||||
* @param sendTxOpts Optional arguments for sending the transaction.
|
||||
* @return Transaction hash.
|
||||
*/
|
||||
@decorators.asyncZeroExErrorHandler
|
||||
public async hardCancelOrdersUpToAsync(
|
||||
targetOrderEpoch: BigNumber,
|
||||
txData: TxData,
|
||||
sendTxOpts: SendTransactionOpts = { shouldValidate: true },
|
||||
): Promise<string> {
|
||||
assert.isBigNumber('targetOrderEpoch', targetOrderEpoch);
|
||||
return this._executeTxThroughCoordinatorAsync(
|
||||
ExchangeFunctionName.CancelOrdersUpTo,
|
||||
txData,
|
||||
sendTxOpts,
|
||||
[],
|
||||
targetOrderEpoch,
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Soft cancel a given order.
|
||||
* Soft cancels are recorded only on coordinator operator servers and do not involve an Ethereum transaction.
|
||||
* See [soft cancels](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/coordinator-specification.md#soft-cancels).
|
||||
* @param order An object that conforms to the Order or SignedOrder interface. The order you would like to cancel.
|
||||
* @return CoordinatorServerCancellationResponse. See [Cancellation Response](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/coordinator-specification.md#response).
|
||||
*/
|
||||
@decorators.asyncZeroExErrorHandler
|
||||
public async softCancelAsync(order: Order): Promise<CoordinatorServerCancellationResponse> {
|
||||
assert.doesConformToSchema('order', order, schemas.orderSchema);
|
||||
assert.isETHAddressHex('feeRecipientAddress', order.feeRecipientAddress);
|
||||
assert.isSenderAddressAsync('makerAddress', order.makerAddress, this._web3Wrapper);
|
||||
|
||||
const data = this._exchangeInstance.cancelOrder(order).getABIEncodedTransactionData();
|
||||
const transaction = await this._generateSignedZeroExTransactionAsync(data, order.makerAddress);
|
||||
const endpoint = await this._getServerEndpointOrThrowAsync(order);
|
||||
|
||||
const response = await this._executeServerRequestAsync(transaction, order.makerAddress, endpoint);
|
||||
if (response.isError) {
|
||||
const approvedOrders = new Array();
|
||||
const cancellations = new Array();
|
||||
const errors = [
|
||||
{
|
||||
...response,
|
||||
orders: [order],
|
||||
},
|
||||
];
|
||||
throw new CoordinatorServerError(
|
||||
CoordinatorServerErrorMsg.CancellationFailed,
|
||||
approvedOrders,
|
||||
cancellations,
|
||||
errors,
|
||||
);
|
||||
} else {
|
||||
return response.body as CoordinatorServerCancellationResponse;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Batch version of softCancelOrderAsync. Requests multiple soft cancels
|
||||
* @param orders An array of orders to cancel.
|
||||
* @return CoordinatorServerCancellationResponse. See [Cancellation Response](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/coordinator-specification.md#response).
|
||||
*/
|
||||
@decorators.asyncZeroExErrorHandler
|
||||
public async batchSoftCancelAsync(orders: SignedOrder[]): Promise<CoordinatorServerCancellationResponse[]> {
|
||||
assert.doesConformToSchema('orders', orders, schemas.ordersSchema);
|
||||
const makerAddress = getMakerAddressOrThrow(orders);
|
||||
assert.isSenderAddressAsync('makerAddress', makerAddress, this._web3Wrapper);
|
||||
const data = this._exchangeInstance.batchCancelOrders(orders).getABIEncodedTransactionData();
|
||||
const transaction = await this._generateSignedZeroExTransactionAsync(data, makerAddress);
|
||||
|
||||
// make server requests
|
||||
const errorResponses: CoordinatorServerResponse[] = [];
|
||||
const successResponses: CoordinatorServerCancellationResponse[] = [];
|
||||
const serverEndpointsToOrders = await this._mapServerEndpointsToOrdersAsync(orders);
|
||||
for (const endpoint of Object.keys(serverEndpointsToOrders)) {
|
||||
const response = await this._executeServerRequestAsync(transaction, makerAddress, endpoint);
|
||||
if (response.isError) {
|
||||
errorResponses.push(response);
|
||||
} else {
|
||||
successResponses.push(response.body as CoordinatorServerCancellationResponse);
|
||||
}
|
||||
}
|
||||
|
||||
// if no errors
|
||||
if (errorResponses.length === 0) {
|
||||
return successResponses;
|
||||
} else {
|
||||
// lookup orders with errors
|
||||
const errorsWithOrders = errorResponses.map(resp => {
|
||||
const endpoint = resp.coordinatorOperator;
|
||||
const _orders = serverEndpointsToOrders[endpoint];
|
||||
return {
|
||||
...resp,
|
||||
orders: _orders,
|
||||
};
|
||||
});
|
||||
|
||||
const approvedOrders = new Array();
|
||||
const cancellations = successResponses;
|
||||
// return errors and approvals
|
||||
throw new CoordinatorServerError(
|
||||
CoordinatorServerErrorMsg.CancellationFailed,
|
||||
approvedOrders,
|
||||
cancellations,
|
||||
errorsWithOrders,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recovers the address of a signer given a hash and signature.
|
||||
* @param hash Any 32 byte hash.
|
||||
* @param signature Proof that the hash has been signed by signer.
|
||||
* @returns Signer address.
|
||||
*/
|
||||
@decorators.asyncZeroExErrorHandler
|
||||
public async getSignerAddressAsync(hash: string, signature: string): Promise<string> {
|
||||
assert.isHexString('hash', hash);
|
||||
assert.isHexString('signature', signature);
|
||||
const signerAddress = await this._contractInstance.getSignerAddress(hash, signature).callAsync();
|
||||
return signerAddress;
|
||||
}
|
||||
|
||||
private async _marketBuySellOrdersAsync(
|
||||
exchangeFn: ExchangeFunctionName,
|
||||
orders: Order[],
|
||||
assetFillAmount: BigNumber,
|
||||
signatures: string[],
|
||||
txData: TxData,
|
||||
sendTxOpts: SendTransactionOpts = { shouldValidate: true },
|
||||
): Promise<string> {
|
||||
assert.doesConformToSchema('orders', orders, schemas.ordersSchema);
|
||||
assert.isBigNumber('assetFillAmount', assetFillAmount);
|
||||
return this._executeTxThroughCoordinatorAsync(
|
||||
exchangeFn,
|
||||
txData,
|
||||
sendTxOpts,
|
||||
orders,
|
||||
orders,
|
||||
assetFillAmount,
|
||||
signatures,
|
||||
);
|
||||
}
|
||||
|
||||
private async _batchFillAsync(
|
||||
exchangeFn: ExchangeFunctionName,
|
||||
orders: Order[],
|
||||
takerAssetFillAmounts: BigNumber[],
|
||||
signatures: string[],
|
||||
txData: TxData,
|
||||
sendTxOpts: SendTransactionOpts = { shouldValidate: true },
|
||||
): Promise<string> {
|
||||
assert.doesConformToSchema('orders', orders, schemas.ordersSchema);
|
||||
takerAssetFillAmounts.forEach(takerAssetFillAmount =>
|
||||
assert.isValidBaseUnitAmount('takerAssetFillAmount', takerAssetFillAmount),
|
||||
);
|
||||
return this._executeTxThroughCoordinatorAsync(
|
||||
exchangeFn,
|
||||
txData,
|
||||
sendTxOpts,
|
||||
orders,
|
||||
orders,
|
||||
takerAssetFillAmounts,
|
||||
signatures,
|
||||
);
|
||||
}
|
||||
|
||||
private async _executeTxThroughCoordinatorAsync(
|
||||
exchangeFn: ExchangeFunctionName,
|
||||
txData: TxData,
|
||||
sendTxOpts: Partial<SendTransactionOpts>,
|
||||
ordersNeedingApprovals: Order[],
|
||||
...args: any[] // tslint:disable-line:trailing-comma
|
||||
): Promise<string> {
|
||||
assert.isETHAddressHex('takerAddress', txData.from);
|
||||
await assert.isSenderAddressAsync('takerAddress', txData.from, this._web3Wrapper);
|
||||
|
||||
// get ABI encoded transaction data for the desired exchange method
|
||||
const data = (this._exchangeInstance as any)[exchangeFn](...args).getABIEncodedTransactionData();
|
||||
|
||||
// generate and sign a ZeroExTransaction
|
||||
const signedZrxTx = await this._generateSignedZeroExTransactionAsync(data, txData.from, txData.gasPrice);
|
||||
|
||||
// get approval signatures from registered coordinator operators
|
||||
const approvalSignatures = await this._getApprovalsAsync(signedZrxTx, ordersNeedingApprovals, txData.from);
|
||||
|
||||
// execute the transaction through the Coordinator Contract
|
||||
const txDataWithDefaults = {
|
||||
...this._txDefaults,
|
||||
...txData, // override defaults
|
||||
};
|
||||
const txHash = this._contractInstance
|
||||
.executeTransaction(signedZrxTx, txData.from, signedZrxTx.signature, approvalSignatures)
|
||||
.sendTransactionAsync(txDataWithDefaults, sendTxOpts);
|
||||
return txHash;
|
||||
}
|
||||
|
||||
private async _generateSignedZeroExTransactionAsync(
|
||||
data: string,
|
||||
signerAddress: string,
|
||||
gasPrice?: BigNumber | string | number,
|
||||
): Promise<SignedZeroExTransaction> {
|
||||
const transaction: ZeroExTransaction = {
|
||||
salt: generatePseudoRandomSalt(),
|
||||
signerAddress,
|
||||
data,
|
||||
domain: {
|
||||
verifyingContract: this.exchangeAddress,
|
||||
chainId: await this._web3Wrapper.getChainIdAsync(),
|
||||
},
|
||||
expirationTimeSeconds: new BigNumber(
|
||||
Math.floor(Date.now() / 1000) +
|
||||
DEFAULT_APPROVAL_EXPIRATION_TIME_SECONDS -
|
||||
DEFAULT_EXPIRATION_TIME_BUFFER_SECONDS,
|
||||
),
|
||||
gasPrice: gasPrice ? new BigNumber(gasPrice) : new BigNumber(1),
|
||||
};
|
||||
const signedZrxTx = await signatureUtils.ecSignTransactionAsync(
|
||||
this._web3Wrapper.getProvider(),
|
||||
transaction,
|
||||
transaction.signerAddress,
|
||||
);
|
||||
return signedZrxTx;
|
||||
}
|
||||
|
||||
private async _getApprovalsAsync(
|
||||
transaction: SignedZeroExTransaction,
|
||||
orders: Order[],
|
||||
txOrigin: string,
|
||||
): Promise<string[]> {
|
||||
const coordinatorOrders = orders.filter(o => o.senderAddress === this.address);
|
||||
if (coordinatorOrders.length === 0) {
|
||||
return [];
|
||||
}
|
||||
const serverEndpointsToOrders = await this._mapServerEndpointsToOrdersAsync(coordinatorOrders);
|
||||
|
||||
// make server requests
|
||||
const errorResponses: CoordinatorServerResponse[] = [];
|
||||
const approvalResponses: CoordinatorServerResponse[] = [];
|
||||
for (const endpoint of Object.keys(serverEndpointsToOrders)) {
|
||||
const response = await this._executeServerRequestAsync(transaction, txOrigin, endpoint);
|
||||
if (response.isError) {
|
||||
errorResponses.push(response);
|
||||
} else {
|
||||
approvalResponses.push(response);
|
||||
}
|
||||
}
|
||||
|
||||
// if no errors
|
||||
if (errorResponses.length === 0) {
|
||||
// concatenate all approval responses
|
||||
return approvalResponses.reduce(
|
||||
(accumulator, response) =>
|
||||
accumulator.concat((response.body as CoordinatorServerApprovalResponse).signatures),
|
||||
[] as string[],
|
||||
);
|
||||
} else {
|
||||
// format errors and approvals
|
||||
// concatenate approvals
|
||||
const notCoordinatorOrders = orders.filter(o => o.senderAddress !== this.address);
|
||||
const approvedOrdersNested = approvalResponses.map(resp => {
|
||||
const endpoint = resp.coordinatorOperator;
|
||||
return serverEndpointsToOrders[endpoint];
|
||||
});
|
||||
const approvedOrders = flatten(approvedOrdersNested.concat(notCoordinatorOrders));
|
||||
|
||||
// lookup orders with errors
|
||||
const errorsWithOrders = errorResponses.map(resp => {
|
||||
const endpoint = resp.coordinatorOperator;
|
||||
return {
|
||||
...resp,
|
||||
orders: serverEndpointsToOrders[endpoint],
|
||||
};
|
||||
});
|
||||
|
||||
// throw informative error
|
||||
const cancellations = new Array();
|
||||
throw new CoordinatorServerError(
|
||||
CoordinatorServerErrorMsg.FillFailed,
|
||||
approvedOrders,
|
||||
cancellations,
|
||||
errorsWithOrders,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async _getServerEndpointOrThrowAsync(order: Order): Promise<string> {
|
||||
const cached = this._feeRecipientToEndpoint[order.feeRecipientAddress];
|
||||
const endpoint =
|
||||
cached !== undefined
|
||||
? cached
|
||||
: await _fetchServerEndpointOrThrowAsync(order.feeRecipientAddress, this._registryInstance);
|
||||
return endpoint;
|
||||
|
||||
async function _fetchServerEndpointOrThrowAsync(
|
||||
feeRecipient: string,
|
||||
registryInstance: CoordinatorRegistryContract,
|
||||
): Promise<string> {
|
||||
const coordinatorOperatorEndpoint = await registryInstance.getCoordinatorEndpoint(feeRecipient).callAsync();
|
||||
if (coordinatorOperatorEndpoint === '' || coordinatorOperatorEndpoint === undefined) {
|
||||
throw new Error(
|
||||
`No Coordinator server endpoint found in Coordinator Registry for feeRecipientAddress: ${feeRecipient}. Registry contract address: [${
|
||||
registryInstance.address
|
||||
}] Order: [${JSON.stringify(order)}]`,
|
||||
);
|
||||
}
|
||||
return coordinatorOperatorEndpoint;
|
||||
}
|
||||
}
|
||||
|
||||
private async _executeServerRequestAsync(
|
||||
signedTransaction: SignedZeroExTransaction,
|
||||
txOrigin: string,
|
||||
endpoint: string,
|
||||
): Promise<CoordinatorServerResponse> {
|
||||
const requestPayload = {
|
||||
signedTransaction,
|
||||
txOrigin,
|
||||
};
|
||||
const response = await fetchAsync(`${endpoint}/v2/request_transaction?chainId=${this.chainId}`, {
|
||||
body: JSON.stringify(requestPayload),
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8',
|
||||
},
|
||||
});
|
||||
|
||||
const isError = response.status !== HttpStatus.OK;
|
||||
const isValidationError = response.status === HttpStatus.BAD_REQUEST;
|
||||
const json = isError && !isValidationError ? undefined : await response.json();
|
||||
|
||||
const result = {
|
||||
isError,
|
||||
status: response.status,
|
||||
body: isError ? undefined : json,
|
||||
error: isError ? json : undefined,
|
||||
request: requestPayload,
|
||||
coordinatorOperator: endpoint,
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private async _mapServerEndpointsToOrdersAsync(
|
||||
coordinatorOrders: Order[],
|
||||
): Promise<{ [endpoint: string]: Order[] }> {
|
||||
const groupByFeeRecipient: { [feeRecipient: string]: Order[] } = {};
|
||||
for (const order of coordinatorOrders) {
|
||||
const feeRecipient = order.feeRecipientAddress;
|
||||
if (groupByFeeRecipient[feeRecipient] === undefined) {
|
||||
groupByFeeRecipient[feeRecipient] = [] as Order[];
|
||||
}
|
||||
groupByFeeRecipient[feeRecipient].push(order);
|
||||
}
|
||||
const serverEndpointsToOrders: { [endpoint: string]: Order[] } = {};
|
||||
for (const orders of Object.values(groupByFeeRecipient)) {
|
||||
const endpoint = await this._getServerEndpointOrThrowAsync(orders[0]);
|
||||
if (serverEndpointsToOrders[endpoint] === undefined) {
|
||||
serverEndpointsToOrders[endpoint] = [];
|
||||
}
|
||||
serverEndpointsToOrders[endpoint] = serverEndpointsToOrders[endpoint].concat(orders);
|
||||
}
|
||||
return serverEndpointsToOrders;
|
||||
}
|
||||
}
|
||||
|
||||
function getMakerAddressOrThrow(orders: Array<Order | SignedOrder>): string {
|
||||
const uniqueMakerAddresses = new Set(orders.map(o => o.makerAddress));
|
||||
if (uniqueMakerAddresses.size > 1) {
|
||||
throw new Error(`All orders in a batch must have the same makerAddress`);
|
||||
}
|
||||
return orders[0].makerAddress;
|
||||
}
|
||||
|
||||
// tslint:disable:max-file-line-count
|
@@ -4,7 +4,6 @@ import { Schema } from '@0x/json-schemas'; // tslint:disable-line:no-unused-vari
|
||||
import { Order } from '@0x/types'; // tslint:disable-line:no-unused-variable
|
||||
import { BigNumber } from '@0x/utils'; // tslint:disable-line:no-unused-variable
|
||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
export const assert = {
|
||||
...sharedAssert,
|
@@ -2,10 +2,6 @@ import { Order, SignedOrder, SignedZeroExTransaction } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
|
||||
export interface CoordinatorServerApprovalResponse {
|
||||
signatures: string[];
|
||||
expirationTimeSeconds: BigNumber[];
|
||||
}
|
||||
export interface CoordinatorServerApprovalRawResponse {
|
||||
signatures: string[];
|
||||
expirationTimeSeconds: BigNumber;
|
||||
}
|
||||
@@ -24,7 +20,7 @@ export interface CoordinatorOutstandingFillSignatures {
|
||||
export interface CoordinatorServerResponse {
|
||||
isError: boolean;
|
||||
status: number;
|
||||
body?: CoordinatorServerCancellationResponse | CoordinatorServerApprovalRawResponse;
|
||||
body?: CoordinatorServerCancellationResponse | CoordinatorServerApprovalResponse;
|
||||
error?: any;
|
||||
request: CoordinatorServerRequest;
|
||||
coordinatorOperator: string;
|
||||
@@ -38,12 +34,12 @@ export interface CoordinatorServerRequest {
|
||||
|
||||
export class CoordinatorServerError extends Error {
|
||||
public message: CoordinatorServerErrorMsg;
|
||||
public approvedOrders?: SignedOrder[] = [];
|
||||
public approvedOrders?: Order[] = [];
|
||||
public cancellations?: CoordinatorServerCancellationResponse[] = [];
|
||||
public errors: CoordinatorServerResponse[];
|
||||
constructor(
|
||||
message: CoordinatorServerErrorMsg,
|
||||
approvedOrders: SignedOrder[],
|
||||
approvedOrders: Order[],
|
||||
cancellations: CoordinatorServerCancellationResponse[],
|
||||
errors: CoordinatorServerResponse[],
|
||||
) {
|
@@ -1,7 +1,6 @@
|
||||
import { hexConcat } from '@0x/contracts-test-utils';
|
||||
import { eip712Utils } from '@0x/order-utils';
|
||||
import { SignedZeroExTransaction } from '@0x/types';
|
||||
import { signTypedDataUtils } from '@0x/utils';
|
||||
import { hexUtils, signTypedDataUtils } from '@0x/utils';
|
||||
|
||||
export const hashUtils = {
|
||||
getApprovalHashBuffer(transaction: SignedZeroExTransaction, verifyingContract: string, txOrigin: string): Buffer {
|
||||
@@ -10,7 +9,7 @@ export const hashUtils = {
|
||||
return hashBuffer;
|
||||
},
|
||||
getApprovalHashHex(transaction: SignedZeroExTransaction, verifyingContract: string, txOrigin: string): string {
|
||||
const hashHex = hexConcat(hashUtils.getApprovalHashBuffer(transaction, verifyingContract, txOrigin));
|
||||
const hashHex = hexUtils.toHex(hashUtils.getApprovalHashBuffer(transaction, verifyingContract, txOrigin));
|
||||
return hashHex;
|
||||
},
|
||||
};
|
@@ -1,3 +1,67 @@
|
||||
export * from './artifacts';
|
||||
export * from './wrappers';
|
||||
export * from '../test/utils';
|
||||
export { artifacts } from './artifacts';
|
||||
export {
|
||||
CoordinatorContract,
|
||||
CoordinatorRegistryContract,
|
||||
LibConstantsContract,
|
||||
LibCoordinatorApprovalContract,
|
||||
LibCoordinatorRichErrorsContract,
|
||||
LibEIP712CoordinatorDomainContract,
|
||||
} from './wrappers';
|
||||
export { CoordinatorRevertErrors } from '@0x/utils';
|
||||
export { CoordinatorServerCancellationResponse } from './client/index';
|
||||
export { ApprovalFactory } from './approval_factory';
|
||||
export { SignedCoordinatorApproval } from './types';
|
||||
export {
|
||||
Order,
|
||||
SignedOrder,
|
||||
SignatureType,
|
||||
SignedZeroExTransaction,
|
||||
EIP712DomainWithDefaultSchema,
|
||||
ZeroExTransaction,
|
||||
} from '@0x/types';
|
||||
export { AwaitTransactionSuccessOpts, SendTransactionOpts } from '@0x/base-contract';
|
||||
export {
|
||||
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,
|
||||
SupportedProvider,
|
||||
TxData,
|
||||
TxDataPayable,
|
||||
Web3JsProvider,
|
||||
GanacheProvider,
|
||||
EIP1193Provider,
|
||||
ZeroExProvider,
|
||||
EIP1193Event,
|
||||
JSONRPCRequestPayload,
|
||||
JSONRPCErrorCallback,
|
||||
Web3JsV1Provider,
|
||||
Web3JsV2Provider,
|
||||
Web3JsV3Provider,
|
||||
JSONRPCResponsePayload,
|
||||
JSONRPCResponseError,
|
||||
} from 'ethereum-types';
|
||||
export { CoordinatorClient, CoordinatorServerErrorMsg } from './client/index';
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import { SignedZeroExTransaction } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
|
||||
export interface CoordinatorApproval {
|
||||
transaction: SignedZeroExTransaction;
|
||||
@@ -8,3 +9,9 @@ export interface CoordinatorApproval {
|
||||
export interface SignedCoordinatorApproval extends CoordinatorApproval {
|
||||
signature: string;
|
||||
}
|
||||
|
||||
export interface CoordinatorTransaction {
|
||||
salt: BigNumber;
|
||||
signerAddress: string;
|
||||
data: string;
|
||||
}
|
@@ -5,15 +5,7 @@
|
||||
*/
|
||||
export * from '../generated-wrappers/coordinator';
|
||||
export * from '../generated-wrappers/coordinator_registry';
|
||||
export * from '../generated-wrappers/i_coordinator_approval_verifier';
|
||||
export * from '../generated-wrappers/i_coordinator_core';
|
||||
export * from '../generated-wrappers/i_coordinator_registry_core';
|
||||
export * from '../generated-wrappers/i_coordinator_signature_validator';
|
||||
export * from '../generated-wrappers/lib_constants';
|
||||
export * from '../generated-wrappers/lib_coordinator_approval';
|
||||
export * from '../generated-wrappers/lib_coordinator_rich_errors';
|
||||
export * from '../generated-wrappers/lib_e_i_p712_coordinator_domain';
|
||||
export * from '../generated-wrappers/mixin_coordinator_approval_verifier';
|
||||
export * from '../generated-wrappers/mixin_coordinator_core';
|
||||
export * from '../generated-wrappers/mixin_coordinator_registry_core';
|
||||
export * from '../generated-wrappers/mixin_signature_validator';
|
||||
|
37
contracts/coordinator/test/artifacts.ts
Normal file
37
contracts/coordinator/test/artifacts.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* -----------------------------------------------------------------------------
|
||||
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
import { ContractArtifact } from 'ethereum-types';
|
||||
|
||||
import * as Coordinator from '../test/generated-artifacts/Coordinator.json';
|
||||
import * as CoordinatorRegistry from '../test/generated-artifacts/CoordinatorRegistry.json';
|
||||
import * as ICoordinatorApprovalVerifier from '../test/generated-artifacts/ICoordinatorApprovalVerifier.json';
|
||||
import * as ICoordinatorCore from '../test/generated-artifacts/ICoordinatorCore.json';
|
||||
import * as ICoordinatorRegistryCore from '../test/generated-artifacts/ICoordinatorRegistryCore.json';
|
||||
import * as ICoordinatorSignatureValidator from '../test/generated-artifacts/ICoordinatorSignatureValidator.json';
|
||||
import * as LibConstants from '../test/generated-artifacts/LibConstants.json';
|
||||
import * as LibCoordinatorApproval from '../test/generated-artifacts/LibCoordinatorApproval.json';
|
||||
import * as LibCoordinatorRichErrors from '../test/generated-artifacts/LibCoordinatorRichErrors.json';
|
||||
import * as LibEIP712CoordinatorDomain from '../test/generated-artifacts/LibEIP712CoordinatorDomain.json';
|
||||
import * as MixinCoordinatorApprovalVerifier from '../test/generated-artifacts/MixinCoordinatorApprovalVerifier.json';
|
||||
import * as MixinCoordinatorCore from '../test/generated-artifacts/MixinCoordinatorCore.json';
|
||||
import * as MixinCoordinatorRegistryCore from '../test/generated-artifacts/MixinCoordinatorRegistryCore.json';
|
||||
import * as MixinSignatureValidator from '../test/generated-artifacts/MixinSignatureValidator.json';
|
||||
export const artifacts = {
|
||||
Coordinator: Coordinator as ContractArtifact,
|
||||
MixinCoordinatorApprovalVerifier: MixinCoordinatorApprovalVerifier as ContractArtifact,
|
||||
MixinCoordinatorCore: MixinCoordinatorCore as ContractArtifact,
|
||||
MixinSignatureValidator: MixinSignatureValidator as ContractArtifact,
|
||||
ICoordinatorApprovalVerifier: ICoordinatorApprovalVerifier as ContractArtifact,
|
||||
ICoordinatorCore: ICoordinatorCore as ContractArtifact,
|
||||
ICoordinatorSignatureValidator: ICoordinatorSignatureValidator as ContractArtifact,
|
||||
LibConstants: LibConstants as ContractArtifact,
|
||||
LibCoordinatorApproval: LibCoordinatorApproval as ContractArtifact,
|
||||
LibCoordinatorRichErrors: LibCoordinatorRichErrors as ContractArtifact,
|
||||
LibEIP712CoordinatorDomain: LibEIP712CoordinatorDomain as ContractArtifact,
|
||||
CoordinatorRegistry: CoordinatorRegistry as ContractArtifact,
|
||||
MixinCoordinatorRegistryCore: MixinCoordinatorRegistryCore as ContractArtifact,
|
||||
ICoordinatorRegistryCore: ICoordinatorRegistryCore as ContractArtifact,
|
||||
};
|
@@ -1,6 +1,8 @@
|
||||
import { blockchainTests, expect, verifyEvents } from '@0x/contracts-test-utils';
|
||||
|
||||
import { artifacts, CoordinatorRegistryContract, CoordinatorRegistryCoordinatorEndpointSetEventArgs } from '../src';
|
||||
import { artifacts } from './artifacts';
|
||||
|
||||
import { CoordinatorRegistryContract, CoordinatorRegistryCoordinatorEndpointSetEventArgs } from './wrappers';
|
||||
|
||||
// tslint:disable:no-unnecessary-type-assertion
|
||||
blockchainTests.resets('Coordinator Registry tests', env => {
|
||||
@@ -23,43 +25,42 @@ blockchainTests.resets('Coordinator Registry tests', env => {
|
||||
});
|
||||
describe('core', () => {
|
||||
it('Should successfully set a Coordinator endpoint', async () => {
|
||||
await coordinatorRegistry.setCoordinatorEndpoint.awaitTransactionSuccessAsync(coordinatorEndpoint, {
|
||||
await coordinatorRegistry.setCoordinatorEndpoint(coordinatorEndpoint).awaitTransactionSuccessAsync({
|
||||
from: coordinatorOperator,
|
||||
});
|
||||
const recordedCoordinatorEndpoint = await coordinatorRegistry.getCoordinatorEndpoint.callAsync(
|
||||
coordinatorOperator,
|
||||
);
|
||||
const recordedCoordinatorEndpoint = await coordinatorRegistry
|
||||
.getCoordinatorEndpoint(coordinatorOperator)
|
||||
.callAsync();
|
||||
expect(recordedCoordinatorEndpoint).to.be.equal(coordinatorEndpoint);
|
||||
});
|
||||
it('Should successfully unset a Coordinator endpoint', async () => {
|
||||
// set Coordinator endpoint
|
||||
await coordinatorRegistry.setCoordinatorEndpoint.awaitTransactionSuccessAsync(coordinatorEndpoint, {
|
||||
await coordinatorRegistry.setCoordinatorEndpoint(coordinatorEndpoint).awaitTransactionSuccessAsync({
|
||||
from: coordinatorOperator,
|
||||
});
|
||||
let recordedCoordinatorEndpoint = await coordinatorRegistry.getCoordinatorEndpoint.callAsync(
|
||||
coordinatorOperator,
|
||||
);
|
||||
let recordedCoordinatorEndpoint = await coordinatorRegistry
|
||||
.getCoordinatorEndpoint(coordinatorOperator)
|
||||
.callAsync();
|
||||
expect(recordedCoordinatorEndpoint).to.be.equal(coordinatorEndpoint);
|
||||
// unset Coordinator endpoint
|
||||
await coordinatorRegistry.setCoordinatorEndpoint.awaitTransactionSuccessAsync(nilCoordinatorEndpoint, {
|
||||
await coordinatorRegistry.setCoordinatorEndpoint(nilCoordinatorEndpoint).awaitTransactionSuccessAsync({
|
||||
from: coordinatorOperator,
|
||||
});
|
||||
recordedCoordinatorEndpoint = await coordinatorRegistry.getCoordinatorEndpoint.callAsync(
|
||||
coordinatorOperator,
|
||||
);
|
||||
recordedCoordinatorEndpoint = await coordinatorRegistry
|
||||
.getCoordinatorEndpoint(coordinatorOperator)
|
||||
.callAsync();
|
||||
expect(recordedCoordinatorEndpoint).to.be.equal(nilCoordinatorEndpoint);
|
||||
});
|
||||
it('Should emit an event when setting Coordinator endpoint', async () => {
|
||||
// set Coordinator endpoint
|
||||
const txReceipt = await coordinatorRegistry.setCoordinatorEndpoint.awaitTransactionSuccessAsync(
|
||||
coordinatorEndpoint,
|
||||
{
|
||||
const txReceipt = await coordinatorRegistry
|
||||
.setCoordinatorEndpoint(coordinatorEndpoint)
|
||||
.awaitTransactionSuccessAsync({
|
||||
from: coordinatorOperator,
|
||||
},
|
||||
);
|
||||
const recordedCoordinatorEndpoint = await coordinatorRegistry.getCoordinatorEndpoint.callAsync(
|
||||
coordinatorOperator,
|
||||
);
|
||||
});
|
||||
const recordedCoordinatorEndpoint = await coordinatorRegistry
|
||||
.getCoordinatorEndpoint(coordinatorOperator)
|
||||
.callAsync();
|
||||
expect(recordedCoordinatorEndpoint).to.be.equal(coordinatorEndpoint);
|
||||
// validate event
|
||||
const expectedEvent: CoordinatorRegistryCoordinatorEndpointSetEventArgs = {
|
||||
|
@@ -1,8 +1,11 @@
|
||||
import { blockchainTests, constants, expect, randomAddress } from '@0x/contracts-test-utils';
|
||||
import { transactionHashUtils } from '@0x/order-utils';
|
||||
import { blockchainTests, constants, expect, randomAddress, transactionHashUtils } from '@0x/contracts-test-utils';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
|
||||
import { artifacts, CoordinatorContract, hashUtils } from '../src';
|
||||
import { hashUtils } from '../src/hash_utils';
|
||||
|
||||
import { artifacts } from './artifacts';
|
||||
|
||||
import { CoordinatorContract } from './wrappers';
|
||||
|
||||
blockchainTests.resets('Libs tests', env => {
|
||||
let coordinatorContract: CoordinatorContract;
|
||||
@@ -42,7 +45,7 @@ blockchainTests.resets('Libs tests', env => {
|
||||
transactionSignature: signedTx.signature,
|
||||
};
|
||||
const expectedApprovalHash = hashUtils.getApprovalHashHex(signedTx, coordinatorContract.address, txOrigin);
|
||||
const approvalHash = await coordinatorContract.getCoordinatorApprovalHash.callAsync(approval);
|
||||
const approvalHash = await coordinatorContract.getCoordinatorApprovalHash(approval).callAsync();
|
||||
expect(expectedApprovalHash).to.eq(approvalHash);
|
||||
});
|
||||
});
|
||||
|
@@ -1,18 +1,22 @@
|
||||
import { constants as exchangeConstants, exchangeDataEncoder, ExchangeFunctionName } from '@0x/contracts-exchange';
|
||||
import { exchangeDataEncoder } from '@0x/contracts-exchange';
|
||||
import {
|
||||
blockchainTests,
|
||||
constants,
|
||||
ExchangeFunctionName,
|
||||
expect,
|
||||
hexConcat,
|
||||
hexSlice,
|
||||
randomAddress,
|
||||
TransactionFactory,
|
||||
transactionHashUtils,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { CoordinatorRevertErrors, transactionHashUtils } from '@0x/order-utils';
|
||||
import { LibBytesRevertErrors } from '@0x/contracts-utils';
|
||||
import { SignatureType, SignedOrder } from '@0x/types';
|
||||
import { BigNumber, LibBytesRevertErrors } from '@0x/utils';
|
||||
import { BigNumber, CoordinatorRevertErrors, hexUtils } from '@0x/utils';
|
||||
|
||||
import { ApprovalFactory, artifacts, CoordinatorContract } from '../src';
|
||||
import { ApprovalFactory } from '../src/approval_factory';
|
||||
|
||||
import { artifacts } from './artifacts';
|
||||
|
||||
import { CoordinatorContract } from './wrappers';
|
||||
|
||||
blockchainTests.resets('Mixins tests', env => {
|
||||
let chainId: number;
|
||||
@@ -70,25 +74,25 @@ blockchainTests.resets('Mixins tests', env => {
|
||||
const data = constants.NULL_BYTES;
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data }, SignatureType.EthSign);
|
||||
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||
const signerAddress = await mixins.getSignerAddress.callAsync(transactionHash, transaction.signature);
|
||||
const signerAddress = await mixins.getSignerAddress(transactionHash, transaction.signature).callAsync();
|
||||
expect(transaction.signerAddress).to.eq(signerAddress);
|
||||
});
|
||||
it('should return the correct address using the EIP712 signature type', async () => {
|
||||
const data = constants.NULL_BYTES;
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data }, SignatureType.EIP712);
|
||||
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||
const signerAddress = await mixins.getSignerAddress.callAsync(transactionHash, transaction.signature);
|
||||
const signerAddress = await mixins.getSignerAddress(transactionHash, transaction.signature).callAsync();
|
||||
expect(transaction.signerAddress).to.eq(signerAddress);
|
||||
});
|
||||
it('should revert with with the Illegal signature type', async () => {
|
||||
const data = constants.NULL_BYTES;
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
transaction.signature = hexConcat(
|
||||
hexSlice(transaction.signature, 0, transaction.signature.length - 1),
|
||||
transaction.signature = hexUtils.concat(
|
||||
hexUtils.slice(transaction.signature, 0, transaction.signature.length - 1),
|
||||
SignatureType.Illegal,
|
||||
);
|
||||
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||
expect(mixins.getSignerAddress.callAsync(transactionHash, transaction.signature)).to.revertWith(
|
||||
expect(mixins.getSignerAddress(transactionHash, transaction.signature).callAsync()).to.revertWith(
|
||||
new CoordinatorRevertErrors.SignatureError(
|
||||
CoordinatorRevertErrors.SignatureErrorCodes.Illegal,
|
||||
transactionHash,
|
||||
@@ -99,9 +103,9 @@ blockchainTests.resets('Mixins tests', env => {
|
||||
it('should revert with with the Invalid signature type', async () => {
|
||||
const data = constants.NULL_BYTES;
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
transaction.signature = hexConcat(SignatureType.Invalid);
|
||||
transaction.signature = hexUtils.concat(SignatureType.Invalid);
|
||||
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||
expect(mixins.getSignerAddress.callAsync(transactionHash, transaction.signature)).to.revertWith(
|
||||
expect(mixins.getSignerAddress(transactionHash, transaction.signature).callAsync()).to.revertWith(
|
||||
new CoordinatorRevertErrors.SignatureError(
|
||||
CoordinatorRevertErrors.SignatureErrorCodes.Invalid,
|
||||
transactionHash,
|
||||
@@ -112,12 +116,12 @@ blockchainTests.resets('Mixins tests', env => {
|
||||
it('should revert with with a signature type that equals `NSignatureTypes`', async () => {
|
||||
const data = constants.NULL_BYTES;
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
transaction.signature = hexConcat(
|
||||
hexSlice(transaction.signature, 0, transaction.signature.length - 1),
|
||||
transaction.signature = hexUtils.concat(
|
||||
hexUtils.slice(transaction.signature, 0, transaction.signature.length - 1),
|
||||
SignatureType.NSignatureTypes,
|
||||
);
|
||||
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||
expect(mixins.getSignerAddress.callAsync(transactionHash, transaction.signature)).to.revertWith(
|
||||
expect(mixins.getSignerAddress(transactionHash, transaction.signature).callAsync()).to.revertWith(
|
||||
new CoordinatorRevertErrors.SignatureError(
|
||||
CoordinatorRevertErrors.SignatureErrorCodes.Unsupported,
|
||||
transactionHash,
|
||||
@@ -128,12 +132,12 @@ blockchainTests.resets('Mixins tests', env => {
|
||||
it("should revert with with a signature type that isn't supported", async () => {
|
||||
const data = constants.NULL_BYTES;
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
transaction.signature = hexConcat(
|
||||
hexSlice(transaction.signature, 0, transaction.signature.length - 1),
|
||||
transaction.signature = hexUtils.concat(
|
||||
hexUtils.slice(transaction.signature, 0, transaction.signature.length - 1),
|
||||
SignatureType.Wallet,
|
||||
);
|
||||
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||
expect(mixins.getSignerAddress.callAsync(transactionHash, transaction.signature)).to.revertWith(
|
||||
expect(mixins.getSignerAddress(transactionHash, transaction.signature).callAsync()).to.revertWith(
|
||||
new CoordinatorRevertErrors.SignatureError(
|
||||
CoordinatorRevertErrors.SignatureErrorCodes.Unsupported,
|
||||
transactionHash,
|
||||
@@ -144,11 +148,11 @@ blockchainTests.resets('Mixins tests', env => {
|
||||
});
|
||||
|
||||
describe('decodeOrdersFromFillData', () => {
|
||||
for (const fnName of exchangeConstants.SINGLE_FILL_FN_NAMES) {
|
||||
for (const fnName of constants.SINGLE_FILL_FN_NAMES) {
|
||||
it(`should correctly decode the orders for ${fnName} data`, async () => {
|
||||
const orders = [defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const decodedOrders = await mixins.decodeOrdersFromFillData.callAsync(data);
|
||||
const decodedOrders = await mixins.decodeOrdersFromFillData(data).callAsync();
|
||||
const decodedSignedOrders = decodedOrders.map(order => ({
|
||||
...order,
|
||||
signature: constants.NULL_BYTES,
|
||||
@@ -158,11 +162,11 @@ blockchainTests.resets('Mixins tests', env => {
|
||||
expect(orders).to.deep.eq(decodedSignedOrders);
|
||||
});
|
||||
}
|
||||
for (const fnName of exchangeConstants.BATCH_FILL_FN_NAMES) {
|
||||
for (const fnName of constants.BATCH_FILL_FN_NAMES) {
|
||||
it(`should correctly decode the orders for ${fnName} data`, async () => {
|
||||
const orders = [defaultOrder, defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const decodedOrders = await mixins.decodeOrdersFromFillData.callAsync(data);
|
||||
const decodedOrders = await mixins.decodeOrdersFromFillData(data).callAsync();
|
||||
const decodedSignedOrders = decodedOrders.map(order => ({
|
||||
...order,
|
||||
signature: constants.NULL_BYTES,
|
||||
@@ -172,11 +176,11 @@ blockchainTests.resets('Mixins tests', env => {
|
||||
expect(orders).to.deep.eq(decodedSignedOrders);
|
||||
});
|
||||
}
|
||||
for (const fnName of exchangeConstants.MARKET_FILL_FN_NAMES) {
|
||||
for (const fnName of constants.MARKET_FILL_FN_NAMES) {
|
||||
it(`should correctly decode the orders for ${fnName} data`, async () => {
|
||||
const orders = [defaultOrder, defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const decodedOrders = await mixins.decodeOrdersFromFillData.callAsync(data);
|
||||
const decodedOrders = await mixins.decodeOrdersFromFillData(data).callAsync();
|
||||
const decodedSignedOrders = decodedOrders.map(order => ({
|
||||
...order,
|
||||
signature: constants.NULL_BYTES,
|
||||
@@ -186,11 +190,11 @@ blockchainTests.resets('Mixins tests', env => {
|
||||
expect(orders).to.deep.eq(decodedSignedOrders);
|
||||
});
|
||||
}
|
||||
for (const fnName of exchangeConstants.MATCH_ORDER_FN_NAMES) {
|
||||
for (const fnName of constants.MATCH_ORDER_FN_NAMES) {
|
||||
it(`should correctly decode the orders for ${fnName} data`, async () => {
|
||||
const orders = [defaultOrder, defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const decodedOrders = await mixins.decodeOrdersFromFillData.callAsync(data);
|
||||
const decodedOrders = await mixins.decodeOrdersFromFillData(data).callAsync();
|
||||
const decodedSignedOrders = decodedOrders.map(order => ({
|
||||
...order,
|
||||
signature: constants.NULL_BYTES,
|
||||
@@ -200,18 +204,18 @@ blockchainTests.resets('Mixins tests', env => {
|
||||
expect(orders).to.deep.eq(decodedSignedOrders);
|
||||
});
|
||||
}
|
||||
for (const fnName of exchangeConstants.CANCEL_ORDER_FN_NAMES) {
|
||||
for (const fnName of constants.CANCEL_ORDER_FN_NAMES) {
|
||||
it(`should correctly decode the orders for ${fnName} data`, async () => {
|
||||
const orders = [defaultOrder, defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const decodedOrders = await mixins.decodeOrdersFromFillData.callAsync(data);
|
||||
const decodedOrders = await mixins.decodeOrdersFromFillData(data).callAsync();
|
||||
const emptyArray: any[] = [];
|
||||
expect(emptyArray).to.deep.eq(decodedOrders);
|
||||
});
|
||||
}
|
||||
it('should decode an empty array for invalid data', async () => {
|
||||
const data = '0x0123456789';
|
||||
const decodedOrders = await mixins.decodeOrdersFromFillData.callAsync(data);
|
||||
const decodedOrders = await mixins.decodeOrdersFromFillData(data).callAsync();
|
||||
const emptyArray: any[] = [];
|
||||
expect(emptyArray).to.deep.eq(decodedOrders);
|
||||
});
|
||||
@@ -222,24 +226,22 @@ blockchainTests.resets('Mixins tests', env => {
|
||||
new BigNumber(3), // the length of data
|
||||
new BigNumber(4),
|
||||
);
|
||||
return expect(mixins.decodeOrdersFromFillData.callAsync(data)).to.revertWith(expectedError);
|
||||
return expect(mixins.decodeOrdersFromFillData(data).callAsync()).to.revertWith(expectedError);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Single order approvals', () => {
|
||||
for (const fnName of exchangeConstants.SINGLE_FILL_FN_NAMES) {
|
||||
for (const fnName of constants.SINGLE_FILL_FN_NAMES) {
|
||||
it(`Should be successful: function=${fnName}, caller=tx_signer, senderAddress=[verifier], approval_sig=[approver1]`, async () => {
|
||||
const orders = [defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
const approval = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approval.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
await mixins
|
||||
.assertValidCoordinatorApprovals(transaction, transactionSignerAddress, transaction.signature, [
|
||||
approval.signature,
|
||||
])
|
||||
.callAsync({ from: transactionSignerAddress });
|
||||
});
|
||||
it(`Should be successful: function=${fnName}, caller=tx_signer, senderAddress=[null], approval_sig=[approver1]`, async () => {
|
||||
const order = {
|
||||
@@ -250,72 +252,58 @@ blockchainTests.resets('Mixins tests', env => {
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
const approval = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approval.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
await mixins
|
||||
.assertValidCoordinatorApprovals(transaction, transactionSignerAddress, transaction.signature, [
|
||||
approval.signature,
|
||||
])
|
||||
.callAsync({ from: transactionSignerAddress });
|
||||
});
|
||||
it(`Should be successful: function=${fnName}, caller=approver1, senderAddress=[verifier], approval_sig=[]`, async () => {
|
||||
const orders = [defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
approvalSignerAddress1,
|
||||
transaction.signature,
|
||||
[],
|
||||
{
|
||||
await mixins
|
||||
.assertValidCoordinatorApprovals(transaction, approvalSignerAddress1, transaction.signature, [])
|
||||
.callAsync({
|
||||
from: approvalSignerAddress1,
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
it(`Should be successful: function=${fnName}, caller=approver1, senderAddress=[verifier], approval_sig=[approver1]`, async () => {
|
||||
const orders = [defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
const approval = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
approvalSignerAddress1,
|
||||
transaction.signature,
|
||||
[approval.signature],
|
||||
{ from: approvalSignerAddress1 },
|
||||
);
|
||||
await mixins
|
||||
.assertValidCoordinatorApprovals(transaction, approvalSignerAddress1, transaction.signature, [
|
||||
approval.signature,
|
||||
])
|
||||
.callAsync({ from: approvalSignerAddress1 });
|
||||
});
|
||||
it(`Should be successful: function=${fnName}, caller=approver1, senderAddress=[verifier], approval_sig=[]`, async () => {
|
||||
const orders = [defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
approvalSignerAddress1,
|
||||
transaction.signature,
|
||||
[],
|
||||
{
|
||||
await mixins
|
||||
.assertValidCoordinatorApprovals(transaction, approvalSignerAddress1, transaction.signature, [])
|
||||
.callAsync({
|
||||
from: approvalSignerAddress1,
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
it(`Should revert: function=${fnName}, caller=tx_signer, senderAddress=[verifier], approval_sig=[invalid]`, async () => {
|
||||
const orders = [defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
const approval = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
|
||||
const signature = hexConcat(
|
||||
hexSlice(approval.signature, 0, 2),
|
||||
const signature = hexUtils.concat(
|
||||
hexUtils.slice(approval.signature, 0, 2),
|
||||
'0xFFFFFFFF',
|
||||
hexSlice(approval.signature, 6),
|
||||
);
|
||||
const tx = mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[signature],
|
||||
{ from: transactionSignerAddress },
|
||||
hexUtils.slice(approval.signature, 6),
|
||||
);
|
||||
const tx = mixins
|
||||
.assertValidCoordinatorApprovals(transaction, transactionSignerAddress, transaction.signature, [
|
||||
signature,
|
||||
])
|
||||
.callAsync({ from: transactionSignerAddress });
|
||||
|
||||
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||
expect(tx).to.revertWith(
|
||||
@@ -328,35 +316,31 @@ blockchainTests.resets('Mixins tests', env => {
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
const approval = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
|
||||
|
||||
const tx = mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approval.signature],
|
||||
{ from: approvalSignerAddress2 },
|
||||
);
|
||||
const tx = mixins
|
||||
.assertValidCoordinatorApprovals(transaction, transactionSignerAddress, transaction.signature, [
|
||||
approval.signature,
|
||||
])
|
||||
.callAsync({ from: approvalSignerAddress2 });
|
||||
expect(tx).to.revertWith(new CoordinatorRevertErrors.InvalidOriginError(transactionSignerAddress));
|
||||
});
|
||||
}
|
||||
});
|
||||
describe('Batch order approvals', () => {
|
||||
for (const fnName of [
|
||||
...exchangeConstants.BATCH_FILL_FN_NAMES,
|
||||
...exchangeConstants.MARKET_FILL_FN_NAMES,
|
||||
...exchangeConstants.MATCH_ORDER_FN_NAMES,
|
||||
...constants.BATCH_FILL_FN_NAMES,
|
||||
...constants.MARKET_FILL_FN_NAMES,
|
||||
...constants.MATCH_ORDER_FN_NAMES,
|
||||
]) {
|
||||
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver1], approval_sig=[approver1]`, async () => {
|
||||
const orders = [defaultOrder, defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
const approval = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approval.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
await mixins
|
||||
.assertValidCoordinatorApprovals(transaction, transactionSignerAddress, transaction.signature, [
|
||||
approval.signature,
|
||||
])
|
||||
.callAsync({ from: transactionSignerAddress });
|
||||
});
|
||||
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[null,null], feeRecipient=[approver1,approver1], approval_sig=[approver1]`, async () => {
|
||||
const orders = [defaultOrder, defaultOrder].map(order => ({
|
||||
@@ -366,13 +350,11 @@ blockchainTests.resets('Mixins tests', env => {
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
const approval = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approval.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
await mixins
|
||||
.assertValidCoordinatorApprovals(transaction, transactionSignerAddress, transaction.signature, [
|
||||
approval.signature,
|
||||
])
|
||||
.callAsync({ from: transactionSignerAddress });
|
||||
});
|
||||
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[null,null], feeRecipient=[approver1,approver1], approval_sig=[]`, async () => {
|
||||
const orders = [defaultOrder, defaultOrder].map(order => ({
|
||||
@@ -381,26 +363,20 @@ blockchainTests.resets('Mixins tests', env => {
|
||||
}));
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
await mixins
|
||||
.assertValidCoordinatorApprovals(transaction, transactionSignerAddress, transaction.signature, [])
|
||||
.callAsync({ from: transactionSignerAddress });
|
||||
});
|
||||
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[verifier,null], feeRecipient=[approver1,approver1], approval_sig=[approver1]`, async () => {
|
||||
const orders = [defaultOrder, { ...defaultOrder, senderAddress: constants.NULL_ADDRESS }];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
const approval = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approval.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
await mixins
|
||||
.assertValidCoordinatorApprovals(transaction, transactionSignerAddress, transaction.signature, [
|
||||
approval.signature,
|
||||
])
|
||||
.callAsync({ from: transactionSignerAddress });
|
||||
});
|
||||
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver2], approval_sig=[approver1,approver2]`, async () => {
|
||||
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
|
||||
@@ -408,25 +384,20 @@ blockchainTests.resets('Mixins tests', env => {
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
const approval1 = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
|
||||
const approval2 = approvalFactory2.newSignedApproval(transaction, transactionSignerAddress);
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approval1.signature, approval2.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
await mixins
|
||||
.assertValidCoordinatorApprovals(transaction, transactionSignerAddress, transaction.signature, [
|
||||
approval1.signature,
|
||||
approval2.signature,
|
||||
])
|
||||
.callAsync({ from: transactionSignerAddress });
|
||||
});
|
||||
it(`Should be successful: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver1], approval_sig=[]`, async () => {
|
||||
const orders = [defaultOrder, defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
approvalSignerAddress1,
|
||||
transaction.signature,
|
||||
[],
|
||||
{ from: approvalSignerAddress1 },
|
||||
);
|
||||
await mixins
|
||||
.assertValidCoordinatorApprovals(transaction, approvalSignerAddress1, transaction.signature, [])
|
||||
.callAsync({ from: approvalSignerAddress1 });
|
||||
});
|
||||
it(`Should revert: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver2], approval_sig=[approver2]`, async () => {
|
||||
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
|
||||
@@ -434,26 +405,20 @@ blockchainTests.resets('Mixins tests', env => {
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
const approval2 = approvalFactory2.newSignedApproval(transaction, transactionSignerAddress);
|
||||
|
||||
const tx = mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approval2.signature],
|
||||
{ from: approvalSignerAddress1 },
|
||||
);
|
||||
const tx = mixins
|
||||
.assertValidCoordinatorApprovals(transaction, transactionSignerAddress, transaction.signature, [
|
||||
approval2.signature,
|
||||
])
|
||||
.callAsync({ from: approvalSignerAddress1 });
|
||||
expect(tx).to.revertWith(new CoordinatorRevertErrors.InvalidOriginError(transactionSignerAddress));
|
||||
});
|
||||
it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver1], approval_sig=[]`, async () => {
|
||||
const orders = [defaultOrder, defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
const tx = mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
const tx = mixins
|
||||
.assertValidCoordinatorApprovals(transaction, transactionSignerAddress, transaction.signature, [])
|
||||
.callAsync({ from: transactionSignerAddress });
|
||||
|
||||
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||
expect(tx).to.revertWith(
|
||||
@@ -465,18 +430,16 @@ blockchainTests.resets('Mixins tests', env => {
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
const approval = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
|
||||
const signature = hexConcat(
|
||||
hexSlice(approval.signature, 0, 2),
|
||||
const signature = hexUtils.concat(
|
||||
hexUtils.slice(approval.signature, 0, 2),
|
||||
'0xFFFFFFFF',
|
||||
hexSlice(approval.signature, 6),
|
||||
);
|
||||
const tx = mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[signature],
|
||||
{ from: transactionSignerAddress },
|
||||
hexUtils.slice(approval.signature, 6),
|
||||
);
|
||||
const tx = mixins
|
||||
.assertValidCoordinatorApprovals(transaction, transactionSignerAddress, transaction.signature, [
|
||||
signature,
|
||||
])
|
||||
.callAsync({ from: transactionSignerAddress });
|
||||
|
||||
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||
expect(tx).to.revertWith(
|
||||
@@ -489,18 +452,17 @@ blockchainTests.resets('Mixins tests', env => {
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
const approval1 = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
|
||||
const approval2 = approvalFactory2.newSignedApproval(transaction, transactionSignerAddress);
|
||||
const approvalSignature2 = hexConcat(
|
||||
hexSlice(approval2.signature, 0, 2),
|
||||
const approvalSignature2 = hexUtils.concat(
|
||||
hexUtils.slice(approval2.signature, 0, 2),
|
||||
'0xFFFFFFFF',
|
||||
hexSlice(approval2.signature, 6),
|
||||
);
|
||||
const tx = mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approval1.signature, approvalSignature2],
|
||||
{ from: transactionSignerAddress },
|
||||
hexUtils.slice(approval2.signature, 6),
|
||||
);
|
||||
const tx = mixins
|
||||
.assertValidCoordinatorApprovals(transaction, transactionSignerAddress, transaction.signature, [
|
||||
approval1.signature,
|
||||
approvalSignature2,
|
||||
])
|
||||
.callAsync({ from: transactionSignerAddress });
|
||||
|
||||
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||
expect(tx).to.revertWith(
|
||||
@@ -512,18 +474,16 @@ blockchainTests.resets('Mixins tests', env => {
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
const approval2 = approvalFactory2.newSignedApproval(transaction, transactionSignerAddress);
|
||||
const approvalSignature2 = hexConcat(
|
||||
hexSlice(approval2.signature, 0, 2),
|
||||
const approvalSignature2 = hexUtils.concat(
|
||||
hexUtils.slice(approval2.signature, 0, 2),
|
||||
'0xFFFFFFFF',
|
||||
hexSlice(approval2.signature, 6),
|
||||
);
|
||||
const tx = mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
approvalSignerAddress1,
|
||||
transaction.signature,
|
||||
[approvalSignature2],
|
||||
{ from: approvalSignerAddress1 },
|
||||
hexUtils.slice(approval2.signature, 6),
|
||||
);
|
||||
const tx = mixins
|
||||
.assertValidCoordinatorApprovals(transaction, approvalSignerAddress1, transaction.signature, [
|
||||
approvalSignature2,
|
||||
])
|
||||
.callAsync({ from: approvalSignerAddress1 });
|
||||
|
||||
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||
expect(tx).to.revertWith(
|
||||
@@ -536,13 +496,11 @@ blockchainTests.resets('Mixins tests', env => {
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
const approval1 = approvalFactory1.newSignedApproval(transaction, transactionSignerAddress);
|
||||
|
||||
const tx = mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approval1.signature],
|
||||
{ from: approvalSignerAddress2 },
|
||||
);
|
||||
const tx = mixins
|
||||
.assertValidCoordinatorApprovals(transaction, transactionSignerAddress, transaction.signature, [
|
||||
approval1.signature,
|
||||
])
|
||||
.callAsync({ from: approvalSignerAddress2 });
|
||||
expect(tx).to.revertWith(new CoordinatorRevertErrors.InvalidOriginError(transactionSignerAddress));
|
||||
});
|
||||
}
|
||||
@@ -552,36 +510,24 @@ blockchainTests.resets('Mixins tests', env => {
|
||||
const orders = [defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.CancelOrder, orders);
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
await mixins
|
||||
.assertValidCoordinatorApprovals(transaction, transactionSignerAddress, transaction.signature, [])
|
||||
.callAsync({ from: transactionSignerAddress });
|
||||
});
|
||||
it('should allow the tx signer to call `batchCancelOrders` without approval', async () => {
|
||||
const orders = [defaultOrder, defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.BatchCancelOrders, orders);
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
await mixins
|
||||
.assertValidCoordinatorApprovals(transaction, transactionSignerAddress, transaction.signature, [])
|
||||
.callAsync({ from: transactionSignerAddress });
|
||||
});
|
||||
it('should allow the tx signer to call `cancelOrdersUpTo` without approval', async () => {
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.CancelOrdersUpTo);
|
||||
const transaction = await transactionFactory.newSignedTransactionAsync({ data });
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
await mixins
|
||||
.assertValidCoordinatorApprovals(transaction, transactionSignerAddress, transaction.signature, [])
|
||||
.callAsync({ from: transactionSignerAddress });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -1,3 +0,0 @@
|
||||
export { hashUtils } from './hash_utils';
|
||||
export { ApprovalFactory } from './approval_factory';
|
||||
export * from './types';
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user