Compare commits
760 Commits
@0x/connec
...
@0x/react-
Author | SHA1 | Date | |
---|---|---|---|
|
e853555165 | ||
|
2ce09d73ac | ||
|
14f48a5f4f | ||
|
8da4e4a830 | ||
|
88ae8311c8 | ||
|
eabf6a466a | ||
|
989f691d06 | ||
|
52a3dae7cb | ||
|
8a6dfacf71 | ||
|
19ca6c13ad | ||
|
9196f122dd | ||
|
55e3d81c58 | ||
|
80caa16718 | ||
|
ddc5aaacdb | ||
|
38825865cc | ||
|
157b2efa1d | ||
|
212a2d229a | ||
|
ccb89fb26a | ||
|
3a0b0c0973 | ||
|
c159ed9ebb | ||
|
e37dbcc273 | ||
|
841e660b1f | ||
|
391aba5f37 | ||
|
f5d30f4a07 | ||
|
230cf7dfb7 | ||
|
8734b70f83 | ||
|
ff9a2b2d9a | ||
|
a41dab2922 | ||
|
f4504106a1 | ||
|
1a5b8041c7 | ||
|
1e2170f8bb | ||
|
0a4a973a7f | ||
|
67137cadac | ||
|
ead4afa06b | ||
|
e39ef95191 | ||
|
3d7585671f | ||
|
44806106db | ||
|
6a8197a4e8 | ||
|
4d4b4e0f2b | ||
|
82da33d742 | ||
|
1c6130a492 | ||
|
dabe5e939f | ||
|
b4ac6d3439 | ||
|
d0ea74e180 | ||
|
a5f77f3964 | ||
|
9401bb53e8 | ||
|
347c6d02cf | ||
|
64a0080616 | ||
|
288a7d4cea | ||
|
1207b68f57 | ||
|
2b82187fe0 | ||
|
3e2dbfc83c | ||
|
6691f490bc | ||
|
a7db900e51 | ||
|
cfa2a90dae | ||
|
feebb45e9d | ||
|
13a2f3a330 | ||
|
1da8801084 | ||
|
7a7b17c4f3 | ||
|
2afb06de13 | ||
|
ac771e2865 | ||
|
4b09204936 | ||
|
e4a5518a7c | ||
|
e83507ba98 | ||
|
6e5b77edb2 | ||
|
98167da8fa | ||
|
11d5fec59b | ||
|
ed02f4ca88 | ||
|
e7c612971d | ||
|
242a2e21b5 | ||
|
92a1e5413b | ||
|
e1ab9aa690 | ||
|
222f7e6fd4 | ||
|
59001f827b | ||
|
549bfe98f1 | ||
|
64e3b6f5ee | ||
|
2fdc0426ff | ||
|
e6c70fda66 | ||
|
50da1354f3 | ||
|
e503cacf57 | ||
|
6a1e0edc78 | ||
|
048f5c2771 | ||
|
d9378e9a8f | ||
|
f77aaaf2e0 | ||
|
380835b151 | ||
|
857d91df85 | ||
|
cd9a6b0de7 | ||
|
86333f4928 | ||
|
d1975bd5dd | ||
|
08b9f27eac | ||
|
5b2cf8a776 | ||
|
7394106880 | ||
|
9134a7ae1e | ||
|
c617e9f483 | ||
|
545fcef716 | ||
|
e20fa3a1bf | ||
|
9d01b47d5b | ||
|
fa4c34df41 | ||
|
9f8ab4d626 | ||
|
1971c9cecd | ||
|
82f730831d | ||
|
035167e5b7 | ||
|
323fb0a965 | ||
|
45aacf122f | ||
|
5dcf7919fc | ||
|
f15560bd89 | ||
|
bf3ae730d6 | ||
|
92ce258bb4 | ||
|
5810e7df82 | ||
|
f2cbf4a561 | ||
|
0c8bb2e675 | ||
|
f51c4f9617 | ||
|
5345f7c983 | ||
|
7d9e43b2e1 | ||
|
570c1e1809 | ||
|
720d335b09 | ||
|
f0ecda1a48 | ||
|
4ed111a7d4 | ||
|
fc257523c7 | ||
|
9775f8d83c | ||
|
01a1b19556 | ||
|
bd228034fd | ||
|
2672a5c59f | ||
|
5f383430eb | ||
|
66ec3e9f4d | ||
|
c269c427a6 | ||
|
212855ebd8 | ||
|
8f78945a73 | ||
|
86d50cf597 | ||
|
8986d506a5 | ||
|
3d2c945c35 | ||
|
8206a3969f | ||
|
43afe67593 | ||
|
ecf939f8c8 | ||
|
f0c4ccfa1e | ||
|
718d48b7d5 | ||
|
d5e88677ae | ||
|
b1cfdc7a6a | ||
|
5c5f815b20 | ||
|
d9edb9675f | ||
|
be6fce5a89 | ||
|
12afeb30ae | ||
|
23df406ff0 | ||
|
c9ecef4fc3 | ||
|
62f0a867a8 | ||
|
f68b8d82e0 | ||
|
6b7cb13e9a | ||
|
613af6013a | ||
|
0db56a781e | ||
|
fe7674b184 | ||
|
44a6fe7310 | ||
|
46690f0c35 | ||
|
cfc4e345cc | ||
|
c68278d824 | ||
|
09600a71cd | ||
|
d66ba70f5e | ||
|
e7c4120d24 | ||
|
1212e534a8 | ||
|
899d0f1e42 | ||
|
dde4ed3754 | ||
|
b596e02752 | ||
|
d5afe696da | ||
|
976be66dee | ||
|
98c8a6387a | ||
|
be1b636e30 | ||
|
8bae0b81aa | ||
|
7ee19e1306 | ||
|
626d0dfa93 | ||
|
8453c616a5 | ||
|
56bc2944d0 | ||
|
b2cf701e30 | ||
|
9a3b29acb7 | ||
|
85b217f167 | ||
|
a7700d6c22 | ||
|
1e274518dd | ||
|
f4f8927c79 | ||
|
60b7890f16 | ||
|
4d46290ef6 | ||
|
00f7e2cfc3 | ||
|
e742708261 | ||
|
8c9de31c5c | ||
|
2bf992f0ac | ||
|
52a01bcc11 | ||
|
f250e03c40 | ||
|
75acbfb042 | ||
|
740fd6f5d9 | ||
|
c58306cd49 | ||
|
985696631c | ||
|
d8a3d7f920 | ||
|
b6b96f0eee | ||
|
3f037ef3cc | ||
|
5c2d3fb3da | ||
|
84d38ea878 | ||
|
6226aa0b23 | ||
|
72af35834b | ||
|
3019e3817f | ||
|
da9e90faf0 | ||
|
e737395c2b | ||
|
5a33167594 | ||
|
dfb7e50948 | ||
|
b43c9f075c | ||
|
f42ce6adb5 | ||
|
bf533bbdbb | ||
|
9328729c4c | ||
|
2e23a044ca | ||
|
691a3a1e72 | ||
|
fc40f9634b | ||
|
4969441f21 | ||
|
d04d50794e | ||
|
5e1fbe34a9 | ||
|
2b75829a0c | ||
|
3b3fd0a3a2 | ||
|
1e4b61008a | ||
|
ffb71e9fd5 | ||
|
c1aeb4bde6 | ||
|
66bca5cd81 | ||
|
b68df3a067 | ||
|
066f3bb646 | ||
|
661c334928 | ||
|
a1895d4157 | ||
|
1e7efe026a | ||
|
02e7da979a | ||
|
bd2839110b | ||
|
1bcb2697e7 | ||
|
fdb9a664f5 | ||
|
8099c334c7 | ||
|
344d28ab78 | ||
|
5e41168305 | ||
|
dd97669cdd | ||
|
af75581659 | ||
|
3c08f5b86a | ||
|
7809cad6cb | ||
|
3e59029966 | ||
|
24249bcb4d | ||
|
543011c3de | ||
|
4d9f2586d9 | ||
|
7f94ebe362 | ||
|
c9bf1eda54 | ||
|
2cd4d9004c | ||
|
35cd0319d4 | ||
|
899030e966 | ||
|
15e3944d23 | ||
|
f1fbaedb0f | ||
|
cf04062a19 | ||
|
2e922bf7db | ||
|
741a731ecb | ||
|
91bff1976c | ||
|
fad500042a | ||
|
bdb2a01385 | ||
|
f372cd2d13 | ||
|
a435da910f | ||
|
f1d96d9673 | ||
|
45226ee1f6 | ||
|
03b8d29740 | ||
|
80a4af249f | ||
|
900d444946 | ||
|
7ca9393d2d | ||
|
b22d7bb310 | ||
|
c742cdfe5c | ||
|
2d3fe02cd7 | ||
|
7d61cb6bac | ||
|
a804433a7a | ||
|
11082320c2 | ||
|
9f9797b123 | ||
|
05d34616b7 | ||
|
c7f474ada1 | ||
|
c7328a63d0 | ||
|
db818794d5 | ||
|
b4b34e4890 | ||
|
eec016380d | ||
|
dcab272be0 | ||
|
418d033a5c | ||
|
e7a7713b80 | ||
|
52a580d845 | ||
|
ea42a0b5f5 | ||
|
a0b63da9a6 | ||
|
8d9932fc42 | ||
|
54be45bedc | ||
|
3c9d2a562b | ||
|
654bd5175c | ||
|
9db660cd3f | ||
|
9aeadff9bd | ||
|
cd631b789b | ||
|
4d98408aaf | ||
|
490aafba4e | ||
|
3cd31d49bb | ||
|
6dd78b8d35 | ||
|
bd51c34098 | ||
|
0e758fadee | ||
|
a67659ff8e | ||
|
d1203f90da | ||
|
c5f6ebee18 | ||
|
41cc5234c4 | ||
|
c2d3e5f052 | ||
|
f944b95e32 | ||
|
0a5b919cd9 | ||
|
b5f85c11fe | ||
|
5d44b5e913 | ||
|
dd1961d86c | ||
|
f3539bf448 | ||
|
d831e559f0 | ||
|
cc079660f3 | ||
|
4460dac697 | ||
|
31190f921c | ||
|
22515d8dce | ||
|
a099d970a4 | ||
|
95bb2c5504 | ||
|
0b1262cc4d | ||
|
f9244f6f7e | ||
|
9dd4ea1584 | ||
|
9d085cdb61 | ||
|
f00a2dbc59 | ||
|
20862d47ab | ||
|
cfa1bf36de | ||
|
c362b33b5a | ||
|
222a151eff | ||
|
d6115cff25 | ||
|
6459522617 | ||
|
16e1f0eea1 | ||
|
0564ac1530 | ||
|
b896f82282 | ||
|
d737d419d9 | ||
|
0de2b6983b | ||
|
25628c34ee | ||
|
54fdccd397 | ||
|
27af4a988d | ||
|
fddad131a2 | ||
|
4d493eeebd | ||
|
14c1495a1a | ||
|
49c029ddb3 | ||
|
ddbe142c4c | ||
|
4710b40e45 | ||
|
be0b296769 | ||
|
0a0ee67740 | ||
|
943d648225 | ||
|
6d835f5cc1 | ||
|
a59cd67acf | ||
|
bbc06be091 | ||
|
d303e9f347 | ||
|
85ee923d89 | ||
|
fe6ba20ff5 | ||
|
18acf50b12 | ||
|
509fabb61c | ||
|
cc3378b4cd | ||
|
8d3ccf333d | ||
|
520919b165 | ||
|
600b86dd31 | ||
|
9c3fdd2584 | ||
|
73974cd90f | ||
|
8a60b3b402 | ||
|
59ed5ae6b7 | ||
|
c687385974 | ||
|
2f72e15ea7 | ||
|
7ea99baeb2 | ||
|
14f3d20772 | ||
|
72f51df25f | ||
|
a94b58e304 | ||
|
d327fabf9c | ||
|
9e02888c74 | ||
|
98c9a847f3 | ||
|
a159f4c9d6 | ||
|
b197731ed2 | ||
|
028f54fdf0 | ||
|
de62a0f8ed | ||
|
a0fcc50a5f | ||
|
4f186e843c | ||
|
df64c20587 | ||
|
c78a602990 | ||
|
0acec57ba9 | ||
|
7423028fea | ||
|
49d951b7be | ||
|
a554c9518d | ||
|
f3c7ba445e | ||
|
3abff41385 | ||
|
6c36832f0e | ||
|
310c18707b | ||
|
422a4a5578 | ||
|
a995b2e1ae | ||
|
90ad681a9e | ||
|
2ff5c39712 | ||
|
ded849fd6d | ||
|
02f7064953 | ||
|
1ddadfce1e | ||
|
7720e5007c | ||
|
ccfd021796 | ||
|
fdcad84cee | ||
|
ad3e3b8421 | ||
|
f67b4f8902 | ||
|
3c0d7319ba | ||
|
7f0aab6ec6 | ||
|
669578a926 | ||
|
fa1db64a8e | ||
|
7cf60fa927 | ||
|
75a0d3e494 | ||
|
a98218ae22 | ||
|
10952e9dc4 | ||
|
b36e23471b | ||
|
f45bf1c95b | ||
|
d4db2587aa | ||
|
62e6336a7d | ||
|
2d8acd4711 | ||
|
d7d95be042 | ||
|
e3c97f0681 | ||
|
1a4fa015b9 | ||
|
63dacfaac5 | ||
|
f7263ac2c6 | ||
|
c7d7e1f0e3 | ||
|
9c54b615f5 | ||
|
69fe1aa981 | ||
|
bfb3d19e3b | ||
|
0b4e62a63e | ||
|
5cdbc03e71 | ||
|
16cd6dd25d | ||
|
fc71e7f99f | ||
|
e740380731 | ||
|
f9921d2c91 | ||
|
d8bfc92cc5 | ||
|
1f2214f891 | ||
|
50835e317f | ||
|
6c8d4dcc1e | ||
|
85a7efbd61 | ||
|
6b5ef10467 | ||
|
0bf46bfcb5 | ||
|
8b8dc7ac78 | ||
|
a017122c44 | ||
|
86a9a892d2 | ||
|
f31a141d78 | ||
|
fa67997424 | ||
|
a5f06c577d | ||
|
e575672877 | ||
|
a34d5b29e8 | ||
|
91ec65da1b | ||
|
ee8d40a66e | ||
|
38ac2e80ed | ||
|
8b70762e34 | ||
|
18c613a611 | ||
|
957f8c56a1 | ||
|
b16446877e | ||
|
9164d58dc7 | ||
|
0beb2f9d3c | ||
|
ccdd66052a | ||
|
518fd814b6 | ||
|
f49bb78dba | ||
|
a54aa77d28 | ||
|
f85e443c9c | ||
|
399e004e7f | ||
|
3099ba71eb | ||
|
951fcf384c | ||
|
c750368a3e | ||
|
3149d86855 | ||
|
3aaf21e34e | ||
|
519c375a42 | ||
|
3ed2c732bd | ||
|
0aa5550d0f | ||
|
1bc8dd83d3 | ||
|
c642cd6fed | ||
|
f72918362d | ||
|
28c4ca73ab | ||
|
a256494ec8 | ||
|
5fd359a64f | ||
|
e043735362 | ||
|
7010b1adb9 | ||
|
17ff262729 | ||
|
1c18838cd8 | ||
|
55b87ae78d | ||
|
e446e902f3 | ||
|
095b52e0b2 | ||
|
4f25ff6a50 | ||
|
6242e0aeec | ||
|
fde9fc9dd4 | ||
|
c481e42673 | ||
|
5e228d7232 | ||
|
6b8e40fdc9 | ||
|
bca44bf9e3 | ||
|
08e49dcf2e | ||
|
0f45409b4d | ||
|
5469d3ec13 | ||
|
d12f8410b9 | ||
|
704c52d229 | ||
|
7dcdda14f5 | ||
|
0f59256ca7 | ||
|
ddee04e98c | ||
|
50f5002b71 | ||
|
e866add4b0 | ||
|
39b93b88c5 | ||
|
deb7e95567 | ||
|
91500501ce | ||
|
6a1caeb9a1 | ||
|
cee7803c37 | ||
|
4e8d0ac7cb | ||
|
47bbcb9935 | ||
|
c0288c5f26 | ||
|
18c2013625 | ||
|
548089888d | ||
|
180c65cfeb | ||
|
6a28e41bc8 | ||
|
f9ef942a98 | ||
|
8272c7a74e | ||
|
4e745489db | ||
|
fc2625a0c0 | ||
|
d0e43ebaf1 | ||
|
879805e316 | ||
|
6845d2e0ef | ||
|
542d1c1c41 | ||
|
31568e7abb | ||
|
4b2488b124 | ||
|
d4c37ecfa3 | ||
|
3f9fd7c060 | ||
|
6ed1412bdd | ||
|
78b9a45158 | ||
|
b8925baa88 | ||
|
87fd3f2a82 | ||
|
0490ef5900 | ||
|
8af164dbd2 | ||
|
c994afbf3c | ||
|
fa36c91bd6 | ||
|
843caf86fb | ||
|
5b8f294aaf | ||
|
44eef5b0e0 | ||
|
aad75840d4 | ||
|
31faef7030 | ||
|
941877a05a | ||
|
4fd4c1e8e1 | ||
|
7cd27cd9c8 | ||
|
54dbef2b4f | ||
|
05424c9f33 | ||
|
346c6fc590 | ||
|
7ed3afe9f0 | ||
|
d29ed6cd49 | ||
|
8c3abf3473 | ||
|
384114d3c7 | ||
|
e8be5a0a8f | ||
|
98c6fa10e6 | ||
|
30d0bdec07 | ||
|
7024f29865 | ||
|
29ebed9514 | ||
|
c0260bc44a | ||
|
6992bff0e0 | ||
|
6957e6e8f7 | ||
|
31dbca7efc | ||
|
82b6dad1ba | ||
|
5063c17e6b | ||
|
3fff3d9c60 | ||
|
2fbe0aed32 | ||
|
3bdc1802cb | ||
|
2675833b0d | ||
|
939a5b477a | ||
|
dc57e7a5b3 | ||
|
a401b8f475 | ||
|
b008fabdac | ||
|
caf286b8cb | ||
|
aea278c022 | ||
|
4e20fe1602 | ||
|
849ca58228 | ||
|
9991fca2e5 | ||
|
9c7bdcfeef | ||
|
b5463d522b | ||
|
af0d830103 | ||
|
d4187dffa3 | ||
|
a7f06f2be5 | ||
|
7ff7d1a185 | ||
|
d3ab612a89 | ||
|
e3bc80e027 | ||
|
a8720806f1 | ||
|
5aac4c2e5d | ||
|
6fa645aab9 | ||
|
842ea4645b | ||
|
e1cfbcd4f6 | ||
|
6f17ff55fa | ||
|
d88af4dfa6 | ||
|
bb0ba21e92 | ||
|
f0753c8e58 | ||
|
4c55004b08 | ||
|
ae24119c09 | ||
|
b8753d8f20 | ||
|
bab368b956 | ||
|
22bc1fb21e | ||
|
63ba764de8 | ||
|
6a7530d741 | ||
|
eb9bf7c4f9 | ||
|
b6571d0ca3 | ||
|
9207d1c680 | ||
|
f783c9bb25 | ||
|
df5786deda | ||
|
de971e6c46 | ||
|
2a6ed0c96e | ||
|
9ec380777a | ||
|
8916d0d367 | ||
|
1e5648111e | ||
|
ae51cfe8b9 | ||
|
05ef250ab4 | ||
|
38e4871f32 | ||
|
a86ba7af2e | ||
|
5704afc54c | ||
|
55c4fc9aca | ||
|
888c17353b | ||
|
054c0e91a3 | ||
|
4dfb610eba | ||
|
008eb8dd8b | ||
|
2882c4bb89 | ||
|
4473851f5b | ||
|
15d9e2d3d5 | ||
|
d5d9df383e | ||
|
243a04b756 | ||
|
b77dcbd39b | ||
|
ad5d4bdfc5 | ||
|
54c17b0068 | ||
|
616907eff8 | ||
|
afaabb3673 | ||
|
1c1f625352 | ||
|
d3f45d2148 | ||
|
7326dbd108 | ||
|
4bf311a282 | ||
|
7f5a3f12ca | ||
|
6a9b71466d | ||
|
70e550a25f | ||
|
1328882ab6 | ||
|
b378a0608d | ||
|
e28c6d6f9c | ||
|
f57f29e426 | ||
|
f2857452e3 | ||
|
af04c294b9 | ||
|
65c8630534 | ||
|
095882d016 | ||
|
1693506f80 | ||
|
667c22169f | ||
|
c53edf6bd9 | ||
|
b601220845 | ||
|
e77a608f45 | ||
|
2822e77716 | ||
|
98227928af | ||
|
430afbdc80 | ||
|
dfdb48ce7d | ||
|
257d1b2b52 | ||
|
07200437b6 | ||
|
ebfa00d555 | ||
|
421f555d57 | ||
|
4b60d941cc | ||
|
60d24ada62 | ||
|
a1c121e2fe | ||
|
b3775a3ca5 | ||
|
5d36c97a46 | ||
|
4bdd412c15 | ||
|
46e3bcd6b9 | ||
|
68a85ddf90 | ||
|
6eba9273cb | ||
|
9de151cc14 | ||
|
b8c9a5dd1f | ||
|
4dfb3507c4 | ||
|
0cac2d407b | ||
|
b3106cd932 | ||
|
c4a467fa96 | ||
|
cf7afbe7f6 | ||
|
a8d58aeac4 | ||
|
28209e9413 | ||
|
68ed56f2d9 | ||
|
0c03747807 | ||
|
dfe7ecbb5b | ||
|
3039d8edfa | ||
|
7c850cc082 | ||
|
0e07ee3d81 | ||
|
14b820f2c1 | ||
|
9366fa3b45 | ||
|
d356e9e65f | ||
|
230ebffd0e | ||
|
5eedc1edca | ||
|
fa6db9411b | ||
|
b88e42a52d | ||
|
8d1b27d130 | ||
|
1148d37102 | ||
|
88704ce417 | ||
|
9f69d2cb76 | ||
|
644f54bfba | ||
|
755c4da8cc | ||
|
483c77fba8 | ||
|
55fd71c5e1 | ||
|
1991bd437f | ||
|
22af796302 | ||
|
a70931ffbf | ||
|
2f7dd177c1 | ||
|
d35a053efd | ||
|
87cc1f9415 | ||
|
e7bb524362 | ||
|
9e03e1c742 | ||
|
6fbfcef1fa | ||
|
9471510086 | ||
|
56320468fd | ||
|
53a70bbffb | ||
|
4f2e547bd6 | ||
|
af82fa7b62 | ||
|
6bd1a5ab80 | ||
|
93568f6fd0 | ||
|
1a3bf81c19 | ||
|
2eeabe3998 | ||
|
b1d86a7a2d | ||
|
6cfc9ba47b | ||
|
350474540b | ||
|
1136e58de7 | ||
|
558ce4713c | ||
|
69c6f50dfb | ||
|
babe01321c | ||
|
ff61dc4391 | ||
|
4da6ede9d0 | ||
|
0b1bab873e | ||
|
6c60341ce8 | ||
|
e1c1878130 | ||
|
fea5a39740 | ||
|
9f2221a885 | ||
|
977cd2505e | ||
|
e3f85a7c0f | ||
|
d81339722e | ||
|
f5c09e02de | ||
|
5e102526ec | ||
|
3c2efd4b67 | ||
|
7bb93a7c32 | ||
|
ac8f4ae2f9 | ||
|
1ff49188b1 | ||
|
2c329023c2 | ||
|
4457a300bf | ||
|
1bff790628 | ||
|
0010ca3e03 | ||
|
9edb5dae88 | ||
|
85b49096dc | ||
|
a7eaa10220 | ||
|
5a46ce55b6 | ||
|
dbebbecab1 | ||
|
c7593e66bf | ||
|
d52dc69279 | ||
|
1c65fa212d | ||
|
be88eb00f8 | ||
|
65c60f5386 | ||
|
83a043e639 | ||
|
8175192f60 | ||
|
7d2c975d73 | ||
|
e5153737d8 | ||
|
88766a02c7 | ||
|
8162394797 | ||
|
fd001186dd | ||
|
128fef8838 | ||
|
1e25a0654a | ||
|
82b0ff6008 | ||
|
ca7c3630f9 | ||
|
260bb8218f | ||
|
a691de7d55 | ||
|
5674c484e2 | ||
|
9286dc284c | ||
|
624b71bd39 | ||
|
b7ea605a3b | ||
|
489a787de8 | ||
|
84150036ed | ||
|
619123720c | ||
|
cc98ff9fe6 | ||
|
39d159f38c | ||
|
ca8eb90cec | ||
|
f8e3e28d85 | ||
|
4d868489bd | ||
|
b06f09f6af | ||
|
bf00f5f945 | ||
|
6ceb6cc301 | ||
|
00a6afaa8e |
@@ -11,9 +11,14 @@ jobs:
|
||||
steps:
|
||||
- checkout
|
||||
- run: echo 'export PATH=$HOME/CIRCLE_PROJECT_REPONAME/node_modules/.bin:$PATH' >> $BASH_ENV
|
||||
- run:
|
||||
# HACK(albrow): Without this, yarn commands will sometimes
|
||||
# fail with a "permission denied" error.
|
||||
name: Set npm path
|
||||
command: npm set prefix=/home/circleci/npm && echo 'export PATH=$HOME/circleci/npm/bin:$PATH' >> /home/circleci/.bashrc
|
||||
- run:
|
||||
name: install-yarn
|
||||
command: sudo npm install --global yarn@1.9.4
|
||||
command: npm install --global yarn@1.9.4
|
||||
- run:
|
||||
name: yarn
|
||||
command: yarn --frozen-lockfile --ignore-engines install || yarn --frozen-lockfile --ignore-engines install
|
||||
@@ -24,6 +29,10 @@ jobs:
|
||||
key: repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo
|
||||
- save_cache:
|
||||
key: python-contract-wrappers-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/python-contract-wrappers/generated
|
||||
build-website:
|
||||
resource_class: medium+
|
||||
docker:
|
||||
@@ -42,16 +51,7 @@ jobs:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: yarn wsrun test:circleci @0x/contracts-multisig
|
||||
- run: yarn wsrun test:circleci @0x/contracts-utils
|
||||
- run: yarn wsrun test:circleci @0x/contracts-exchange-libs
|
||||
- run: yarn wsrun test:circleci @0x/contracts-erc20
|
||||
- run: yarn wsrun test:circleci @0x/contracts-erc721
|
||||
- run: yarn wsrun test:circleci @0x/contracts-extensions
|
||||
- run: yarn wsrun test:circleci @0x/contracts-asset-proxy
|
||||
- run: yarn wsrun test:circleci @0x/contracts-exchange
|
||||
- run: yarn wsrun test:circleci @0x/contracts-exchange-forwarder
|
||||
- run: yarn wsrun test:circleci @0x/contracts-tec
|
||||
- run: yarn wsrun test:circleci @0x/contracts-multisig @0x/contracts-utils @0x/contracts-exchange-libs @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-extensions @0x/contracts-asset-proxy @0x/contracts-exchange @0x/contracts-exchange-forwarder @0x/contracts-coordinator @0x/contracts-dev-utils
|
||||
test-contracts-geth:
|
||||
docker:
|
||||
- image: circleci/node:9-browsers
|
||||
@@ -63,16 +63,7 @@ jobs:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
# HACK(albrow): we need to sleep 10 seconds to ensure the devnet is
|
||||
# initialized
|
||||
- run: sleep 10 && TEST_PROVIDER=geth yarn wsrun test @0x/contracts-multisig
|
||||
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-utils
|
||||
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-exchange-libs
|
||||
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-erc20
|
||||
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-erc721
|
||||
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-extensions
|
||||
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-asset-proxy
|
||||
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-exchange
|
||||
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-exchange-forwarder
|
||||
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-tec
|
||||
- run: sleep 10 && TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-multisig @0x/contracts-utils @0x/contracts-exchange-libs @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-extensions @0x/contracts-asset-proxy @0x/contracts-exchange @0x/contracts-exchange-forwarder @0x/contracts-coordinator @0x/contracts-dev-utils
|
||||
test-publish:
|
||||
resource_class: medium+
|
||||
docker:
|
||||
@@ -93,20 +84,6 @@ jobs:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: yarn test:generate_docs:circleci
|
||||
test-pipeline:
|
||||
docker:
|
||||
- image: circleci/node:9
|
||||
- image: postgres:11-alpine
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: ZEROEX_DATA_PIPELINE_TEST_DB_URL='postgresql://postgres@localhost/postgres' yarn wsrun test:circleci @0x/pipeline
|
||||
- save_cache:
|
||||
key: coverage-pipeline-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/pipeline/coverage/lcov.info
|
||||
test-rest:
|
||||
docker:
|
||||
- image: circleci/node:9-browsers
|
||||
@@ -117,6 +94,8 @@ jobs:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: yarn wsrun test:circleci @0x/contracts-test-utils
|
||||
- run: yarn wsrun test:circleci @0x/abi-gen
|
||||
- run: yarn wsrun test:circleci @0x/asset-buyer
|
||||
- run: yarn wsrun test:circleci @0x/contract-artifacts
|
||||
- run: yarn wsrun test:circleci @0x/assert
|
||||
- run: yarn wsrun test:circleci @0x/base-contract
|
||||
- run: yarn wsrun test:circleci @0x/connect
|
||||
@@ -141,6 +120,10 @@ jobs:
|
||||
key: coverage-assert-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/assert/coverage/lcov.info
|
||||
- save_cache:
|
||||
key: coverage-asset-buyer-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/asset-buyer/coverage/lcov.info
|
||||
- save_cache:
|
||||
key: coverage-base-contract-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
@@ -199,21 +182,27 @@ jobs:
|
||||
- image: circleci/python
|
||||
- image: 0xorg/ganache-cli:2.2.2
|
||||
- image: 0xorg/launch-kit-ci
|
||||
command: |
|
||||
yarn start:ts -p 3000:3000
|
||||
environment:
|
||||
RPC_URL: http://localhost:8545
|
||||
NETWORK_ID: 50
|
||||
WHITELIST_ALL_TOKENS: True
|
||||
command: bash -c "until curl -sfd'{\"method\":\"net_listening\"}' http://localhost:8545 | grep true; do continue; done; forever ts/lib/index.js"
|
||||
steps:
|
||||
- checkout
|
||||
- run: sudo chown -R circleci:circleci /usr/local/bin
|
||||
- run: sudo chown -R circleci:circleci /usr/local/lib/python3.7
|
||||
- restore_cache:
|
||||
key: deps9-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
|
||||
key: installed-py-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
key: python-contract-wrappers-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run:
|
||||
command: |
|
||||
cd python-packages
|
||||
python -m ensurepip
|
||||
./pre_install
|
||||
./install
|
||||
- save_cache:
|
||||
key: deps9-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
|
||||
key: installed-py-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- '/usr/local/bin'
|
||||
- '/usr/local/lib/python3.7/site-packages'
|
||||
@@ -254,14 +243,14 @@ jobs:
|
||||
- run: sudo chown -R circleci:circleci /usr/local/bin
|
||||
- run: sudo chown -R circleci:circleci /usr/local/lib/python3.7
|
||||
- restore_cache:
|
||||
key: deps9-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
|
||||
key: installed-py-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run:
|
||||
command: |
|
||||
cd python-packages/order_utils
|
||||
python -m ensurepip
|
||||
python -m pip install .
|
||||
- save_cache:
|
||||
key: deps9-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
|
||||
key: installed-py-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- '/usr/local/bin'
|
||||
- '/usr/local/lib/python3.7/site-packages'
|
||||
@@ -282,11 +271,14 @@ jobs:
|
||||
- run: sudo chown -R circleci:circleci /usr/local/bin
|
||||
- run: sudo chown -R circleci:circleci /usr/local/lib/python3.7
|
||||
- restore_cache:
|
||||
key: deps9-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
|
||||
key: installed-py-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
key: python-contract-wrappers-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run:
|
||||
command: |
|
||||
python -m ensurepip
|
||||
cd python-packages
|
||||
./pre_install
|
||||
./install
|
||||
./lint
|
||||
static-tests:
|
||||
@@ -316,6 +308,9 @@ jobs:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-assert-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-asset-buyer-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-base-contract-{{ .Environment.CIRCLE_SHA1 }}
|
||||
@@ -388,12 +383,11 @@ workflows:
|
||||
- test-contracts-ganache:
|
||||
requires:
|
||||
- build
|
||||
- test-contracts-geth:
|
||||
requires:
|
||||
- build
|
||||
- test-pipeline:
|
||||
requires:
|
||||
- build
|
||||
# TODO(albrow): Tests always fail on Geth right now because our fork
|
||||
# is outdated. Uncomment once we have updated our Geth fork.
|
||||
# - test-contracts-geth:
|
||||
# requires:
|
||||
# - build
|
||||
- test-rest:
|
||||
requires:
|
||||
- build
|
||||
@@ -413,6 +407,8 @@ workflows:
|
||||
- static-tests-python:
|
||||
requires:
|
||||
- test-python
|
||||
- test-python
|
||||
- test-python:
|
||||
requires:
|
||||
- build
|
||||
# skip python tox run for now, as we don't yet have multiple test environments to support.
|
||||
#- test-rest-python
|
||||
|
6
.gitattributes
vendored
6
.gitattributes
vendored
@@ -1,7 +1,7 @@
|
||||
*.sol linguist-language=Solidity
|
||||
|
||||
# Automatically collapse generated files in GitHub.
|
||||
*.svg linguist-generated
|
||||
packages/contract-artifacts/artifacts/*json linguist-generated
|
||||
packages/abi-gen-wrappers/wrappers/*.ts liguist-generated
|
||||
*.svg linguist-generated=true
|
||||
packages/contract-artifacts/artifacts/*json linguist-generated=true
|
||||
packages/abi-gen-wrappers/src/generated-wrappers/*.ts liguist-generated=true
|
||||
|
||||
|
80
.github/autolabeler.yml
vendored
80
.github/autolabeler.yml
vendored
@@ -1,43 +1,43 @@
|
||||
python: ['python-packages']
|
||||
contracts: ['contracts']
|
||||
sol-doc: ['packages/sol-doc']
|
||||
sol-resolver: ['packages/sol-resolver']
|
||||
contracts-gen: ['packages/contracts-gen']
|
||||
sra-spec: ['packages/sra-spec']
|
||||
subproviders: ['packages/subproviders']
|
||||
contract-addresses: ['packages/contract-addresses']
|
||||
migrations: ['packages/migrations']
|
||||
web3-wrapper: ['packages/web3-wrapper']
|
||||
sol-compiler: ['packages/sol-compiler']
|
||||
types: ['packages/types']
|
||||
instant: ['packages/instant']
|
||||
abi-gen-templates: ['packages/abi-gen-templates']
|
||||
abi-gen: ['packages/abi-gen']
|
||||
website: ['packages/website']
|
||||
sol-coverage: ['packages/sol-coverage']
|
||||
sol-profiler: ['packages/sol-profiler']
|
||||
sol-trace: ['packages/sol-trace']
|
||||
sol-tracing-utils: ['packages/sol-tracing-utils']
|
||||
utils: ['packages/utils']
|
||||
tslint-config: ['packages/tslint-config']
|
||||
asset-buyer: ['packages/asset-buyer']
|
||||
order-watcher: ['packages/order-watcher']
|
||||
react-docs: ['packages/react-docs']
|
||||
order-utils: ['packages/order-utils']
|
||||
react-shared: ['packages/react-shared']
|
||||
assert: ['packages/assert']
|
||||
base-contract: ['packages/base-contract']
|
||||
typescript-typings: ['packages/typescript-typings']
|
||||
@0x/sol-doc: ['packages/sol-doc']
|
||||
@0x/sol-resolver: ['packages/sol-resolver']
|
||||
@0x/contracts-gen: ['packages/contracts-gen']
|
||||
@0x/sra-spec: ['packages/sra-spec']
|
||||
@0x/subproviders: ['packages/subproviders']
|
||||
@0x/contract-addresses: ['packages/contract-addresses']
|
||||
@0x/migrations: ['packages/migrations']
|
||||
@0x/web3-wrapper: ['packages/web3-wrapper']
|
||||
@0x/sol-compiler: ['packages/sol-compiler']
|
||||
@0x/types: ['packages/types']
|
||||
@0x/instant: ['packages/instant']
|
||||
@0x/abi-gen-templates: ['packages/abi-gen-templates']
|
||||
@0x/abi-gen: ['packages/abi-gen']
|
||||
@0x/website: ['packages/website']
|
||||
@0x/sol-coverage: ['packages/sol-coverage']
|
||||
@0x/sol-profiler: ['packages/sol-profiler']
|
||||
@0x/sol-trace: ['packages/sol-trace']
|
||||
@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/order-watcher: ['packages/order-watcher']
|
||||
@0x/react-docs: ['packages/react-docs']
|
||||
@0x/order-utils: ['packages/order-utils']
|
||||
@0x/react-shared: ['packages/react-shared']
|
||||
@0x/assert: ['packages/assert']
|
||||
@0x/base-contract: ['packages/base-contract']
|
||||
@0x/typescript-typings: ['packages/typescript-typings']
|
||||
0x.js: ['packages/0x.js']
|
||||
abi-gen-wrappers: ['packages/abi-gen-wrappers']
|
||||
metacoin: ['packages/metacoin']
|
||||
contract-artifacts: ['packages/contract-artifacts']
|
||||
dev-utils: ['packages/dev-utils']
|
||||
contract-wrappers: ['packages/contract-wrappers']
|
||||
json-schemas: ['packages/json-schemas']
|
||||
ethereum-types: ['ethereum-types']
|
||||
connect: ['packages/connect']
|
||||
fill-scenarios: ['packages/fill-scenarios']
|
||||
dev-tools-pages: ['packages/dev-tools-pages']
|
||||
testnet-faucets: ['packages/testnet-faucets']
|
||||
monorepo-scripts: ['packages/monorepo-scripts']
|
||||
@0x/abi-gen-wrappers: ['packages/abi-gen-wrappers']
|
||||
@0x/metacoin: ['packages/metacoin']
|
||||
@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/fill-scenarios: ['packages/fill-scenarios']
|
||||
@0x/dev-tools-pages: ['packages/dev-tools-pages']
|
||||
@0x/testnet-faucets: ['packages/testnet-faucets']
|
||||
@0x/monorepo-scripts: ['packages/monorepo-scripts']
|
||||
|
15
.gitignore
vendored
15
.gitignore
vendored
@@ -83,7 +83,7 @@ packages/react-docs/example/public/bundle*
|
||||
packages/testnet-faucets/server/
|
||||
|
||||
# generated contract artifacts/
|
||||
contracts/tec/generated-artifacts/
|
||||
contracts/coordinator/generated-artifacts/
|
||||
contracts/exchange/generated-artifacts/
|
||||
contracts/asset-proxy/generated-artifacts/
|
||||
contracts/multisig/generated-artifacts/
|
||||
@@ -91,14 +91,18 @@ contracts/utils/generated-artifacts/
|
||||
contracts/exchange-libs/generated-artifacts/
|
||||
contracts/erc20/generated-artifacts/
|
||||
contracts/erc721/generated-artifacts/
|
||||
contracts/erc1155/generated-artifacts/
|
||||
contracts/extensions/generated-artifacts/
|
||||
contracts/exchange-forwarder/generated-artifacts/
|
||||
contracts/dev-utils/generated-artifacts/
|
||||
packages/sol-tracing-utils/test/fixtures/artifacts/
|
||||
packages/metacoin/artifacts/
|
||||
python-packages/contract_artifacts/src/zero_ex/contract_artifacts/artifacts/
|
||||
|
||||
# generated contract wrappers
|
||||
packages/abi-gen-wrappers/wrappers
|
||||
contracts/tec/generated-wrappers/
|
||||
packages/abi-gen-wrappers/src/generated-wrappers/
|
||||
packages/python-contract-wrappers/generated/
|
||||
contracts/coordinator/generated-wrappers/
|
||||
contracts/exchange/generated-wrappers/
|
||||
contracts/asset-proxy/generated-wrappers/
|
||||
contracts/multisig/generated-wrappers/
|
||||
@@ -106,10 +110,15 @@ contracts/utils/generated-wrappers/
|
||||
contracts/exchange-libs/generated-wrappers/
|
||||
contracts/erc20/generated-wrappers/
|
||||
contracts/erc721/generated-wrappers/
|
||||
contracts/erc1155/generated-wrappers/
|
||||
contracts/extensions/generated-wrappers/
|
||||
contracts/exchange-forwarder/generated-wrappers/
|
||||
contracts/dev-utils/generated-wrappers/
|
||||
packages/metacoin/src/contract_wrappers
|
||||
|
||||
# cli test output
|
||||
packages/abi-gen/test-cli/output
|
||||
|
||||
# solc-bin in sol-compiler
|
||||
packages/sol-compiler/solc_bin/
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
lib
|
||||
.nyc_output
|
||||
/contracts/tec/generated-wrappers
|
||||
/contracts/tec/generated-artifacts
|
||||
/contracts/coordinator/generated-wrappers
|
||||
/contracts/coordinator/generated-artifacts
|
||||
/contracts/exchange/generated-wrappers
|
||||
/contracts/exchange/generated-artifacts
|
||||
/contracts/asset-proxy/generated-wrappers
|
||||
@@ -16,13 +16,15 @@ lib
|
||||
/contracts/erc20/generated-artifacts
|
||||
/contracts/erc721/generated-wrappers
|
||||
/contracts/erc721/generated-artifacts
|
||||
/contracts/erc1155/generated-wrappers
|
||||
/contracts/erc1155/generated-artifacts
|
||||
/contracts/extensions/generated-wrappers
|
||||
/contracts/extensions/generated-artifacts
|
||||
/contracts/exchange-forwarder/generated-wrappers
|
||||
/contracts/exchange-forwarder/generated-artifacts
|
||||
/packages/abi-gen-wrappers/src/generated-wrappers
|
||||
/packages/contract-artifacts/artifacts
|
||||
/python-packages/contract_artifacts/src/zero_ex/contract_artifacts/artifacts
|
||||
/contracts/dev-utils/generated-wrappers
|
||||
/contracts/dev-utils/generated-artifacts
|
||||
/packages/abi-gen/test-cli/output
|
||||
/packages/json-schemas/schemas
|
||||
/python-packages/json_schemas/src/zero_ex/json_schemas/schemas
|
||||
/packages/metacoin/src/contract_wrappers
|
||||
|
10
CODEOWNERS
10
CODEOWNERS
@@ -11,10 +11,10 @@ packages/website/ @BMillman19 @fragosti @fabioberger @steveklebanoff
|
||||
|
||||
# Dev tools & setup
|
||||
.circleci/ @LogvinovLeon
|
||||
packages/abi-gen/ @LogvinovLeon
|
||||
packages/base-contract/ @LogvinovLeon
|
||||
packages/abi-gen/ @feuGeneA
|
||||
packages/base-contract/ @xianny
|
||||
packages/connect/ @fragosti
|
||||
packages/abi-gen-templates/ @LogvinovLeon
|
||||
packages/abi-gen-templates/ @feuGeneA @xianny
|
||||
packages/contract-addresses/ @albrow
|
||||
packages/contract-artifacts/ @albrow
|
||||
packages/dev-utils/ @LogvinovLeon @fabioberger
|
||||
@@ -23,6 +23,7 @@ packages/ethereum-types/ @LogvinovLeon
|
||||
packages/metacoin/ @LogvinovLeon
|
||||
packages/monorepo-scripts/ @fabioberger
|
||||
packages/order-utils/ @fabioberger @LogvinovLeon
|
||||
packages/python-contract-wrappers/ @feuGeneA
|
||||
packages/sol-compiler/ @LogvinovLeon
|
||||
packages/sol-coverage/ @LogvinovLeon
|
||||
packages/sol-profiler/ @LogvinovLeon
|
||||
@@ -33,6 +34,7 @@ packages/subproviders/ @fabioberger @dekz
|
||||
packages/verdaccio/ @albrow
|
||||
packages/web3-wrapper/ @LogvinovLeon @fabioberger
|
||||
python-packages/ @feuGeneA
|
||||
packages/utils/ @hysz
|
||||
|
||||
# Protocol/smart contracts
|
||||
contracts/core/test/ @albrow
|
||||
contracts/ @abandeali1 @hysz
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
<!--- Before submitting please check to see if this issue was already reported -->
|
||||
|
||||
<!--- Provide a general summary of the issue in the Title above -->
|
||||
<!--- Prefix your issue title with the package name it relates to (e.g., `0x.js: ` or `general:`) -->
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
|
@@ -34,11 +34,14 @@ Visit our [developer portal](https://0xproject.com/docs/order-utils) for a compr
|
||||
|
||||
### Solidity Packages
|
||||
|
||||
These packages are all under development. See [/contracts/README.md](/contracts/README.md) for a list of deployed packages.
|
||||
|
||||
| Package | Version | Description |
|
||||
| ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [`@0x/contracts-asset-proxy`](/contracts/asset-proxy) | [](https://www.npmjs.com/package/@0x/contracts-asset-proxy) | [`AssetProxy`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#assetproxy) contracts used within the protocol |
|
||||
| [`@0x/contracts-erc20`](/contracts/erc20) | [](https://www.npmjs.com/package/@0x/contracts-erc20) | Implementations of various ERC20 tokens |
|
||||
| [`@0x/contracts-erc721`](/contracts/erc721) | [](https://www.npmjs.com/package/@0x/contracts-erc721) | Implementations of various ERC721 tokens |
|
||||
| [`@0x/contracts-erc1155`](/contracts/erc1155) | [](https://www.npmjs.com/package/@0x/contracts-erc1155) | Implementations of various ERC1155 tokens |
|
||||
| [`@0x/contracts-exchange`](/contracts/exchange) | [](https://www.npmjs.com/package/@0x/contracts-exchange) | The [`Exchange`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#exchange) contract used for settling trades within the protocol |
|
||||
| [`@0x/contracts-exchange-forwarder`](/contracts/exchange-forwarder) | [](https://www.npmjs.com/package/@0x/contracts-exchange-forwarder) | A [`Forwarder`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/forwarder-specification.md) contract used to simplify UX for interacting with the protocol |
|
||||
| [`@0x/contracts-exchange-libs`](/contracts/exchange-libs) | [](https://www.npmjs.com/package/@0x/contracts-exchange-libs) | Protocol specific libraries used within the [`Exchange`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#exchange) contract |
|
||||
@@ -46,7 +49,8 @@ Visit our [developer portal](https://0xproject.com/docs/order-utils) for a compr
|
||||
| [`@0x/contracts-multisig`](/contracts/multisig) | [](https://www.npmjs.com/package/@0x/contracts-multisig) | Various implementations of multisignature wallets, including the [`AssetProxyOwner`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#assetproxyowner) contract that has permissions to upgrade the protocol |
|
||||
| [`@0x/contracts-test-utils`](/contracts/test-utils) | [](https://www.npmjs.com/package/@0x/contracts-test-utils) | Typescript/Javascript shared utilities used for testing contracts |
|
||||
| [`@0x/contracts-utils`](/contracts/utils) | [](https://www.npmjs.com/package/@0x/contracts-utils) | Generic libraries and utilities used throughout all of the contracts |
|
||||
| [`@0x/contracts-tec`](/contracts/tec) | [](https://www.npmjs.com/package/@0x/contracts-tec) | A contract that allows users to execute 0x transactions with permission from a TEC (Trade Execution Coordinator) |
|
||||
| [`@0x/contracts-coordinator`](/contracts/coordinator) | [](https://www.npmjs.com/package/@0x/contracts-coordinator) | A contract that allows users to execute 0x transactions with permission from a Coordinator |
|
||||
| [`@0x/contracts-dev-utils`](/contracts/dev-utils) | [](https://www.npmjs.com/package/@0x/contracts-dev-utils) | A contract contains utility functions for developers (such as validating many orders using a single eth_call) |
|
||||
|
||||
### Typescript/Javascript Packages
|
||||
|
||||
|
16
contracts/README.md
Normal file
16
contracts/README.md
Normal file
@@ -0,0 +1,16 @@
|
||||
#### Deployed Contract Packages
|
||||
|
||||
| Contract | Package | Version | Git Tag |
|
||||
| --------------- | ------------------------------------------------------------------- | -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| AssetProxyOwner | [`@0x/contracts-multisig`](/contracts/multisig) | [v1.0.2](https://www.npmjs.com/package/@0x/contracts-multisig/v/1.0.2) | [@0x/contracts-multisig@1.0.2](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-multisig@1.0.2) |
|
||||
| ERC20Proxy | [`@0x/contracts-asset-proxy`](/contracts/asset-proxy) | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-asset-proxy/v/1.0.1) | [@0x/contracts-asset-proxy@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-asset-proxy@1.0.1) |
|
||||
| ERC721Proxy | [`@0x/contracts-asset-proxy`](/contracts/asset-proxy) | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-asset-proxy/v/1.0.1) | [@0x/contracts-asset-proxy@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-asset-proxy@1.0.1) |
|
||||
| Exchange | [`@0x/contracts-exchange`](/contracts/exchange) | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-exchange/v/1.0.1) | [@0x/contracts-exchange@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-exchange@1.0.1) |
|
||||
| DutchAuction | [`@0x/contracts-extensions`](/contracts/extensions) | [v1.0.2](https://www.npmjs.com/package/@0x/contracts-extensions/v/1.0.2) | [@0x/contracts-extensions@1.0.2](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-extensions@1.0.2) |
|
||||
| Forwarder | [`@0x/contracts-exchange-forwarder`](/contracts/exchange-forwarder) | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-exchange-forwarder/v/1.0.1) | [@0x/contracts-exchange-forwarder@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-exchange-forwarder@1.0.1) |
|
||||
| MultiAssetProxy | [`@0x/contracts-asset-proxy`](/contracts/asset-proxy) | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-asset-proxy/v/1.0.1) | [@0x/contracts-asset-proxy@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-asset-proxy@1.0.1) |
|
||||
| ZRXToken | [`@0x/contracts-erc20`](/contracts/erc20) | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-erc20/v/1.0.1) | [@0x/contracts-erc20@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-erc20@1.0.1) |
|
||||
|
||||
#### Development
|
||||
|
||||
Building solidity files will update the contract artifact in `{package-name}/generated-artifacts/{contract}.json`, but does not automatically update the `abi-gen-wrappers` package, which are generated from the artifact JSON. To ensure consistency, clean and rebuild `abi-gen-wrappers` after any changes to the artifact JSON.
|
@@ -1,4 +1,100 @@
|
||||
[
|
||||
{
|
||||
"version": "2.2.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add `LibAssetProxyIds` contract",
|
||||
"pr": 1835
|
||||
},
|
||||
{
|
||||
"note": "Updated ERC1155 Asset Proxy. Less optimization. More explicit handling of edge cases.",
|
||||
"pr": 1852
|
||||
},
|
||||
{
|
||||
"note": "Implement StaticCallProxy",
|
||||
"pr": 1863
|
||||
}
|
||||
],
|
||||
"timestamp": 1563006338
|
||||
},
|
||||
{
|
||||
"timestamp": 1558712885,
|
||||
"version": "2.1.5",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1557961111,
|
||||
"version": "2.1.4",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1557799313,
|
||||
"version": "2.1.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "2.1.2",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Update tests to use contract-built-in `awaitTransactionSuccessAsync`",
|
||||
"pr": 1797
|
||||
}
|
||||
],
|
||||
"timestamp": 1557507213
|
||||
},
|
||||
{
|
||||
"version": "2.1.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
],
|
||||
"timestamp": 1554997931
|
||||
},
|
||||
{
|
||||
"version": "2.1.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Run Web3ProviderEngine without excess block polling",
|
||||
"pr": 1695
|
||||
}
|
||||
],
|
||||
"timestamp": 1553183790
|
||||
},
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Do not reexport external dependencies",
|
||||
"pr": 1682
|
||||
},
|
||||
{
|
||||
"note": "Add ERC1155Proxy",
|
||||
"pr": 1661
|
||||
},
|
||||
{
|
||||
"note": "Bumped solidity version to ^0.5.5",
|
||||
"pr": 1701
|
||||
},
|
||||
{
|
||||
"note": "Integration testing for ERC1155Proxy",
|
||||
"pr": 1673
|
||||
}
|
||||
],
|
||||
"timestamp": 1553091633
|
||||
},
|
||||
{
|
||||
"timestamp": 1551479279,
|
||||
"version": "1.0.9",
|
||||
|
@@ -5,6 +5,43 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v2.2.0 - _July 13, 2019_
|
||||
|
||||
* Add `LibAssetProxyIds` contract (#1835)
|
||||
* Updated ERC1155 Asset Proxy. Less optimization. More explicit handling of edge cases. (#1852)
|
||||
* Implement StaticCallProxy (#1863)
|
||||
|
||||
## v2.1.5 - _May 24, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.1.4 - _May 15, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.1.3 - _May 14, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.1.2 - _May 10, 2019_
|
||||
|
||||
* Update tests to use contract-built-in `awaitTransactionSuccessAsync` (#1797)
|
||||
|
||||
## v2.1.1 - _April 11, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.1.0 - _March 21, 2019_
|
||||
|
||||
* Run Web3ProviderEngine without excess block polling (#1695)
|
||||
|
||||
## v2.0.0 - _March 20, 2019_
|
||||
|
||||
* Do not reexport external dependencies (#1682)
|
||||
* Add ERC1155Proxy (#1661)
|
||||
* Bumped solidity version to ^0.5.5 (#1701)
|
||||
* Integration testing for ERC1155Proxy (#1673)
|
||||
|
||||
## v1.0.9 - _March 1, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,14 +1,20 @@
|
||||
{
|
||||
"artifactsDir": "./generated-artifacts",
|
||||
"contractsDir": "./contracts",
|
||||
"useDockerisedSolc": true,
|
||||
"useDockerisedSolc": false,
|
||||
"isOfflineMode": false,
|
||||
"compilerSettings": {
|
||||
"optimizer": { "enabled": true, "runs": 1000000 },
|
||||
"evmVersion": "constantinople",
|
||||
"optimizer": {
|
||||
"enabled": true,
|
||||
"runs": 1000000,
|
||||
"details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
|
||||
},
|
||||
"outputSelection": {
|
||||
"*": {
|
||||
"*": [
|
||||
"abi",
|
||||
"devdoc",
|
||||
"evm.bytecode.object",
|
||||
"evm.bytecode.sourceMap",
|
||||
"evm.deployedBytecode.object",
|
||||
@@ -18,17 +24,15 @@
|
||||
}
|
||||
},
|
||||
"contracts": [
|
||||
"@0x/contracts-erc20/contracts/test/DummyERC20Token.sol",
|
||||
"@0x/contracts-erc20/contracts/test/DummyMultipleReturnERC20Token.sol",
|
||||
"@0x/contracts-erc20/contracts/test/DummyNoReturnERC20Token.sol",
|
||||
"@0x/contracts-erc721/contracts/test/DummyERC721Receiver.sol",
|
||||
"@0x/contracts-erc721/contracts/test/DummyERC721Token.sol",
|
||||
"src/ERC1155Proxy.sol",
|
||||
"src/ERC20Proxy.sol",
|
||||
"src/ERC721Proxy.sol",
|
||||
"src/MixinAuthorizable.sol",
|
||||
"src/MultiAssetProxy.sol",
|
||||
"src/StaticCallProxy.sol",
|
||||
"src/interfaces/IAssetData.sol",
|
||||
"src/interfaces/IAssetProxy.sol",
|
||||
"src/interfaces/IAuthorizable.sol"
|
||||
"src/interfaces/IAuthorizable.sol",
|
||||
"test/TestStaticCallTarget.sol"
|
||||
]
|
||||
}
|
||||
|
375
contracts/asset-proxy/contracts/src/ERC1155Proxy.sol
Normal file
375
contracts/asset-proxy/contracts/src/ERC1155Proxy.sol
Normal file
@@ -0,0 +1,375 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
|
||||
import "./MixinAuthorizable.sol";
|
||||
|
||||
|
||||
contract ERC1155Proxy is
|
||||
MixinAuthorizable
|
||||
{
|
||||
|
||||
// Id of this proxy.
|
||||
bytes4 constant internal PROXY_ID = bytes4(keccak256("ERC1155Assets(address,uint256[],uint256[],bytes)"));
|
||||
|
||||
// solhint-disable-next-line payable-fallback
|
||||
function ()
|
||||
external
|
||||
{
|
||||
// Input calldata to this function is encoded as follows:
|
||||
// -- TABLE #1 --
|
||||
// | Area | Offset (**) | Length | Contents |
|
||||
// |----------|-------------|-------------|---------------------------------|
|
||||
// | Header | 0 | 4 | function selector |
|
||||
// | Params | | 4 * 32 | function parameters: |
|
||||
// | | 4 | | 1. offset to assetData (*) |
|
||||
// | | 36 | | 2. from |
|
||||
// | | 68 | | 3. to |
|
||||
// | | 100 | | 4. amount |
|
||||
// | Data | | | assetData: |
|
||||
// | | 132 | 32 | assetData Length |
|
||||
// | | 164 | (see below) | assetData Contents |
|
||||
//
|
||||
//
|
||||
// Asset data is encoded as follows:
|
||||
// -- TABLE #2 --
|
||||
// | Area | Offset | Length | Contents |
|
||||
// |----------|-------------|---------|-------------------------------------|
|
||||
// | Header | 0 | 4 | assetProxyId |
|
||||
// | Params | | 4 * 32 | function parameters: |
|
||||
// | | 4 | | 1. address of ERC1155 contract |
|
||||
// | | 36 | | 2. offset to ids (*) |
|
||||
// | | 68 | | 3. offset to values (*) |
|
||||
// | | 100 | | 4. offset to data (*) |
|
||||
// | Data | | | ids: |
|
||||
// | | 132 | 32 | 1. ids Length |
|
||||
// | | 164 | a | 2. ids Contents |
|
||||
// | | | | values: |
|
||||
// | | 164 + a | 32 | 1. values Length |
|
||||
// | | 196 + a | b | 2. values Contents |
|
||||
// | | | | data: |
|
||||
// | | 196 + a + b | 32 | 1. data Length |
|
||||
// | | 228 + a + b | c | 2. data Contents |
|
||||
//
|
||||
//
|
||||
// Calldata for target ERC155 asset is encoded for safeBatchTransferFrom:
|
||||
// -- TABLE #3 --
|
||||
// | Area | Offset (**) | Length | Contents |
|
||||
// |----------|-------------|---------|-------------------------------------|
|
||||
// | Header | 0 | 4 | safeBatchTransferFrom selector |
|
||||
// | Params | | 5 * 32 | function parameters: |
|
||||
// | | 4 | | 1. from address |
|
||||
// | | 36 | | 2. to address |
|
||||
// | | 68 | | 3. offset to ids (*) |
|
||||
// | | 100 | | 4. offset to values (*) |
|
||||
// | | 132 | | 5. offset to data (*) |
|
||||
// | Data | | | ids: |
|
||||
// | | 164 | 32 | 1. ids Length |
|
||||
// | | 196 | a | 2. ids Contents |
|
||||
// | | | | values: |
|
||||
// | | 196 + a | 32 | 1. values Length |
|
||||
// | | 228 + a | b | 2. values Contents |
|
||||
// | | | | data: |
|
||||
// | | 228 + a + b | 32 | 1. data Length |
|
||||
// | | 260 + a + b | c | 2. data Contents |
|
||||
//
|
||||
//
|
||||
// (*): offset is computed from start of function parameters, so offset
|
||||
// by an additional 4 bytes in the calldata.
|
||||
//
|
||||
// (**): the `Offset` column is computed assuming no calldata compression;
|
||||
// offsets in the Data Area are dynamic and should be evaluated in
|
||||
// real-time.
|
||||
//
|
||||
// WARNING: The ABIv2 specification allows additional padding between
|
||||
// the Params and Data section. This will result in a larger
|
||||
// offset to assetData.
|
||||
//
|
||||
// Note: Table #1 and Table #2 exist in Calldata. We construct Table #3 in memory.
|
||||
//
|
||||
//
|
||||
assembly {
|
||||
// The first 4 bytes of calldata holds the function selector
|
||||
let selector := and(calldataload(0), 0xffffffff00000000000000000000000000000000000000000000000000000000)
|
||||
|
||||
// `transferFrom` will be called with the following parameters:
|
||||
// assetData Encoded byte array.
|
||||
// from Address to transfer asset from.
|
||||
// to Address to transfer asset to.
|
||||
// amount Amount of asset to transfer.
|
||||
// bytes4(keccak256("transferFrom(bytes,address,address,uint256)")) = 0xa85e59e4
|
||||
if eq(selector, 0xa85e59e400000000000000000000000000000000000000000000000000000000) {
|
||||
|
||||
// To lookup a value in a mapping, we load from the storage location keccak256(k, p),
|
||||
// where k is the key left padded to 32 bytes and p is the storage slot
|
||||
mstore(0, caller)
|
||||
mstore(32, authorized_slot)
|
||||
|
||||
// Revert if authorized[msg.sender] == false
|
||||
if iszero(sload(keccak256(0, 64))) {
|
||||
// Revert with `Error("SENDER_NOT_AUTHORIZED")`
|
||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(64, 0x0000001553454e4445525f4e4f545f415554484f52495a454400000000000000)
|
||||
mstore(96, 0)
|
||||
revert(0, 100)
|
||||
}
|
||||
|
||||
// Construct Table #3 in memory, starting at memory offset 0.
|
||||
// The algorithm below maps calldata (Table #1) and assetData (Table #2) to memory (Table #3).
|
||||
// Once Table #3 ha been constructed in memory, the destination erc1155 contract is called using this
|
||||
// as its calldata. This process is divided into three steps, below.
|
||||
|
||||
////////// STEP 1/3 - Map calldata to memory (Table #1 -> Table #3) //////////
|
||||
|
||||
// Store the safeBatchTransferFrom function selector, which is computed using:
|
||||
// bytes4(keccak256("safeBatchTransferFrom(address,address,uint256[],uint256[],bytes)"))
|
||||
mstore(0, 0x2eb2c2d600000000000000000000000000000000000000000000000000000000)
|
||||
|
||||
// Copy `from` and `to` fields from calldata (Table #1) into memory (Table #3)
|
||||
calldatacopy(
|
||||
4, // aligned such that `from` and `to` are at the correct location for Table #3
|
||||
36, // beginning of `from` field from Table #1
|
||||
64 // 32 bytes for `from` + 32 bytes for `to` field
|
||||
)
|
||||
|
||||
////////// STEP 2/3 - Map assetData to memory (Table #2 -> Table #3) //////////
|
||||
|
||||
// Map relevant fields from assetData (Table #2) into memory (Table #3)
|
||||
// The Contents column of Table #2 is the same as Table #3,
|
||||
// beginning from parameter 3 - `offset to ids (*)`
|
||||
// The `values` from assetData (Table #2) are multiplied by `amount` (Table #1)
|
||||
// when they are copied into memory.
|
||||
|
||||
// Load offset to `assetData`
|
||||
let assetDataOffset := add(calldataload(4), 4)
|
||||
|
||||
// Load length in bytes of `assetData`
|
||||
let assetDataLength := calldataload(assetDataOffset)
|
||||
|
||||
// Assert that the length of asset data:
|
||||
// 1. Must be at least 132 bytes (Table #2)
|
||||
// 2. Must be a multiple of 32 (excluding the 4-byte selector)
|
||||
if or(lt(assetDataLength, 132), mod(sub(assetDataLength, 4), 32)) {
|
||||
// Revert with `Error("INVALID_ASSET_DATA_LENGTH")`
|
||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(64, 0x00000019494e56414c49445f41535345545f444154415f4c454e475448000000)
|
||||
mstore(96, 0)
|
||||
revert(0, 100)
|
||||
}
|
||||
|
||||
// End of asset data in calldata
|
||||
// +32 for length field
|
||||
let assetDataEnd := add(assetDataOffset, add(assetDataLength, 32))
|
||||
if gt(assetDataEnd, calldatasize()) {
|
||||
// Revert with `Error("INVALID_ASSET_DATA_END")`
|
||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(64, 0x00000016494e56414c49445f41535345545f444154415f454e44000000000000)
|
||||
mstore(96, 0)
|
||||
revert(0, 100)
|
||||
}
|
||||
|
||||
// Load offset to parameters section in asset data
|
||||
let paramsInAssetDataOffset := add(assetDataOffset, 36)
|
||||
|
||||
// Offset of end of Data Area in memory.
|
||||
// This value will grow as we construct Table #3.
|
||||
let dataAreaEndOffset := 164
|
||||
|
||||
// Load amount by which to scale values
|
||||
let amount := calldataload(100)
|
||||
|
||||
// Store pointer to `ids` (Table #3)
|
||||
// Subtract 4 for `safeBatchTransferFrom` selector
|
||||
mstore(68, sub(dataAreaEndOffset, 4))
|
||||
|
||||
// Ensure length of `ids` does not overflow
|
||||
let idsOffset := add(paramsInAssetDataOffset, calldataload(add(assetDataOffset, 68)))
|
||||
let idsLength := calldataload(idsOffset)
|
||||
let idsLengthInBytes := mul(idsLength, 32)
|
||||
if sub(div(idsLengthInBytes, 32), idsLength) {
|
||||
// Revert with `Error("UINT256_OVERFLOW")`
|
||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(64, 0x0000001055494e543235365f4f564552464c4f57000000000000000000000000)
|
||||
mstore(96, 0)
|
||||
revert(0, 100)
|
||||
}
|
||||
|
||||
// Ensure `ids` does not resolve to outside of `assetData`
|
||||
let idsBegin := add(idsOffset, 32)
|
||||
let idsEnd := add(idsBegin, idsLengthInBytes)
|
||||
if gt(idsEnd, assetDataEnd) {
|
||||
// Revert with `Error("INVALID_IDS_OFFSET")`
|
||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(64, 0x00000012494e56414c49445f4944535f4f464653455400000000000000000000)
|
||||
mstore(96, 0)
|
||||
revert(0, 100)
|
||||
}
|
||||
|
||||
// Copy `ids` from `assetData` (Table #2) to memory (Table #3)
|
||||
calldatacopy(
|
||||
dataAreaEndOffset,
|
||||
idsOffset,
|
||||
add(idsLengthInBytes, 32)
|
||||
)
|
||||
dataAreaEndOffset := add(dataAreaEndOffset, add(idsLengthInBytes, 32))
|
||||
|
||||
// Store pointer to `values` (Table #3)
|
||||
// Subtract 4 for `safeBatchTrasferFrom` selector
|
||||
mstore(100, sub(dataAreaEndOffset, 4))
|
||||
|
||||
// Ensure length of `values` does not overflow
|
||||
let valuesOffset := add(paramsInAssetDataOffset, calldataload(add(assetDataOffset, 100)))
|
||||
let valuesLength := calldataload(valuesOffset)
|
||||
let valuesLengthInBytes := mul(valuesLength, 32)
|
||||
if sub(div(valuesLengthInBytes, 32), valuesLength) {
|
||||
// Revert with `Error("UINT256_OVERFLOW")`
|
||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(64, 0x0000001055494e543235365f4f564552464c4f57000000000000000000000000)
|
||||
mstore(96, 0)
|
||||
revert(0, 100)
|
||||
}
|
||||
|
||||
// Ensure `values` does not resolve to outside of `assetData`
|
||||
let valuesBegin := add(valuesOffset, 32)
|
||||
let valuesEnd := add(valuesBegin, valuesLengthInBytes)
|
||||
if gt(valuesEnd, assetDataEnd) {
|
||||
// Revert with `Error("INVALID_VALUES_OFFSET")`
|
||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(64, 0x00000015494e56414c49445f56414c5545535f4f464653455400000000000000)
|
||||
mstore(96, 0)
|
||||
revert(0, 100)
|
||||
}
|
||||
|
||||
// Store length of `values`
|
||||
mstore(dataAreaEndOffset, valuesLength)
|
||||
dataAreaEndOffset := add(dataAreaEndOffset, 32)
|
||||
|
||||
// Scale and store elements of `values`
|
||||
for { let currentValueOffset := valuesBegin }
|
||||
lt(currentValueOffset, valuesEnd)
|
||||
{ currentValueOffset := add(currentValueOffset, 32) }
|
||||
{
|
||||
// Load value and generate scaled value
|
||||
let currentValue := calldataload(currentValueOffset)
|
||||
let currentValueScaled := mul(currentValue, amount)
|
||||
|
||||
// Revert if `amount` != 0 and multiplication resulted in an overflow
|
||||
if iszero(or(
|
||||
iszero(amount),
|
||||
eq(div(currentValueScaled, amount), currentValue)
|
||||
)) {
|
||||
// Revert with `Error("UINT256_OVERFLOW")`
|
||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(64, 0x0000001055494e543235365f4f564552464c4f57000000000000000000000000)
|
||||
mstore(96, 0)
|
||||
revert(0, 100)
|
||||
}
|
||||
|
||||
// There was no overflow, store the scaled token value
|
||||
mstore(dataAreaEndOffset, currentValueScaled)
|
||||
dataAreaEndOffset := add(dataAreaEndOffset, 32)
|
||||
}
|
||||
|
||||
// Store pointer to `data` (Table #3)
|
||||
// Subtract 4 for `safeBatchTrasferFrom` selector
|
||||
mstore(132, sub(dataAreaEndOffset, 4))
|
||||
|
||||
// Ensure `data` does not resolve to outside of `assetData`
|
||||
let dataOffset := add(paramsInAssetDataOffset, calldataload(add(assetDataOffset, 132)))
|
||||
let dataLengthInBytes := calldataload(dataOffset)
|
||||
let dataBegin := add(dataOffset, 32)
|
||||
let dataEnd := add(dataBegin, dataLengthInBytes)
|
||||
if gt(dataEnd, assetDataEnd) {
|
||||
// Revert with `Error("INVALID_DATA_OFFSET")`
|
||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(64, 0x00000013494e56414c49445f444154415f4f4646534554000000000000000000)
|
||||
mstore(96, 0)
|
||||
revert(0, 100)
|
||||
}
|
||||
|
||||
// Copy `data` from `assetData` (Table #2) to memory (Table #3)
|
||||
calldatacopy(
|
||||
dataAreaEndOffset,
|
||||
dataOffset,
|
||||
add(dataLengthInBytes, 32)
|
||||
)
|
||||
|
||||
// Update the end of data offset to be word-aligned
|
||||
let dataLengthInWords := div(add(dataLengthInBytes, 31), 32)
|
||||
let dataLengthInBytesWordAligned := mul(dataLengthInWords, 32)
|
||||
dataAreaEndOffset := add(dataAreaEndOffset, add(dataLengthInBytesWordAligned, 32))
|
||||
|
||||
////////// STEP 3/3 - Execute Transfer //////////
|
||||
// Load the address of the destination erc1155 contract from asset data (Table #2)
|
||||
// +32 bytes for assetData Length
|
||||
// +4 bytes for assetProxyId
|
||||
let assetAddress := and(
|
||||
calldataload(add(assetDataOffset, 36)),
|
||||
0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff
|
||||
)
|
||||
|
||||
// Call into the destination erc1155 contract using as calldata Table #3 (constructed in-memory above)
|
||||
let success := call(
|
||||
gas, // forward all gas
|
||||
assetAddress, // call address of erc1155 asset
|
||||
0, // don't send any ETH
|
||||
0, // pointer to start of input
|
||||
dataAreaEndOffset, // length of input is the end of the Data Area (Table #3)
|
||||
0, // write output over memory that won't be reused
|
||||
0 // don't copy output to memory
|
||||
)
|
||||
|
||||
// Revert with reason given by AssetProxy if `transferFrom` call failed
|
||||
if iszero(success) {
|
||||
returndatacopy(
|
||||
0, // copy to memory at 0
|
||||
0, // copy from return data at 0
|
||||
returndatasize() // copy all return data
|
||||
)
|
||||
revert(0, returndatasize())
|
||||
}
|
||||
|
||||
// Return if call was successful
|
||||
return(0, 0)
|
||||
}
|
||||
|
||||
// Revert if undefined function is called
|
||||
revert(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Gets the proxy id associated with the proxy address.
|
||||
/// @return Proxy id.
|
||||
function getProxyId()
|
||||
external
|
||||
pure
|
||||
returns (bytes4)
|
||||
{
|
||||
return PROXY_ID;
|
||||
}
|
||||
}
|
@@ -16,7 +16,7 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity 0.4.24;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "./MixinAuthorizable.sol";
|
||||
|
||||
|
@@ -16,7 +16,7 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity 0.4.24;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "./MixinAuthorizable.sol";
|
||||
|
||||
|
@@ -16,7 +16,7 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.4.24;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/Ownable.sol";
|
||||
import "./mixins/MAssetProxyDispatcher.sol";
|
||||
@@ -28,7 +28,7 @@ contract MixinAssetProxyDispatcher is
|
||||
MAssetProxyDispatcher
|
||||
{
|
||||
// Mapping from Asset Proxy Id's to their respective Asset Proxy
|
||||
mapping (bytes4 => IAssetProxy) public assetProxies;
|
||||
mapping (bytes4 => address) public assetProxies;
|
||||
|
||||
/// @dev Registers an asset proxy to its asset proxy id.
|
||||
/// Once an asset proxy is registered, it cannot be unregistered.
|
||||
@@ -37,10 +37,8 @@ contract MixinAssetProxyDispatcher is
|
||||
external
|
||||
onlyOwner
|
||||
{
|
||||
IAssetProxy assetProxyContract = IAssetProxy(assetProxy);
|
||||
|
||||
// Ensure that no asset proxy exists with current id.
|
||||
bytes4 assetProxyId = assetProxyContract.getProxyId();
|
||||
bytes4 assetProxyId = IAssetProxy(assetProxy).getProxyId();
|
||||
address currentAssetProxy = assetProxies[assetProxyId];
|
||||
require(
|
||||
currentAssetProxy == address(0),
|
||||
@@ -48,7 +46,7 @@ contract MixinAssetProxyDispatcher is
|
||||
);
|
||||
|
||||
// Add asset proxy and log registration.
|
||||
assetProxies[assetProxyId] = assetProxyContract;
|
||||
assetProxies[assetProxyId] = assetProxy;
|
||||
emit AssetProxyRegistered(
|
||||
assetProxyId,
|
||||
assetProxy
|
||||
|
@@ -16,7 +16,7 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.4.24;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/Ownable.sol";
|
||||
import "./mixins/MAuthorizable.sol";
|
||||
|
@@ -16,7 +16,7 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity 0.4.24;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "./MixinAssetProxyDispatcher.sol";
|
||||
import "./MixinAuthorizable.sol";
|
||||
|
207
contracts/asset-proxy/contracts/src/StaticCallProxy.sol
Normal file
207
contracts/asset-proxy/contracts/src/StaticCallProxy.sol
Normal file
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
|
||||
|
||||
contract StaticCallProxy {
|
||||
|
||||
// Id of this proxy.
|
||||
bytes4 constant internal PROXY_ID = bytes4(keccak256("StaticCall(address,bytes,bytes32)"));
|
||||
|
||||
// solhint-disable-next-line payable-fallback
|
||||
function ()
|
||||
external
|
||||
{
|
||||
assembly {
|
||||
// The first 4 bytes of calldata holds the function selector
|
||||
let selector := and(calldataload(0), 0xffffffff00000000000000000000000000000000000000000000000000000000)
|
||||
|
||||
// `transferFrom` will be called with the following parameters:
|
||||
// assetData Encoded byte array.
|
||||
// from Address to transfer asset from.
|
||||
// to Address to transfer asset to.
|
||||
// amount Amount of asset to transfer.
|
||||
// bytes4(keccak256("transferFrom(bytes,address,address,uint256)")) = 0xa85e59e4
|
||||
if eq(selector, 0xa85e59e400000000000000000000000000000000000000000000000000000000) {
|
||||
|
||||
// `transferFrom`.
|
||||
// The function is marked `external`, so no abi decoding is done for
|
||||
// us. Instead, we expect the `calldata` memory to contain the
|
||||
// following:
|
||||
//
|
||||
// | Area | Offset | Length | Contents |
|
||||
// |----------|--------|---------|-------------------------------------|
|
||||
// | Header | 0 | 4 | function selector |
|
||||
// | Params | | 4 * 32 | function parameters: |
|
||||
// | | 4 | | 1. offset to assetData (*) |
|
||||
// | | 36 | | 2. from |
|
||||
// | | 68 | | 3. to |
|
||||
// | | 100 | | 4. amount |
|
||||
// | Data | | | assetData: |
|
||||
// | | 132 | 32 | assetData Length |
|
||||
// | | 164 | ** | assetData Contents |
|
||||
//
|
||||
// (*): offset is computed from start of function parameters, so offset
|
||||
// by an additional 4 bytes in the calldata.
|
||||
//
|
||||
// (**): see table below to compute length of assetData Contents
|
||||
// (***): Note that the `from`, `to`, and `amount` params in calldata are ignored in this function.
|
||||
//
|
||||
// WARNING: The ABIv2 specification allows additional padding between
|
||||
// the Params and Data section. This will result in a larger
|
||||
// offset to assetData.
|
||||
|
||||
// Load offset to `assetData`
|
||||
let assetDataOffset := add(calldataload(4), 4)
|
||||
|
||||
// Validate length of `assetData`
|
||||
let assetDataLen := calldataload(assetDataOffset)
|
||||
if or(lt(assetDataLen, 100), mod(sub(assetDataLen, 4), 32)) {
|
||||
// Revert with `Error("INVALID_ASSET_DATA_LENGTH")`
|
||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(64, 0x00000019494e56414c49445f41535345545f444154415f4c454e475448000000)
|
||||
mstore(96, 0)
|
||||
revert(0, 100)
|
||||
}
|
||||
|
||||
// Ensure that `assetData` ends inside of calldata
|
||||
let assetDataEnd := add(assetDataOffset, add(assetDataLen, 32))
|
||||
if gt(assetDataEnd, calldatasize()) {
|
||||
// Revert with `Error("INVALID_ASSET_DATA_END")`
|
||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(64, 0x00000016494e56414c49445f41535345545f444154415f454e44000000000000)
|
||||
mstore(96, 0)
|
||||
revert(0, 100)
|
||||
}
|
||||
|
||||
// Asset data is encoded as follows:
|
||||
// | Area | Offset | Length | Contents |
|
||||
// |----------|-------------|---------|--------------------------------------|
|
||||
// | Header | 0 | 4 | assetProxyId |
|
||||
// | Params | | 4 * 32 | function parameters: |
|
||||
// | | 4 | | 1. address of callTarget |
|
||||
// | | 36 | | 2. offset to staticCallData (*) |
|
||||
// | | 68 | | 3. expected 32 byte hash of output |
|
||||
// | Data | | | staticCallData: |
|
||||
// | | 100 | 32 | 1. staticCallData Length |
|
||||
// | | 132 | a | 2. staticCallData Contents |
|
||||
|
||||
// In order to find the offset to `staticCallData`, we must add:
|
||||
// assetDataOffset
|
||||
// + 32 (assetData len)
|
||||
// + 4 (proxyId)
|
||||
// + 32 (callTarget)
|
||||
let paramsInAssetDataOffset := add(assetDataOffset, 36)
|
||||
let staticCallDataOffset := add(paramsInAssetDataOffset, calldataload(add(assetDataOffset, 68)))
|
||||
|
||||
// Load length of `staticCallData`
|
||||
let staticCallDataLen := calldataload(staticCallDataOffset)
|
||||
|
||||
// Ensure `staticCallData` does not begin to outside of `assetData`
|
||||
let staticCallDataBegin := add(staticCallDataOffset, 32)
|
||||
let staticCallDataEnd := add(staticCallDataBegin, staticCallDataLen)
|
||||
if gt(staticCallDataEnd, assetDataEnd) {
|
||||
// Revert with `Error("INVALID_STATIC_CALL_DATA_OFFSET")`
|
||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(64, 0x0000001f494e56414c49445f5354415449435f43414c4c5f444154415f4f4646)
|
||||
mstore(96, 0x5345540000000000000000000000000000000000000000000000000000000000)
|
||||
revert(0, 100)
|
||||
}
|
||||
|
||||
// Copy `staticCallData` into memory
|
||||
calldatacopy(
|
||||
0, // memory can be safely overwritten from beginning
|
||||
staticCallDataBegin, // start of `staticCallData`
|
||||
staticCallDataLen // copy the entire `staticCallData`
|
||||
)
|
||||
|
||||
// In order to find the offset to `callTarget`, we must add:
|
||||
// assetDataOffset
|
||||
// + 32 (assetData len)
|
||||
// + 4 (proxyId)
|
||||
let callTarget := and(
|
||||
calldataload(add(assetDataOffset, 36)),
|
||||
0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff
|
||||
)
|
||||
|
||||
// Perform `callTarget.staticcall(staticCallData)`
|
||||
let success := staticcall(
|
||||
gas, // forward all gas
|
||||
callTarget, // call address `callTarget`
|
||||
0, // pointer to start of input
|
||||
staticCallDataLen, // length of input
|
||||
0, // start of memory can be safely overwritten
|
||||
0 // don't copy output to memory
|
||||
)
|
||||
|
||||
// Copy entire output to start of memory
|
||||
let outputLen := returndatasize()
|
||||
returndatacopy(
|
||||
0, // copy to memory at 0
|
||||
0, // copy from return data at 0
|
||||
outputLen // copy all return data
|
||||
)
|
||||
|
||||
// Revert with reason given by `callTarget` if staticcall is unsuccessful
|
||||
if iszero(success) {
|
||||
revert(0, outputLen)
|
||||
}
|
||||
|
||||
// Calculate hash of output
|
||||
let callResultHash := keccak256(0, outputLen)
|
||||
|
||||
// In order to find the offset to `expectedCallResultHash`, we must add:
|
||||
// assetDataOffset
|
||||
// + 32 (assetData len)
|
||||
// + 4 (proxyId)
|
||||
// + 32 (callTarget)
|
||||
// + 32 (staticCallDataOffset)
|
||||
let expectedResultHash := calldataload(add(assetDataOffset, 100))
|
||||
|
||||
if sub(callResultHash, expectedResultHash) {
|
||||
// Revert with `Error("UNEXPECTED_STATIC_CALL_RESULT")`
|
||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(64, 0x0000001d554e45585045435445445f5354415449435f43414c4c5f524553554c)
|
||||
mstore(96, 0x5400000000000000000000000000000000000000000000000000000000000000)
|
||||
revert(0, 100)
|
||||
}
|
||||
|
||||
// Return if output matched expected output
|
||||
return(0, 0)
|
||||
}
|
||||
|
||||
// Revert if undefined function is called
|
||||
revert(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Gets the proxy id associated with the proxy address.
|
||||
/// @return Proxy id.
|
||||
function getProxyId()
|
||||
external
|
||||
pure
|
||||
returns (bytes4)
|
||||
{
|
||||
return PROXY_ID;
|
||||
}
|
||||
}
|
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
|
||||
// solhint-disable
|
||||
pragma solidity ^0.4.24;
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
|
||||
@@ -26,19 +26,33 @@ pragma experimental ABIEncoderV2;
|
||||
// This argument is ABI encoded as one of the methods of this interface.
|
||||
interface IAssetData {
|
||||
|
||||
function ERC20Token(address tokenContract)
|
||||
function ERC20Token(address tokenAddress)
|
||||
external;
|
||||
|
||||
function ERC721Token(
|
||||
address tokenContract,
|
||||
address tokenAddress,
|
||||
uint256 tokenId
|
||||
)
|
||||
external;
|
||||
|
||||
function MultiAsset(
|
||||
uint256[] amounts,
|
||||
bytes[] nestedAssetData
|
||||
function ERC1155Assets(
|
||||
address tokenAddress,
|
||||
uint256[] calldata tokenIds,
|
||||
uint256[] calldata tokenValues,
|
||||
bytes calldata callbackData
|
||||
)
|
||||
external;
|
||||
|
||||
function MultiAsset(
|
||||
uint256[] calldata amounts,
|
||||
bytes[] calldata nestedAssetData
|
||||
)
|
||||
external;
|
||||
|
||||
function StaticCall(
|
||||
address callTarget,
|
||||
bytes calldata staticCallData,
|
||||
bytes32 callResultHash
|
||||
)
|
||||
external;
|
||||
|
||||
}
|
||||
|
@@ -16,7 +16,7 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.4.24;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "./IAuthorizable.sol";
|
||||
|
||||
@@ -30,7 +30,7 @@ contract IAssetProxy is
|
||||
/// @param to Address to transfer asset to.
|
||||
/// @param amount Amount of asset to transfer.
|
||||
function transferFrom(
|
||||
bytes assetData,
|
||||
bytes calldata assetData,
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount
|
||||
|
@@ -16,7 +16,7 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.4.24;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
|
||||
contract IAssetProxyDispatcher {
|
||||
|
@@ -16,7 +16,7 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.4.24;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/interfaces/IOwnable.sol";
|
||||
|
||||
|
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
|
||||
contract LibAssetProxyIds {
|
||||
|
||||
// AssetProxy Ids are equiavalent the first 4 bytes of the keccak256 hash of the function signature assigned to each AssetProxy.
|
||||
|
||||
// ERC20Token(address)
|
||||
bytes4 constant public ERC20_PROXY_ID = 0xf47261b0;
|
||||
|
||||
// ERC721Token(address,uint256)
|
||||
bytes4 constant public ERC721_PROXY_ID = 0x02571792;
|
||||
|
||||
// ERC1155Assets(address,uint256[],uint256[],bytes)
|
||||
bytes4 constant public ERC1155_PROXY_ID = 0xa7cb5fb7;
|
||||
|
||||
// MultiAsset(uint256[],bytes[])
|
||||
bytes4 constant public MULTI_ASSET_PROXY_ID = 0x94cfcdd7;
|
||||
|
||||
// StaticCall(address,bytes,bytes32)
|
||||
bytes4 constant public STATIC_CALL_PROXY_ID = 0xc339d10a;
|
||||
}
|
@@ -16,7 +16,7 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.4.24;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "../interfaces/IAssetProxyDispatcher.sol";
|
||||
|
||||
|
@@ -16,7 +16,7 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.4.24;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "../interfaces/IAuthorizable.sol";
|
||||
|
||||
|
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||
|
||||
|
||||
contract TestStaticCallTarget {
|
||||
|
||||
using LibBytes for bytes;
|
||||
|
||||
uint256 internal _state;
|
||||
|
||||
function updateState()
|
||||
external
|
||||
{
|
||||
_state++;
|
||||
}
|
||||
|
||||
function assertEvenNumber(uint256 target)
|
||||
external
|
||||
pure
|
||||
{
|
||||
require(
|
||||
target % 2 == 0,
|
||||
"TARGET_NOT_EVEN"
|
||||
);
|
||||
}
|
||||
|
||||
function isOddNumber(uint256 target)
|
||||
external
|
||||
pure
|
||||
returns (bool isOdd)
|
||||
{
|
||||
isOdd = target % 2 == 1;
|
||||
return isOdd;
|
||||
}
|
||||
|
||||
function noInputFunction()
|
||||
external
|
||||
pure
|
||||
{
|
||||
assert(msg.data.length == 4 && msg.data.readBytes4(0) == bytes4(keccak256("noInputFunction()")));
|
||||
}
|
||||
|
||||
function dynamicInputFunction(bytes calldata a)
|
||||
external
|
||||
pure
|
||||
{
|
||||
bytes memory abiEncodedData = abi.encodeWithSignature("dynamicInputFunction(bytes)", a);
|
||||
assert(msg.data.equals(abiEncodedData));
|
||||
}
|
||||
|
||||
function returnComplexType(uint256 a, uint256 b)
|
||||
external
|
||||
view
|
||||
returns (bytes memory result)
|
||||
{
|
||||
result = abi.encodePacked(
|
||||
address(this),
|
||||
a,
|
||||
b
|
||||
);
|
||||
return result;
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-asset-proxy",
|
||||
"version": "1.0.9",
|
||||
"version": "2.2.0",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -24,6 +24,7 @@
|
||||
"clean": "shx rm -rf lib generated-artifacts generated-wrappers",
|
||||
"generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../../node_modules/@0x/abi-gen-templates/contract.handlebars --partials '../../node_modules/@0x/abi-gen-templates/partials/**/*.handlebars' --output generated-wrappers --backend ethers",
|
||||
"lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||
"fix": "tslint --fix --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||
"coverage:report:text": "istanbul report text",
|
||||
"coverage:report:html": "istanbul report html && open coverage/index.html",
|
||||
"profiler:report:html": "istanbul report html && open coverage/index.html",
|
||||
@@ -33,7 +34,7 @@
|
||||
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol"
|
||||
},
|
||||
"config": {
|
||||
"abis": "./generated-artifacts/@(DummyERC20Token|DummyERC721Receiver|DummyERC721Token|DummyMultipleReturnERC20Token|DummyNoReturnERC20Token|ERC20Proxy|ERC721Proxy|IAssetData|IAssetProxy|IAuthorizable|MixinAuthorizable|MultiAssetProxy).json",
|
||||
"abis": "./generated-artifacts/@(ERC1155Proxy|ERC20Proxy|ERC721Proxy|IAssetData|IAssetProxy|IAuthorizable|MixinAuthorizable|MultiAssetProxy|StaticCallProxy|TestStaticCallTarget).json",
|
||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
|
||||
},
|
||||
"repository": {
|
||||
@@ -46,12 +47,12 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/protocol/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^2.0.6",
|
||||
"@0x/contracts-gen": "^1.0.5",
|
||||
"@0x/contracts-test-utils": "^3.0.8",
|
||||
"@0x/dev-utils": "^2.1.3",
|
||||
"@0x/sol-compiler": "^3.1.3",
|
||||
"@0x/tslint-config": "^3.0.0",
|
||||
"@0x/abi-gen": "^2.1.0",
|
||||
"@0x/contracts-gen": "^1.0.10",
|
||||
"@0x/contracts-test-utils": "^3.1.8",
|
||||
"@0x/dev-utils": "^2.2.4",
|
||||
"@0x/sol-compiler": "^3.1.9",
|
||||
"@0x/tslint-config": "^3.0.1",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/node": "*",
|
||||
"chai": "^4.0.1",
|
||||
@@ -67,16 +68,18 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^5.0.2",
|
||||
"@0x/contracts-erc20": "^1.0.9",
|
||||
"@0x/contracts-erc721": "^1.0.9",
|
||||
"@0x/contracts-utils": "2.0.1",
|
||||
"@0x/order-utils": "^7.0.2",
|
||||
"@0x/types": "^2.1.1",
|
||||
"@0x/typescript-typings": "^4.1.0",
|
||||
"@0x/utils": "^4.2.2",
|
||||
"@0x/web3-wrapper": "^6.0.2",
|
||||
"ethereum-types": "^2.1.0",
|
||||
"@0x/base-contract": "^5.1.1",
|
||||
"@0x/contracts-erc1155": "^1.1.7",
|
||||
"@0x/contracts-erc20": "^2.2.6",
|
||||
"@0x/contracts-erc721": "^2.1.7",
|
||||
"@0x/contracts-utils": "^3.1.7",
|
||||
"@0x/order-utils": "^8.2.0",
|
||||
"@0x/types": "^2.4.0",
|
||||
"@0x/typescript-typings": "^4.2.3",
|
||||
"@0x/utils": "^4.4.0",
|
||||
"@0x/web3-wrapper": "^6.0.7",
|
||||
"ethereum-types": "^2.1.3",
|
||||
"ethereumjs-util": "^5.1.1",
|
||||
"lodash": "^4.17.11"
|
||||
},
|
||||
"publishConfig": {
|
||||
|
@@ -5,11 +5,7 @@
|
||||
*/
|
||||
import { ContractArtifact } from 'ethereum-types';
|
||||
|
||||
import * as DummyERC20Token from '../generated-artifacts/DummyERC20Token.json';
|
||||
import * as DummyERC721Receiver from '../generated-artifacts/DummyERC721Receiver.json';
|
||||
import * as DummyERC721Token from '../generated-artifacts/DummyERC721Token.json';
|
||||
import * as DummyMultipleReturnERC20Token from '../generated-artifacts/DummyMultipleReturnERC20Token.json';
|
||||
import * as DummyNoReturnERC20Token from '../generated-artifacts/DummyNoReturnERC20Token.json';
|
||||
import * as ERC1155Proxy from '../generated-artifacts/ERC1155Proxy.json';
|
||||
import * as ERC20Proxy from '../generated-artifacts/ERC20Proxy.json';
|
||||
import * as ERC721Proxy from '../generated-artifacts/ERC721Proxy.json';
|
||||
import * as IAssetData from '../generated-artifacts/IAssetData.json';
|
||||
@@ -17,17 +13,17 @@ import * as IAssetProxy from '../generated-artifacts/IAssetProxy.json';
|
||||
import * as IAuthorizable from '../generated-artifacts/IAuthorizable.json';
|
||||
import * as MixinAuthorizable from '../generated-artifacts/MixinAuthorizable.json';
|
||||
import * as MultiAssetProxy from '../generated-artifacts/MultiAssetProxy.json';
|
||||
import * as StaticCallProxy from '../generated-artifacts/StaticCallProxy.json';
|
||||
import * as TestStaticCallTarget from '../generated-artifacts/TestStaticCallTarget.json';
|
||||
export const artifacts = {
|
||||
DummyERC20Token: DummyERC20Token as ContractArtifact,
|
||||
DummyMultipleReturnERC20Token: DummyMultipleReturnERC20Token as ContractArtifact,
|
||||
DummyNoReturnERC20Token: DummyNoReturnERC20Token as ContractArtifact,
|
||||
DummyERC721Receiver: DummyERC721Receiver as ContractArtifact,
|
||||
DummyERC721Token: DummyERC721Token as ContractArtifact,
|
||||
ERC1155Proxy: ERC1155Proxy as ContractArtifact,
|
||||
ERC20Proxy: ERC20Proxy as ContractArtifact,
|
||||
ERC721Proxy: ERC721Proxy as ContractArtifact,
|
||||
MixinAuthorizable: MixinAuthorizable as ContractArtifact,
|
||||
MultiAssetProxy: MultiAssetProxy as ContractArtifact,
|
||||
StaticCallProxy: StaticCallProxy as ContractArtifact,
|
||||
IAssetData: IAssetData as ContractArtifact,
|
||||
IAssetProxy: IAssetProxy as ContractArtifact,
|
||||
IAuthorizable: IAuthorizable as ContractArtifact,
|
||||
TestStaticCallTarget: TestStaticCallTarget as ContractArtifact,
|
||||
};
|
||||
|
@@ -3,11 +3,7 @@
|
||||
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
export * from '../generated-wrappers/dummy_erc20_token';
|
||||
export * from '../generated-wrappers/dummy_erc721_receiver';
|
||||
export * from '../generated-wrappers/dummy_erc721_token';
|
||||
export * from '../generated-wrappers/dummy_multiple_return_erc20_token';
|
||||
export * from '../generated-wrappers/dummy_no_return_erc20_token';
|
||||
export * from '../generated-wrappers/erc1155_proxy';
|
||||
export * from '../generated-wrappers/erc20_proxy';
|
||||
export * from '../generated-wrappers/erc721_proxy';
|
||||
export * from '../generated-wrappers/i_asset_data';
|
||||
@@ -15,3 +11,5 @@ export * from '../generated-wrappers/i_asset_proxy';
|
||||
export * from '../generated-wrappers/i_authorizable';
|
||||
export * from '../generated-wrappers/mixin_authorizable';
|
||||
export * from '../generated-wrappers/multi_asset_proxy';
|
||||
export * from '../generated-wrappers/static_call_proxy';
|
||||
export * from '../generated-wrappers/test_static_call_target';
|
||||
|
@@ -53,16 +53,18 @@ describe('Authorizable', () => {
|
||||
);
|
||||
});
|
||||
it('should allow owner to add an authorized address', async () => {
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }),
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const isAuthorized = await authorizable.authorized.callAsync(address);
|
||||
expect(isAuthorized).to.be.true();
|
||||
});
|
||||
it('should throw if owner attempts to authorize a duplicate address', async () => {
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }),
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
return expectTransactionFailedAsync(
|
||||
@@ -74,8 +76,9 @@ describe('Authorizable', () => {
|
||||
|
||||
describe('removeAuthorizedAddress', () => {
|
||||
it('should throw if not called by owner', async () => {
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }),
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
return expectTransactionFailedAsync(
|
||||
@@ -87,14 +90,14 @@ describe('Authorizable', () => {
|
||||
});
|
||||
|
||||
it('should allow owner to remove an authorized address', async () => {
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }),
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await authorizable.removeAuthorizedAddress.sendTransactionAsync(address, {
|
||||
from: owner,
|
||||
}),
|
||||
await authorizable.removeAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const isAuthorized = await authorizable.authorized.callAsync(address);
|
||||
@@ -113,8 +116,9 @@ describe('Authorizable', () => {
|
||||
|
||||
describe('removeAuthorizedAddressAtIndex', () => {
|
||||
it('should throw if not called by owner', async () => {
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }),
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const index = new BigNumber(0);
|
||||
@@ -126,8 +130,9 @@ describe('Authorizable', () => {
|
||||
);
|
||||
});
|
||||
it('should throw if index is >= authorities.length', async () => {
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }),
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const index = new BigNumber(1);
|
||||
@@ -150,12 +155,14 @@ describe('Authorizable', () => {
|
||||
it('should throw if address at index does not match target', async () => {
|
||||
const address1 = address;
|
||||
const address2 = notOwner;
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await authorizable.addAuthorizedAddress.sendTransactionAsync(address1, { from: owner }),
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
address1,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await authorizable.addAuthorizedAddress.sendTransactionAsync(address2, { from: owner }),
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
address2,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const address1Index = new BigNumber(0);
|
||||
@@ -167,15 +174,16 @@ describe('Authorizable', () => {
|
||||
);
|
||||
});
|
||||
it('should allow owner to remove an authorized address', async () => {
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }),
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const index = new BigNumber(0);
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, {
|
||||
from: owner,
|
||||
}),
|
||||
await authorizable.removeAuthorizedAddressAtIndex.awaitTransactionSuccessAsync(
|
||||
address,
|
||||
index,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const isAuthorized = await authorizable.authorized.callAsync(address);
|
||||
@@ -187,20 +195,17 @@ describe('Authorizable', () => {
|
||||
it('should return all authorized addresses', async () => {
|
||||
const initial = await authorizable.getAuthorizedAddresses.callAsync();
|
||||
expect(initial).to.have.length(0);
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await authorizable.addAuthorizedAddress.sendTransactionAsync(address, {
|
||||
from: owner,
|
||||
}),
|
||||
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const afterAdd = await authorizable.getAuthorizedAddresses.callAsync();
|
||||
expect(afterAdd).to.have.length(1);
|
||||
expect(afterAdd).to.include(address);
|
||||
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await authorizable.removeAuthorizedAddress.sendTransactionAsync(address, {
|
||||
from: owner,
|
||||
}),
|
||||
await authorizable.removeAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const afterRemove = await authorizable.getAuthorizedAddresses.callAsync();
|
||||
|
1948
contracts/asset-proxy/test/erc1155_proxy.ts
Normal file
1948
contracts/asset-proxy/test/erc1155_proxy.ts
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,10 @@
|
||||
import { env, EnvVars } from '@0x/dev-utils';
|
||||
|
||||
import { coverage, profiler, provider } from '@0x/contracts-test-utils';
|
||||
import { providerUtils } from '@0x/utils';
|
||||
|
||||
before('start web3 provider', () => {
|
||||
provider.start();
|
||||
providerUtils.startProviderEngine(provider);
|
||||
});
|
||||
after('generate coverage report', async () => {
|
||||
if (env.parseBoolean(EnvVars.SolidityCoverage)) {
|
||||
|
@@ -1,3 +1,16 @@
|
||||
import { ERC1155MintableContract, Erc1155Wrapper } from '@0x/contracts-erc1155';
|
||||
import {
|
||||
artifacts as erc20Artifacts,
|
||||
DummyERC20TokenContract,
|
||||
DummyERC20TokenTransferEventArgs,
|
||||
DummyMultipleReturnERC20TokenContract,
|
||||
DummyNoReturnERC20TokenContract,
|
||||
} from '@0x/contracts-erc20';
|
||||
import {
|
||||
artifacts as erc721Artifacts,
|
||||
DummyERC721ReceiverContract,
|
||||
DummyERC721TokenContract,
|
||||
} from '@0x/contracts-erc721';
|
||||
import {
|
||||
chaiSetup,
|
||||
constants,
|
||||
@@ -18,12 +31,7 @@ import * as _ from 'lodash';
|
||||
|
||||
import {
|
||||
artifacts,
|
||||
DummyERC20TokenContract,
|
||||
DummyERC20TokenTransferEventArgs,
|
||||
DummyERC721ReceiverContract,
|
||||
DummyERC721TokenContract,
|
||||
DummyMultipleReturnERC20TokenContract,
|
||||
DummyNoReturnERC20TokenContract,
|
||||
ERC1155ProxyWrapper,
|
||||
ERC20ProxyContract,
|
||||
ERC20Wrapper,
|
||||
ERC721ProxyContract,
|
||||
@@ -36,16 +44,8 @@ import {
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
const assetProxyInterface = new IAssetProxyContract(
|
||||
artifacts.IAssetProxy.compilerOutput.abi,
|
||||
constants.NULL_ADDRESS,
|
||||
provider,
|
||||
);
|
||||
const assetDataInterface = new IAssetDataContract(
|
||||
artifacts.IAssetData.compilerOutput.abi,
|
||||
constants.NULL_ADDRESS,
|
||||
provider,
|
||||
);
|
||||
const assetProxyInterface = new IAssetProxyContract(constants.NULL_ADDRESS, provider);
|
||||
const assetDataInterface = new IAssetDataContract(constants.NULL_ADDRESS, provider);
|
||||
|
||||
// tslint:disable:no-unnecessary-type-assertion
|
||||
describe('Asset Transfer Proxies', () => {
|
||||
@@ -71,6 +71,15 @@ describe('Asset Transfer Proxies', () => {
|
||||
let erc721AFromTokenId: BigNumber;
|
||||
let erc721BFromTokenId: BigNumber;
|
||||
|
||||
let erc1155Proxy: ERC721ProxyContract;
|
||||
let erc1155ProxyWrapper: ERC1155ProxyWrapper;
|
||||
let erc1155Contract: ERC1155MintableContract;
|
||||
let erc1155Contract2: ERC1155MintableContract;
|
||||
let erc1155Wrapper: Erc1155Wrapper;
|
||||
let erc1155Wrapper2: Erc1155Wrapper;
|
||||
let erc1155FungibleTokens: BigNumber[];
|
||||
let erc1155NonFungibleTokensOwnedBySpender: BigNumber[];
|
||||
|
||||
before(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
@@ -94,50 +103,62 @@ describe('Asset Transfer Proxies', () => {
|
||||
);
|
||||
|
||||
// Configure ERC20Proxy
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(authorized, {
|
||||
from: owner,
|
||||
}),
|
||||
await erc20Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
authorized,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(multiAssetProxy.address, {
|
||||
from: owner,
|
||||
}),
|
||||
await erc20Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
multiAssetProxy.address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
|
||||
// Configure ERC721Proxy
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(authorized, {
|
||||
from: owner,
|
||||
}),
|
||||
await erc721Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
authorized,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(multiAssetProxy.address, {
|
||||
from: owner,
|
||||
}),
|
||||
await erc721Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
multiAssetProxy.address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
|
||||
// Configure ERC115Proxy
|
||||
erc1155ProxyWrapper = new ERC1155ProxyWrapper(provider, usedAddresses, owner);
|
||||
erc1155Proxy = await erc1155ProxyWrapper.deployProxyAsync();
|
||||
await erc1155Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
authorized,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await erc1155Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
multiAssetProxy.address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
|
||||
// Configure MultiAssetProxy
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await multiAssetProxy.addAuthorizedAddress.sendTransactionAsync(authorized, {
|
||||
from: owner,
|
||||
}),
|
||||
await multiAssetProxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
||||
authorized,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await multiAssetProxy.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, {
|
||||
from: owner,
|
||||
}),
|
||||
await multiAssetProxy.registerAssetProxy.awaitTransactionSuccessAsync(
|
||||
erc20Proxy.address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await multiAssetProxy.registerAssetProxy.sendTransactionAsync(erc721Proxy.address, {
|
||||
from: owner,
|
||||
}),
|
||||
await multiAssetProxy.registerAssetProxy.awaitTransactionSuccessAsync(
|
||||
erc721Proxy.address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await multiAssetProxy.registerAssetProxy.awaitTransactionSuccessAsync(
|
||||
erc1155Proxy.address,
|
||||
{ from: owner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
|
||||
@@ -148,7 +169,7 @@ describe('Asset Transfer Proxies', () => {
|
||||
constants.DUMMY_TOKEN_DECIMALS,
|
||||
);
|
||||
noReturnErc20Token = await DummyNoReturnERC20TokenContract.deployFrom0xArtifactAsync(
|
||||
artifacts.DummyNoReturnERC20Token,
|
||||
erc20Artifacts.DummyNoReturnERC20Token,
|
||||
provider,
|
||||
txDefaults,
|
||||
constants.DUMMY_TOKEN_NAME,
|
||||
@@ -157,7 +178,7 @@ describe('Asset Transfer Proxies', () => {
|
||||
constants.DUMMY_TOKEN_TOTAL_SUPPLY,
|
||||
);
|
||||
multipleReturnErc20Token = await DummyMultipleReturnERC20TokenContract.deployFrom0xArtifactAsync(
|
||||
artifacts.DummyMultipleReturnERC20Token,
|
||||
erc20Artifacts.DummyMultipleReturnERC20Token,
|
||||
provider,
|
||||
txDefaults,
|
||||
constants.DUMMY_TOKEN_NAME,
|
||||
@@ -167,38 +188,39 @@ describe('Asset Transfer Proxies', () => {
|
||||
);
|
||||
|
||||
await erc20Wrapper.setBalancesAndAllowancesAsync();
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await noReturnErc20Token.setBalance.sendTransactionAsync(fromAddress, constants.INITIAL_ERC20_BALANCE),
|
||||
await noReturnErc20Token.setBalance.awaitTransactionSuccessAsync(
|
||||
fromAddress,
|
||||
constants.INITIAL_ERC20_BALANCE,
|
||||
{
|
||||
from: owner,
|
||||
},
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await noReturnErc20Token.approve.sendTransactionAsync(
|
||||
erc20Proxy.address,
|
||||
constants.INITIAL_ERC20_ALLOWANCE,
|
||||
{ from: fromAddress },
|
||||
),
|
||||
await noReturnErc20Token.approve.awaitTransactionSuccessAsync(
|
||||
erc20Proxy.address,
|
||||
constants.INITIAL_ERC20_ALLOWANCE,
|
||||
{ from: fromAddress },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await multipleReturnErc20Token.setBalance.sendTransactionAsync(
|
||||
fromAddress,
|
||||
constants.INITIAL_ERC20_BALANCE,
|
||||
),
|
||||
await multipleReturnErc20Token.setBalance.awaitTransactionSuccessAsync(
|
||||
fromAddress,
|
||||
constants.INITIAL_ERC20_BALANCE,
|
||||
{
|
||||
from: owner,
|
||||
},
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await multipleReturnErc20Token.approve.sendTransactionAsync(
|
||||
erc20Proxy.address,
|
||||
constants.INITIAL_ERC20_ALLOWANCE,
|
||||
{ from: fromAddress },
|
||||
),
|
||||
await multipleReturnErc20Token.approve.awaitTransactionSuccessAsync(
|
||||
erc20Proxy.address,
|
||||
constants.INITIAL_ERC20_ALLOWANCE,
|
||||
{ from: fromAddress },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
|
||||
// Deploy and configure ERC721 tokens and receiver
|
||||
[erc721TokenA, erc721TokenB] = await erc721Wrapper.deployDummyTokensAsync();
|
||||
erc721Receiver = await DummyERC721ReceiverContract.deployFrom0xArtifactAsync(
|
||||
artifacts.DummyERC721Receiver,
|
||||
erc721Artifacts.DummyERC721Receiver,
|
||||
provider,
|
||||
txDefaults,
|
||||
);
|
||||
@@ -207,6 +229,22 @@ describe('Asset Transfer Proxies', () => {
|
||||
const erc721Balances = await erc721Wrapper.getBalancesAsync();
|
||||
erc721AFromTokenId = erc721Balances[fromAddress][erc721TokenA.address][0];
|
||||
erc721BFromTokenId = erc721Balances[fromAddress][erc721TokenB.address][0];
|
||||
|
||||
// Deploy & configure ERC1155 tokens and receiver
|
||||
[erc1155Wrapper, erc1155Wrapper2] = await erc1155ProxyWrapper.deployDummyContractsAsync();
|
||||
erc1155Contract = erc1155Wrapper.getContract();
|
||||
erc1155Contract2 = erc1155Wrapper2.getContract();
|
||||
await erc1155ProxyWrapper.setBalancesAndAllowancesAsync();
|
||||
erc1155FungibleTokens = erc1155ProxyWrapper.getFungibleTokenIds();
|
||||
const nonFungibleTokens = erc1155ProxyWrapper.getNonFungibleTokenIds();
|
||||
const tokenBalances = await erc1155ProxyWrapper.getBalancesAsync();
|
||||
erc1155NonFungibleTokensOwnedBySpender = [];
|
||||
_.each(nonFungibleTokens, (nonFungibleToken: BigNumber) => {
|
||||
const nonFungibleTokenAsString = nonFungibleToken.toString();
|
||||
const nonFungibleTokenHeldBySpender =
|
||||
tokenBalances.nonFungible[fromAddress][erc1155Contract.address][nonFungibleTokenAsString][0];
|
||||
erc1155NonFungibleTokensOwnedBySpender.push(nonFungibleTokenHeldBySpender);
|
||||
});
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
@@ -364,10 +402,10 @@ describe('Asset Transfer Proxies', () => {
|
||||
toAddress,
|
||||
amount,
|
||||
);
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await erc20TokenA.approve.sendTransactionAsync(erc20Proxy.address, allowance, {
|
||||
from: fromAddress,
|
||||
}),
|
||||
await erc20TokenA.approve.awaitTransactionSuccessAsync(
|
||||
erc20Proxy.address,
|
||||
allowance,
|
||||
{ from: fromAddress },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const erc20Balances = await erc20Wrapper.getBalancesAsync();
|
||||
@@ -396,10 +434,10 @@ describe('Asset Transfer Proxies', () => {
|
||||
toAddress,
|
||||
amount,
|
||||
);
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await noReturnErc20Token.approve.sendTransactionAsync(erc20Proxy.address, allowance, {
|
||||
from: fromAddress,
|
||||
}),
|
||||
await noReturnErc20Token.approve.awaitTransactionSuccessAsync(
|
||||
erc20Proxy.address,
|
||||
allowance,
|
||||
{ from: fromAddress },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const initialFromBalance = await noReturnErc20Token.balanceOf.callAsync(fromAddress);
|
||||
@@ -562,7 +600,7 @@ describe('Asset Transfer Proxies', () => {
|
||||
erc721Receiver.address,
|
||||
amount,
|
||||
);
|
||||
const logDecoder = new LogDecoder(web3Wrapper, artifacts);
|
||||
const logDecoder = new LogDecoder(web3Wrapper, { ...artifacts, ...erc721Artifacts });
|
||||
const tx = await logDecoder.getTxWithDecodedLogsAsync(
|
||||
await web3Wrapper.sendTransactionAsync({
|
||||
to: erc721Proxy.address,
|
||||
@@ -637,10 +675,10 @@ describe('Asset Transfer Proxies', () => {
|
||||
const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
|
||||
expect(ownerFromAsset).to.be.equal(fromAddress);
|
||||
// Remove transfer approval for fromAddress.
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await erc721TokenA.approve.sendTransactionAsync(constants.NULL_ADDRESS, erc721AFromTokenId, {
|
||||
from: fromAddress,
|
||||
}),
|
||||
await erc721TokenA.approve.awaitTransactionSuccessAsync(
|
||||
constants.NULL_ADDRESS,
|
||||
erc721AFromTokenId,
|
||||
{ from: fromAddress },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
// Perform a transfer; expect this to fail.
|
||||
@@ -754,7 +792,7 @@ describe('Asset Transfer Proxies', () => {
|
||||
inputAmount,
|
||||
);
|
||||
const erc20Balances = await erc20Wrapper.getBalancesAsync();
|
||||
const logDecoder = new LogDecoder(web3Wrapper, artifacts);
|
||||
const logDecoder = new LogDecoder(web3Wrapper, { ...artifacts, ...erc20Artifacts });
|
||||
const tx = await logDecoder.getTxWithDecodedLogsAsync(
|
||||
await web3Wrapper.sendTransactionAsync({
|
||||
to: multiAssetProxy.address,
|
||||
@@ -938,6 +976,314 @@ describe('Asset Transfer Proxies', () => {
|
||||
expect(newOwnerFromAsset1).to.be.equal(toAddress);
|
||||
expect(newOwnerFromAsset2).to.be.equal(toAddress);
|
||||
});
|
||||
it('should transfer a fungible ERC1155 token', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [fromAddress, toAddress];
|
||||
const tokensToTransfer = erc1155FungibleTokens.slice(0, 1);
|
||||
const valuesToTransfer = [new BigNumber(25)];
|
||||
const valueMultiplier = new BigNumber(23);
|
||||
const receiverCallbackData = '0x0102030405';
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [
|
||||
// from
|
||||
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||
// to
|
||||
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// encode erc1155 asset data
|
||||
const erc1155AssetData = assetDataUtils.encodeERC1155AssetData(
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
// encode multi-asset data
|
||||
const multiAssetAmount = new BigNumber(5);
|
||||
const amounts = [valueMultiplier];
|
||||
const nestedAssetData = [erc1155AssetData];
|
||||
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
|
||||
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
assetData,
|
||||
fromAddress,
|
||||
toAddress,
|
||||
multiAssetAmount,
|
||||
);
|
||||
// execute transfer
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await web3Wrapper.sendTransactionAsync({
|
||||
to: multiAssetProxy.address,
|
||||
data,
|
||||
from: authorized,
|
||||
}),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
// check balances
|
||||
const totalValueTransferred = valuesToTransfer[0].times(valueMultiplier).times(multiAssetAmount);
|
||||
const expectedFinalBalances = [
|
||||
// from
|
||||
expectedInitialBalances[0].minus(totalValueTransferred),
|
||||
// to
|
||||
expectedInitialBalances[1].plus(totalValueTransferred),
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||
});
|
||||
it('should successfully transfer multiple fungible tokens of the same ERC1155 contract', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [fromAddress, toAddress];
|
||||
const tokensToTransfer = erc1155FungibleTokens.slice(0, 3);
|
||||
const valuesToTransfer = [new BigNumber(25), new BigNumber(35), new BigNumber(45)];
|
||||
const valueMultiplier = new BigNumber(23);
|
||||
const receiverCallbackData = '0x0102030405';
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [
|
||||
// from
|
||||
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||
// to
|
||||
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// encode erc1155 asset data
|
||||
const erc1155AssetData = assetDataUtils.encodeERC1155AssetData(
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
// encode multi-asset data
|
||||
const multiAssetAmount = new BigNumber(5);
|
||||
const amounts = [valueMultiplier];
|
||||
const nestedAssetData = [erc1155AssetData];
|
||||
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
|
||||
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
assetData,
|
||||
fromAddress,
|
||||
toAddress,
|
||||
multiAssetAmount,
|
||||
);
|
||||
// execute transfer
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await web3Wrapper.sendTransactionAsync({
|
||||
to: multiAssetProxy.address,
|
||||
data,
|
||||
from: authorized,
|
||||
}),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
// check balances
|
||||
const totalValuesTransferred = _.map(valuesToTransfer, (value: BigNumber) => {
|
||||
return value.times(valueMultiplier).times(multiAssetAmount);
|
||||
});
|
||||
const expectedFinalBalances = [
|
||||
// from
|
||||
expectedInitialBalances[0].minus(totalValuesTransferred[0]),
|
||||
expectedInitialBalances[1].minus(totalValuesTransferred[1]),
|
||||
expectedInitialBalances[2].minus(totalValuesTransferred[2]),
|
||||
// to
|
||||
expectedInitialBalances[3].plus(totalValuesTransferred[0]),
|
||||
expectedInitialBalances[4].plus(totalValuesTransferred[1]),
|
||||
expectedInitialBalances[5].plus(totalValuesTransferred[2]),
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||
});
|
||||
it('should successfully transfer multiple fungible/non-fungible tokens of the same ERC1155 contract', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [fromAddress, toAddress];
|
||||
const fungibleTokensToTransfer = erc1155FungibleTokens.slice(0, 1);
|
||||
const nonFungibleTokensToTransfer = erc1155NonFungibleTokensOwnedBySpender.slice(0, 1);
|
||||
const tokensToTransfer = fungibleTokensToTransfer.concat(nonFungibleTokensToTransfer);
|
||||
const valuesToTransfer = [new BigNumber(25), new BigNumber(1)];
|
||||
const valueMultiplier = new BigNumber(1);
|
||||
const receiverCallbackData = '0x0102030405';
|
||||
// check balances before transfer
|
||||
const nftOwnerBalance = new BigNumber(1);
|
||||
const nftNotOwnerBalance = new BigNumber(0);
|
||||
const expectedInitialBalances = [
|
||||
// from
|
||||
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||
nftOwnerBalance,
|
||||
// to
|
||||
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||
nftNotOwnerBalance,
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// encode erc1155 asset data
|
||||
const erc1155AssetData = assetDataUtils.encodeERC1155AssetData(
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
// encode multi-asset data
|
||||
const multiAssetAmount = new BigNumber(1);
|
||||
const amounts = [valueMultiplier];
|
||||
const nestedAssetData = [erc1155AssetData];
|
||||
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
|
||||
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
assetData,
|
||||
fromAddress,
|
||||
toAddress,
|
||||
multiAssetAmount,
|
||||
);
|
||||
// execute transfer
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await web3Wrapper.sendTransactionAsync({
|
||||
to: multiAssetProxy.address,
|
||||
data,
|
||||
from: authorized,
|
||||
}),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
// check balances
|
||||
const totalValuesTransferred = _.map(valuesToTransfer, (value: BigNumber) => {
|
||||
return value.times(valueMultiplier).times(multiAssetAmount);
|
||||
});
|
||||
const expectedFinalBalances = [
|
||||
// from
|
||||
expectedInitialBalances[0].minus(totalValuesTransferred[0]),
|
||||
expectedInitialBalances[1].minus(totalValuesTransferred[1]),
|
||||
// to
|
||||
expectedInitialBalances[2].plus(totalValuesTransferred[0]),
|
||||
expectedInitialBalances[3].plus(totalValuesTransferred[1]),
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||
});
|
||||
it('should successfully transfer multiple different ERC1155 tokens', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [fromAddress, toAddress];
|
||||
const tokensToTransfer = erc1155FungibleTokens.slice(0, 1);
|
||||
const valuesToTransfer = [new BigNumber(25)];
|
||||
const valueMultiplier = new BigNumber(23);
|
||||
const receiverCallbackData = '0x0102030405';
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [
|
||||
// from
|
||||
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||
// to
|
||||
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
await erc1155Wrapper2.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// encode erc1155 asset data
|
||||
const erc1155AssetData1 = assetDataUtils.encodeERC1155AssetData(
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
const erc1155AssetData2 = assetDataUtils.encodeERC1155AssetData(
|
||||
erc1155Contract2.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
// encode multi-asset data
|
||||
const multiAssetAmount = new BigNumber(5);
|
||||
const amounts = [valueMultiplier, valueMultiplier];
|
||||
const nestedAssetData = [erc1155AssetData1, erc1155AssetData2];
|
||||
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
|
||||
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
assetData,
|
||||
fromAddress,
|
||||
toAddress,
|
||||
multiAssetAmount,
|
||||
);
|
||||
// execute transfer
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await web3Wrapper.sendTransactionAsync({
|
||||
to: multiAssetProxy.address,
|
||||
data,
|
||||
from: authorized,
|
||||
}),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
// check balances
|
||||
const totalValueTransferred = valuesToTransfer[0].times(valueMultiplier).times(multiAssetAmount);
|
||||
const expectedFinalBalances = [
|
||||
// from
|
||||
expectedInitialBalances[0].minus(totalValueTransferred),
|
||||
// to
|
||||
expectedInitialBalances[1].plus(totalValueTransferred),
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||
await erc1155Wrapper2.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||
});
|
||||
it('should successfully transfer a combination of ERC20, ERC721, and ERC1155 tokens', async () => {
|
||||
// setup test parameters
|
||||
const inputAmount = new BigNumber(1);
|
||||
const erc20Amount = new BigNumber(10);
|
||||
const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
|
||||
const erc721Amount = new BigNumber(1);
|
||||
const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
|
||||
const erc1155TokenHolders = [fromAddress, toAddress];
|
||||
const erc1155TokensToTransfer = erc1155FungibleTokens.slice(0, 1);
|
||||
const erc1155ValuesToTransfer = [new BigNumber(25)];
|
||||
const erc1155Amount = new BigNumber(23);
|
||||
const erc1155ReceiverCallbackData = '0x0102030405';
|
||||
const erc1155AssetData = assetDataUtils.encodeERC1155AssetData(
|
||||
erc1155Contract.address,
|
||||
erc1155TokensToTransfer,
|
||||
erc1155ValuesToTransfer,
|
||||
erc1155ReceiverCallbackData,
|
||||
);
|
||||
const amounts = [erc20Amount, erc721Amount, erc1155Amount];
|
||||
const nestedAssetData = [erc20AssetData, erc721AssetData, erc1155AssetData];
|
||||
const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
|
||||
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
assetData,
|
||||
fromAddress,
|
||||
toAddress,
|
||||
inputAmount,
|
||||
);
|
||||
// check balances before transfer
|
||||
const erc20Balances = await erc20Wrapper.getBalancesAsync();
|
||||
const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
|
||||
expect(ownerFromAsset).to.be.equal(fromAddress);
|
||||
const erc1155ExpectedInitialBalances = [
|
||||
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(
|
||||
erc1155TokenHolders,
|
||||
erc1155TokensToTransfer,
|
||||
erc1155ExpectedInitialBalances,
|
||||
);
|
||||
// execute transfer
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await web3Wrapper.sendTransactionAsync({
|
||||
to: multiAssetProxy.address,
|
||||
data,
|
||||
from: authorized,
|
||||
gas: 1000000,
|
||||
}),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
// check balances after transfer
|
||||
const newBalances = await erc20Wrapper.getBalancesAsync();
|
||||
const totalAmount = inputAmount.times(erc20Amount);
|
||||
expect(newBalances[fromAddress][erc20TokenA.address]).to.be.bignumber.equal(
|
||||
erc20Balances[fromAddress][erc20TokenA.address].minus(totalAmount),
|
||||
);
|
||||
expect(newBalances[toAddress][erc20TokenA.address]).to.be.bignumber.equal(
|
||||
erc20Balances[toAddress][erc20TokenA.address].plus(totalAmount),
|
||||
);
|
||||
const newOwnerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
|
||||
expect(newOwnerFromAsset).to.be.equal(toAddress);
|
||||
const erc1155TotalValueTransferred = erc1155ValuesToTransfer[0].times(erc1155Amount).times(inputAmount);
|
||||
const expectedFinalBalances = [
|
||||
erc1155ExpectedInitialBalances[0].minus(erc1155TotalValueTransferred),
|
||||
erc1155ExpectedInitialBalances[1].plus(erc1155TotalValueTransferred),
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(
|
||||
erc1155TokenHolders,
|
||||
erc1155TokensToTransfer,
|
||||
expectedFinalBalances,
|
||||
);
|
||||
});
|
||||
it('should successfully transfer a combination of ERC20 and ERC721 tokens', async () => {
|
||||
const inputAmount = new BigNumber(1);
|
||||
const erc20Amount = new BigNumber(10);
|
||||
|
250
contracts/asset-proxy/test/static_call_proxy.ts
Normal file
250
contracts/asset-proxy/test/static_call_proxy.ts
Normal file
@@ -0,0 +1,250 @@
|
||||
import {
|
||||
chaiSetup,
|
||||
constants,
|
||||
expectTransactionFailedAsync,
|
||||
expectTransactionFailedWithoutReasonAsync,
|
||||
provider,
|
||||
txDefaults,
|
||||
web3Wrapper,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||
import { assetDataUtils } from '@0x/order-utils';
|
||||
import { AssetProxyId, RevertReason } from '@0x/types';
|
||||
import { AbiEncoder, BigNumber } from '@0x/utils';
|
||||
import * as chai from 'chai';
|
||||
import * as ethUtil from 'ethereumjs-util';
|
||||
|
||||
import { artifacts, IAssetProxyContract, StaticCallProxyContract, TestStaticCallTargetContract } from '../src';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
|
||||
describe('StaticCallProxy', () => {
|
||||
const amount = constants.ZERO_AMOUNT;
|
||||
let fromAddress: string;
|
||||
let toAddress: string;
|
||||
|
||||
let staticCallProxy: IAssetProxyContract;
|
||||
let staticCallTarget: TestStaticCallTargetContract;
|
||||
|
||||
before(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
after(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
before(async () => {
|
||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
||||
[fromAddress, toAddress] = accounts.slice(0, 2);
|
||||
const staticCallProxyWithoutTransferFrom = await StaticCallProxyContract.deployFrom0xArtifactAsync(
|
||||
artifacts.StaticCallProxy,
|
||||
provider,
|
||||
txDefaults,
|
||||
);
|
||||
staticCallProxy = new IAssetProxyContract(staticCallProxyWithoutTransferFrom.address, provider, txDefaults);
|
||||
staticCallTarget = await TestStaticCallTargetContract.deployFrom0xArtifactAsync(
|
||||
artifacts.TestStaticCallTarget,
|
||||
provider,
|
||||
txDefaults,
|
||||
);
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
afterEach(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
|
||||
describe('general', () => {
|
||||
it('should revert if undefined function is called', async () => {
|
||||
const undefinedSelector = '0x01020304';
|
||||
await expectTransactionFailedWithoutReasonAsync(
|
||||
web3Wrapper.sendTransactionAsync({
|
||||
from: fromAddress,
|
||||
to: staticCallProxy.address,
|
||||
value: constants.ZERO_AMOUNT,
|
||||
data: undefinedSelector,
|
||||
}),
|
||||
);
|
||||
});
|
||||
it('should have an id of 0xc339d10a', async () => {
|
||||
const proxyId = await staticCallProxy.getProxyId.callAsync();
|
||||
const expectedProxyId = AssetProxyId.StaticCall;
|
||||
expect(proxyId).to.equal(expectedProxyId);
|
||||
});
|
||||
});
|
||||
describe('transferFrom', () => {
|
||||
it('should revert if assetData lies outside the bounds of calldata', async () => {
|
||||
const staticCallData = staticCallTarget.noInputFunction.getABIEncodedTransactionData();
|
||||
const expectedResultHash = constants.KECCAK256_NULL;
|
||||
const assetData = assetDataUtils.encodeStaticCallAssetData(
|
||||
staticCallTarget.address,
|
||||
staticCallData,
|
||||
expectedResultHash,
|
||||
);
|
||||
const txData = staticCallProxy.transferFrom.getABIEncodedTransactionData(
|
||||
assetData,
|
||||
fromAddress,
|
||||
toAddress,
|
||||
amount,
|
||||
);
|
||||
const offsetToAssetData = '0000000000000000000000000000000000000000000000000000000000000080';
|
||||
const txDataEndBuffer = ethUtil.toBuffer((txData.length - 2) / 2 - 4);
|
||||
const paddedTxDataEndBuffer = ethUtil.setLengthLeft(txDataEndBuffer, 32);
|
||||
const invalidOffsetToAssetData = ethUtil.bufferToHex(paddedTxDataEndBuffer).slice(2);
|
||||
const newAssetData = '0000000000000000000000000000000000000000000000000000000000000304';
|
||||
const badTxData = `${txData.replace(offsetToAssetData, invalidOffsetToAssetData)}${newAssetData}`;
|
||||
await expectTransactionFailedAsync(
|
||||
web3Wrapper.sendTransactionAsync({
|
||||
to: staticCallProxy.address,
|
||||
from: fromAddress,
|
||||
data: badTxData,
|
||||
}),
|
||||
RevertReason.InvalidAssetDataEnd,
|
||||
);
|
||||
});
|
||||
it('should revert if the length of assetData, excluding the proxyId, is not a multiple of 32', async () => {
|
||||
const staticCallData = staticCallTarget.noInputFunction.getABIEncodedTransactionData();
|
||||
const expectedResultHash = constants.KECCAK256_NULL;
|
||||
const assetData = `${assetDataUtils.encodeStaticCallAssetData(
|
||||
staticCallTarget.address,
|
||||
staticCallData,
|
||||
expectedResultHash,
|
||||
)}01`;
|
||||
await expectTransactionFailedAsync(
|
||||
staticCallProxy.transferFrom.sendTransactionAsync(assetData, fromAddress, toAddress, amount),
|
||||
RevertReason.InvalidAssetDataLength,
|
||||
);
|
||||
});
|
||||
it('should revert if the length of assetData is less than 100 bytes', async () => {
|
||||
const staticCallData = constants.NULL_BYTES;
|
||||
const expectedResultHash = constants.KECCAK256_NULL;
|
||||
const assetData = assetDataUtils
|
||||
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||
.slice(0, -128);
|
||||
const assetDataByteLen = (assetData.length - 2) / 2;
|
||||
expect((assetDataByteLen - 4) % 32).to.equal(0);
|
||||
await expectTransactionFailedAsync(
|
||||
staticCallProxy.transferFrom.sendTransactionAsync(assetData, fromAddress, toAddress, amount),
|
||||
RevertReason.InvalidAssetDataLength,
|
||||
);
|
||||
});
|
||||
it('should revert if the offset to `staticCallData` points to outside of assetData', async () => {
|
||||
const staticCallData = staticCallTarget.noInputFunction.getABIEncodedTransactionData();
|
||||
const expectedResultHash = constants.KECCAK256_NULL;
|
||||
const assetData = assetDataUtils.encodeStaticCallAssetData(
|
||||
staticCallTarget.address,
|
||||
staticCallData,
|
||||
expectedResultHash,
|
||||
);
|
||||
const offsetToStaticCallData = '0000000000000000000000000000000000000000000000000000000000000060';
|
||||
const assetDataEndBuffer = ethUtil.toBuffer((assetData.length - 2) / 2 - 4);
|
||||
const paddedAssetDataEndBuffer = ethUtil.setLengthLeft(assetDataEndBuffer, 32);
|
||||
const invalidOffsetToStaticCallData = ethUtil.bufferToHex(paddedAssetDataEndBuffer).slice(2);
|
||||
const newStaticCallData = '0000000000000000000000000000000000000000000000000000000000000304';
|
||||
const badAssetData = `${assetData.replace(
|
||||
offsetToStaticCallData,
|
||||
invalidOffsetToStaticCallData,
|
||||
)}${newStaticCallData}`;
|
||||
await expectTransactionFailedAsync(
|
||||
staticCallProxy.transferFrom.sendTransactionAsync(badAssetData, fromAddress, toAddress, amount),
|
||||
RevertReason.InvalidStaticCallDataOffset,
|
||||
);
|
||||
});
|
||||
it('should revert if the callTarget attempts to write to state', async () => {
|
||||
const staticCallData = staticCallTarget.updateState.getABIEncodedTransactionData();
|
||||
const expectedResultHash = constants.KECCAK256_NULL;
|
||||
const assetData = assetDataUtils.encodeStaticCallAssetData(
|
||||
staticCallTarget.address,
|
||||
staticCallData,
|
||||
expectedResultHash,
|
||||
);
|
||||
await expectTransactionFailedWithoutReasonAsync(
|
||||
staticCallProxy.transferFrom.sendTransactionAsync(assetData, fromAddress, toAddress, amount),
|
||||
);
|
||||
});
|
||||
it('should revert with data provided by the callTarget if the staticcall reverts', async () => {
|
||||
const staticCallData = staticCallTarget.assertEvenNumber.getABIEncodedTransactionData(new BigNumber(1));
|
||||
const expectedResultHash = constants.KECCAK256_NULL;
|
||||
const assetData = assetDataUtils.encodeStaticCallAssetData(
|
||||
staticCallTarget.address,
|
||||
staticCallData,
|
||||
expectedResultHash,
|
||||
);
|
||||
await expectTransactionFailedAsync(
|
||||
staticCallProxy.transferFrom.sendTransactionAsync(assetData, fromAddress, toAddress, amount),
|
||||
RevertReason.TargetNotEven,
|
||||
);
|
||||
});
|
||||
it('should revert if the hash of the output is different than expected expected', async () => {
|
||||
const staticCallData = staticCallTarget.isOddNumber.getABIEncodedTransactionData(new BigNumber(0));
|
||||
const trueAsBuffer = ethUtil.toBuffer('0x0000000000000000000000000000000000000000000000000000000000000001');
|
||||
const expectedResultHash = ethUtil.bufferToHex(ethUtil.sha3(trueAsBuffer));
|
||||
const assetData = assetDataUtils.encodeStaticCallAssetData(
|
||||
staticCallTarget.address,
|
||||
staticCallData,
|
||||
expectedResultHash,
|
||||
);
|
||||
await expectTransactionFailedAsync(
|
||||
staticCallProxy.transferFrom.sendTransactionAsync(assetData, fromAddress, toAddress, amount),
|
||||
RevertReason.UnexpectedStaticCallResult,
|
||||
);
|
||||
});
|
||||
it('should be successful if a function call with no inputs is successful', async () => {
|
||||
const staticCallData = staticCallTarget.noInputFunction.getABIEncodedTransactionData();
|
||||
const expectedResultHash = constants.KECCAK256_NULL;
|
||||
const assetData = assetDataUtils.encodeStaticCallAssetData(
|
||||
staticCallTarget.address,
|
||||
staticCallData,
|
||||
expectedResultHash,
|
||||
);
|
||||
await staticCallProxy.transferFrom.awaitTransactionSuccessAsync(assetData, fromAddress, toAddress, amount);
|
||||
});
|
||||
it('should be successful if a function call with one static input returns the correct value', async () => {
|
||||
const staticCallData = staticCallTarget.isOddNumber.getABIEncodedTransactionData(new BigNumber(1));
|
||||
const trueAsBuffer = ethUtil.toBuffer('0x0000000000000000000000000000000000000000000000000000000000000001');
|
||||
const expectedResultHash = ethUtil.bufferToHex(ethUtil.sha3(trueAsBuffer));
|
||||
const assetData = assetDataUtils.encodeStaticCallAssetData(
|
||||
staticCallTarget.address,
|
||||
staticCallData,
|
||||
expectedResultHash,
|
||||
);
|
||||
await staticCallProxy.transferFrom.awaitTransactionSuccessAsync(assetData, fromAddress, toAddress, amount);
|
||||
});
|
||||
it('should be successful if a function with one dynamic input is successful', async () => {
|
||||
const dynamicInput = '0x0102030405060708';
|
||||
const staticCallData = staticCallTarget.dynamicInputFunction.getABIEncodedTransactionData(dynamicInput);
|
||||
const expectedResultHash = constants.KECCAK256_NULL;
|
||||
const assetData = assetDataUtils.encodeStaticCallAssetData(
|
||||
staticCallTarget.address,
|
||||
staticCallData,
|
||||
expectedResultHash,
|
||||
);
|
||||
await staticCallProxy.transferFrom.awaitTransactionSuccessAsync(assetData, fromAddress, toAddress, amount);
|
||||
});
|
||||
it('should be successful if a function call returns a complex type', async () => {
|
||||
const a = new BigNumber(1);
|
||||
const b = new BigNumber(2);
|
||||
const staticCallData = staticCallTarget.returnComplexType.getABIEncodedTransactionData(a, b);
|
||||
const abiEncoder = new AbiEncoder.DynamicBytes({
|
||||
name: '',
|
||||
type: 'bytes',
|
||||
});
|
||||
const aHex = '0000000000000000000000000000000000000000000000000000000000000001';
|
||||
const bHex = '0000000000000000000000000000000000000000000000000000000000000002';
|
||||
const expectedResults = `${staticCallTarget.address}${aHex}${bHex}`;
|
||||
const offset = '0000000000000000000000000000000000000000000000000000000000000020';
|
||||
const encodedExpectedResultWithOffset = `0x${offset}${abiEncoder.encode(expectedResults).slice(2)}`;
|
||||
const expectedResultHash = ethUtil.bufferToHex(
|
||||
ethUtil.sha3(ethUtil.toBuffer(encodedExpectedResultWithOffset)),
|
||||
);
|
||||
const assetData = assetDataUtils.encodeStaticCallAssetData(
|
||||
staticCallTarget.address,
|
||||
staticCallData,
|
||||
expectedResultHash,
|
||||
);
|
||||
await staticCallProxy.transferFrom.awaitTransactionSuccessAsync(assetData, fromAddress, toAddress, amount);
|
||||
});
|
||||
});
|
||||
});
|
401
contracts/asset-proxy/test/utils/erc1155_proxy_wrapper.ts
Normal file
401
contracts/asset-proxy/test/utils/erc1155_proxy_wrapper.ts
Normal file
@@ -0,0 +1,401 @@
|
||||
import { artifacts as erc1155Artifacts, ERC1155MintableContract, Erc1155Wrapper } from '@0x/contracts-erc1155';
|
||||
import {
|
||||
constants,
|
||||
ERC1155FungibleHoldingsByOwner,
|
||||
ERC1155HoldingsByOwner,
|
||||
ERC1155NonFungibleHoldingsByOwner,
|
||||
LogDecoder,
|
||||
txDefaults,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { assetDataUtils } from '@0x/order-utils';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import { Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { artifacts, ERC1155ProxyContract, IAssetProxyContract } from '../../src';
|
||||
|
||||
export class ERC1155ProxyWrapper {
|
||||
private readonly _tokenOwnerAddresses: string[];
|
||||
private readonly _fungibleTokenIds: string[];
|
||||
private readonly _nonFungibleTokenIds: string[];
|
||||
private readonly _nfts: Array<{ id: BigNumber; tokenId: BigNumber }>;
|
||||
private readonly _contractOwnerAddress: string;
|
||||
private readonly _web3Wrapper: Web3Wrapper;
|
||||
private readonly _provider: Provider;
|
||||
private readonly _logDecoder: LogDecoder;
|
||||
private readonly _dummyTokenWrappers: Erc1155Wrapper[];
|
||||
private readonly _assetProxyInterface: IAssetProxyContract;
|
||||
private _proxyContract?: ERC1155ProxyContract;
|
||||
private _proxyIdIfExists?: string;
|
||||
private _initialTokenIdsByOwner: ERC1155HoldingsByOwner = { fungible: {}, nonFungible: {} };
|
||||
|
||||
constructor(provider: Provider, tokenOwnerAddresses: string[], contractOwnerAddress: string) {
|
||||
this._web3Wrapper = new Web3Wrapper(provider);
|
||||
this._provider = provider;
|
||||
const allArtifacts = _.merge(artifacts, erc1155Artifacts);
|
||||
this._logDecoder = new LogDecoder(this._web3Wrapper, allArtifacts);
|
||||
this._dummyTokenWrappers = [];
|
||||
this._assetProxyInterface = new IAssetProxyContract(constants.NULL_ADDRESS, provider);
|
||||
this._tokenOwnerAddresses = tokenOwnerAddresses;
|
||||
this._contractOwnerAddress = contractOwnerAddress;
|
||||
this._fungibleTokenIds = [];
|
||||
this._nonFungibleTokenIds = [];
|
||||
this._nfts = [];
|
||||
}
|
||||
/**
|
||||
* @dev Deploys dummy ERC1155 contracts
|
||||
* @return An array of ERC1155 wrappers; one for each deployed contract.
|
||||
*/
|
||||
public async deployDummyContractsAsync(): Promise<Erc1155Wrapper[]> {
|
||||
// tslint:disable-next-line:no-unused-variable
|
||||
for (const i of _.times(constants.NUM_DUMMY_ERC1155_CONTRACTS_TO_DEPLOY)) {
|
||||
const erc1155Contract = await ERC1155MintableContract.deployFrom0xArtifactAsync(
|
||||
erc1155Artifacts.ERC1155Mintable,
|
||||
this._provider,
|
||||
txDefaults,
|
||||
);
|
||||
const erc1155Wrapper = new Erc1155Wrapper(erc1155Contract, this._provider, this._contractOwnerAddress);
|
||||
this._dummyTokenWrappers.push(erc1155Wrapper);
|
||||
}
|
||||
return this._dummyTokenWrappers;
|
||||
}
|
||||
/**
|
||||
* @dev Deploys the ERC1155 proxy
|
||||
* @return Deployed ERC1155 proxy contract instance
|
||||
*/
|
||||
public async deployProxyAsync(): Promise<ERC1155ProxyContract> {
|
||||
this._proxyContract = await ERC1155ProxyContract.deployFrom0xArtifactAsync(
|
||||
artifacts.ERC1155Proxy,
|
||||
this._provider,
|
||||
txDefaults,
|
||||
);
|
||||
this._proxyIdIfExists = await this._proxyContract.getProxyId.callAsync();
|
||||
return this._proxyContract;
|
||||
}
|
||||
/**
|
||||
* @dev Gets the ERC1155 proxy id
|
||||
*/
|
||||
public getProxyId(): string {
|
||||
this._validateProxyContractExistsOrThrow();
|
||||
return this._proxyIdIfExists as string;
|
||||
}
|
||||
/**
|
||||
* @dev generates abi-encoded tx data for transferring erc1155 fungible/non-fungible tokens.
|
||||
* @param from source address
|
||||
* @param to destination address
|
||||
* @param contractAddress address of erc155 contract
|
||||
* @param tokensToTransfer array of erc1155 tokens to transfer
|
||||
* @param valuesToTransfer array of corresponding values for each erc1155 token to transfer
|
||||
* @param valueMultiplier each value in `valuesToTransfer` is multiplied by this
|
||||
* @param receiverCallbackData callback data if `to` is a contract
|
||||
* @param authorizedSender sender of `transferFrom` transaction
|
||||
* @param extraData extra data to append to `transferFrom` transaction. Optional.
|
||||
* @return abi encoded tx data.
|
||||
*/
|
||||
public getTransferFromAbiEncodedTxData(
|
||||
from: string,
|
||||
to: string,
|
||||
contractAddress: string,
|
||||
tokensToTransfer: BigNumber[],
|
||||
valuesToTransfer: BigNumber[],
|
||||
valueMultiplier: BigNumber,
|
||||
receiverCallbackData: string,
|
||||
authorizedSender: string,
|
||||
assetData_?: string,
|
||||
): string {
|
||||
this._validateProxyContractExistsOrThrow();
|
||||
const assetData =
|
||||
assetData_ === undefined
|
||||
? assetDataUtils.encodeERC1155AssetData(
|
||||
contractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
)
|
||||
: assetData_;
|
||||
const data = this._assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
assetData,
|
||||
from,
|
||||
to,
|
||||
valueMultiplier,
|
||||
);
|
||||
return data;
|
||||
}
|
||||
/**
|
||||
* @dev transfers erc1155 fungible/non-fungible tokens.
|
||||
* @param txData: abi-encoded tx data
|
||||
* @param authorizedSender sender of `transferFrom` transaction
|
||||
*/
|
||||
public async transferFromRawAsync(
|
||||
txData: string,
|
||||
authorizedSender: string,
|
||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||
const txHash = await this._web3Wrapper.sendTransactionAsync({
|
||||
to: (this._proxyContract as ERC1155ProxyContract).address,
|
||||
data: txData,
|
||||
from: authorizedSender,
|
||||
gas: 300000,
|
||||
});
|
||||
const txReceipt = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
|
||||
return txReceipt;
|
||||
}
|
||||
/**
|
||||
* @dev transfers erc1155 fungible/non-fungible tokens.
|
||||
* @param from source address
|
||||
* @param to destination address
|
||||
* @param contractAddress address of erc155 contract
|
||||
* @param tokensToTransfer array of erc1155 tokens to transfer
|
||||
* @param valuesToTransfer array of corresponding values for each erc1155 token to transfer
|
||||
* @param valueMultiplier each value in `valuesToTransfer` is multiplied by this
|
||||
* @param receiverCallbackData callback data if `to` is a contract
|
||||
* @param authorizedSender sender of `transferFrom` transaction
|
||||
* @param extraData extra data to append to `transferFrom` transaction. Optional.
|
||||
* @return tranasction hash.
|
||||
*/
|
||||
public async transferFromAsync(
|
||||
from: string,
|
||||
to: string,
|
||||
contractAddress: string,
|
||||
tokensToTransfer: BigNumber[],
|
||||
valuesToTransfer: BigNumber[],
|
||||
valueMultiplier: BigNumber,
|
||||
receiverCallbackData: string,
|
||||
authorizedSender: string,
|
||||
assetData_?: string,
|
||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||
this._validateProxyContractExistsOrThrow();
|
||||
const assetData =
|
||||
assetData_ === undefined
|
||||
? assetDataUtils.encodeERC1155AssetData(
|
||||
contractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
)
|
||||
: assetData_;
|
||||
const data = this._assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
assetData,
|
||||
from,
|
||||
to,
|
||||
valueMultiplier,
|
||||
);
|
||||
const txHash = await this._web3Wrapper.sendTransactionAsync({
|
||||
to: (this._proxyContract as ERC1155ProxyContract).address,
|
||||
data,
|
||||
from: authorizedSender,
|
||||
gas: 300000,
|
||||
});
|
||||
const txReceipt = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
|
||||
return txReceipt;
|
||||
}
|
||||
/**
|
||||
* @dev For each deployed ERC1155 contract, this function mints a set of fungible/non-fungible
|
||||
* tokens for each token owner address (`_tokenOwnerAddresses`).
|
||||
* @return Balances of each token owner, across all ERC1155 contracts and tokens.
|
||||
*/
|
||||
public async setBalancesAndAllowancesAsync(): Promise<ERC1155HoldingsByOwner> {
|
||||
this._validateDummyTokenContractsExistOrThrow();
|
||||
this._validateProxyContractExistsOrThrow();
|
||||
this._initialTokenIdsByOwner = {
|
||||
fungible: {},
|
||||
nonFungible: {},
|
||||
};
|
||||
const fungibleHoldingsByOwner: ERC1155FungibleHoldingsByOwner = {};
|
||||
const nonFungibleHoldingsByOwner: ERC1155NonFungibleHoldingsByOwner = {};
|
||||
// Set balances accordingly
|
||||
for (const dummyWrapper of this._dummyTokenWrappers) {
|
||||
const dummyAddress = dummyWrapper.getContract().address;
|
||||
// tslint:disable-next-line:no-unused-variable
|
||||
for (const i of _.times(constants.NUM_ERC1155_FUNGIBLE_TOKENS_MINT)) {
|
||||
// Create a fungible token
|
||||
const tokenId = await dummyWrapper.mintFungibleTokensAsync(
|
||||
this._tokenOwnerAddresses,
|
||||
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||
);
|
||||
const tokenIdAsString = tokenId.toString();
|
||||
this._fungibleTokenIds.push(tokenIdAsString);
|
||||
// Mint tokens for each owner for this token
|
||||
for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
|
||||
// tslint:disable-next-line:no-unused-variable
|
||||
if (fungibleHoldingsByOwner[tokenOwnerAddress] === undefined) {
|
||||
fungibleHoldingsByOwner[tokenOwnerAddress] = {};
|
||||
}
|
||||
if (fungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress] === undefined) {
|
||||
fungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress] = {};
|
||||
}
|
||||
fungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress][tokenIdAsString] =
|
||||
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE;
|
||||
await dummyWrapper.setApprovalForAllAsync(
|
||||
tokenOwnerAddress,
|
||||
(this._proxyContract as ERC1155ProxyContract).address,
|
||||
true,
|
||||
);
|
||||
}
|
||||
}
|
||||
// Non-fungible tokens
|
||||
// tslint:disable-next-line:no-unused-variable
|
||||
for (const j of _.times(constants.NUM_ERC1155_NONFUNGIBLE_TOKENS_MINT)) {
|
||||
const [tokenId, nftIds] = await dummyWrapper.mintNonFungibleTokensAsync(this._tokenOwnerAddresses);
|
||||
const tokenIdAsString = tokenId.toString();
|
||||
this._nonFungibleTokenIds.push(tokenIdAsString);
|
||||
_.each(this._tokenOwnerAddresses, async (tokenOwnerAddress: string, i: number) => {
|
||||
if (nonFungibleHoldingsByOwner[tokenOwnerAddress] === undefined) {
|
||||
nonFungibleHoldingsByOwner[tokenOwnerAddress] = {};
|
||||
}
|
||||
if (nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress] === undefined) {
|
||||
nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress] = {};
|
||||
}
|
||||
if (nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress][tokenIdAsString] === undefined) {
|
||||
nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress][tokenIdAsString] = [];
|
||||
}
|
||||
this._nfts.push({ id: nftIds[i], tokenId });
|
||||
nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress][tokenIdAsString].push(nftIds[i]);
|
||||
await dummyWrapper.setApprovalForAllAsync(
|
||||
tokenOwnerAddress,
|
||||
(this._proxyContract as ERC1155ProxyContract).address,
|
||||
true,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
this._initialTokenIdsByOwner = {
|
||||
fungible: fungibleHoldingsByOwner,
|
||||
nonFungible: nonFungibleHoldingsByOwner,
|
||||
};
|
||||
return this._initialTokenIdsByOwner;
|
||||
}
|
||||
/**
|
||||
* @dev For each deployed ERC1155 contract, this function quieries the set of fungible/non-fungible
|
||||
* tokens for each token owner address (`_tokenOwnerAddresses`).
|
||||
* @return Balances of each token owner, across all ERC1155 contracts and tokens.
|
||||
*/
|
||||
public async getBalancesAsync(): Promise<ERC1155HoldingsByOwner> {
|
||||
this._validateDummyTokenContractsExistOrThrow();
|
||||
this._validateBalancesAndAllowancesSetOrThrow();
|
||||
const tokenHoldingsByOwner: ERC1155FungibleHoldingsByOwner = {};
|
||||
const nonFungibleHoldingsByOwner: ERC1155NonFungibleHoldingsByOwner = {};
|
||||
for (const dummyTokenWrapper of this._dummyTokenWrappers) {
|
||||
const tokenContract = dummyTokenWrapper.getContract();
|
||||
const tokenAddress = tokenContract.address;
|
||||
// Construct batch balance call
|
||||
const tokenOwners: string[] = [];
|
||||
const tokenIds: BigNumber[] = [];
|
||||
for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
|
||||
for (const tokenId of this._fungibleTokenIds) {
|
||||
tokenOwners.push(tokenOwnerAddress);
|
||||
tokenIds.push(new BigNumber(tokenId));
|
||||
}
|
||||
for (const nft of this._nfts) {
|
||||
tokenOwners.push(tokenOwnerAddress);
|
||||
tokenIds.push(nft.id);
|
||||
}
|
||||
}
|
||||
const balances = await dummyTokenWrapper.getBalancesAsync(tokenOwners, tokenIds);
|
||||
// Parse out balances into fungible / non-fungible token holdings
|
||||
let i = 0;
|
||||
for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
|
||||
// Fungible tokens
|
||||
for (const tokenId of this._fungibleTokenIds) {
|
||||
if (tokenHoldingsByOwner[tokenOwnerAddress] === undefined) {
|
||||
tokenHoldingsByOwner[tokenOwnerAddress] = {};
|
||||
}
|
||||
if (tokenHoldingsByOwner[tokenOwnerAddress][tokenAddress] === undefined) {
|
||||
tokenHoldingsByOwner[tokenOwnerAddress][tokenAddress] = {};
|
||||
}
|
||||
tokenHoldingsByOwner[tokenOwnerAddress][tokenAddress][tokenId] = balances[i++];
|
||||
}
|
||||
// Non-fungible tokens
|
||||
for (const nft of this._nfts) {
|
||||
if (nonFungibleHoldingsByOwner[tokenOwnerAddress] === undefined) {
|
||||
nonFungibleHoldingsByOwner[tokenOwnerAddress] = {};
|
||||
}
|
||||
if (nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress] === undefined) {
|
||||
nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress] = {};
|
||||
}
|
||||
if (
|
||||
nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress][nft.tokenId.toString()] ===
|
||||
undefined
|
||||
) {
|
||||
nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress][nft.tokenId.toString()] = [];
|
||||
}
|
||||
const isOwner = balances[i++];
|
||||
if (isOwner.isEqualTo(1)) {
|
||||
nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress][nft.tokenId.toString()].push(
|
||||
nft.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const holdingsByOwner = {
|
||||
fungible: tokenHoldingsByOwner,
|
||||
nonFungible: nonFungibleHoldingsByOwner,
|
||||
};
|
||||
return holdingsByOwner;
|
||||
}
|
||||
/**
|
||||
* @dev Checks if proxy is approved to transfer tokens on behalf of `userAddress`.
|
||||
* @param userAddress owner of ERC1155 tokens.
|
||||
* @param contractAddress address of ERC1155 contract.
|
||||
* @return True iff the proxy is approved for all. False otherwise.
|
||||
*/
|
||||
public async isProxyApprovedForAllAsync(userAddress: string, contractAddress: string): Promise<boolean> {
|
||||
this._validateProxyContractExistsOrThrow();
|
||||
const tokenContract = this._getContractFromAddress(contractAddress);
|
||||
const operator = (this._proxyContract as ERC1155ProxyContract).address;
|
||||
const didApproveAll = await tokenContract.isApprovedForAll.callAsync(userAddress, operator);
|
||||
return didApproveAll;
|
||||
}
|
||||
public getFungibleTokenIds(): BigNumber[] {
|
||||
const fungibleTokenIds = _.map(this._fungibleTokenIds, (tokenIdAsString: string) => {
|
||||
return new BigNumber(tokenIdAsString);
|
||||
});
|
||||
return fungibleTokenIds;
|
||||
}
|
||||
public getNonFungibleTokenIds(): BigNumber[] {
|
||||
const nonFungibleTokenIds = _.map(this._nonFungibleTokenIds, (tokenIdAsString: string) => {
|
||||
return new BigNumber(tokenIdAsString);
|
||||
});
|
||||
return nonFungibleTokenIds;
|
||||
}
|
||||
public getTokenOwnerAddresses(): string[] {
|
||||
return this._tokenOwnerAddresses;
|
||||
}
|
||||
public getContractWrapper(contractAddress: string): Erc1155Wrapper {
|
||||
const tokenWrapper = _.find(this._dummyTokenWrappers, (wrapper: Erc1155Wrapper) => {
|
||||
return wrapper.getContract().address === contractAddress;
|
||||
});
|
||||
if (tokenWrapper === undefined) {
|
||||
throw new Error(`Contract: ${contractAddress} was not deployed through ERC1155ProxyWrapper`);
|
||||
}
|
||||
return tokenWrapper;
|
||||
}
|
||||
private _getContractFromAddress(tokenAddress: string): ERC1155MintableContract {
|
||||
const tokenContractIfExists = _.find(this._dummyTokenWrappers, c => c.getContract().address === tokenAddress);
|
||||
if (tokenContractIfExists === undefined) {
|
||||
throw new Error(`Token: ${tokenAddress} was not deployed through ERC1155ProxyWrapper`);
|
||||
}
|
||||
return tokenContractIfExists.getContract();
|
||||
}
|
||||
private _validateDummyTokenContractsExistOrThrow(): void {
|
||||
if (this._dummyTokenWrappers === undefined) {
|
||||
throw new Error('Dummy ERC1155 tokens not yet deployed, please call "deployDummyTokensAsync"');
|
||||
}
|
||||
}
|
||||
private _validateProxyContractExistsOrThrow(): void {
|
||||
if (this._proxyContract === undefined) {
|
||||
throw new Error('ERC1155 proxy contract not yet deployed, please call "deployProxyAsync"');
|
||||
}
|
||||
}
|
||||
private _validateBalancesAndAllowancesSetOrThrow(): void {
|
||||
if (
|
||||
_.keys(this._initialTokenIdsByOwner.fungible).length === 0 ||
|
||||
_.keys(this._initialTokenIdsByOwner.nonFungible).length === 0
|
||||
) {
|
||||
throw new Error(
|
||||
'Dummy ERC1155 balances and allowances not yet set, please call "setBalancesAndAllowancesAsync"',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,16 +1,15 @@
|
||||
import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contracts-erc20';
|
||||
import { constants, ERC20BalancesByOwner, txDefaults } from '@0x/contracts-test-utils';
|
||||
import { assetDataUtils } from '@0x/order-utils';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import { ZeroExProvider } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { artifacts, DummyERC20TokenContract, ERC20ProxyContract } from '../../src';
|
||||
import { artifacts, ERC20ProxyContract } from '../../src';
|
||||
|
||||
export class ERC20Wrapper {
|
||||
private readonly _tokenOwnerAddresses: string[];
|
||||
private readonly _contractOwnerAddress: string;
|
||||
private readonly _web3Wrapper: Web3Wrapper;
|
||||
private readonly _provider: ZeroExProvider;
|
||||
private readonly _dummyTokenContracts: DummyERC20TokenContract[];
|
||||
private _proxyContract?: ERC20ProxyContract;
|
||||
@@ -24,7 +23,6 @@ export class ERC20Wrapper {
|
||||
*/
|
||||
constructor(provider: ZeroExProvider, tokenOwnerAddresses: string[], contractOwnerAddress: string) {
|
||||
this._dummyTokenContracts = [];
|
||||
this._web3Wrapper = new Web3Wrapper(provider);
|
||||
this._provider = provider;
|
||||
this._tokenOwnerAddresses = tokenOwnerAddresses;
|
||||
this._contractOwnerAddress = contractOwnerAddress;
|
||||
@@ -36,7 +34,7 @@ export class ERC20Wrapper {
|
||||
for (let i = 0; i < numberToDeploy; i++) {
|
||||
this._dummyTokenContracts.push(
|
||||
await DummyERC20TokenContract.deployFrom0xArtifactAsync(
|
||||
artifacts.DummyERC20Token,
|
||||
erc20Artifacts.DummyERC20Token,
|
||||
this._provider,
|
||||
txDefaults,
|
||||
constants.DUMMY_TOKEN_NAME,
|
||||
@@ -66,20 +64,16 @@ export class ERC20Wrapper {
|
||||
this._validateProxyContractExistsOrThrow();
|
||||
for (const dummyTokenContract of this._dummyTokenContracts) {
|
||||
for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
|
||||
await this._web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await dummyTokenContract.setBalance.sendTransactionAsync(
|
||||
tokenOwnerAddress,
|
||||
constants.INITIAL_ERC20_BALANCE,
|
||||
{ from: this._contractOwnerAddress },
|
||||
),
|
||||
await dummyTokenContract.setBalance.awaitTransactionSuccessAsync(
|
||||
tokenOwnerAddress,
|
||||
constants.INITIAL_ERC20_BALANCE,
|
||||
{ from: this._contractOwnerAddress },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await this._web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await dummyTokenContract.approve.sendTransactionAsync(
|
||||
(this._proxyContract as ERC20ProxyContract).address,
|
||||
constants.INITIAL_ERC20_ALLOWANCE,
|
||||
{ from: tokenOwnerAddress },
|
||||
),
|
||||
await dummyTokenContract.approve.awaitTransactionSuccessAsync(
|
||||
(this._proxyContract as ERC20ProxyContract).address,
|
||||
constants.INITIAL_ERC20_ALLOWANCE,
|
||||
{ from: tokenOwnerAddress },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
}
|
||||
@@ -92,10 +86,10 @@ export class ERC20Wrapper {
|
||||
}
|
||||
public async setBalanceAsync(userAddress: string, assetData: string, amount: BigNumber): Promise<void> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(assetData);
|
||||
await this._web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await tokenContract.setBalance.sendTransactionAsync(userAddress, amount, {
|
||||
from: this._contractOwnerAddress,
|
||||
}),
|
||||
await tokenContract.setBalance.awaitTransactionSuccessAsync(
|
||||
userAddress,
|
||||
amount,
|
||||
{ from: this._contractOwnerAddress },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
}
|
||||
@@ -108,10 +102,10 @@ export class ERC20Wrapper {
|
||||
public async setAllowanceAsync(userAddress: string, assetData: string, amount: BigNumber): Promise<void> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(assetData);
|
||||
const proxyAddress = (this._proxyContract as ERC20ProxyContract).address;
|
||||
await this._web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await tokenContract.approve.sendTransactionAsync(proxyAddress, amount, {
|
||||
from: userAddress,
|
||||
}),
|
||||
await tokenContract.approve.awaitTransactionSuccessAsync(
|
||||
proxyAddress,
|
||||
amount,
|
||||
{ from: userAddress },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
}
|
||||
@@ -132,7 +126,7 @@ export class ERC20Wrapper {
|
||||
_.forEach(balances, (balance, balanceIndex) => {
|
||||
const tokenAddress = balanceInfo[balanceIndex].tokenAddress;
|
||||
const tokenOwnerAddress = balanceInfo[balanceIndex].tokenOwnerAddress;
|
||||
if (_.isUndefined(balancesByOwner[tokenOwnerAddress])) {
|
||||
if (balancesByOwner[tokenOwnerAddress] === undefined) {
|
||||
balancesByOwner[tokenOwnerAddress] = {};
|
||||
}
|
||||
const wrappedBalance = new BigNumber(balance);
|
||||
@@ -141,7 +135,7 @@ export class ERC20Wrapper {
|
||||
return balancesByOwner;
|
||||
}
|
||||
public addDummyTokenContract(dummy: DummyERC20TokenContract): void {
|
||||
if (!_.isUndefined(this._dummyTokenContracts)) {
|
||||
if (this._dummyTokenContracts !== undefined) {
|
||||
this._dummyTokenContracts.push(dummy);
|
||||
}
|
||||
}
|
||||
@@ -159,18 +153,18 @@ export class ERC20Wrapper {
|
||||
const erc20ProxyData = assetDataUtils.decodeERC20AssetData(assetData);
|
||||
const tokenAddress = erc20ProxyData.tokenAddress;
|
||||
const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === tokenAddress);
|
||||
if (_.isUndefined(tokenContractIfExists)) {
|
||||
if (tokenContractIfExists === undefined) {
|
||||
throw new Error(`Token: ${tokenAddress} was not deployed through ERC20Wrapper`);
|
||||
}
|
||||
return tokenContractIfExists;
|
||||
}
|
||||
private _validateDummyTokenContractsExistOrThrow(): void {
|
||||
if (_.isUndefined(this._dummyTokenContracts)) {
|
||||
if (this._dummyTokenContracts === undefined) {
|
||||
throw new Error('Dummy ERC20 tokens not yet deployed, please call "deployDummyTokensAsync"');
|
||||
}
|
||||
}
|
||||
private _validateProxyContractExistsOrThrow(): void {
|
||||
if (_.isUndefined(this._proxyContract)) {
|
||||
if (this._proxyContract === undefined) {
|
||||
throw new Error('ERC20 proxy contract not yet deployed, please call "deployProxyAsync"');
|
||||
}
|
||||
}
|
||||
|
@@ -1,23 +1,21 @@
|
||||
import { artifacts as erc721Artifacts, DummyERC721TokenContract } from '@0x/contracts-erc721';
|
||||
import { constants, ERC721TokenIdsByOwner, txDefaults } from '@0x/contracts-test-utils';
|
||||
import { generatePseudoRandomSalt } from '@0x/order-utils';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import { ZeroExProvider } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { artifacts, DummyERC721TokenContract, ERC721ProxyContract } from '../../src';
|
||||
import { artifacts, ERC721ProxyContract } from '../../src';
|
||||
|
||||
export class ERC721Wrapper {
|
||||
private readonly _tokenOwnerAddresses: string[];
|
||||
private readonly _contractOwnerAddress: string;
|
||||
private readonly _web3Wrapper: Web3Wrapper;
|
||||
private readonly _provider: ZeroExProvider;
|
||||
private readonly _dummyTokenContracts: DummyERC721TokenContract[];
|
||||
private _proxyContract?: ERC721ProxyContract;
|
||||
private _proxyIdIfExists?: string;
|
||||
private _initialTokenIdsByOwner: ERC721TokenIdsByOwner = {};
|
||||
constructor(provider: ZeroExProvider, tokenOwnerAddresses: string[], contractOwnerAddress: string) {
|
||||
this._web3Wrapper = new Web3Wrapper(provider);
|
||||
this._provider = provider;
|
||||
this._dummyTokenContracts = [];
|
||||
this._tokenOwnerAddresses = tokenOwnerAddresses;
|
||||
@@ -28,7 +26,7 @@ export class ERC721Wrapper {
|
||||
for (const i of _.times(constants.NUM_DUMMY_ERC721_TO_DEPLOY)) {
|
||||
this._dummyTokenContracts.push(
|
||||
await DummyERC721TokenContract.deployFrom0xArtifactAsync(
|
||||
artifacts.DummyERC721Token,
|
||||
erc721Artifacts.DummyERC721Token,
|
||||
this._provider,
|
||||
txDefaults,
|
||||
constants.DUMMY_TOKEN_NAME,
|
||||
@@ -61,12 +59,12 @@ export class ERC721Wrapper {
|
||||
for (const i of _.times(constants.NUM_ERC721_TOKENS_TO_MINT)) {
|
||||
const tokenId = generatePseudoRandomSalt();
|
||||
await this.mintAsync(dummyTokenContract.address, tokenId, tokenOwnerAddress);
|
||||
if (_.isUndefined(this._initialTokenIdsByOwner[tokenOwnerAddress])) {
|
||||
if (this._initialTokenIdsByOwner[tokenOwnerAddress] === undefined) {
|
||||
this._initialTokenIdsByOwner[tokenOwnerAddress] = {
|
||||
[dummyTokenContract.address]: [],
|
||||
};
|
||||
}
|
||||
if (_.isUndefined(this._initialTokenIdsByOwner[tokenOwnerAddress][dummyTokenContract.address])) {
|
||||
if (this._initialTokenIdsByOwner[tokenOwnerAddress][dummyTokenContract.address] === undefined) {
|
||||
this._initialTokenIdsByOwner[tokenOwnerAddress][dummyTokenContract.address] = [];
|
||||
}
|
||||
this._initialTokenIdsByOwner[tokenOwnerAddress][dummyTokenContract.address].push(tokenId);
|
||||
@@ -90,20 +88,20 @@ export class ERC721Wrapper {
|
||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||
const tokenOwner = await this.ownerOfAsync(tokenAddress, tokenId);
|
||||
const proxyAddress = (this._proxyContract as ERC721ProxyContract).address;
|
||||
await this._web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await tokenContract.setApprovalForAll.sendTransactionAsync(proxyAddress, isApproved, {
|
||||
from: tokenOwner,
|
||||
}),
|
||||
await tokenContract.setApprovalForAll.awaitTransactionSuccessAsync(
|
||||
proxyAddress,
|
||||
isApproved,
|
||||
{ from: tokenOwner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
}
|
||||
public async approveAsync(to: string, tokenAddress: string, tokenId: BigNumber): Promise<void> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||
const tokenOwner = await this.ownerOfAsync(tokenAddress, tokenId);
|
||||
await this._web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await tokenContract.approve.sendTransactionAsync(to, tokenId, {
|
||||
from: tokenOwner,
|
||||
}),
|
||||
await tokenContract.approve.awaitTransactionSuccessAsync(
|
||||
to,
|
||||
tokenId,
|
||||
{ from: tokenOwner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
}
|
||||
@@ -114,28 +112,29 @@ export class ERC721Wrapper {
|
||||
userAddress: string,
|
||||
): Promise<void> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||
await this._web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await tokenContract.transferFrom.sendTransactionAsync(currentOwner, userAddress, tokenId, {
|
||||
from: currentOwner,
|
||||
}),
|
||||
await tokenContract.transferFrom.awaitTransactionSuccessAsync(
|
||||
currentOwner,
|
||||
userAddress,
|
||||
tokenId,
|
||||
{ from: currentOwner },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
}
|
||||
public async mintAsync(tokenAddress: string, tokenId: BigNumber, userAddress: string): Promise<void> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||
await this._web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await tokenContract.mint.sendTransactionAsync(userAddress, tokenId, {
|
||||
from: this._contractOwnerAddress,
|
||||
}),
|
||||
await tokenContract.mint.awaitTransactionSuccessAsync(
|
||||
userAddress,
|
||||
tokenId,
|
||||
{ from: this._contractOwnerAddress },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
}
|
||||
public async burnAsync(tokenAddress: string, tokenId: BigNumber, owner: string): Promise<void> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||
await this._web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await tokenContract.burn.sendTransactionAsync(owner, tokenId, {
|
||||
from: this._contractOwnerAddress,
|
||||
}),
|
||||
await tokenContract.burn.awaitTransactionSuccessAsync(
|
||||
owner,
|
||||
tokenId,
|
||||
{ from: this._contractOwnerAddress },
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
}
|
||||
@@ -188,12 +187,12 @@ export class ERC721Wrapper {
|
||||
_.forEach(tokenOwnerAddresses, (tokenOwnerAddress, ownerIndex) => {
|
||||
const tokenAddress = tokenInfo[ownerIndex].tokenAddress;
|
||||
const tokenId = tokenInfo[ownerIndex].tokenId;
|
||||
if (_.isUndefined(tokenIdsByOwner[tokenOwnerAddress])) {
|
||||
if (tokenIdsByOwner[tokenOwnerAddress] === undefined) {
|
||||
tokenIdsByOwner[tokenOwnerAddress] = {
|
||||
[tokenAddress]: [],
|
||||
};
|
||||
}
|
||||
if (_.isUndefined(tokenIdsByOwner[tokenOwnerAddress][tokenAddress])) {
|
||||
if (tokenIdsByOwner[tokenOwnerAddress][tokenAddress] === undefined) {
|
||||
tokenIdsByOwner[tokenOwnerAddress][tokenAddress] = [];
|
||||
}
|
||||
tokenIdsByOwner[tokenOwnerAddress][tokenAddress].push(tokenId);
|
||||
@@ -209,18 +208,18 @@ export class ERC721Wrapper {
|
||||
}
|
||||
private _getTokenContractFromAssetData(tokenAddress: string): DummyERC721TokenContract {
|
||||
const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === tokenAddress);
|
||||
if (_.isUndefined(tokenContractIfExists)) {
|
||||
if (tokenContractIfExists === undefined) {
|
||||
throw new Error(`Token: ${tokenAddress} was not deployed through ERC20Wrapper`);
|
||||
}
|
||||
return tokenContractIfExists;
|
||||
}
|
||||
private _validateDummyTokenContractsExistOrThrow(): void {
|
||||
if (_.isUndefined(this._dummyTokenContracts)) {
|
||||
if (this._dummyTokenContracts === undefined) {
|
||||
throw new Error('Dummy ERC721 tokens not yet deployed, please call "deployDummyTokensAsync"');
|
||||
}
|
||||
}
|
||||
private _validateProxyContractExistsOrThrow(): void {
|
||||
if (_.isUndefined(this._proxyContract)) {
|
||||
if (this._proxyContract === undefined) {
|
||||
throw new Error('ERC721 proxy contract not yet deployed, please call "deployProxyAsync"');
|
||||
}
|
||||
}
|
||||
|
@@ -1,2 +1,3 @@
|
||||
export * from './erc20_wrapper';
|
||||
export * from './erc721_wrapper';
|
||||
export * from './erc1155_proxy_wrapper';
|
||||
|
@@ -3,18 +3,16 @@
|
||||
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
|
||||
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
|
||||
"files": [
|
||||
"generated-artifacts/DummyERC20Token.json",
|
||||
"generated-artifacts/DummyERC721Receiver.json",
|
||||
"generated-artifacts/DummyERC721Token.json",
|
||||
"generated-artifacts/DummyMultipleReturnERC20Token.json",
|
||||
"generated-artifacts/DummyNoReturnERC20Token.json",
|
||||
"generated-artifacts/ERC1155Proxy.json",
|
||||
"generated-artifacts/ERC20Proxy.json",
|
||||
"generated-artifacts/ERC721Proxy.json",
|
||||
"generated-artifacts/IAssetData.json",
|
||||
"generated-artifacts/IAssetProxy.json",
|
||||
"generated-artifacts/IAuthorizable.json",
|
||||
"generated-artifacts/MixinAuthorizable.json",
|
||||
"generated-artifacts/MultiAssetProxy.json"
|
||||
"generated-artifacts/MultiAssetProxy.json",
|
||||
"generated-artifacts/StaticCallProxy.json",
|
||||
"generated-artifacts/TestStaticCallTarget.json"
|
||||
],
|
||||
"exclude": ["./deploy/solc/solc_bin"]
|
||||
}
|
||||
|
92
contracts/coordinator/CHANGELOG.json
Normal file
92
contracts/coordinator/CHANGELOG.json
Normal file
@@ -0,0 +1,92 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1563006338,
|
||||
"version": "2.0.5",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1558712885,
|
||||
"version": "2.0.4",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1557961111,
|
||||
"version": "2.0.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1557799313,
|
||||
"version": "2.0.2",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1557507213,
|
||||
"version": "2.0.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Make `decodeOrdersFromFillData`, `getCoordinatorApprovalHash`, and `getTransactionHash` public",
|
||||
"pr": 1729
|
||||
},
|
||||
{
|
||||
"note": "Make `assertValidTransactionOrdersApproval` internal",
|
||||
"pr": 1729
|
||||
}
|
||||
],
|
||||
"timestamp": 1554997931
|
||||
},
|
||||
{
|
||||
"version": "1.1.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Run Web3ProviderEngine without excess block polling",
|
||||
"pr": 1695
|
||||
}
|
||||
],
|
||||
"timestamp": 1553183790
|
||||
},
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Created Coordinator package"
|
||||
},
|
||||
{
|
||||
"note": "Use separate EIP712 domains for transactions and approvals",
|
||||
"pr": 1705
|
||||
},
|
||||
{
|
||||
"note": "Add `SignatureType.Invalid`",
|
||||
"pr": 1705
|
||||
},
|
||||
{
|
||||
"note": "Set `evmVersion` to `constantinople`",
|
||||
"pr": 1707
|
||||
}
|
||||
],
|
||||
"timestamp": 1553091633
|
||||
}
|
||||
]
|
42
contracts/coordinator/CHANGELOG.md
Normal file
42
contracts/coordinator/CHANGELOG.md
Normal file
@@ -0,0 +1,42 @@
|
||||
<!--
|
||||
changelogUtils.file is auto-generated using the monorepo-scripts package. Don't edit directly.
|
||||
Edit the package's CHANGELOG.json file only.
|
||||
-->
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v2.0.5 - _July 13, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.0.4 - _May 24, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.0.3 - _May 15, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.0.2 - _May 14, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.0.1 - _May 10, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.0.0 - _April 11, 2019_
|
||||
|
||||
* Make `decodeOrdersFromFillData`, `getCoordinatorApprovalHash`, and `getTransactionHash` public (#1729)
|
||||
* Make `assertValidTransactionOrdersApproval` internal (#1729)
|
||||
|
||||
## v1.1.0 - _March 21, 2019_
|
||||
|
||||
* Run Web3ProviderEngine without excess block polling (#1695)
|
||||
|
||||
## v1.0.0 - _March 20, 2019_
|
||||
|
||||
* Created Coordinator package
|
||||
* Use separate EIP712 domains for transactions and approvals (#1705)
|
||||
* Add `SignatureType.Invalid` (#1705)
|
||||
* Set `evmVersion` to `constantinople` (#1707)
|
@@ -1,13 +1,13 @@
|
||||
## Trade Execution Coordinator (TEC)
|
||||
## Coordinator
|
||||
|
||||
This package contains a contract that allows users to call arbitrary functions on the Exchange contract with permission from one or more TECs (Trade Execution Coordinators). Addresses of the deployed contracts can be found in the 0x [wiki](https://0xproject.com/wiki#Deployed-Addresses) or the [DEPLOYS](./DEPLOYS.json) file within this package.
|
||||
This package contains a contract that allows users to call arbitrary functions on the Exchange contract with permission from one or more Coordinators. Addresses of the deployed contracts can be found in the 0x [wiki](https://0xproject.com/wiki#Deployed-Addresses) or the [DEPLOYS](./DEPLOYS.json) file within this package.
|
||||
|
||||
## Installation
|
||||
|
||||
**Install**
|
||||
|
||||
```bash
|
||||
npm install @0x/contracts-tec --save
|
||||
npm install @0x/contracts-coordinator --save
|
||||
```
|
||||
|
||||
## Bug bounty
|
||||
@@ -41,13 +41,13 @@ 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-tec yarn build
|
||||
PKG=@0x/contracts-coordinator yarn build
|
||||
```
|
||||
|
||||
Or continuously rebuild on change:
|
||||
|
||||
```bash
|
||||
PKG=@0x/contracts-tec yarn watch
|
||||
PKG=@0x/contracts-coordinator yarn watch
|
||||
```
|
||||
|
||||
### Clean
|
@@ -1,13 +1,19 @@
|
||||
{
|
||||
"artifactsDir": "./generated-artifacts",
|
||||
"contractsDir": "./contracts",
|
||||
"useDockerisedSolc": true,
|
||||
"useDockerisedSolc": false,
|
||||
"compilerSettings": {
|
||||
"optimizer": { "enabled": true, "runs": 1000000 },
|
||||
"evmVersion": "constantinople",
|
||||
"optimizer": {
|
||||
"enabled": true,
|
||||
"runs": 1000000,
|
||||
"details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
|
||||
},
|
||||
"outputSelection": {
|
||||
"*": {
|
||||
"*": [
|
||||
"abi",
|
||||
"devdoc",
|
||||
"evm.bytecode.object",
|
||||
"evm.bytecode.sourceMap",
|
||||
"evm.deployedBytecode.object",
|
||||
@@ -16,5 +22,5 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"contracts": ["src/TEC.sol", "test/TestLibs.sol", "test/TestMixins.sol"]
|
||||
"contracts": ["src/Coordinator.sol", "src/registry/CoordinatorRegistry.sol"]
|
||||
}
|
@@ -16,21 +16,21 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity 0.5.3;
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental "ABIEncoderV2";
|
||||
|
||||
import "./libs/LibConstants.sol";
|
||||
import "./MixinSignatureValidator.sol";
|
||||
import "./MixinTECApprovalVerifier.sol";
|
||||
import "./MixinTECCore.sol";
|
||||
import "./MixinCoordinatorApprovalVerifier.sol";
|
||||
import "./MixinCoordinatorCore.sol";
|
||||
|
||||
|
||||
// solhint-disable no-empty-blocks
|
||||
contract TEC is
|
||||
contract Coordinator is
|
||||
LibConstants,
|
||||
MixinSignatureValidator,
|
||||
MixinTECApprovalVerifier,
|
||||
MixinTECCore
|
||||
MixinCoordinatorApprovalVerifier,
|
||||
MixinCoordinatorCore
|
||||
{
|
||||
constructor (address _exchange)
|
||||
public
|
@@ -16,26 +16,26 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.3;
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental "ABIEncoderV2";
|
||||
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibExchangeSelectors.sol";
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibAddressArray.sol";
|
||||
import "./libs/LibTECApproval.sol";
|
||||
import "./libs/LibCoordinatorApproval.sol";
|
||||
import "./libs/LibZeroExTransaction.sol";
|
||||
import "./mixins/MSignatureValidator.sol";
|
||||
import "./mixins/MTECApprovalVerifier.sol";
|
||||
import "./mixins/MCoordinatorApprovalVerifier.sol";
|
||||
|
||||
|
||||
// solhint-disable avoid-tx-origin
|
||||
contract MixinTECApprovalVerifier is
|
||||
contract MixinCoordinatorApprovalVerifier is
|
||||
LibExchangeSelectors,
|
||||
LibTECApproval,
|
||||
LibCoordinatorApproval,
|
||||
LibZeroExTransaction,
|
||||
MSignatureValidator,
|
||||
MTECApprovalVerifier
|
||||
MCoordinatorApprovalVerifier
|
||||
{
|
||||
using LibBytes for bytes;
|
||||
using LibAddressArray for address[];
|
||||
@@ -43,11 +43,13 @@ contract MixinTECApprovalVerifier is
|
||||
/// @dev Validates that the 0x transaction has been approved by all of the feeRecipients
|
||||
/// that correspond to each order in the transaction's Exchange calldata.
|
||||
/// @param transaction 0x transaction containing salt, signerAddress, and data.
|
||||
/// @param txOrigin Required signer of Ethereum transaction calling this function.
|
||||
/// @param transactionSignature Proof that the transaction has been signed by the signer.
|
||||
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
|
||||
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order in the transaction's Exchange calldata.
|
||||
function assertValidTECApprovals(
|
||||
function assertValidCoordinatorApprovals(
|
||||
LibZeroExTransaction.ZeroExTransaction memory transaction,
|
||||
address txOrigin,
|
||||
bytes memory transactionSignature,
|
||||
uint256[] memory approvalExpirationTimeSeconds,
|
||||
bytes[] memory approvalSignatures
|
||||
@@ -56,7 +58,7 @@ contract MixinTECApprovalVerifier is
|
||||
view
|
||||
{
|
||||
// Get the orders from the the Exchange calldata in the 0x transaction
|
||||
LibOrder.Order[] memory orders = decodeFillDataOrders(transaction.data);
|
||||
LibOrder.Order[] memory orders = decodeOrdersFromFillData(transaction.data);
|
||||
|
||||
// No approval is required for non-fill methods
|
||||
if (orders.length > 0) {
|
||||
@@ -64,6 +66,7 @@ contract MixinTECApprovalVerifier is
|
||||
assertValidTransactionOrdersApproval(
|
||||
transaction,
|
||||
orders,
|
||||
txOrigin,
|
||||
transactionSignature,
|
||||
approvalExpirationTimeSeconds,
|
||||
approvalSignatures
|
||||
@@ -71,87 +74,11 @@ contract MixinTECApprovalVerifier is
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Validates that the feeRecipients of a batch of order have approved a 0x transaction.
|
||||
/// @param transaction 0x transaction containing salt, signerAddress, and data.
|
||||
/// @param orders Array of order structs containing order specifications.
|
||||
/// @param transactionSignature Proof that the transaction has been signed by the signer.
|
||||
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
|
||||
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order.
|
||||
function assertValidTransactionOrdersApproval(
|
||||
LibZeroExTransaction.ZeroExTransaction memory transaction,
|
||||
LibOrder.Order[] memory orders,
|
||||
bytes memory transactionSignature,
|
||||
uint256[] memory approvalExpirationTimeSeconds,
|
||||
bytes[] memory approvalSignatures
|
||||
)
|
||||
public
|
||||
view
|
||||
{
|
||||
// Hash 0x transaction
|
||||
bytes32 transactionHash = getTransactionHash(transaction);
|
||||
|
||||
// Create empty list of approval signers
|
||||
address[] memory approvalSignerAddresses = new address[](0);
|
||||
|
||||
uint256 signaturesLength = approvalSignatures.length;
|
||||
for (uint256 i = 0; i < signaturesLength; i++) {
|
||||
// Create approval message
|
||||
uint256 currentApprovalExpirationTimeSeconds = approvalExpirationTimeSeconds[i];
|
||||
TECApproval memory approval = TECApproval({
|
||||
transactionHash: transactionHash,
|
||||
transactionSignature: transactionSignature,
|
||||
approvalExpirationTimeSeconds: currentApprovalExpirationTimeSeconds
|
||||
});
|
||||
|
||||
// Ensure approval has not expired
|
||||
require(
|
||||
// solhint-disable-next-line not-rely-on-time
|
||||
currentApprovalExpirationTimeSeconds > block.timestamp,
|
||||
"APPROVAL_EXPIRED"
|
||||
);
|
||||
|
||||
// Hash approval message and recover signer address
|
||||
bytes32 approvalHash = getTECApprovalHash(approval);
|
||||
address approvalSignerAddress = getSignerAddress(approvalHash, approvalSignatures[i]);
|
||||
|
||||
// Add approval signer to list of signers
|
||||
approvalSignerAddresses = approvalSignerAddresses.append(approvalSignerAddress);
|
||||
}
|
||||
|
||||
uint256 ordersLength = orders.length;
|
||||
for (uint256 i = 0; i < ordersLength; i++) {
|
||||
// Do not check approval if the order's senderAddress is null
|
||||
if (orders[i].senderAddress == address(0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ethereum transaction signer gives implicit signature of approval
|
||||
address approverAddress = orders[i].feeRecipientAddress;
|
||||
if (approverAddress == tx.origin) {
|
||||
approvalSignerAddresses = approvalSignerAddresses.append(tx.origin);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ensure feeRecipient of order has approved this 0x transaction
|
||||
bool isOrderApproved = approvalSignerAddresses.contains(approverAddress);
|
||||
require(
|
||||
isOrderApproved,
|
||||
"INVALID_APPROVAL_SIGNATURE"
|
||||
);
|
||||
|
||||
// The Ethereum transaction signer must be the 0x transaction signer or an approver of the 0x transaction
|
||||
require(
|
||||
transaction.signerAddress == tx.origin || approvalSignerAddresses.contains(tx.origin),
|
||||
"INVALID_SENDER"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Decodes the orders from Exchange calldata representing any fill method.
|
||||
/// @param data Exchange calldata representing a fill method.
|
||||
/// @return The orders from the Exchange calldata.
|
||||
function decodeFillDataOrders(bytes memory data)
|
||||
internal
|
||||
function decodeOrdersFromFillData(bytes memory data)
|
||||
public
|
||||
pure
|
||||
returns (LibOrder.Order[] memory orders)
|
||||
{
|
||||
@@ -197,4 +124,80 @@ contract MixinTECApprovalVerifier is
|
||||
}
|
||||
return orders;
|
||||
}
|
||||
|
||||
/// @dev Validates that the feeRecipients of a batch of order have approved a 0x transaction.
|
||||
/// @param transaction 0x transaction containing salt, signerAddress, and data.
|
||||
/// @param orders Array of order structs containing order specifications.
|
||||
/// @param txOrigin Required signer of Ethereum transaction calling this function.
|
||||
/// @param transactionSignature Proof that the transaction has been signed by the signer.
|
||||
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
|
||||
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order.
|
||||
function assertValidTransactionOrdersApproval(
|
||||
LibZeroExTransaction.ZeroExTransaction memory transaction,
|
||||
LibOrder.Order[] memory orders,
|
||||
address txOrigin,
|
||||
bytes memory transactionSignature,
|
||||
uint256[] memory approvalExpirationTimeSeconds,
|
||||
bytes[] memory approvalSignatures
|
||||
)
|
||||
internal
|
||||
view
|
||||
{
|
||||
// Verify that Ethereum tx signer is the same as the approved txOrigin
|
||||
require(
|
||||
tx.origin == txOrigin,
|
||||
"INVALID_ORIGIN"
|
||||
);
|
||||
|
||||
// Hash 0x transaction
|
||||
bytes32 transactionHash = getTransactionHash(transaction);
|
||||
|
||||
// Create empty list of approval signers
|
||||
address[] memory approvalSignerAddresses = new address[](0);
|
||||
|
||||
uint256 signaturesLength = approvalSignatures.length;
|
||||
for (uint256 i = 0; i != signaturesLength; i++) {
|
||||
// Create approval message
|
||||
uint256 currentApprovalExpirationTimeSeconds = approvalExpirationTimeSeconds[i];
|
||||
CoordinatorApproval memory approval = CoordinatorApproval({
|
||||
txOrigin: txOrigin,
|
||||
transactionHash: transactionHash,
|
||||
transactionSignature: transactionSignature,
|
||||
approvalExpirationTimeSeconds: currentApprovalExpirationTimeSeconds
|
||||
});
|
||||
|
||||
// Ensure approval has not expired
|
||||
require(
|
||||
// solhint-disable-next-line not-rely-on-time
|
||||
currentApprovalExpirationTimeSeconds > block.timestamp,
|
||||
"APPROVAL_EXPIRED"
|
||||
);
|
||||
|
||||
// Hash approval message and recover signer address
|
||||
bytes32 approvalHash = getCoordinatorApprovalHash(approval);
|
||||
address approvalSignerAddress = getSignerAddress(approvalHash, approvalSignatures[i]);
|
||||
|
||||
// Add approval signer to list of signers
|
||||
approvalSignerAddresses = approvalSignerAddresses.append(approvalSignerAddress);
|
||||
}
|
||||
|
||||
// Ethereum transaction signer gives implicit signature of approval
|
||||
approvalSignerAddresses = approvalSignerAddresses.append(tx.origin);
|
||||
|
||||
uint256 ordersLength = orders.length;
|
||||
for (uint256 i = 0; i != ordersLength; i++) {
|
||||
// Do not check approval if the order's senderAddress is null
|
||||
if (orders[i].senderAddress == address(0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ensure feeRecipient of order has approved this 0x transaction
|
||||
address approverAddress = orders[i].feeRecipientAddress;
|
||||
bool isOrderApproved = approvalSignerAddresses.contains(approverAddress);
|
||||
require(
|
||||
isOrderApproved,
|
||||
"INVALID_APPROVAL_SIGNATURE"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@@ -16,27 +16,29 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.3;
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental "ABIEncoderV2";
|
||||
|
||||
import "./libs/LibZeroExTransaction.sol";
|
||||
import "./libs/LibConstants.sol";
|
||||
import "./mixins/MTECApprovalVerifier.sol";
|
||||
import "./interfaces/ITECCore.sol";
|
||||
import "./mixins/MCoordinatorApprovalVerifier.sol";
|
||||
import "./interfaces/ICoordinatorCore.sol";
|
||||
|
||||
|
||||
contract MixinTECCore is
|
||||
contract MixinCoordinatorCore is
|
||||
LibConstants,
|
||||
MTECApprovalVerifier,
|
||||
ITECCore
|
||||
MCoordinatorApprovalVerifier,
|
||||
ICoordinatorCore
|
||||
{
|
||||
/// @dev Executes a 0x transaction that has been signed by the feeRecipients that correspond to each order in the transaction's Exchange calldata.
|
||||
/// @param transaction 0x transaction containing salt, signerAddress, and data.
|
||||
/// @param txOrigin Required signer of Ethereum transaction calling this function.
|
||||
/// @param transactionSignature Proof that the transaction has been signed by the signer.
|
||||
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
|
||||
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order in the transaction's Exchange calldata.
|
||||
function executeTransaction(
|
||||
LibZeroExTransaction.ZeroExTransaction memory transaction,
|
||||
address txOrigin,
|
||||
bytes memory transactionSignature,
|
||||
uint256[] memory approvalExpirationTimeSeconds,
|
||||
bytes[] memory approvalSignatures
|
||||
@@ -44,8 +46,9 @@ contract MixinTECCore is
|
||||
public
|
||||
{
|
||||
// Validate that the 0x transaction has been approves by each feeRecipient
|
||||
assertValidTECApprovals(
|
||||
assertValidCoordinatorApprovals(
|
||||
transaction,
|
||||
txOrigin,
|
||||
transactionSignature,
|
||||
approvalExpirationTimeSeconds,
|
||||
approvalSignatures
|
@@ -16,7 +16,7 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.3;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||
import "./mixins/MSignatureValidator.sol";
|
||||
@@ -59,6 +59,17 @@ contract MixinSignatureValidator is
|
||||
if (signatureType == SignatureType.Illegal) {
|
||||
revert("SIGNATURE_ILLEGAL");
|
||||
|
||||
// Always invalid signature.
|
||||
// Like Illegal, this is always implicitly available and therefore
|
||||
// offered explicitly. It can be implicitly created by providing
|
||||
// a correctly formatted but incorrect signature.
|
||||
} else if (signatureType == SignatureType.Invalid) {
|
||||
require(
|
||||
signature.length == 0,
|
||||
"LENGTH_0_REQUIRED"
|
||||
);
|
||||
revert("SIGNATURE_INVALID");
|
||||
|
||||
// Signature using EIP712
|
||||
} else if (signatureType == SignatureType.EIP712) {
|
||||
require(
|
@@ -16,23 +16,25 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.3;
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental "ABIEncoderV2";
|
||||
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||
import "../libs/LibZeroExTransaction.sol";
|
||||
|
||||
|
||||
contract ITECApprovalVerifier {
|
||||
contract ICoordinatorApprovalVerifier {
|
||||
|
||||
/// @dev Validates that the 0x transaction has been approved by all of the feeRecipients
|
||||
/// that correspond to each order in the transaction's Exchange calldata.
|
||||
/// @param transaction 0x transaction containing salt, signerAddress, and data.
|
||||
/// @param txOrigin Required signer of Ethereum transaction calling this function.
|
||||
/// @param transactionSignature Proof that the transaction has been signed by the signer.
|
||||
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
|
||||
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order in the transaction's Exchange calldata.
|
||||
function assertValidTECApprovals(
|
||||
function assertValidCoordinatorApprovals(
|
||||
LibZeroExTransaction.ZeroExTransaction memory transaction,
|
||||
address txOrigin,
|
||||
bytes memory transactionSignature,
|
||||
uint256[] memory approvalExpirationTimeSeconds,
|
||||
bytes[] memory approvalSignatures
|
||||
@@ -40,19 +42,11 @@ contract ITECApprovalVerifier {
|
||||
public
|
||||
view;
|
||||
|
||||
/// @dev Validates that the feeRecipients of a batch of order have approved a 0x transaction.
|
||||
/// @param transaction 0x transaction containing salt, signerAddress, and data.
|
||||
/// @param orders Array of order structs containing order specifications.
|
||||
/// @param transactionSignature Proof that the transaction has been signed by the signer.
|
||||
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
|
||||
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order.
|
||||
function assertValidTransactionOrdersApproval(
|
||||
LibZeroExTransaction.ZeroExTransaction memory transaction,
|
||||
LibOrder.Order[] memory orders,
|
||||
bytes memory transactionSignature,
|
||||
uint256[] memory approvalExpirationTimeSeconds,
|
||||
bytes[] memory approvalSignatures
|
||||
)
|
||||
/// @dev Decodes the orders from Exchange calldata representing any fill method.
|
||||
/// @param data Exchange calldata representing a fill method.
|
||||
/// @return The orders from the Exchange calldata.
|
||||
function decodeOrdersFromFillData(bytes memory data)
|
||||
public
|
||||
view;
|
||||
pure
|
||||
returns (LibOrder.Order[] memory orders);
|
||||
}
|
@@ -16,21 +16,23 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.3;
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental "ABIEncoderV2";
|
||||
|
||||
import "../libs/LibZeroExTransaction.sol";
|
||||
|
||||
|
||||
contract ITECCore {
|
||||
contract ICoordinatorCore {
|
||||
|
||||
/// @dev Executes a 0x transaction that has been signed by the feeRecipients that correspond to each order in the transaction's Exchange calldata.
|
||||
/// @param transaction 0x transaction containing salt, signerAddress, and data.
|
||||
/// @param txOrigin Required signer of Ethereum transaction calling this function.
|
||||
/// @param transactionSignature Proof that the transaction has been signed by the signer.
|
||||
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
|
||||
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order in the transaction's Exchange calldata.
|
||||
function executeTransaction(
|
||||
LibZeroExTransaction.ZeroExTransaction memory transaction,
|
||||
address txOrigin,
|
||||
bytes memory transactionSignature,
|
||||
uint256[] memory approvalExpirationTimeSeconds,
|
||||
bytes[] memory approvalSignatures
|
@@ -16,7 +16,7 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.3;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
|
||||
contract ISignatureValidator {
|
@@ -15,7 +15,7 @@
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
pragma solidity ^0.5.3;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
|
||||
contract ITransactions {
|
@@ -16,7 +16,7 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.3;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "../interfaces/ITransactions.sol";
|
||||
|
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental "ABIEncoderV2";
|
||||
|
||||
import "./LibEIP712Domain.sol";
|
||||
|
||||
|
||||
contract LibCoordinatorApproval is
|
||||
LibEIP712Domain
|
||||
{
|
||||
// Hash for the EIP712 Coordinator approval message
|
||||
// keccak256(abi.encodePacked(
|
||||
// "CoordinatorApproval(",
|
||||
// "address txOrigin,",
|
||||
// "bytes32 transactionHash,",
|
||||
// "bytes transactionSignature,",
|
||||
// "uint256 approvalExpirationTimeSeconds",
|
||||
// ")"
|
||||
// ));
|
||||
bytes32 constant internal EIP712_COORDINATOR_APPROVAL_SCHEMA_HASH = 0x2fbcdbaa76bc7589916958ae919dfbef04d23f6bbf26de6ff317b32c6cc01e05;
|
||||
|
||||
struct CoordinatorApproval {
|
||||
address txOrigin; // Required signer of Ethereum transaction that is submitting approval.
|
||||
bytes32 transactionHash; // EIP712 hash of the transaction.
|
||||
bytes transactionSignature; // Signature of the 0x transaction.
|
||||
uint256 approvalExpirationTimeSeconds; // Timestamp in seconds for which the approval expires.
|
||||
}
|
||||
|
||||
/// @dev Calculated the EIP712 hash of the Coordinator approval mesasage using the domain separator of this contract.
|
||||
/// @param approval Coordinator approval message containing the transaction hash, transaction signature, and expiration of the approval.
|
||||
/// @return EIP712 hash of the Coordinator approval message with the domain separator of this contract.
|
||||
function getCoordinatorApprovalHash(CoordinatorApproval memory approval)
|
||||
public
|
||||
view
|
||||
returns (bytes32 approvalHash)
|
||||
{
|
||||
approvalHash = hashEIP712CoordinatorMessage(hashCoordinatorApproval(approval));
|
||||
return approvalHash;
|
||||
}
|
||||
|
||||
/// @dev Calculated the EIP712 hash of the Coordinator approval mesasage with no domain separator.
|
||||
/// @param approval Coordinator approval message containing the transaction hash, transaction signature, and expiration of the approval.
|
||||
/// @return EIP712 hash of the Coordinator approval message with no domain separator.
|
||||
function hashCoordinatorApproval(CoordinatorApproval memory approval)
|
||||
internal
|
||||
pure
|
||||
returns (bytes32 result)
|
||||
{
|
||||
bytes32 schemaHash = EIP712_COORDINATOR_APPROVAL_SCHEMA_HASH;
|
||||
bytes memory transactionSignature = approval.transactionSignature;
|
||||
address txOrigin = approval.txOrigin;
|
||||
bytes32 transactionHash = approval.transactionHash;
|
||||
uint256 approvalExpirationTimeSeconds = approval.approvalExpirationTimeSeconds;
|
||||
|
||||
// Assembly for more efficiently computing:
|
||||
// keccak256(abi.encodePacked(
|
||||
// EIP712_COORDINATOR_APPROVAL_SCHEMA_HASH,
|
||||
// approval.txOrigin,
|
||||
// approval.transactionHash,
|
||||
// keccak256(approval.transactionSignature)
|
||||
// approval.approvalExpirationTimeSeconds,
|
||||
// ));
|
||||
|
||||
assembly {
|
||||
// Compute hash of transaction signature
|
||||
let transactionSignatureHash := keccak256(add(transactionSignature, 32), mload(transactionSignature))
|
||||
|
||||
// Load free memory pointer
|
||||
let memPtr := mload(64)
|
||||
|
||||
mstore(memPtr, schemaHash) // hash of schema
|
||||
mstore(add(memPtr, 32), txOrigin) // txOrigin
|
||||
mstore(add(memPtr, 64), transactionHash) // transactionHash
|
||||
mstore(add(memPtr, 96), transactionSignatureHash) // transactionSignatureHash
|
||||
mstore(add(memPtr, 128), approvalExpirationTimeSeconds) // approvalExpirationTimeSeconds
|
||||
// Compute hash
|
||||
result := keccak256(memPtr, 160)
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
131
contracts/coordinator/contracts/src/libs/LibEIP712Domain.sol
Normal file
131
contracts/coordinator/contracts/src/libs/LibEIP712Domain.sol
Normal file
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "./LibConstants.sol";
|
||||
|
||||
|
||||
contract LibEIP712Domain is
|
||||
LibConstants
|
||||
{
|
||||
|
||||
// EIP191 header for EIP712 prefix
|
||||
string constant internal EIP191_HEADER = "\x19\x01";
|
||||
|
||||
// EIP712 Domain Name value for the Coordinator
|
||||
string constant internal EIP712_COORDINATOR_DOMAIN_NAME = "0x Protocol Coordinator";
|
||||
|
||||
// EIP712 Domain Version value for the Coordinator
|
||||
string constant internal EIP712_COORDINATOR_DOMAIN_VERSION = "1.0.0";
|
||||
|
||||
// EIP712 Domain Name value for the Exchange
|
||||
string constant internal EIP712_EXCHANGE_DOMAIN_NAME = "0x Protocol";
|
||||
|
||||
// EIP712 Domain Version value for the Exchange
|
||||
string constant internal EIP712_EXCHANGE_DOMAIN_VERSION = "2";
|
||||
|
||||
// Hash of the EIP712 Domain Separator Schema
|
||||
bytes32 constant internal EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH = keccak256(abi.encodePacked(
|
||||
"EIP712Domain(",
|
||||
"string name,",
|
||||
"string version,",
|
||||
"address verifyingContract",
|
||||
")"
|
||||
));
|
||||
|
||||
// Hash of the EIP712 Domain Separator data for the Coordinator
|
||||
// solhint-disable-next-line var-name-mixedcase
|
||||
bytes32 public EIP712_COORDINATOR_DOMAIN_HASH;
|
||||
|
||||
// Hash of the EIP712 Domain Separator data for the Exchange
|
||||
// solhint-disable-next-line var-name-mixedcase
|
||||
bytes32 public EIP712_EXCHANGE_DOMAIN_HASH;
|
||||
|
||||
constructor ()
|
||||
public
|
||||
{
|
||||
EIP712_COORDINATOR_DOMAIN_HASH = keccak256(abi.encodePacked(
|
||||
EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH,
|
||||
keccak256(bytes(EIP712_COORDINATOR_DOMAIN_NAME)),
|
||||
keccak256(bytes(EIP712_COORDINATOR_DOMAIN_VERSION)),
|
||||
uint256(address(this))
|
||||
));
|
||||
|
||||
EIP712_EXCHANGE_DOMAIN_HASH = keccak256(abi.encodePacked(
|
||||
EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH,
|
||||
keccak256(bytes(EIP712_EXCHANGE_DOMAIN_NAME)),
|
||||
keccak256(bytes(EIP712_EXCHANGE_DOMAIN_VERSION)),
|
||||
uint256(address(EXCHANGE))
|
||||
));
|
||||
}
|
||||
|
||||
/// @dev Calculates EIP712 encoding for a hash struct in the EIP712 domain
|
||||
/// of this contract.
|
||||
/// @param hashStruct The EIP712 hash struct.
|
||||
/// @return EIP712 hash applied to this EIP712 Domain.
|
||||
function hashEIP712CoordinatorMessage(bytes32 hashStruct)
|
||||
internal
|
||||
view
|
||||
returns (bytes32 result)
|
||||
{
|
||||
return hashEIP712Message(EIP712_COORDINATOR_DOMAIN_HASH, hashStruct);
|
||||
}
|
||||
|
||||
/// @dev Calculates EIP712 encoding for a hash struct in the EIP712 domain
|
||||
/// of the Exchange contract.
|
||||
/// @param hashStruct The EIP712 hash struct.
|
||||
/// @return EIP712 hash applied to the Exchange EIP712 Domain.
|
||||
function hashEIP712ExchangeMessage(bytes32 hashStruct)
|
||||
internal
|
||||
view
|
||||
returns (bytes32 result)
|
||||
{
|
||||
return hashEIP712Message(EIP712_EXCHANGE_DOMAIN_HASH, hashStruct);
|
||||
}
|
||||
|
||||
/// @dev Calculates EIP712 encoding for a hash struct with a given domain hash.
|
||||
/// @param eip712DomainHash Hash of the domain domain separator data.
|
||||
/// @param hashStruct The EIP712 hash struct.
|
||||
/// @return EIP712 hash applied to the Exchange EIP712 Domain.
|
||||
function hashEIP712Message(bytes32 eip712DomainHash, bytes32 hashStruct)
|
||||
internal
|
||||
pure
|
||||
returns (bytes32 result)
|
||||
{
|
||||
// Assembly for more efficient computing:
|
||||
// keccak256(abi.encodePacked(
|
||||
// EIP191_HEADER,
|
||||
// EIP712_DOMAIN_HASH,
|
||||
// hashStruct
|
||||
// ));
|
||||
|
||||
assembly {
|
||||
// Load free memory pointer
|
||||
let memPtr := mload(64)
|
||||
|
||||
mstore(memPtr, 0x1901000000000000000000000000000000000000000000000000000000000000) // EIP191 header
|
||||
mstore(add(memPtr, 2), eip712DomainHash) // EIP712 domain hash
|
||||
mstore(add(memPtr, 34), hashStruct) // Hash of struct
|
||||
|
||||
// Compute hash
|
||||
result := keccak256(memPtr, 66)
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@@ -16,7 +16,8 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.3;
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental "ABIEncoderV2";
|
||||
|
||||
import "./LibEIP712Domain.sol";
|
||||
|
||||
@@ -25,13 +26,14 @@ contract LibZeroExTransaction is
|
||||
LibEIP712Domain
|
||||
{
|
||||
// Hash for the EIP712 0x transaction schema
|
||||
bytes32 constant internal EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH = keccak256(abi.encodePacked(
|
||||
"ZeroExTransaction(",
|
||||
"uint256 salt,",
|
||||
"address signerAddress,",
|
||||
"bytes data",
|
||||
")"
|
||||
));
|
||||
// keccak256(abi.encodePacked(
|
||||
// "ZeroExTransaction(",
|
||||
// "uint256 salt,",
|
||||
// "address signerAddress,",
|
||||
// "bytes data",
|
||||
// ")"
|
||||
// ));
|
||||
bytes32 constant internal EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH = 0x213c6f636f3ea94e701c0adf9b2624aa45a6c694f9a292c094f9a81c24b5df4c;
|
||||
|
||||
struct ZeroExTransaction {
|
||||
uint256 salt; // Arbitrary number to ensure uniqueness of transaction hash.
|
||||
@@ -39,16 +41,16 @@ contract LibZeroExTransaction is
|
||||
bytes data; // AbiV2 encoded calldata.
|
||||
}
|
||||
|
||||
/// @dev Calculates the EIP712 hash of a 0x transaction using the domain separator of this contract.
|
||||
/// @dev Calculates the EIP712 hash of a 0x transaction using the domain separator of the Exchange contract.
|
||||
/// @param transaction 0x transaction containing salt, signerAddress, and data.
|
||||
/// @return EIP712 hash of the transaction with the domain separator of this contract.
|
||||
function getTransactionHash(ZeroExTransaction memory transaction)
|
||||
internal
|
||||
public
|
||||
view
|
||||
returns (bytes32 transactionHash)
|
||||
{
|
||||
// Note: this transaction hash will differ from the hash produced by the Exchange contract because it utilizes a different domain hash.
|
||||
transactionHash = hashEIP712Message(hashZeroExTransaction(transaction));
|
||||
// Hash the transaction with the domain separator of the Exchange contract.
|
||||
transactionHash = hashEIP712ExchangeMessage(hashZeroExTransaction(transaction));
|
||||
return transactionHash;
|
||||
}
|
||||
|
||||
@@ -61,8 +63,7 @@ contract LibZeroExTransaction is
|
||||
returns (bytes32 result)
|
||||
{
|
||||
bytes32 schemaHash = EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH;
|
||||
bytes32 dataHash = keccak256(transaction.data);
|
||||
// TODO(abandeali1): optimize by loading from memory in assembly
|
||||
bytes memory data = transaction.data;
|
||||
uint256 salt = transaction.salt;
|
||||
address signerAddress = transaction.signerAddress;
|
||||
|
||||
@@ -75,6 +76,9 @@ contract LibZeroExTransaction is
|
||||
// ));
|
||||
|
||||
assembly {
|
||||
// Compute hash of data
|
||||
let dataHash := keccak256(add(data, 32), mload(data))
|
||||
|
||||
// Load free memory pointer
|
||||
let memPtr := mload(64)
|
||||
|
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental "ABIEncoderV2";
|
||||
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||
import "../interfaces/ICoordinatorApprovalVerifier.sol";
|
||||
|
||||
|
||||
contract MCoordinatorApprovalVerifier is
|
||||
ICoordinatorApprovalVerifier
|
||||
{
|
||||
/// @dev Validates that the feeRecipients of a batch of order have approved a 0x transaction.
|
||||
/// @param transaction 0x transaction containing salt, signerAddress, and data.
|
||||
/// @param orders Array of order structs containing order specifications.
|
||||
/// @param txOrigin Required signer of Ethereum transaction calling this function.
|
||||
/// @param transactionSignature Proof that the transaction has been signed by the signer.
|
||||
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
|
||||
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order.
|
||||
function assertValidTransactionOrdersApproval(
|
||||
LibZeroExTransaction.ZeroExTransaction memory transaction,
|
||||
LibOrder.Order[] memory orders,
|
||||
address txOrigin,
|
||||
bytes memory transactionSignature,
|
||||
uint256[] memory approvalExpirationTimeSeconds,
|
||||
bytes[] memory approvalSignatures
|
||||
)
|
||||
internal
|
||||
view;
|
||||
}
|
@@ -16,7 +16,7 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.3;
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "../interfaces/ISignatureValidator.sol";
|
||||
|
||||
@@ -27,8 +27,9 @@ contract MSignatureValidator is
|
||||
// Allowed signature types.
|
||||
enum SignatureType {
|
||||
Illegal, // 0x00, default value
|
||||
EIP712, // 0x01
|
||||
EthSign, // 0x02
|
||||
NSignatureTypes // 0x03, number of signature types. Always leave at end.
|
||||
Invalid, // 0x01
|
||||
EIP712, // 0x02
|
||||
EthSign, // 0x03
|
||||
NSignatureTypes // 0x04, number of signature types. Always leave at end.
|
||||
}
|
||||
}
|
@@ -16,15 +16,17 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity 0.5.3;
|
||||
pragma experimental "ABIEncoderV2";
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "../src/MixinSignatureValidator.sol";
|
||||
import "../src/MixinTECApprovalVerifier.sol";
|
||||
import "./MixinCoordinatorRegistryCore.sol";
|
||||
|
||||
|
||||
// solhint-disable no-empty-blocks
|
||||
contract TestMixins is
|
||||
MixinSignatureValidator,
|
||||
MixinTECApprovalVerifier
|
||||
{}
|
||||
contract CoordinatorRegistry is
|
||||
MixinCoordinatorRegistryCore
|
||||
{
|
||||
constructor ()
|
||||
public
|
||||
MixinCoordinatorRegistryCore()
|
||||
{}
|
||||
}
|
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "./interfaces/ICoordinatorRegistryCore.sol";
|
||||
|
||||
|
||||
// solhint-disable no-empty-blocks
|
||||
contract MixinCoordinatorRegistryCore is
|
||||
ICoordinatorRegistryCore
|
||||
{
|
||||
// mapping from `coordinatorOperator` -> `coordinatorEndpoint`
|
||||
mapping (address => string) internal coordinatorEndpoints;
|
||||
|
||||
/// @dev Called by a Coordinator operator to set the endpoint of their Coordinator.
|
||||
/// @param coordinatorEndpoint endpoint of the Coordinator.
|
||||
function setCoordinatorEndpoint(string calldata coordinatorEndpoint) external {
|
||||
address coordinatorOperator = msg.sender;
|
||||
coordinatorEndpoints[coordinatorOperator] = coordinatorEndpoint;
|
||||
emit CoordinatorEndpointSet(coordinatorOperator, coordinatorEndpoint);
|
||||
}
|
||||
|
||||
/// @dev Gets the endpoint for a Coordinator.
|
||||
/// @param coordinatorOperator operator of the Coordinator endpoint.
|
||||
function getCoordinatorEndpoint(address coordinatorOperator)
|
||||
external
|
||||
view
|
||||
returns (string memory coordinatorEndpoint)
|
||||
{
|
||||
return coordinatorEndpoints[coordinatorOperator];
|
||||
}
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
|
||||
// solhint-disable no-empty-blocks
|
||||
contract ICoordinatorRegistryCore
|
||||
{
|
||||
/// @dev Emitted when a Coordinator endpoint is set.
|
||||
event CoordinatorEndpointSet(
|
||||
address coordinatorOperator,
|
||||
string coordinatorEndpoint
|
||||
);
|
||||
|
||||
/// @dev Called by a Coordinator operator to set the endpoint of their Coordinator.
|
||||
/// @param coordinatorEndpoint endpoint of the Coordinator.
|
||||
function setCoordinatorEndpoint(string calldata coordinatorEndpoint) external;
|
||||
|
||||
/// @dev Gets the endpoint for a Coordinator.
|
||||
/// @param coordinatorOperator operator of the Coordinator endpoint.
|
||||
function getCoordinatorEndpoint(address coordinatorOperator)
|
||||
external
|
||||
view
|
||||
returns (string memory coordinatorEndpoint);
|
||||
}
|
89
contracts/coordinator/package.json
Normal file
89
contracts/coordinator/package.json
Normal file
@@ -0,0 +1,89 @@
|
||||
{
|
||||
"name": "@0x/contracts-coordinator",
|
||||
"version": "2.0.5",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
"description": "Smart contract extensions of 0x protocol",
|
||||
"main": "lib/src/index.js",
|
||||
"directories": {
|
||||
"test": "test"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "yarn pre_build && tsc -b",
|
||||
"build:ci": "yarn build",
|
||||
"pre_build": "run-s compile generate_contract_wrappers",
|
||||
"test": "yarn run_mocha",
|
||||
"rebuild_and_test": "run-s build test",
|
||||
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
||||
"test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html",
|
||||
"test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha",
|
||||
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
|
||||
"compile": "sol-compiler",
|
||||
"watch": "sol-compiler -w",
|
||||
"clean": "shx rm -rf lib generated-artifacts generated-wrappers",
|
||||
"generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../../node_modules/@0x/abi-gen-templates/contract.handlebars --partials '../../node_modules/@0x/abi-gen-templates/partials/**/*.handlebars' --output generated-wrappers --backend ethers",
|
||||
"lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||
"fix": "tslint --fix --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||
"coverage:report:text": "istanbul report text",
|
||||
"coverage:report:html": "istanbul report html && open coverage/index.html",
|
||||
"profiler:report:html": "istanbul report html && open coverage/index.html",
|
||||
"coverage:report:lcov": "istanbul report lcov",
|
||||
"test:circleci": "yarn test",
|
||||
"contracts:gen": "contracts-gen",
|
||||
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol"
|
||||
},
|
||||
"config": {
|
||||
"abis": "./generated-artifacts/@(Coordinator|CoordinatorRegistry).json",
|
||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/0xProject/0x-monorepo.git"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/0xProject/0x-monorepo/issues"
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^2.1.0",
|
||||
"@0x/contracts-gen": "^1.0.10",
|
||||
"@0x/contracts-test-utils": "^3.1.8",
|
||||
"@0x/dev-utils": "^2.2.4",
|
||||
"@0x/sol-compiler": "^3.1.9",
|
||||
"@0x/tslint-config": "^3.0.1",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/node": "*",
|
||||
"chai": "^4.0.1",
|
||||
"chai-as-promised": "^7.1.0",
|
||||
"chai-bignumber": "^3.0.0",
|
||||
"dirty-chai": "^2.0.1",
|
||||
"make-promises-safe": "^1.1.0",
|
||||
"mocha": "^4.1.0",
|
||||
"npm-run-all": "^4.1.2",
|
||||
"shx": "^0.2.2",
|
||||
"solhint": "^1.4.1",
|
||||
"tslint": "5.11.0",
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^5.1.1",
|
||||
"@0x/contracts-asset-proxy": "^2.2.0",
|
||||
"@0x/contracts-erc20": "^2.2.6",
|
||||
"@0x/contracts-exchange": "^2.1.6",
|
||||
"@0x/contracts-exchange-libs": "^3.0.0",
|
||||
"@0x/contracts-utils": "^3.1.7",
|
||||
"@0x/order-utils": "^8.2.0",
|
||||
"@0x/types": "^2.4.0",
|
||||
"@0x/typescript-typings": "^4.2.3",
|
||||
"@0x/utils": "^4.4.0",
|
||||
"@0x/web3-wrapper": "^6.0.7",
|
||||
"ethereum-types": "^2.1.3",
|
||||
"ethereumjs-util": "^5.1.1",
|
||||
"lodash": "^4.17.11"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
@@ -5,11 +5,9 @@
|
||||
*/
|
||||
import { ContractArtifact } from 'ethereum-types';
|
||||
|
||||
import * as TEC from '../generated-artifacts/TEC.json';
|
||||
import * as TestLibs from '../generated-artifacts/TestLibs.json';
|
||||
import * as TestMixins from '../generated-artifacts/TestMixins.json';
|
||||
import * as Coordinator from '../generated-artifacts/Coordinator.json';
|
||||
import * as CoordinatorRegistry from '../generated-artifacts/CoordinatorRegistry.json';
|
||||
export const artifacts = {
|
||||
TEC: TEC as ContractArtifact,
|
||||
TestLibs: TestLibs as ContractArtifact,
|
||||
TestMixins: TestMixins as ContractArtifact,
|
||||
Coordinator: Coordinator as ContractArtifact,
|
||||
CoordinatorRegistry: CoordinatorRegistry as ContractArtifact,
|
||||
};
|
@@ -3,6 +3,5 @@
|
||||
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
export * from '../generated-wrappers/tec';
|
||||
export * from '../generated-wrappers/test_libs';
|
||||
export * from '../generated-wrappers/test_mixins';
|
||||
export * from '../generated-wrappers/coordinator';
|
||||
export * from '../generated-wrappers/coordinator_registry';
|
@@ -1,4 +1,5 @@
|
||||
import { DummyERC20TokenContract, ERC20ProxyContract, ERC20Wrapper } from '@0x/contracts-asset-proxy';
|
||||
import { ERC20ProxyContract, ERC20Wrapper } from '@0x/contracts-asset-proxy';
|
||||
import { DummyERC20TokenContract } from '@0x/contracts-erc20';
|
||||
import {
|
||||
artifacts as exchangeArtifacts,
|
||||
ExchangeCancelEventArgs,
|
||||
@@ -24,14 +25,14 @@ import { BigNumber } from '@0x/utils';
|
||||
import * as chai from 'chai';
|
||||
import { LogWithDecodedArgs } from 'ethereum-types';
|
||||
|
||||
import { ApprovalFactory, artifacts, constants, exchangeDataEncoder, TECContract } from '../src';
|
||||
import { ApprovalFactory, artifacts, constants, CoordinatorContract, exchangeDataEncoder } from '../src';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
web3Wrapper.abiDecoder.addABI(exchangeArtifacts.Exchange.compilerOutput.abi);
|
||||
// tslint:disable:no-unnecessary-type-assertion
|
||||
describe('TEC tests', () => {
|
||||
describe('Coordinator tests', () => {
|
||||
let makerAddress: string;
|
||||
let owner: string;
|
||||
let takerAddress: string;
|
||||
@@ -41,7 +42,7 @@ describe('TEC tests', () => {
|
||||
let erc20TokenA: DummyERC20TokenContract;
|
||||
let erc20TokenB: DummyERC20TokenContract;
|
||||
let zrxToken: DummyERC20TokenContract;
|
||||
let tecContract: TECContract;
|
||||
let coordinatorContract: CoordinatorContract;
|
||||
let exchange: ExchangeContract;
|
||||
|
||||
let erc20Wrapper: ERC20Wrapper;
|
||||
@@ -86,8 +87,8 @@ describe('TEC tests', () => {
|
||||
devConstants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
|
||||
tecContract = await TECContract.deployFrom0xArtifactAsync(
|
||||
artifacts.TEC,
|
||||
coordinatorContract = await CoordinatorContract.deployFrom0xArtifactAsync(
|
||||
artifacts.Coordinator,
|
||||
provider,
|
||||
txDefaults,
|
||||
exchange.address,
|
||||
@@ -97,7 +98,7 @@ describe('TEC tests', () => {
|
||||
const defaultOrderParams = {
|
||||
...devConstants.STATIC_ORDER_PARAMS,
|
||||
exchangeAddress: exchange.address,
|
||||
senderAddress: tecContract.address,
|
||||
senderAddress: coordinatorContract.address,
|
||||
makerAddress,
|
||||
feeRecipientAddress,
|
||||
makerAssetData: assetDataUtils.encodeERC20AssetData(erc20TokenA.address),
|
||||
@@ -109,7 +110,7 @@ describe('TEC tests', () => {
|
||||
orderFactory = new OrderFactory(makerPrivateKey, defaultOrderParams);
|
||||
makerTransactionFactory = new TransactionFactory(makerPrivateKey, exchange.address);
|
||||
takerTransactionFactory = new TransactionFactory(takerPrivateKey, exchange.address);
|
||||
approvalFactory = new ApprovalFactory(feeRecipientPrivateKey, tecContract.address);
|
||||
approvalFactory = new ApprovalFactory(feeRecipientPrivateKey, coordinatorContract.address);
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
@@ -126,10 +127,15 @@ describe('TEC tests', () => {
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
const approval = approvalFactory.newSignedApproval(
|
||||
transaction,
|
||||
takerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await tecContract.executeTransaction.sendTransactionAsync(
|
||||
await coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
@@ -144,7 +150,7 @@ describe('TEC tests', () => {
|
||||
const fillLogArgs = (fillLogs[0] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
|
||||
expect(fillLogArgs.makerAddress).to.eq(makerAddress);
|
||||
expect(fillLogArgs.takerAddress).to.eq(takerAddress);
|
||||
expect(fillLogArgs.senderAddress).to.eq(tecContract.address);
|
||||
expect(fillLogArgs.senderAddress).to.eq(coordinatorContract.address);
|
||||
expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
|
||||
expect(fillLogArgs.makerAssetData).to.eq(orders[0].makerAssetData);
|
||||
expect(fillLogArgs.takerAssetData).to.eq(orders[0].takerAssetData);
|
||||
@@ -159,8 +165,9 @@ describe('TEC tests', () => {
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await tecContract.executeTransaction.sendTransactionAsync(
|
||||
await coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
feeRecipientAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
@@ -175,7 +182,7 @@ describe('TEC tests', () => {
|
||||
const fillLogArgs = (fillLogs[0] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
|
||||
expect(fillLogArgs.makerAddress).to.eq(makerAddress);
|
||||
expect(fillLogArgs.takerAddress).to.eq(takerAddress);
|
||||
expect(fillLogArgs.senderAddress).to.eq(tecContract.address);
|
||||
expect(fillLogArgs.senderAddress).to.eq(coordinatorContract.address);
|
||||
expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
|
||||
expect(fillLogArgs.makerAssetData).to.eq(orders[0].makerAssetData);
|
||||
expect(fillLogArgs.takerAssetData).to.eq(orders[0].takerAssetData);
|
||||
@@ -190,10 +197,17 @@ describe('TEC tests', () => {
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
await expectTransactionFailedAsync(
|
||||
tecContract.executeTransaction.sendTransactionAsync(transaction, transaction.signature, [], [], {
|
||||
from: takerAddress,
|
||||
gas: devConstants.MAX_EXECUTE_TRANSACTION_GAS,
|
||||
}),
|
||||
coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{
|
||||
from: takerAddress,
|
||||
gas: devConstants.MAX_EXECUTE_TRANSACTION_GAS,
|
||||
},
|
||||
),
|
||||
RevertReason.InvalidApprovalSignature,
|
||||
);
|
||||
});
|
||||
@@ -203,11 +217,16 @@ describe('TEC tests', () => {
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
const approval = approvalFactory.newSignedApproval(
|
||||
transaction,
|
||||
takerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`;
|
||||
await expectTransactionFailedAsync(
|
||||
tecContract.executeTransaction.sendTransactionAsync(
|
||||
coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[signature],
|
||||
@@ -222,10 +241,15 @@ describe('TEC tests', () => {
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
const approval = approvalFactory.newSignedApproval(
|
||||
transaction,
|
||||
takerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await expectTransactionFailedAsync(
|
||||
tecContract.executeTransaction.sendTransactionAsync(
|
||||
coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
@@ -240,16 +264,21 @@ describe('TEC tests', () => {
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
const approval = approvalFactory.newSignedApproval(
|
||||
transaction,
|
||||
takerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await expectTransactionFailedAsync(
|
||||
tecContract.executeTransaction.sendTransactionAsync(
|
||||
coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: owner },
|
||||
),
|
||||
RevertReason.InvalidSender,
|
||||
RevertReason.InvalidOrigin,
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -262,10 +291,15 @@ describe('TEC tests', () => {
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
const approval = approvalFactory.newSignedApproval(
|
||||
transaction,
|
||||
takerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await tecContract.executeTransaction.sendTransactionAsync(
|
||||
await coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
@@ -281,7 +315,7 @@ describe('TEC tests', () => {
|
||||
const fillLogArgs = (fillLogs[index] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
|
||||
expect(fillLogArgs.makerAddress).to.eq(makerAddress);
|
||||
expect(fillLogArgs.takerAddress).to.eq(takerAddress);
|
||||
expect(fillLogArgs.senderAddress).to.eq(tecContract.address);
|
||||
expect(fillLogArgs.senderAddress).to.eq(coordinatorContract.address);
|
||||
expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
|
||||
expect(fillLogArgs.makerAssetData).to.eq(order.makerAssetData);
|
||||
expect(fillLogArgs.takerAssetData).to.eq(order.takerAssetData);
|
||||
@@ -297,8 +331,9 @@ describe('TEC tests', () => {
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await tecContract.executeTransaction.sendTransactionAsync(
|
||||
await coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
feeRecipientAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
@@ -314,7 +349,7 @@ describe('TEC tests', () => {
|
||||
const fillLogArgs = (fillLogs[index] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
|
||||
expect(fillLogArgs.makerAddress).to.eq(makerAddress);
|
||||
expect(fillLogArgs.takerAddress).to.eq(takerAddress);
|
||||
expect(fillLogArgs.senderAddress).to.eq(tecContract.address);
|
||||
expect(fillLogArgs.senderAddress).to.eq(coordinatorContract.address);
|
||||
expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
|
||||
expect(fillLogArgs.makerAssetData).to.eq(order.makerAssetData);
|
||||
expect(fillLogArgs.takerAssetData).to.eq(order.takerAssetData);
|
||||
@@ -331,11 +366,16 @@ describe('TEC tests', () => {
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
const approval = approvalFactory.newSignedApproval(
|
||||
transaction,
|
||||
takerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`;
|
||||
await expectTransactionFailedAsync(
|
||||
tecContract.executeTransaction.sendTransactionAsync(
|
||||
coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[signature],
|
||||
@@ -350,10 +390,15 @@ describe('TEC tests', () => {
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
const approval = approvalFactory.newSignedApproval(
|
||||
transaction,
|
||||
takerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await expectTransactionFailedAsync(
|
||||
tecContract.executeTransaction.sendTransactionAsync(
|
||||
coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
@@ -368,16 +413,21 @@ describe('TEC tests', () => {
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
const approval = approvalFactory.newSignedApproval(
|
||||
transaction,
|
||||
takerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await expectTransactionFailedAsync(
|
||||
tecContract.executeTransaction.sendTransactionAsync(
|
||||
coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: owner },
|
||||
),
|
||||
RevertReason.InvalidSender,
|
||||
RevertReason.InvalidOrigin,
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -385,12 +435,19 @@ describe('TEC tests', () => {
|
||||
describe('cancels', () => {
|
||||
it('cancelOrder call should be successful without an approval', async () => {
|
||||
const orders = [await orderFactory.newSignedOrderAsync()];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(constants.CANCEL_ORDERS, orders);
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(constants.CANCEL_ORDER, orders);
|
||||
const transaction = makerTransactionFactory.newSignedTransaction(data);
|
||||
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await tecContract.executeTransaction.sendTransactionAsync(transaction, transaction.signature, [], [], {
|
||||
from: makerAddress,
|
||||
}),
|
||||
await coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
makerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{
|
||||
from: makerAddress,
|
||||
},
|
||||
),
|
||||
);
|
||||
const cancelLogs = transactionReceipt.logs.filter(
|
||||
log => (log as LogWithDecodedArgs<ExchangeCancelEventArgs>).event === 'Cancel',
|
||||
@@ -398,7 +455,7 @@ describe('TEC tests', () => {
|
||||
expect(cancelLogs.length).to.eq(1);
|
||||
const cancelLogArgs = (cancelLogs[0] as LogWithDecodedArgs<ExchangeCancelEventArgs>).args;
|
||||
expect(cancelLogArgs.makerAddress).to.eq(makerAddress);
|
||||
expect(cancelLogArgs.senderAddress).to.eq(tecContract.address);
|
||||
expect(cancelLogArgs.senderAddress).to.eq(coordinatorContract.address);
|
||||
expect(cancelLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
|
||||
expect(cancelLogArgs.makerAssetData).to.eq(orders[0].makerAssetData);
|
||||
expect(cancelLogArgs.takerAssetData).to.eq(orders[0].takerAssetData);
|
||||
@@ -409,9 +466,16 @@ describe('TEC tests', () => {
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(constants.BATCH_CANCEL_ORDERS, orders);
|
||||
const transaction = makerTransactionFactory.newSignedTransaction(data);
|
||||
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await tecContract.executeTransaction.sendTransactionAsync(transaction, transaction.signature, [], [], {
|
||||
from: makerAddress,
|
||||
}),
|
||||
await coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
makerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{
|
||||
from: makerAddress,
|
||||
},
|
||||
),
|
||||
);
|
||||
const cancelLogs = transactionReceipt.logs.filter(
|
||||
log => (log as LogWithDecodedArgs<ExchangeCancelEventArgs>).event === 'Cancel',
|
||||
@@ -420,7 +484,7 @@ describe('TEC tests', () => {
|
||||
orders.forEach((order, index) => {
|
||||
const cancelLogArgs = (cancelLogs[index] as LogWithDecodedArgs<ExchangeCancelEventArgs>).args;
|
||||
expect(cancelLogArgs.makerAddress).to.eq(makerAddress);
|
||||
expect(cancelLogArgs.senderAddress).to.eq(tecContract.address);
|
||||
expect(cancelLogArgs.senderAddress).to.eq(coordinatorContract.address);
|
||||
expect(cancelLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
|
||||
expect(cancelLogArgs.makerAssetData).to.eq(order.makerAssetData);
|
||||
expect(cancelLogArgs.takerAssetData).to.eq(order.takerAssetData);
|
||||
@@ -432,9 +496,16 @@ describe('TEC tests', () => {
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(constants.CANCEL_ORDERS_UP_TO, orders);
|
||||
const transaction = makerTransactionFactory.newSignedTransaction(data);
|
||||
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await tecContract.executeTransaction.sendTransactionAsync(transaction, transaction.signature, [], [], {
|
||||
from: makerAddress,
|
||||
}),
|
||||
await coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
makerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{
|
||||
from: makerAddress,
|
||||
},
|
||||
),
|
||||
);
|
||||
const cancelLogs = transactionReceipt.logs.filter(
|
||||
log => (log as LogWithDecodedArgs<ExchangeCancelUpToEventArgs>).event === 'CancelUpTo',
|
||||
@@ -442,8 +513,9 @@ describe('TEC tests', () => {
|
||||
expect(cancelLogs.length).to.eq(1);
|
||||
const cancelLogArgs = (cancelLogs[0] as LogWithDecodedArgs<ExchangeCancelUpToEventArgs>).args;
|
||||
expect(cancelLogArgs.makerAddress).to.eq(makerAddress);
|
||||
expect(cancelLogArgs.senderAddress).to.eq(tecContract.address);
|
||||
expect(cancelLogArgs.senderAddress).to.eq(coordinatorContract.address);
|
||||
expect(cancelLogArgs.orderEpoch).to.bignumber.eq(new BigNumber(1));
|
||||
});
|
||||
});
|
||||
});
|
||||
// tslint:disable:max-file-line-count
|
81
contracts/coordinator/test/coordinator_registry.ts
Normal file
81
contracts/coordinator/test/coordinator_registry.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import { artifacts as exchangeArtifacts } from '@0x/contracts-exchange';
|
||||
import { chaiSetup, provider, web3Wrapper } from '@0x/contracts-test-utils';
|
||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||
import * as chai from 'chai';
|
||||
import { LogWithDecodedArgs } from 'ethereum-types';
|
||||
|
||||
import { CoordinatorRegistryCoordinatorEndpointSetEventArgs } from '../src';
|
||||
|
||||
import { CoordinatorRegistryWrapper } from './utils/coordinator_registry_wrapper';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
web3Wrapper.abiDecoder.addABI(exchangeArtifacts.Exchange.compilerOutput.abi);
|
||||
// tslint:disable:no-unnecessary-type-assertion
|
||||
describe('Coordinator Registry tests', () => {
|
||||
let coordinatorOperator: string;
|
||||
const coordinatorEndpoint = 'http://sometec.0x.org';
|
||||
const nilCoordinatorEndpoint = '';
|
||||
let coordinatorRegistryWrapper: CoordinatorRegistryWrapper;
|
||||
// tests
|
||||
before(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
after(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
before(async () => {
|
||||
// setup accounts (skip owner)
|
||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
||||
[, coordinatorOperator] = accounts;
|
||||
// deploy coordinator registry
|
||||
coordinatorRegistryWrapper = new CoordinatorRegistryWrapper(provider);
|
||||
await coordinatorRegistryWrapper.deployCoordinatorRegistryAsync();
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
afterEach(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
describe('core', () => {
|
||||
it('Should successfully set a Coordinator endpoint', async () => {
|
||||
await coordinatorRegistryWrapper.setCoordinatorEndpointAsync(coordinatorOperator, coordinatorEndpoint);
|
||||
const recordedCoordinatorEndpoint = await coordinatorRegistryWrapper.getCoordinatorEndpointAsync(
|
||||
coordinatorOperator,
|
||||
);
|
||||
expect(recordedCoordinatorEndpoint).to.be.equal(coordinatorEndpoint);
|
||||
});
|
||||
it('Should successfully unset a Coordinator endpoint', async () => {
|
||||
// set Coordinator endpoint
|
||||
await coordinatorRegistryWrapper.setCoordinatorEndpointAsync(coordinatorOperator, coordinatorEndpoint);
|
||||
let recordedCoordinatorEndpoint = await coordinatorRegistryWrapper.getCoordinatorEndpointAsync(
|
||||
coordinatorOperator,
|
||||
);
|
||||
expect(recordedCoordinatorEndpoint).to.be.equal(coordinatorEndpoint);
|
||||
// unset Coordinator endpoint
|
||||
await coordinatorRegistryWrapper.setCoordinatorEndpointAsync(coordinatorOperator, nilCoordinatorEndpoint);
|
||||
recordedCoordinatorEndpoint = await coordinatorRegistryWrapper.getCoordinatorEndpointAsync(
|
||||
coordinatorOperator,
|
||||
);
|
||||
expect(recordedCoordinatorEndpoint).to.be.equal(nilCoordinatorEndpoint);
|
||||
});
|
||||
it('Should emit an event when setting Coordinator endpoint', async () => {
|
||||
// set Coordinator endpoint
|
||||
const txReceipt = await coordinatorRegistryWrapper.setCoordinatorEndpointAsync(
|
||||
coordinatorOperator,
|
||||
coordinatorEndpoint,
|
||||
);
|
||||
const recordedCoordinatorEndpoint = await coordinatorRegistryWrapper.getCoordinatorEndpointAsync(
|
||||
coordinatorOperator,
|
||||
);
|
||||
expect(recordedCoordinatorEndpoint).to.be.equal(coordinatorEndpoint);
|
||||
// validate event
|
||||
expect(txReceipt.logs.length).to.be.equal(1);
|
||||
const log = txReceipt.logs[0] as LogWithDecodedArgs<CoordinatorRegistryCoordinatorEndpointSetEventArgs>;
|
||||
expect(log.args.coordinatorOperator).to.be.equal(coordinatorOperator);
|
||||
expect(log.args.coordinatorEndpoint).to.be.equal(coordinatorEndpoint);
|
||||
});
|
||||
});
|
||||
});
|
@@ -1,8 +1,10 @@
|
||||
import { env, EnvVars } from '@0x/dev-utils';
|
||||
|
||||
import { coverage, profiler, provider } from '@0x/contracts-test-utils';
|
||||
import { providerUtils } from '@0x/utils';
|
||||
|
||||
before('start web3 provider', () => {
|
||||
provider.start();
|
||||
providerUtils.startProviderEngine(provider);
|
||||
});
|
||||
after('generate coverage report', async () => {
|
||||
if (env.parseBoolean(EnvVars.SolidityCoverage)) {
|
@@ -1,16 +1,18 @@
|
||||
import { chaiSetup, constants, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils';
|
||||
import { addressUtils, chaiSetup, constants, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils';
|
||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||
import { transactionHashUtils } from '@0x/order-utils';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as chai from 'chai';
|
||||
|
||||
import { artifacts, hashUtils, TestLibsContract } from '../src';
|
||||
import { artifacts, CoordinatorContract, hashUtils } from '../src';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
|
||||
describe('Libs tests', () => {
|
||||
let testLibs: TestLibsContract;
|
||||
let coordinatorContract: CoordinatorContract;
|
||||
const exchangeAddress = addressUtils.generatePseudoRandomAddress();
|
||||
|
||||
before(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
@@ -19,7 +21,12 @@ describe('Libs tests', () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
before(async () => {
|
||||
testLibs = await TestLibsContract.deployFrom0xArtifactAsync(artifacts.TestLibs, provider, txDefaults);
|
||||
coordinatorContract = await CoordinatorContract.deployFrom0xArtifactAsync(
|
||||
artifacts.Coordinator,
|
||||
provider,
|
||||
txDefaults,
|
||||
exchangeAddress,
|
||||
);
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
@@ -31,13 +38,13 @@ describe('Libs tests', () => {
|
||||
describe('getTransactionHash', () => {
|
||||
it('should return the correct transaction hash', async () => {
|
||||
const tx = {
|
||||
verifyingContractAddress: testLibs.address,
|
||||
verifyingContractAddress: exchangeAddress,
|
||||
salt: new BigNumber(0),
|
||||
signerAddress: constants.NULL_ADDRESS,
|
||||
data: '0x1234',
|
||||
};
|
||||
const expectedTxHash = hashUtils.getTransactionHashHex(tx);
|
||||
const txHash = await testLibs.publicGetTransactionHash.callAsync(tx);
|
||||
const expectedTxHash = transactionHashUtils.getTransactionHashHex(tx);
|
||||
const txHash = await coordinatorContract.getTransactionHash.callAsync(tx);
|
||||
expect(expectedTxHash).to.eq(txHash);
|
||||
});
|
||||
});
|
||||
@@ -45,20 +52,27 @@ describe('Libs tests', () => {
|
||||
describe('getApprovalHash', () => {
|
||||
it('should return the correct approval hash', async () => {
|
||||
const signedTx = {
|
||||
verifyingContractAddress: testLibs.address,
|
||||
verifyingContractAddress: exchangeAddress,
|
||||
salt: new BigNumber(0),
|
||||
signerAddress: constants.NULL_ADDRESS,
|
||||
data: '0x1234',
|
||||
signature: '0x5678',
|
||||
};
|
||||
const approvalExpirationTimeSeconds = new BigNumber(0);
|
||||
const txOrigin = constants.NULL_ADDRESS;
|
||||
const approval = {
|
||||
transactionHash: hashUtils.getTransactionHashHex(signedTx),
|
||||
txOrigin,
|
||||
transactionHash: transactionHashUtils.getTransactionHashHex(signedTx),
|
||||
transactionSignature: signedTx.signature,
|
||||
approvalExpirationTimeSeconds,
|
||||
};
|
||||
const expectedApprovalHash = hashUtils.getApprovalHashHex(signedTx, approvalExpirationTimeSeconds);
|
||||
const approvalHash = await testLibs.publicGetTECApprovalHash.callAsync(approval);
|
||||
const expectedApprovalHash = hashUtils.getApprovalHashHex(
|
||||
signedTx,
|
||||
coordinatorContract.address,
|
||||
txOrigin,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const approvalHash = await coordinatorContract.getCoordinatorApprovalHash.callAsync(approval);
|
||||
expect(expectedApprovalHash).to.eq(approvalHash);
|
||||
});
|
||||
});
|
@@ -1,28 +1,22 @@
|
||||
import {
|
||||
addressUtils,
|
||||
chaiSetup,
|
||||
constants as devConstants,
|
||||
expectContractCallFailedAsync,
|
||||
getLatestBlockTimestampAsync,
|
||||
provider,
|
||||
TransactionFactory,
|
||||
txDefaults,
|
||||
web3Wrapper,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||
import { RevertReason, SignedOrder } from '@0x/types';
|
||||
import { transactionHashUtils } from '@0x/order-utils';
|
||||
import { RevertReason, SignatureType, SignedOrder } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as chai from 'chai';
|
||||
import * as ethUtil from 'ethereumjs-util';
|
||||
|
||||
import {
|
||||
ApprovalFactory,
|
||||
artifacts,
|
||||
constants,
|
||||
exchangeDataEncoder,
|
||||
hashUtils,
|
||||
TECSignatureType,
|
||||
TECTransactionFactory,
|
||||
TestMixinsContract,
|
||||
} from '../src';
|
||||
import { ApprovalFactory, artifacts, constants, CoordinatorContract, exchangeDataEncoder } from '../src';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
@@ -32,11 +26,13 @@ describe('Mixins tests', () => {
|
||||
let transactionSignerAddress: string;
|
||||
let approvalSignerAddress1: string;
|
||||
let approvalSignerAddress2: string;
|
||||
let mixins: TestMixinsContract;
|
||||
let transactionFactory: TECTransactionFactory;
|
||||
let mixins: CoordinatorContract;
|
||||
let transactionFactory: TransactionFactory;
|
||||
let approvalFactory1: ApprovalFactory;
|
||||
let approvalFactory2: ApprovalFactory;
|
||||
let defaultOrder: SignedOrder;
|
||||
const exchangeAddress = addressUtils.generatePseudoRandomAddress();
|
||||
|
||||
before(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
@@ -44,7 +40,12 @@ describe('Mixins tests', () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
before(async () => {
|
||||
mixins = await TestMixinsContract.deployFrom0xArtifactAsync(artifacts.TestMixins, provider, txDefaults);
|
||||
mixins = await CoordinatorContract.deployFrom0xArtifactAsync(
|
||||
artifacts.Coordinator,
|
||||
provider,
|
||||
txDefaults,
|
||||
exchangeAddress,
|
||||
);
|
||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
||||
[transactionSignerAddress, approvalSignerAddress1, approvalSignerAddress2] = accounts.slice(0, 3);
|
||||
defaultOrder = {
|
||||
@@ -67,7 +68,7 @@ describe('Mixins tests', () => {
|
||||
devConstants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(transactionSignerAddress)];
|
||||
const approvalSignerPrivateKey1 = devConstants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(approvalSignerAddress1)];
|
||||
const approvalSignerPrivateKey2 = devConstants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(approvalSignerAddress2)];
|
||||
transactionFactory = new TECTransactionFactory(transactionSignerPrivateKey, mixins.address);
|
||||
transactionFactory = new TransactionFactory(transactionSignerPrivateKey, exchangeAddress);
|
||||
approvalFactory1 = new ApprovalFactory(approvalSignerPrivateKey1, mixins.address);
|
||||
approvalFactory2 = new ApprovalFactory(approvalSignerPrivateKey2, mixins.address);
|
||||
});
|
||||
@@ -81,41 +82,52 @@ describe('Mixins tests', () => {
|
||||
describe('getSignerAddress', () => {
|
||||
it('should return the correct address using the EthSign signature type', async () => {
|
||||
const data = devConstants.NULL_BYTES;
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data, TECSignatureType.EthSign);
|
||||
const transactionHash = hashUtils.getTransactionHashHex(transaction);
|
||||
const transaction = transactionFactory.newSignedTransaction(data, SignatureType.EthSign);
|
||||
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||
const signerAddress = await mixins.getSignerAddress.callAsync(transactionHash, transaction.signature);
|
||||
expect(transaction.signerAddress).to.eq(signerAddress);
|
||||
});
|
||||
it('should return the correct address using the EIP712 signature type', async () => {
|
||||
const data = devConstants.NULL_BYTES;
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data, TECSignatureType.EIP712);
|
||||
const transactionHash = hashUtils.getTransactionHashHex(transaction);
|
||||
const transaction = transactionFactory.newSignedTransaction(data, SignatureType.EIP712);
|
||||
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||
const signerAddress = await mixins.getSignerAddress.callAsync(transactionHash, transaction.signature);
|
||||
expect(transaction.signerAddress).to.eq(signerAddress);
|
||||
});
|
||||
it('should revert with with the Illegal signature type', async () => {
|
||||
const data = devConstants.NULL_BYTES;
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const illegalSignatureByte = ethUtil.toBuffer(TECSignatureType.Illegal).toString('hex');
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const illegalSignatureByte = ethUtil.toBuffer(SignatureType.Illegal).toString('hex');
|
||||
transaction.signature = `${transaction.signature.slice(
|
||||
0,
|
||||
transaction.signature.length - 2,
|
||||
)}${illegalSignatureByte}`;
|
||||
const transactionHash = hashUtils.getTransactionHashHex(transaction);
|
||||
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.getSignerAddress.callAsync(transactionHash, transaction.signature),
|
||||
RevertReason.SignatureIllegal,
|
||||
);
|
||||
});
|
||||
it('should revert with with the Invalid signature type', async () => {
|
||||
const data = devConstants.NULL_BYTES;
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const invalidSignatureByte = ethUtil.toBuffer(SignatureType.Invalid).toString('hex');
|
||||
transaction.signature = `0x${invalidSignatureByte}`;
|
||||
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.getSignerAddress.callAsync(transactionHash, transaction.signature),
|
||||
RevertReason.SignatureInvalid,
|
||||
);
|
||||
});
|
||||
it("should revert with with a signature type that doesn't exist", async () => {
|
||||
const data = devConstants.NULL_BYTES;
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const invalidSignatureByte = '03';
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const invalidSignatureByte = '04';
|
||||
transaction.signature = `${transaction.signature.slice(
|
||||
0,
|
||||
transaction.signature.length - 2,
|
||||
)}${invalidSignatureByte}`;
|
||||
const transactionHash = hashUtils.getTransactionHashHex(transaction);
|
||||
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.getSignerAddress.callAsync(transactionHash, transaction.signature),
|
||||
RevertReason.SignatureUnsupported,
|
||||
@@ -123,25 +135,86 @@ describe('Mixins tests', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('decodeOrdersFromFillData', () => {
|
||||
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 decodedSignedOrders = decodedOrders.map(order => ({
|
||||
...order,
|
||||
exchangeAddress: devConstants.NULL_ADDRESS,
|
||||
signature: devConstants.NULL_BYTES,
|
||||
}));
|
||||
expect(orders).to.deep.eq(decodedSignedOrders);
|
||||
});
|
||||
}
|
||||
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 decodedSignedOrders = decodedOrders.map(order => ({
|
||||
...order,
|
||||
exchangeAddress: devConstants.NULL_ADDRESS,
|
||||
signature: devConstants.NULL_BYTES,
|
||||
}));
|
||||
expect(orders).to.deep.eq(decodedSignedOrders);
|
||||
});
|
||||
}
|
||||
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 decodedSignedOrders = decodedOrders.map(order => ({
|
||||
...order,
|
||||
exchangeAddress: devConstants.NULL_ADDRESS,
|
||||
signature: devConstants.NULL_BYTES,
|
||||
}));
|
||||
expect(orders).to.deep.eq(decodedSignedOrders);
|
||||
});
|
||||
}
|
||||
for (const fnName of [constants.CANCEL_ORDER, constants.BATCH_CANCEL_ORDERS, constants.CANCEL_ORDERS_UP_TO]) {
|
||||
it(`should correctly decode the orders for ${fnName} data`, async () => {
|
||||
const orders = [defaultOrder, defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const decodedOrders = await mixins.decodeOrdersFromFillData.callAsync(data);
|
||||
const emptyArray: any[] = [];
|
||||
expect(emptyArray).to.deep.eq(decodedOrders);
|
||||
});
|
||||
}
|
||||
it('should decode an empty array for invalid data', async () => {
|
||||
const data = '0x0123456789';
|
||||
const decodedOrders = await mixins.decodeOrdersFromFillData.callAsync(data);
|
||||
const emptyArray: any[] = [];
|
||||
expect(emptyArray).to.deep.eq(decodedOrders);
|
||||
});
|
||||
it('should revert if data is less than 4 bytes long', async () => {
|
||||
const data = '0x010203';
|
||||
await expectContractCallFailedAsync(
|
||||
mixins.decodeOrdersFromFillData.callAsync(data),
|
||||
RevertReason.LibBytesGreaterOrEqualTo4LengthRequired,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Single order approvals', () => {
|
||||
for (const fnName of constants.SINGLE_FILL_FN_NAMES) {
|
||||
it(`Should be successful: function=${fnName}, caller=tx_signer, senderAddress=[verifier], approval_sig=[approver1], expiration=[valid]`, async () => {
|
||||
const orders = [defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory1.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
await mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
const approval = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
orders,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await mixins.assertValidTECApprovals.callAsync(
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
@@ -155,20 +228,17 @@ describe('Mixins tests', () => {
|
||||
};
|
||||
const orders = [order];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory1.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
await mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
const approval = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
orders,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await mixins.assertValidTECApprovals.callAsync(
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
@@ -178,36 +248,32 @@ describe('Mixins tests', () => {
|
||||
it(`Should be successful: function=${fnName}, caller=approver1, senderAddress=[verifier], approval_sig=[], expiration=[]`, async () => {
|
||||
const orders = [defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
await mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
approvalSignerAddress1,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{ from: approvalSignerAddress1 },
|
||||
{
|
||||
from: approvalSignerAddress1,
|
||||
},
|
||||
);
|
||||
await mixins.assertValidTECApprovals.callAsync(transaction, transaction.signature, [], [], {
|
||||
from: approvalSignerAddress1,
|
||||
});
|
||||
});
|
||||
it(`Should be successful: function=${fnName}, caller=approver1, senderAddress=[verifier], approval_sig=[approver1], expiration=[invalid]`, async () => {
|
||||
const orders = [defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory1.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
await mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
const approval = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
orders,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: approvalSignerAddress1 },
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await mixins.assertValidTECApprovals.callAsync(
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
approvalSignerAddress1,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
@@ -217,41 +283,34 @@ describe('Mixins tests', () => {
|
||||
it(`Should be successful: function=${fnName}, caller=approver1, senderAddress=[verifier], approval_sig=[], expiration=[]`, async () => {
|
||||
const orders = [defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
await mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
approvalSignerAddress1,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{ from: approvalSignerAddress1 },
|
||||
{
|
||||
from: approvalSignerAddress1,
|
||||
},
|
||||
);
|
||||
await mixins.assertValidTECApprovals.callAsync(transaction, transaction.signature, [], [], {
|
||||
from: approvalSignerAddress1,
|
||||
});
|
||||
});
|
||||
it(`Should revert: function=${fnName}, caller=tx_signer, senderAddress=[verifier], approval_sig=[invalid], expiration=[valid]`, async () => {
|
||||
const orders = [defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory1.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
const approval = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`;
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[signature],
|
||||
{ from: transactionSignerAddress },
|
||||
),
|
||||
RevertReason.InvalidApprovalSignature,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTECApprovals.callAsync(
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[signature],
|
||||
@@ -263,24 +322,18 @@ describe('Mixins tests', () => {
|
||||
it(`Should revert: function=${fnName}, caller=tx_signer, senderAddress=[verifier], approval_sig=[approver1], expiration=[invalid]`, async () => {
|
||||
const orders = [defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory1.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
),
|
||||
RevertReason.ApprovalExpired,
|
||||
const approval = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTECApprovals.callAsync(
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
@@ -292,30 +345,24 @@ describe('Mixins tests', () => {
|
||||
it(`Should revert: function=${fnName}, caller=approver2, senderAddress=[verifier], approval_sig=[approver1], expiration=[valid]`, async () => {
|
||||
const orders = [defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory1.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: approvalSignerAddress2 },
|
||||
),
|
||||
RevertReason.InvalidSender,
|
||||
const approval = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTECApprovals.callAsync(
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: approvalSignerAddress2 },
|
||||
),
|
||||
RevertReason.InvalidSender,
|
||||
RevertReason.InvalidOrigin,
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -329,20 +376,17 @@ describe('Mixins tests', () => {
|
||||
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver1], approval_sig=[approver1], expiration=[valid]`, async () => {
|
||||
const orders = [defaultOrder, defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory1.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
await mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
const approval = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
orders,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await mixins.assertValidTECApprovals.callAsync(
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
@@ -355,20 +399,17 @@ describe('Mixins tests', () => {
|
||||
senderAddress: devConstants.NULL_ADDRESS,
|
||||
}));
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory1.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
await mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
const approval = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
orders,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await mixins.assertValidTECApprovals.callAsync(
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
@@ -381,36 +422,30 @@ describe('Mixins tests', () => {
|
||||
senderAddress: devConstants.NULL_ADDRESS,
|
||||
}));
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
await mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
await mixins.assertValidTECApprovals.callAsync(transaction, transaction.signature, [], [], {
|
||||
from: transactionSignerAddress,
|
||||
});
|
||||
});
|
||||
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[verifier,null], feeRecipient=[approver1,approver1], approval_sig=[approver1], expiration=[valid]`, async () => {
|
||||
const orders = [defaultOrder, { ...defaultOrder, senderAddress: devConstants.NULL_ADDRESS }];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory1.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
await mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
const approval = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
orders,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await mixins.assertValidTECApprovals.callAsync(
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
@@ -420,21 +455,22 @@ describe('Mixins tests', () => {
|
||||
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver2], approval_sig=[approver1,approver2], expiration=[valid,valid]`, async () => {
|
||||
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval1 = approvalFactory1.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
const approval2 = approvalFactory2.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
await mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
const approval1 = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
orders,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds, approvalExpirationTimeSeconds],
|
||||
[approval1.signature, approval2.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await mixins.assertValidTECApprovals.callAsync(
|
||||
const approval2 = approvalFactory2.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds, approvalExpirationTimeSeconds],
|
||||
[approval1.signature, approval2.signature],
|
||||
@@ -444,50 +480,47 @@ describe('Mixins tests', () => {
|
||||
it(`Should be successful: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver1], approval_sig=[], expiration=[]`, async () => {
|
||||
const orders = [defaultOrder, defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
await mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
approvalSignerAddress1,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{ from: approvalSignerAddress1 },
|
||||
);
|
||||
await mixins.assertValidTECApprovals.callAsync(transaction, transaction.signature, [], [], {
|
||||
from: approvalSignerAddress1,
|
||||
});
|
||||
});
|
||||
it(`Should be successful: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver2], approval_sig=[approver2], expiration=[valid]`, async () => {
|
||||
it(`Should revert: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver2], approval_sig=[approver2], expiration=[valid]`, async () => {
|
||||
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval2 = approvalFactory2.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
await mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
const approval2 = approvalFactory2.newSignedApproval(
|
||||
transaction,
|
||||
orders,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval2.signature],
|
||||
{ from: approvalSignerAddress1 },
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await mixins.assertValidTECApprovals.callAsync(
|
||||
transaction,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval2.signature],
|
||||
{ from: approvalSignerAddress1 },
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval2.signature],
|
||||
{ from: approvalSignerAddress1 },
|
||||
),
|
||||
RevertReason.InvalidOrigin,
|
||||
);
|
||||
});
|
||||
it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver1], approval_sig=[], expiration=[]`, async () => {
|
||||
const orders = [defaultOrder, defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
@@ -495,35 +528,23 @@ describe('Mixins tests', () => {
|
||||
),
|
||||
RevertReason.InvalidApprovalSignature,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTECApprovals.callAsync(transaction, transaction.signature, [], [], {
|
||||
from: transactionSignerAddress,
|
||||
}),
|
||||
RevertReason.InvalidApprovalSignature,
|
||||
);
|
||||
});
|
||||
it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver1], approval_sig=[invalid], expiration=[valid]`, async () => {
|
||||
const orders = [defaultOrder, defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory1.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
const approval = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`;
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[signature],
|
||||
{ from: transactionSignerAddress },
|
||||
),
|
||||
RevertReason.InvalidApprovalSignature,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTECApprovals.callAsync(
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[signature],
|
||||
@@ -535,26 +556,24 @@ describe('Mixins tests', () => {
|
||||
it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[valid,invalid], expiration=[valid,valid]`, async () => {
|
||||
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval1 = approvalFactory1.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
const approval2 = approvalFactory2.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
const approval1 = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const approval2 = approvalFactory2.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const approvalSignature2 = `${approval2.signature.slice(0, 4)}FFFFFFFF${approval2.signature.slice(12)}`;
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds, approvalExpirationTimeSeconds],
|
||||
[approval1.signature, approvalSignature2],
|
||||
{ from: transactionSignerAddress },
|
||||
),
|
||||
RevertReason.InvalidApprovalSignature,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTECApprovals.callAsync(
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds, approvalExpirationTimeSeconds],
|
||||
[approval1.signature, approvalSignature2],
|
||||
@@ -566,25 +585,19 @@ describe('Mixins tests', () => {
|
||||
it(`Should revert: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[invalid], expiration=[valid]`, async () => {
|
||||
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval2 = approvalFactory2.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
const approval2 = approvalFactory2.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const approvalSignature2 = `${approval2.signature.slice(0, 4)}FFFFFFFF${approval2.signature.slice(12)}`;
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approvalSignature2],
|
||||
{ from: approvalSignerAddress1 },
|
||||
),
|
||||
RevertReason.InvalidApprovalSignature,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTECApprovals.callAsync(
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
approvalSignerAddress1,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approvalSignature2],
|
||||
@@ -596,26 +609,24 @@ describe('Mixins tests', () => {
|
||||
it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[valid,valid], expiration=[valid,invalid]`, async () => {
|
||||
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds1 = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approvalExpirationTimeSeconds2 = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER);
|
||||
const approval1 = approvalFactory1.newSignedApproval(transaction, approvalExpirationTimeSeconds1);
|
||||
const approval2 = approvalFactory2.newSignedApproval(transaction, approvalExpirationTimeSeconds2);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds1, approvalExpirationTimeSeconds2],
|
||||
[approval1.signature, approval2.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
),
|
||||
RevertReason.ApprovalExpired,
|
||||
const approval1 = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds1,
|
||||
);
|
||||
const approval2 = approvalFactory2.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds2,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTECApprovals.callAsync(
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds1, approvalExpirationTimeSeconds2],
|
||||
[approval1.signature, approval2.signature],
|
||||
@@ -627,24 +638,18 @@ describe('Mixins tests', () => {
|
||||
it(`Should revert: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[valid], expiration=[invalid]`, async () => {
|
||||
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER);
|
||||
const approval2 = approvalFactory2.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval2.signature],
|
||||
{ from: approvalSignerAddress1 },
|
||||
),
|
||||
RevertReason.ApprovalExpired,
|
||||
const approval2 = approvalFactory2.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTECApprovals.callAsync(
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
approvalSignerAddress1,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval2.signature],
|
||||
@@ -656,58 +661,67 @@ describe('Mixins tests', () => {
|
||||
it(`Should revert: function=${fnName} caller=approver2, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver1], approval_sig=[valid], expiration=[valid]`, async () => {
|
||||
const orders = [defaultOrder, defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval1 = approvalFactory1.newSignedApproval(transaction, approvalExpirationTimeSeconds);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval1.signature],
|
||||
{ from: approvalSignerAddress2 },
|
||||
),
|
||||
RevertReason.InvalidSender,
|
||||
const approval1 = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTECApprovals.callAsync(
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval1.signature],
|
||||
{ from: approvalSignerAddress2 },
|
||||
),
|
||||
RevertReason.InvalidSender,
|
||||
RevertReason.InvalidOrigin,
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
describe('cancels', () => {
|
||||
it('should allow the tx signer to call `cancelOrders` without approval', async () => {
|
||||
it('should allow the tx signer to call `cancelOrder` without approval', async () => {
|
||||
const orders = [defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(constants.CANCEL_ORDERS, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
await mixins.assertValidTECApprovals.callAsync(transaction, transaction.signature, [], [], {
|
||||
from: transactionSignerAddress,
|
||||
});
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(constants.CANCEL_ORDER, orders);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
});
|
||||
it('should allow the tx signer to call `batchCancelOrders` without approval', async () => {
|
||||
const orders = [defaultOrder, defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(constants.BATCH_CANCEL_ORDERS, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
await mixins.assertValidTECApprovals.callAsync(transaction, transaction.signature, [], [], {
|
||||
from: transactionSignerAddress,
|
||||
});
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
});
|
||||
it('should allow the tx signer to call `cancelOrdersUpTo` without approval', async () => {
|
||||
const orders: SignedOrder[] = [];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(constants.CANCEL_ORDERS_UP_TO, orders);
|
||||
const transaction = transactionFactory.newSignedTECTransaction(data);
|
||||
await mixins.assertValidTECApprovals.callAsync(transaction, transaction.signature, [], [], {
|
||||
from: transactionSignerAddress,
|
||||
});
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
@@ -1,29 +1,35 @@
|
||||
import { SignedZeroExTransaction } from '@0x/types';
|
||||
import { signingUtils } from '@0x/contracts-test-utils';
|
||||
import { SignatureType, SignedZeroExTransaction } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as ethUtil from 'ethereumjs-util';
|
||||
|
||||
import { hashUtils, SignedTECApproval, signingUtils, TECSignatureType } from './index';
|
||||
import { hashUtils, SignedCoordinatorApproval } from './index';
|
||||
|
||||
export class ApprovalFactory {
|
||||
private readonly _privateKey: Buffer;
|
||||
private readonly _verifyingContractAddress: string;
|
||||
|
||||
constructor(privateKey: Buffer, verifyingContractAddress: string) {
|
||||
this._privateKey = privateKey;
|
||||
this._verifyingContractAddress = verifyingContractAddress;
|
||||
}
|
||||
|
||||
public newSignedApproval(
|
||||
transaction: SignedZeroExTransaction,
|
||||
txOrigin: string,
|
||||
approvalExpirationTimeSeconds: BigNumber,
|
||||
signatureType: TECSignatureType = TECSignatureType.EthSign,
|
||||
): SignedTECApproval {
|
||||
const tecTransaction = {
|
||||
...transaction,
|
||||
verifyingContractAddress: this._verifyingContractAddress,
|
||||
};
|
||||
const approvalHashBuff = hashUtils.getApprovalHashBuffer(tecTransaction, approvalExpirationTimeSeconds);
|
||||
signatureType: SignatureType = SignatureType.EthSign,
|
||||
): SignedCoordinatorApproval {
|
||||
const approvalHashBuff = hashUtils.getApprovalHashBuffer(
|
||||
transaction,
|
||||
this._verifyingContractAddress,
|
||||
txOrigin,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const signatureBuff = signingUtils.signMessage(approvalHashBuff, this._privateKey, signatureType);
|
||||
const signedApproval = {
|
||||
transaction: tecTransaction,
|
||||
txOrigin,
|
||||
transaction,
|
||||
approvalExpirationTimeSeconds,
|
||||
signature: ethUtil.addHexPrefix(signatureBuff.toString('hex')),
|
||||
};
|
@@ -1,21 +1,11 @@
|
||||
import { BigNumber } from '@0x/utils';
|
||||
|
||||
export const constants = {
|
||||
TEC_DOMAIN_NAME: '0x Protocol Trade Execution Coordinator',
|
||||
TEC_DOMAIN_VERSION: '1.0.0',
|
||||
TEC_APPROVAL_SCHEMA: {
|
||||
name: 'TECApproval',
|
||||
parameters: [
|
||||
{ name: 'transactionHash', type: 'bytes32' },
|
||||
{ name: 'transactionSignature', type: 'bytes' },
|
||||
{ name: 'approvalExpirationTimeSeconds', type: 'uint256' },
|
||||
],
|
||||
},
|
||||
SINGLE_FILL_FN_NAMES: ['fillOrder', 'fillOrKillOrder', 'fillOrderNoThrow'],
|
||||
BATCH_FILL_FN_NAMES: ['batchFillOrders', 'batchFillOrKillOrders', 'batchFillOrdersNoThrow'],
|
||||
MARKET_FILL_FN_NAMES: ['marketBuyOrders', 'marketBuyOrdersNoThrow', 'marketSellOrders', 'marketSellOrdersNoThrow'],
|
||||
MATCH_ORDERS: 'matchOrders',
|
||||
CANCEL_ORDERS: 'cancelOrders',
|
||||
CANCEL_ORDER: 'cancelOrder',
|
||||
BATCH_CANCEL_ORDERS: 'batchCancelOrders',
|
||||
CANCEL_ORDERS_UP_TO: 'cancelOrdersUpTo',
|
||||
TIME_BUFFER: new BigNumber(1000),
|
@@ -0,0 +1,64 @@
|
||||
import { LogDecoder, txDefaults } from '@0x/contracts-test-utils';
|
||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import { TransactionReceiptWithDecodedLogs, ZeroExProvider } from 'ethereum-types';
|
||||
|
||||
import { artifacts, CoordinatorRegistryContract } from '../../src';
|
||||
|
||||
export class CoordinatorRegistryWrapper {
|
||||
private readonly _web3Wrapper: Web3Wrapper;
|
||||
private readonly _provider: ZeroExProvider;
|
||||
private readonly _logDecoder: LogDecoder;
|
||||
private _coordinatorRegistryContract?: CoordinatorRegistryContract;
|
||||
/**
|
||||
* Instanitates an CoordinatorRegistryWrapper
|
||||
* @param provider Web3 provider to use for all JSON RPC requests
|
||||
* Instance of CoordinatorRegistryWrapper
|
||||
*/
|
||||
constructor(provider: ZeroExProvider) {
|
||||
this._web3Wrapper = new Web3Wrapper(provider);
|
||||
this._provider = provider;
|
||||
this._logDecoder = new LogDecoder(this._web3Wrapper, artifacts);
|
||||
}
|
||||
public async deployCoordinatorRegistryAsync(): Promise<CoordinatorRegistryContract> {
|
||||
this._coordinatorRegistryContract = await CoordinatorRegistryContract.deployFrom0xArtifactAsync(
|
||||
artifacts.CoordinatorRegistry,
|
||||
this._provider,
|
||||
txDefaults,
|
||||
);
|
||||
if (this._coordinatorRegistryContract === undefined) {
|
||||
throw new Error(`Failed to deploy Coordinator Registry contract.`);
|
||||
}
|
||||
return this._coordinatorRegistryContract;
|
||||
}
|
||||
public async setCoordinatorEndpointAsync(
|
||||
coordinatorOperator: string,
|
||||
coordinatorEndpoint: string,
|
||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||
this._assertCoordinatorRegistryDeployed();
|
||||
const txReceipt = await this._logDecoder.getTxWithDecodedLogsAsync(
|
||||
await (this
|
||||
._coordinatorRegistryContract as CoordinatorRegistryContract).setCoordinatorEndpoint.sendTransactionAsync(
|
||||
coordinatorEndpoint,
|
||||
{
|
||||
from: coordinatorOperator,
|
||||
},
|
||||
),
|
||||
);
|
||||
return txReceipt;
|
||||
}
|
||||
public async getCoordinatorEndpointAsync(coordinatorOperator: string): Promise<string> {
|
||||
this._assertCoordinatorRegistryDeployed();
|
||||
const coordinatorEndpoint = await (this
|
||||
._coordinatorRegistryContract as CoordinatorRegistryContract).getCoordinatorEndpoint.callAsync(
|
||||
coordinatorOperator,
|
||||
);
|
||||
return coordinatorEndpoint;
|
||||
}
|
||||
private _assertCoordinatorRegistryDeployed(): void {
|
||||
if (this._coordinatorRegistryContract === undefined) {
|
||||
throw new Error(
|
||||
'The Coordinator Registry contract was not deployed through the CoordinatorRegistryWrapper. Call `deployCoordinatorRegistryAsync` to deploy.',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
import { artifacts, IExchangeContract } from '@0x/contracts-exchange';
|
||||
import { IExchangeContract } from '@0x/contracts-exchange';
|
||||
import { constants as devConstants, provider } from '@0x/contracts-test-utils';
|
||||
import { SignedOrder } from '@0x/types';
|
||||
|
||||
@@ -6,11 +6,7 @@ import { constants } from './index';
|
||||
|
||||
export const exchangeDataEncoder = {
|
||||
encodeOrdersToExchangeData(fnName: string, orders: SignedOrder[]): string {
|
||||
const exchangeInstance = new IExchangeContract(
|
||||
artifacts.IExchange.compilerOutput.abi,
|
||||
devConstants.NULL_ADDRESS,
|
||||
provider,
|
||||
);
|
||||
const exchangeInstance = new IExchangeContract(devConstants.NULL_ADDRESS, provider);
|
||||
let data;
|
||||
if (constants.SINGLE_FILL_FN_NAMES.indexOf(fnName) !== -1) {
|
||||
data = (exchangeInstance as any)[fnName].getABIEncodedTransactionData(
|
||||
@@ -37,7 +33,7 @@ export const exchangeDataEncoder = {
|
||||
orders[0].signature,
|
||||
orders[1].signature,
|
||||
);
|
||||
} else if (fnName === constants.CANCEL_ORDERS) {
|
||||
} else if (fnName === constants.CANCEL_ORDER) {
|
||||
data = exchangeInstance.cancelOrder.getABIEncodedTransactionData(orders[0]);
|
||||
} else if (fnName === constants.BATCH_CANCEL_ORDERS) {
|
||||
data = exchangeInstance.batchCancelOrders.getABIEncodedTransactionData(orders);
|
33
contracts/coordinator/test/utils/hash_utils.ts
Normal file
33
contracts/coordinator/test/utils/hash_utils.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { eip712Utils } from '@0x/order-utils';
|
||||
import { SignedZeroExTransaction } from '@0x/types';
|
||||
import { BigNumber, signTypedDataUtils } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
export const hashUtils = {
|
||||
getApprovalHashBuffer(
|
||||
transaction: SignedZeroExTransaction,
|
||||
verifyingContractAddress: string,
|
||||
txOrigin: string,
|
||||
approvalExpirationTimeSeconds: BigNumber,
|
||||
): Buffer {
|
||||
const typedData = eip712Utils.createCoordinatorApprovalTypedData(
|
||||
transaction,
|
||||
verifyingContractAddress,
|
||||
txOrigin,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const hashBuffer = signTypedDataUtils.generateTypedDataHash(typedData);
|
||||
return hashBuffer;
|
||||
},
|
||||
getApprovalHashHex(
|
||||
transaction: SignedZeroExTransaction,
|
||||
verifyingContractAddress: string,
|
||||
txOrigin: string,
|
||||
approvalExpirationTimeSeconds: BigNumber,
|
||||
): string {
|
||||
const hashHex = `0x${hashUtils
|
||||
.getApprovalHashBuffer(transaction, verifyingContractAddress, txOrigin, approvalExpirationTimeSeconds)
|
||||
.toString('hex')}`;
|
||||
return hashHex;
|
||||
},
|
||||
};
|
@@ -1,6 +1,4 @@
|
||||
export { hashUtils } from './hash_utils';
|
||||
export { signingUtils } from './signing_utils';
|
||||
export { TECTransactionFactory } from './tec_transaction_factory';
|
||||
export { ApprovalFactory } from './approval_factory';
|
||||
export { constants } from './constants';
|
||||
export { exchangeDataEncoder } from './exchange_data_encoder';
|
@@ -1,18 +1,12 @@
|
||||
import { SignedZeroExTransaction } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
|
||||
export interface TECApproval {
|
||||
export interface CoordinatorApproval {
|
||||
transaction: SignedZeroExTransaction;
|
||||
txOrigin: string;
|
||||
approvalExpirationTimeSeconds: BigNumber;
|
||||
}
|
||||
|
||||
export interface SignedTECApproval extends TECApproval {
|
||||
export interface SignedCoordinatorApproval extends CoordinatorApproval {
|
||||
signature: string;
|
||||
}
|
||||
|
||||
export enum TECSignatureType {
|
||||
Illegal,
|
||||
EIP712,
|
||||
EthSign,
|
||||
NSignatureTypes,
|
||||
}
|
@@ -2,10 +2,6 @@
|
||||
"extends": "../../tsconfig",
|
||||
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
|
||||
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
|
||||
"files": [
|
||||
"generated-artifacts/TEC.json",
|
||||
"generated-artifacts/TestLibs.json",
|
||||
"generated-artifacts/TestMixins.json"
|
||||
],
|
||||
"files": ["generated-artifacts/Coordinator.json", "generated-artifacts/CoordinatorRegistry.json"],
|
||||
"exclude": ["./deploy/solc/solc_bin"]
|
||||
}
|
40
contracts/dev-utils/CHANGELOG.json
Normal file
40
contracts/dev-utils/CHANGELOG.json
Normal file
@@ -0,0 +1,40 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1563006338,
|
||||
"version": "0.0.2",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "0.0.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Create dev-utils package",
|
||||
"pr": 1848
|
||||
},
|
||||
{
|
||||
"note": "Add `LibAssetData` and `LibTransactionDecoder` contracts",
|
||||
"pr": 1848
|
||||
},
|
||||
{
|
||||
"note": "Refactor `LibAssetData` to only check 0x-specific allowances",
|
||||
"pr": 1848
|
||||
},
|
||||
{
|
||||
"note": "Refactor `LibAssetData` balance/allowance checks to never revert",
|
||||
"pr": 1848
|
||||
},
|
||||
{
|
||||
"note": "Refactor `OrderValidationUtils` to calculate `fillableTakerAssetAmount`",
|
||||
"pr": 1848
|
||||
},
|
||||
{
|
||||
"note": "Add support for StaticCallProxy",
|
||||
"pr": 1863
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
19
contracts/dev-utils/CHANGELOG.md
Normal file
19
contracts/dev-utils/CHANGELOG.md
Normal file
@@ -0,0 +1,19 @@
|
||||
<!--
|
||||
changelogUtils.file is auto-generated using the monorepo-scripts package. Don't edit directly.
|
||||
Edit the package's CHANGELOG.json file only.
|
||||
-->
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v0.0.2 - _July 13, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v0.0.1 - _Invalid date_
|
||||
|
||||
* Create dev-utils package (#1848)
|
||||
* Add `LibAssetData` and `LibTransactionDecoder` contracts (#1848)
|
||||
* Refactor `LibAssetData` to only check 0x-specific allowances (#1848)
|
||||
* Refactor `LibAssetData` balance/allowance checks to never revert (#1848)
|
||||
* Refactor `OrderValidationUtils` to calculate `fillableTakerAssetAmount` (#1848)
|
||||
* Add support for StaticCallProxy (#1863)
|
1
contracts/dev-utils/DEPLOYS.json
Normal file
1
contracts/dev-utils/DEPLOYS.json
Normal file
@@ -0,0 +1 @@
|
||||
[]
|
73
contracts/dev-utils/README.md
Normal file
73
contracts/dev-utils/README.md
Normal file
@@ -0,0 +1,73 @@
|
||||
## Dev-Utils
|
||||
|
||||
This package implements various utilities for developers. For example, the `DevUtils` contract can query batches of balances or allowances given some `assetData`, can validate batches of orders, and can decode 0x-specific calldata. Addresses of the deployed contracts can be found in the 0x [wiki](https://0xproject.com/wiki#Deployed-Addresses) or the [DEPLOYS](./DEPLOYS.json) file within this package.
|
||||
|
||||
## Installation
|
||||
|
||||
**Install**
|
||||
|
||||
```bash
|
||||
npm install @0x/contracts-dev-utils --save
|
||||
```
|
||||
|
||||
## Bug bounty
|
||||
|
||||
A bug bounty for the 2.0.0 contracts is ongoing! Instructions can be found [here](https://0xproject.com/wiki#Bug-Bounty).
|
||||
|
||||
## Contributing
|
||||
|
||||
We strongly recommend that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository.
|
||||
|
||||
For proposals regarding the 0x protocol's smart contract architecture, message format, or additional functionality, go to the [0x Improvement Proposals (ZEIPs)](https://github.com/0xProject/ZEIPs) repository and follow the contribution guidelines provided therein.
|
||||
|
||||
Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started.
|
||||
|
||||
### Install Dependencies
|
||||
|
||||
If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them:
|
||||
|
||||
```bash
|
||||
yarn config set workspaces-experimental true
|
||||
```
|
||||
|
||||
Then install dependencies
|
||||
|
||||
```bash
|
||||
yarn install
|
||||
```
|
||||
|
||||
### Build
|
||||
|
||||
To build this package and all other monorepo packages that it depends on, run the following from the monorepo root directory:
|
||||
|
||||
```bash
|
||||
PKG=@0x/contracts-extensions yarn build
|
||||
```
|
||||
|
||||
Or continuously rebuild on change:
|
||||
|
||||
```bash
|
||||
PKG=@0x/contracts-extensions yarn watch
|
||||
```
|
||||
|
||||
### Clean
|
||||
|
||||
```bash
|
||||
yarn clean
|
||||
```
|
||||
|
||||
### Lint
|
||||
|
||||
```bash
|
||||
yarn lint
|
||||
```
|
||||
|
||||
### Run Tests
|
||||
|
||||
```bash
|
||||
yarn test
|
||||
```
|
||||
|
||||
#### Testing options
|
||||
|
||||
Contracts testing options like coverage, profiling, revert traces or backing node choosing - are described [here](../TESTING.md).
|
32
contracts/dev-utils/compiler.json
Normal file
32
contracts/dev-utils/compiler.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"artifactsDir": "./generated-artifacts",
|
||||
"contractsDir": "./contracts",
|
||||
"useDockerisedSolc": false,
|
||||
"isOfflineMode": false,
|
||||
"compilerSettings": {
|
||||
"evmVersion": "constantinople",
|
||||
"optimizer": {
|
||||
"enabled": true,
|
||||
"runs": 1000000,
|
||||
"details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
|
||||
},
|
||||
"outputSelection": {
|
||||
"*": {
|
||||
"*": [
|
||||
"abi",
|
||||
"devdoc",
|
||||
"evm.bytecode.object",
|
||||
"evm.bytecode.sourceMap",
|
||||
"evm.deployedBytecode.object",
|
||||
"evm.deployedBytecode.sourceMap"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"contracts": [
|
||||
"src/DevUtils.sol",
|
||||
"src/LibAssetData.sol",
|
||||
"src/LibTransactionDecoder.sol",
|
||||
"src/EthBalanceChecker.sol"
|
||||
]
|
||||
}
|
37
contracts/dev-utils/contracts/src/DevUtils.sol
Normal file
37
contracts/dev-utils/contracts/src/DevUtils.sol
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "./OrderValidationUtils.sol";
|
||||
import "./LibTransactionDecoder.sol";
|
||||
import "./EthBalanceChecker.sol";
|
||||
|
||||
|
||||
// solhint-disable no-empty-blocks
|
||||
contract DevUtils is
|
||||
OrderValidationUtils,
|
||||
LibTransactionDecoder,
|
||||
EthBalanceChecker
|
||||
{
|
||||
constructor (address _exchange, bytes memory _zrxAssetData)
|
||||
public
|
||||
OrderValidationUtils(_exchange, _zrxAssetData)
|
||||
{}
|
||||
}
|
39
contracts/dev-utils/contracts/src/EthBalanceChecker.sol
Normal file
39
contracts/dev-utils/contracts/src/EthBalanceChecker.sol
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
|
||||
contract EthBalanceChecker {
|
||||
|
||||
/// @dev Batch fetches ETH balances
|
||||
/// @param addresses Array of addresses.
|
||||
/// @return Array of ETH balances.
|
||||
function getEthBalances(address[] memory addresses)
|
||||
public
|
||||
view
|
||||
returns (uint256[] memory)
|
||||
{
|
||||
uint256[] memory balances = new uint256[](addresses.length);
|
||||
for (uint256 i = 0; i != addresses.length; i++) {
|
||||
balances[i] = addresses[i].balance;
|
||||
}
|
||||
return balances;
|
||||
}
|
||||
|
||||
}
|
541
contracts/dev-utils/contracts/src/LibAssetData.sol
Normal file
541
contracts/dev-utils/contracts/src/LibAssetData.sol
Normal file
@@ -0,0 +1,541 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||
import "@0x/contracts-asset-proxy/contracts/src/libs/LibAssetProxyIds.sol";
|
||||
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
||||
|
||||
|
||||
contract LibAssetData is
|
||||
LibAssetProxyIds
|
||||
{
|
||||
// 2^256 - 1
|
||||
uint256 constant internal _MAX_UINT256 = uint256(-1);
|
||||
|
||||
// ERC20 selectors
|
||||
bytes4 constant internal _ERC20_BALANCE_OF_SELECTOR = 0x70a08231;
|
||||
bytes4 constant internal _ERC20_ALLOWANCE_SELECTOR = 0xdd62ed3e;
|
||||
|
||||
// ERC721 selectors
|
||||
bytes4 constant internal _ERC721_OWNER_OF_SELECTOR = 0x6352211e;
|
||||
bytes4 constant internal _ERC721_IS_APPROVED_FOR_ALL_SELECTOR = 0xe985e9c5;
|
||||
bytes4 constant internal _ERC721_GET_APPROVED_SELECTOR = 0x081812fc;
|
||||
|
||||
// ERC1155 selectors
|
||||
bytes4 constant internal _ERC1155_BALANCE_OF_SELECTOR = 0x00fdd58e;
|
||||
bytes4 constant internal _ERC1155_IS_APPROVED_FOR_ALL_SELECTOR = 0xe985e9c5;
|
||||
|
||||
// `transferFrom` selector for all AssetProxy contracts
|
||||
bytes4 constant internal _ASSET_PROXY_TRANSFER_FROM_SELECTOR = 0xa85e59e4;
|
||||
|
||||
using LibBytes for bytes;
|
||||
|
||||
// solhint-disable var-name-mixedcase
|
||||
IExchange internal _EXCHANGE;
|
||||
address internal _ERC20_PROXY_ADDRESS;
|
||||
address internal _ERC721_PROXY_ADDRESS;
|
||||
address internal _ERC1155_PROXY_ADDRESS;
|
||||
address internal _STATIC_CALL_PROXY_ADDRESS;
|
||||
// solhint-enable var-name-mixedcase
|
||||
|
||||
constructor (address _exchange)
|
||||
public
|
||||
{
|
||||
_EXCHANGE = IExchange(_exchange);
|
||||
_ERC20_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(ERC20_PROXY_ID);
|
||||
_ERC721_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(ERC721_PROXY_ID);
|
||||
_ERC1155_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(ERC1155_PROXY_ID);
|
||||
_STATIC_CALL_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(STATIC_CALL_PROXY_ID);
|
||||
}
|
||||
|
||||
/// @dev Returns the owner's balance of the assets(s) specified in
|
||||
/// assetData. When the asset data contains multiple assets (eg in
|
||||
/// ERC1155 or Multi-Asset), the return value indicates how many
|
||||
/// complete "baskets" of those assets are owned by owner.
|
||||
/// @param ownerAddress Owner of the assets specified by assetData.
|
||||
/// @param assetData Details of asset, encoded per the AssetProxy contract specification.
|
||||
/// @return Number of assets (or asset baskets) held by owner.
|
||||
function getBalance(address ownerAddress, bytes memory assetData)
|
||||
public
|
||||
view
|
||||
returns (uint256 balance)
|
||||
{
|
||||
// Get id of AssetProxy contract
|
||||
bytes4 assetProxyId = assetData.readBytes4(0);
|
||||
|
||||
if (assetProxyId == ERC20_PROXY_ID) {
|
||||
// Get ERC20 token address
|
||||
address tokenAddress = assetData.readAddress(16);
|
||||
|
||||
// Encode data for `balanceOf(ownerAddress)`
|
||||
bytes memory balanceOfData = abi.encodeWithSelector(_ERC20_BALANCE_OF_SELECTOR, ownerAddress);
|
||||
|
||||
// Query balance
|
||||
(bool success, bytes memory returnData) = tokenAddress.staticcall(balanceOfData);
|
||||
balance = success && returnData.length == 32 ? returnData.readUint256(0) : 0;
|
||||
} else if (assetProxyId == ERC721_PROXY_ID) {
|
||||
// Get ERC721 token address and id
|
||||
(, address tokenAddress, uint256 tokenId) = decodeERC721AssetData(assetData);
|
||||
|
||||
// Check if id is owned by ownerAddress
|
||||
balance = getERC721TokenOwner(tokenAddress, tokenId) == ownerAddress ? 1 : 0;
|
||||
} else if (assetProxyId == ERC1155_PROXY_ID) {
|
||||
// Get ERC1155 token address, array of ids, and array of values
|
||||
(, address tokenAddress, uint256[] memory tokenIds, uint256[] memory tokenValues,) = decodeERC1155AssetData(assetData);
|
||||
|
||||
uint256 length = tokenIds.length;
|
||||
for (uint256 i = 0; i != length; i++) {
|
||||
// Encode data for `balanceOf(ownerAddress, tokenIds[i])
|
||||
bytes memory balanceOfData = abi.encodeWithSelector(
|
||||
_ERC1155_BALANCE_OF_SELECTOR,
|
||||
ownerAddress,
|
||||
tokenIds[i]
|
||||
);
|
||||
|
||||
// Query balance
|
||||
(bool success, bytes memory returnData) = tokenAddress.staticcall(balanceOfData);
|
||||
uint256 totalBalance = success && returnData.length == 32 ? returnData.readUint256(0) : 0;
|
||||
|
||||
// Scale total balance down by corresponding value in assetData
|
||||
uint256 scaledBalance = totalBalance / tokenValues[i];
|
||||
if (scaledBalance < balance || balance == 0) {
|
||||
balance = scaledBalance;
|
||||
}
|
||||
}
|
||||
} else if (assetProxyId == STATIC_CALL_PROXY_ID) {
|
||||
// Encode data for `staticCallProxy.transferFrom(assetData,...)`
|
||||
bytes memory transferFromData = abi.encodeWithSelector(
|
||||
_ASSET_PROXY_TRANSFER_FROM_SELECTOR,
|
||||
assetData,
|
||||
address(0), // `from` address is not used
|
||||
address(0), // `to` address is not used
|
||||
0 // `amount` is not used
|
||||
);
|
||||
|
||||
// Check if staticcall would be successful
|
||||
(bool success,) = _STATIC_CALL_PROXY_ADDRESS.staticcall(transferFromData);
|
||||
|
||||
// Success means that the staticcall can be made an unlimited amount of times
|
||||
balance = success ? _MAX_UINT256 : 0;
|
||||
} else if (assetProxyId == MULTI_ASSET_PROXY_ID) {
|
||||
// Get array of values and array of assetDatas
|
||||
(, uint256[] memory assetAmounts, bytes[] memory nestedAssetData) = decodeMultiAssetData(assetData);
|
||||
|
||||
uint256 length = nestedAssetData.length;
|
||||
for (uint256 i = 0; i != length; i++) {
|
||||
// Query balance of individual assetData
|
||||
uint256 totalBalance = getBalance(ownerAddress, nestedAssetData[i]);
|
||||
|
||||
// Scale total balance down by corresponding value in assetData
|
||||
uint256 scaledBalance = totalBalance / assetAmounts[i];
|
||||
if (scaledBalance < balance || balance == 0) {
|
||||
balance = scaledBalance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Balance will be 0 if assetProxyId is unknown
|
||||
return balance;
|
||||
}
|
||||
|
||||
/// @dev Calls getBalance() for each element of assetData.
|
||||
/// @param ownerAddress Owner of the assets specified by assetData.
|
||||
/// @param assetData Array of asset details, each encoded per the AssetProxy contract specification.
|
||||
/// @return Array of asset balances from getBalance(), with each element
|
||||
/// corresponding to the same-indexed element in the assetData input.
|
||||
function getBatchBalances(address ownerAddress, bytes[] memory assetData)
|
||||
public
|
||||
view
|
||||
returns (uint256[] memory balances)
|
||||
{
|
||||
uint256 length = assetData.length;
|
||||
balances = new uint256[](length);
|
||||
for (uint256 i = 0; i != length; i++) {
|
||||
balances[i] = getBalance(ownerAddress, assetData[i]);
|
||||
}
|
||||
return balances;
|
||||
}
|
||||
|
||||
/// @dev Returns the number of asset(s) (described by assetData) that
|
||||
/// the corresponding AssetProxy contract is authorized to spend. When the asset data contains
|
||||
/// multiple assets (eg for Multi-Asset), the return value indicates
|
||||
/// how many complete "baskets" of those assets may be spent by all of the corresponding
|
||||
/// AssetProxy contracts.
|
||||
/// @param ownerAddress Owner of the assets specified by assetData.
|
||||
/// @param assetData Details of asset, encoded per the AssetProxy contract specification.
|
||||
/// @return Number of assets (or asset baskets) that the corresponding AssetProxy is authorized to spend.
|
||||
function getAssetProxyAllowance(address ownerAddress, bytes memory assetData)
|
||||
public
|
||||
view
|
||||
returns (uint256 allowance)
|
||||
{
|
||||
// Get id of AssetProxy contract
|
||||
bytes4 assetProxyId = assetData.readBytes4(0);
|
||||
|
||||
if (assetProxyId == MULTI_ASSET_PROXY_ID) {
|
||||
// Get array of values and array of assetDatas
|
||||
(, uint256[] memory amounts, bytes[] memory nestedAssetData) = decodeMultiAssetData(assetData);
|
||||
|
||||
uint256 length = nestedAssetData.length;
|
||||
for (uint256 i = 0; i != length; i++) {
|
||||
// Query allowance of individual assetData
|
||||
uint256 totalAllowance = getAssetProxyAllowance(ownerAddress, nestedAssetData[i]);
|
||||
|
||||
// Scale total allowance down by corresponding value in assetData
|
||||
uint256 scaledAllowance = totalAllowance / amounts[i];
|
||||
if (scaledAllowance < allowance || allowance == 0) {
|
||||
allowance = scaledAllowance;
|
||||
}
|
||||
}
|
||||
return allowance;
|
||||
}
|
||||
|
||||
if (assetProxyId == ERC20_PROXY_ID) {
|
||||
// Get ERC20 token address
|
||||
address tokenAddress = assetData.readAddress(16);
|
||||
|
||||
// Encode data for `allowance(ownerAddress, _ERC20_PROXY_ADDRESS)`
|
||||
bytes memory allowanceData = abi.encodeWithSelector(
|
||||
_ERC20_ALLOWANCE_SELECTOR,
|
||||
ownerAddress,
|
||||
_ERC20_PROXY_ADDRESS
|
||||
);
|
||||
|
||||
// Query allowance
|
||||
(bool success, bytes memory returnData) = tokenAddress.staticcall(allowanceData);
|
||||
allowance = success && returnData.length == 32 ? returnData.readUint256(0) : 0;
|
||||
} else if (assetProxyId == ERC721_PROXY_ID) {
|
||||
// Get ERC721 token address and id
|
||||
(, address tokenAddress, uint256 tokenId) = decodeERC721AssetData(assetData);
|
||||
|
||||
// Encode data for `isApprovedForAll(ownerAddress, _ERC721_PROXY_ADDRESS)`
|
||||
bytes memory isApprovedForAllData = abi.encodeWithSelector(
|
||||
_ERC721_IS_APPROVED_FOR_ALL_SELECTOR,
|
||||
ownerAddress,
|
||||
_ERC721_PROXY_ADDRESS
|
||||
);
|
||||
|
||||
(bool success, bytes memory returnData) = tokenAddress.staticcall(isApprovedForAllData);
|
||||
|
||||
// If not approved for all, call `getApproved(tokenId)`
|
||||
if (!success || returnData.length != 32 || returnData.readUint256(0) != 1) {
|
||||
// Encode data for `getApproved(tokenId)`
|
||||
bytes memory getApprovedData = abi.encodeWithSelector(_ERC721_GET_APPROVED_SELECTOR, tokenId);
|
||||
(success, returnData) = tokenAddress.staticcall(getApprovedData);
|
||||
|
||||
// Allowance is 1 if successful and the approved address is the ERC721Proxy
|
||||
allowance = success && returnData.length == 32 && returnData.readAddress(12) == _ERC721_PROXY_ADDRESS ? 1 : 0;
|
||||
} else {
|
||||
// Allowance is 2^256 - 1 if `isApprovedForAll` returned true
|
||||
allowance = _MAX_UINT256;
|
||||
}
|
||||
} else if (assetProxyId == ERC1155_PROXY_ID) {
|
||||
// Get ERC1155 token address
|
||||
(, address tokenAddress, , , ) = decodeERC1155AssetData(assetData);
|
||||
|
||||
// Encode data for `isApprovedForAll(ownerAddress, _ERC1155_PROXY_ADDRESS)`
|
||||
bytes memory isApprovedForAllData = abi.encodeWithSelector(
|
||||
_ERC1155_IS_APPROVED_FOR_ALL_SELECTOR,
|
||||
ownerAddress,
|
||||
_ERC1155_PROXY_ADDRESS
|
||||
);
|
||||
|
||||
// Query allowance
|
||||
(bool success, bytes memory returnData) = tokenAddress.staticcall(isApprovedForAllData);
|
||||
allowance = success && returnData.length == 32 && returnData.readUint256(0) == 1 ? _MAX_UINT256 : 0;
|
||||
} else if (assetProxyId == STATIC_CALL_PROXY_ID) {
|
||||
// The StaticCallProxy does not require any approvals
|
||||
allowance = _MAX_UINT256;
|
||||
}
|
||||
|
||||
// Allowance will be 0 if the assetProxyId is unknown
|
||||
return allowance;
|
||||
}
|
||||
|
||||
/// @dev Calls getAssetProxyAllowance() for each element of assetData.
|
||||
/// @param ownerAddress Owner of the assets specified by assetData.
|
||||
/// @param assetData Array of asset details, each encoded per the AssetProxy contract specification.
|
||||
/// @return An array of asset allowances from getAllowance(), with each
|
||||
/// element corresponding to the same-indexed element in the assetData input.
|
||||
function getBatchAssetProxyAllowances(address ownerAddress, bytes[] memory assetData)
|
||||
public
|
||||
view
|
||||
returns (uint256[] memory allowances)
|
||||
{
|
||||
uint256 length = assetData.length;
|
||||
allowances = new uint256[](length);
|
||||
for (uint256 i = 0; i != length; i++) {
|
||||
allowances[i] = getAssetProxyAllowance(ownerAddress, assetData[i]);
|
||||
}
|
||||
return allowances;
|
||||
}
|
||||
|
||||
/// @dev Calls getBalance() and getAllowance() for assetData.
|
||||
/// @param ownerAddress Owner of the assets specified by assetData.
|
||||
/// @param assetData Details of asset, encoded per the AssetProxy contract specification.
|
||||
/// @return Number of assets (or asset baskets) held by owner, and number
|
||||
/// of assets (or asset baskets) that the corresponding AssetProxy is authorized to spend.
|
||||
function getBalanceAndAssetProxyAllowance(address ownerAddress, bytes memory assetData)
|
||||
public
|
||||
view
|
||||
returns (uint256 balance, uint256 allowance)
|
||||
{
|
||||
balance = getBalance(ownerAddress, assetData);
|
||||
allowance = getAssetProxyAllowance(ownerAddress, assetData);
|
||||
return (balance, allowance);
|
||||
}
|
||||
|
||||
/// @dev Calls getBatchBalances() and getBatchAllowances() for each element of assetData.
|
||||
/// @param ownerAddress Owner of the assets specified by assetData.
|
||||
/// @param assetData Array of asset details, each encoded per the AssetProxy contract specification.
|
||||
/// @return An array of asset balances from getBalance(), and an array of
|
||||
/// asset allowances from getAllowance(), with each element
|
||||
/// corresponding to the same-indexed element in the assetData input.
|
||||
function getBatchBalancesAndAssetProxyAllowances(address ownerAddress, bytes[] memory assetData)
|
||||
public
|
||||
view
|
||||
returns (uint256[] memory balances, uint256[] memory allowances)
|
||||
{
|
||||
balances = getBatchBalances(ownerAddress, assetData);
|
||||
allowances = getBatchAssetProxyAllowances(ownerAddress, assetData);
|
||||
return (balances, allowances);
|
||||
}
|
||||
|
||||
/// @dev Encode ERC-20 asset data into the format described in the AssetProxy contract specification.
|
||||
/// @param tokenAddress The address of the ERC-20 contract hosting the asset to be traded.
|
||||
/// @return AssetProxy-compliant data describing the asset.
|
||||
function encodeERC20AssetData(address tokenAddress)
|
||||
public
|
||||
pure
|
||||
returns (bytes memory assetData)
|
||||
{
|
||||
assetData = abi.encodeWithSelector(ERC20_PROXY_ID, tokenAddress);
|
||||
return assetData;
|
||||
}
|
||||
|
||||
/// @dev Decode ERC-20 asset data from the format described in the AssetProxy contract specification.
|
||||
/// @param assetData AssetProxy-compliant asset data describing an ERC-20 asset.
|
||||
/// @return The ERC-20 AssetProxy identifier, and the address of the ERC-20
|
||||
/// contract hosting this asset.
|
||||
function decodeERC20AssetData(bytes memory assetData)
|
||||
public
|
||||
pure
|
||||
returns (
|
||||
bytes4 assetProxyId,
|
||||
address tokenAddress
|
||||
)
|
||||
{
|
||||
assetProxyId = assetData.readBytes4(0);
|
||||
|
||||
require(
|
||||
assetProxyId == ERC20_PROXY_ID,
|
||||
"WRONG_PROXY_ID"
|
||||
);
|
||||
|
||||
tokenAddress = assetData.readAddress(16);
|
||||
return (assetProxyId, tokenAddress);
|
||||
}
|
||||
|
||||
/// @dev Encode ERC-721 asset data into the format described in the AssetProxy specification.
|
||||
/// @param tokenAddress The address of the ERC-721 contract hosting the asset to be traded.
|
||||
/// @param tokenId The identifier of the specific asset to be traded.
|
||||
/// @return AssetProxy-compliant asset data describing the asset.
|
||||
function encodeERC721AssetData(address tokenAddress, uint256 tokenId)
|
||||
public
|
||||
pure
|
||||
returns (bytes memory assetData)
|
||||
{
|
||||
assetData = abi.encodeWithSelector(
|
||||
ERC721_PROXY_ID,
|
||||
tokenAddress,
|
||||
tokenId
|
||||
);
|
||||
return assetData;
|
||||
}
|
||||
|
||||
/// @dev Decode ERC-721 asset data from the format described in the AssetProxy contract specification.
|
||||
/// @param assetData AssetProxy-compliant asset data describing an ERC-721 asset.
|
||||
/// @return The ERC-721 AssetProxy identifier, the address of the ERC-721
|
||||
/// contract hosting this asset, and the identifier of the specific
|
||||
/// asset to be traded.
|
||||
function decodeERC721AssetData(bytes memory assetData)
|
||||
public
|
||||
pure
|
||||
returns (
|
||||
bytes4 assetProxyId,
|
||||
address tokenAddress,
|
||||
uint256 tokenId
|
||||
)
|
||||
{
|
||||
assetProxyId = assetData.readBytes4(0);
|
||||
|
||||
require(
|
||||
assetProxyId == ERC721_PROXY_ID,
|
||||
"WRONG_PROXY_ID"
|
||||
);
|
||||
|
||||
tokenAddress = assetData.readAddress(16);
|
||||
tokenId = assetData.readUint256(36);
|
||||
return (assetProxyId, tokenAddress, tokenId);
|
||||
}
|
||||
|
||||
/// @dev Encode ERC-1155 asset data into the format described in the AssetProxy contract specification.
|
||||
/// @param tokenAddress The address of the ERC-1155 contract hosting the asset(s) to be traded.
|
||||
/// @param tokenIds The identifiers of the specific assets to be traded.
|
||||
/// @param tokenValues The amounts of each asset to be traded.
|
||||
/// @param callbackData Data to be passed to receiving contracts when a transfer is performed.
|
||||
/// @return AssetProxy-compliant asset data describing the set of assets.
|
||||
function encodeERC1155AssetData(
|
||||
address tokenAddress,
|
||||
uint256[] memory tokenIds,
|
||||
uint256[] memory tokenValues,
|
||||
bytes memory callbackData
|
||||
)
|
||||
public
|
||||
pure
|
||||
returns (bytes memory assetData)
|
||||
{
|
||||
assetData = abi.encodeWithSelector(
|
||||
ERC1155_PROXY_ID,
|
||||
tokenAddress,
|
||||
tokenIds,
|
||||
tokenValues,
|
||||
callbackData
|
||||
);
|
||||
return assetData;
|
||||
}
|
||||
|
||||
/// @dev Decode ERC-1155 asset data from the format described in the AssetProxy contract specification.
|
||||
/// @param assetData AssetProxy-compliant asset data describing an ERC-1155 set of assets.
|
||||
/// @return The ERC-1155 AssetProxy identifier, the address of the ERC-1155
|
||||
/// contract hosting the assets, an array of the identifiers of the
|
||||
/// assets to be traded, an array of asset amounts to be traded, and
|
||||
/// callback data. Each element of the arrays corresponds to the
|
||||
/// same-indexed element of the other array. Return values specified as
|
||||
/// `memory` are returned as pointers to locations within the memory of
|
||||
/// the input parameter `assetData`.
|
||||
function decodeERC1155AssetData(bytes memory assetData)
|
||||
public
|
||||
pure
|
||||
returns (
|
||||
bytes4 assetProxyId,
|
||||
address tokenAddress,
|
||||
uint256[] memory tokenIds,
|
||||
uint256[] memory tokenValues,
|
||||
bytes memory callbackData
|
||||
)
|
||||
{
|
||||
assetProxyId = assetData.readBytes4(0);
|
||||
|
||||
require(
|
||||
assetProxyId == ERC1155_PROXY_ID,
|
||||
"WRONG_PROXY_ID"
|
||||
);
|
||||
|
||||
assembly {
|
||||
// Skip selector and length to get to the first parameter:
|
||||
assetData := add(assetData, 36)
|
||||
// Read the value of the first parameter:
|
||||
tokenAddress := mload(assetData)
|
||||
// Point to the next parameter's data:
|
||||
tokenIds := add(assetData, mload(add(assetData, 32)))
|
||||
// Point to the next parameter's data:
|
||||
tokenValues := add(assetData, mload(add(assetData, 64)))
|
||||
// Point to the next parameter's data:
|
||||
callbackData := add(assetData, mload(add(assetData, 96)))
|
||||
}
|
||||
|
||||
return (
|
||||
assetProxyId,
|
||||
tokenAddress,
|
||||
tokenIds,
|
||||
tokenValues,
|
||||
callbackData
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Encode data for multiple assets, per the AssetProxy contract specification.
|
||||
/// @param amounts The amounts of each asset to be traded.
|
||||
/// @param nestedAssetData AssetProxy-compliant data describing each asset to be traded.
|
||||
/// @return AssetProxy-compliant data describing the set of assets.
|
||||
function encodeMultiAssetData(uint256[] memory amounts, bytes[] memory nestedAssetData)
|
||||
public
|
||||
pure
|
||||
returns (bytes memory assetData)
|
||||
{
|
||||
assetData = abi.encodeWithSelector(
|
||||
MULTI_ASSET_PROXY_ID,
|
||||
amounts,
|
||||
nestedAssetData
|
||||
);
|
||||
return assetData;
|
||||
}
|
||||
|
||||
/// @dev Decode multi-asset data from the format described in the AssetProxy contract specification.
|
||||
/// @param assetData AssetProxy-compliant data describing a multi-asset basket.
|
||||
/// @return The Multi-Asset AssetProxy identifier, an array of the amounts
|
||||
/// of the assets to be traded, and an array of the
|
||||
/// AssetProxy-compliant data describing each asset to be traded. Each
|
||||
/// element of the arrays corresponds to the same-indexed element of the other array.
|
||||
function decodeMultiAssetData(bytes memory assetData)
|
||||
public
|
||||
pure
|
||||
returns (
|
||||
bytes4 assetProxyId,
|
||||
uint256[] memory amounts,
|
||||
bytes[] memory nestedAssetData
|
||||
)
|
||||
{
|
||||
assetProxyId = assetData.readBytes4(0);
|
||||
|
||||
require(
|
||||
assetProxyId == MULTI_ASSET_PROXY_ID,
|
||||
"WRONG_PROXY_ID"
|
||||
);
|
||||
|
||||
// solhint-disable indent
|
||||
(amounts, nestedAssetData) = abi.decode(
|
||||
assetData.slice(4, assetData.length),
|
||||
(uint256[], bytes[])
|
||||
);
|
||||
// solhint-enable indent
|
||||
}
|
||||
|
||||
/// @dev Calls `asset.ownerOf(tokenId)`, but returns a null owner instead of reverting on an unowned asset.
|
||||
/// @param tokenAddress Address of ERC721 asset.
|
||||
/// @param tokenId The identifier for the specific NFT.
|
||||
/// @return Owner of tokenId or null address if unowned.
|
||||
function getERC721TokenOwner(address tokenAddress, uint256 tokenId)
|
||||
public
|
||||
view
|
||||
returns (address ownerAddress)
|
||||
{
|
||||
bytes memory ownerOfCalldata = abi.encodeWithSelector(
|
||||
_ERC721_OWNER_OF_SELECTOR,
|
||||
tokenId
|
||||
);
|
||||
|
||||
(bool success, bytes memory returnData) = tokenAddress.staticcall(ownerOfCalldata);
|
||||
|
||||
ownerAddress = (success && returnData.length == 32) ? returnData.readAddress(12) : address(0);
|
||||
return ownerAddress;
|
||||
}
|
||||
}
|
193
contracts/dev-utils/contracts/src/LibTransactionDecoder.sol
Normal file
193
contracts/dev-utils/contracts/src/LibTransactionDecoder.sol
Normal file
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibExchangeSelectors.sol";
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||
|
||||
|
||||
contract LibTransactionDecoder is
|
||||
LibExchangeSelectors
|
||||
{
|
||||
using LibBytes for bytes;
|
||||
|
||||
/// @dev Decodes the call data for an Exchange contract method call.
|
||||
/// @param transactionData ABI-encoded calldata for an Exchange
|
||||
/// contract method call.
|
||||
/// @return The name of the function called, and the parameters it was
|
||||
/// given. For single-order fills and cancels, the arrays will have
|
||||
/// just one element.
|
||||
function decodeZeroExTransactionData(bytes memory transactionData)
|
||||
public
|
||||
pure
|
||||
returns(
|
||||
string memory functionName,
|
||||
LibOrder.Order[] memory orders,
|
||||
uint256[] memory takerAssetFillAmounts,
|
||||
bytes[] memory signatures
|
||||
)
|
||||
{
|
||||
bytes4 functionSelector = transactionData.readBytes4(0);
|
||||
|
||||
if (functionSelector == BATCH_CANCEL_ORDERS_SELECTOR) {
|
||||
functionName = "batchCancelOrders";
|
||||
} else if (functionSelector == BATCH_FILL_ORDERS_SELECTOR) {
|
||||
functionName = "batchFillOrders";
|
||||
} else if (functionSelector == BATCH_FILL_ORDERS_NO_THROW_SELECTOR) {
|
||||
functionName = "batchFillOrdersNoThrow";
|
||||
} else if (functionSelector == BATCH_FILL_OR_KILL_ORDERS_SELECTOR) {
|
||||
functionName = "batchFillOrKillOrders";
|
||||
} else if (functionSelector == CANCEL_ORDER_SELECTOR) {
|
||||
functionName = "cancelOrder";
|
||||
} else if (functionSelector == FILL_ORDER_SELECTOR) {
|
||||
functionName = "fillOrder";
|
||||
} else if (functionSelector == FILL_ORDER_NO_THROW_SELECTOR) {
|
||||
functionName = "fillOrderNoThrow";
|
||||
} else if (functionSelector == FILL_OR_KILL_ORDER_SELECTOR) {
|
||||
functionName = "fillOrKillOrder";
|
||||
} else if (functionSelector == MARKET_BUY_ORDERS_SELECTOR) {
|
||||
functionName = "marketBuyOrders";
|
||||
} else if (functionSelector == MARKET_BUY_ORDERS_NO_THROW_SELECTOR) {
|
||||
functionName = "marketBuyOrdersNoThrow";
|
||||
} else if (functionSelector == MARKET_SELL_ORDERS_SELECTOR) {
|
||||
functionName = "marketSellOrders";
|
||||
} else if (functionSelector == MARKET_SELL_ORDERS_NO_THROW_SELECTOR) {
|
||||
functionName = "marketSellOrdersNoThrow";
|
||||
} else if (functionSelector == MATCH_ORDERS_SELECTOR) {
|
||||
functionName = "matchOrders";
|
||||
} else if (
|
||||
functionSelector == CANCEL_ORDERS_UP_TO_SELECTOR ||
|
||||
functionSelector == EXECUTE_TRANSACTION_SELECTOR
|
||||
// TODO: add new noThrow cancel functions when https://github.com/0xProject/ZEIPs/issues/35 is merged.
|
||||
) {
|
||||
revert("UNIMPLEMENTED");
|
||||
} else {
|
||||
revert("UNKNOWN_FUNCTION_SELECTOR");
|
||||
}
|
||||
|
||||
if (functionSelector == BATCH_CANCEL_ORDERS_SELECTOR) {
|
||||
// solhint-disable-next-line indent
|
||||
orders = abi.decode(transactionData.slice(4, transactionData.length), (LibOrder.Order[]));
|
||||
takerAssetFillAmounts = new uint256[](0);
|
||||
signatures = new bytes[](0);
|
||||
} else if (
|
||||
functionSelector == BATCH_FILL_OR_KILL_ORDERS_SELECTOR ||
|
||||
functionSelector == BATCH_FILL_ORDERS_NO_THROW_SELECTOR ||
|
||||
functionSelector == BATCH_FILL_ORDERS_SELECTOR
|
||||
) {
|
||||
(orders, takerAssetFillAmounts, signatures) = _makeReturnValuesForBatchFill(transactionData);
|
||||
} else if (functionSelector == CANCEL_ORDER_SELECTOR) {
|
||||
orders = new LibOrder.Order[](1);
|
||||
orders[0] = abi.decode(transactionData.slice(4, transactionData.length), (LibOrder.Order));
|
||||
takerAssetFillAmounts = new uint256[](0);
|
||||
signatures = new bytes[](0);
|
||||
} else if (
|
||||
functionSelector == FILL_OR_KILL_ORDER_SELECTOR ||
|
||||
functionSelector == FILL_ORDER_SELECTOR ||
|
||||
functionSelector == FILL_ORDER_NO_THROW_SELECTOR
|
||||
) {
|
||||
(orders, takerAssetFillAmounts, signatures) = _makeReturnValuesForSingleOrderFill(transactionData);
|
||||
} else if (
|
||||
functionSelector == MARKET_BUY_ORDERS_SELECTOR ||
|
||||
functionSelector == MARKET_BUY_ORDERS_NO_THROW_SELECTOR ||
|
||||
functionSelector == MARKET_SELL_ORDERS_SELECTOR ||
|
||||
functionSelector == MARKET_SELL_ORDERS_NO_THROW_SELECTOR
|
||||
) {
|
||||
(orders, takerAssetFillAmounts, signatures) = _makeReturnValuesForMarketFill(transactionData);
|
||||
} else if (functionSelector == MATCH_ORDERS_SELECTOR) {
|
||||
(
|
||||
LibOrder.Order memory leftOrder,
|
||||
LibOrder.Order memory rightOrder,
|
||||
bytes memory leftSignature,
|
||||
bytes memory rightSignature
|
||||
) = abi.decode(
|
||||
transactionData.slice(4, transactionData.length),
|
||||
(LibOrder.Order, LibOrder.Order, bytes, bytes)
|
||||
);
|
||||
|
||||
orders = new LibOrder.Order[](2);
|
||||
orders[0] = leftOrder;
|
||||
orders[1] = rightOrder;
|
||||
|
||||
takerAssetFillAmounts = new uint256[](2);
|
||||
takerAssetFillAmounts[0] = leftOrder.takerAssetAmount;
|
||||
takerAssetFillAmounts[1] = rightOrder.takerAssetAmount;
|
||||
|
||||
signatures = new bytes[](2);
|
||||
signatures[0] = leftSignature;
|
||||
signatures[1] = rightSignature;
|
||||
}
|
||||
}
|
||||
|
||||
function _makeReturnValuesForSingleOrderFill(bytes memory transactionData)
|
||||
private
|
||||
pure
|
||||
returns(
|
||||
LibOrder.Order[] memory orders,
|
||||
uint256[] memory takerAssetFillAmounts,
|
||||
bytes[] memory signatures
|
||||
)
|
||||
{
|
||||
orders = new LibOrder.Order[](1);
|
||||
takerAssetFillAmounts = new uint256[](1);
|
||||
signatures = new bytes[](1);
|
||||
// solhint-disable-next-line indent
|
||||
(orders[0], takerAssetFillAmounts[0], signatures[0]) = abi.decode(
|
||||
transactionData.slice(4, transactionData.length),
|
||||
(LibOrder.Order, uint256, bytes)
|
||||
);
|
||||
}
|
||||
|
||||
function _makeReturnValuesForBatchFill(bytes memory transactionData)
|
||||
private
|
||||
pure
|
||||
returns(
|
||||
LibOrder.Order[] memory orders,
|
||||
uint256[] memory takerAssetFillAmounts,
|
||||
bytes[] memory signatures
|
||||
)
|
||||
{
|
||||
// solhint-disable-next-line indent
|
||||
(orders, takerAssetFillAmounts, signatures) = abi.decode(
|
||||
transactionData.slice(4, transactionData.length),
|
||||
// solhint-disable-next-line indent
|
||||
(LibOrder.Order[], uint256[], bytes[])
|
||||
);
|
||||
}
|
||||
|
||||
function _makeReturnValuesForMarketFill(bytes memory transactionData)
|
||||
private
|
||||
pure
|
||||
returns(
|
||||
LibOrder.Order[] memory orders,
|
||||
uint256[] memory takerAssetFillAmounts,
|
||||
bytes[] memory signatures
|
||||
)
|
||||
{
|
||||
takerAssetFillAmounts = new uint256[](1);
|
||||
// solhint-disable-next-line indent
|
||||
(orders, takerAssetFillAmounts[0], signatures) = abi.decode(
|
||||
transactionData.slice(4, transactionData.length),
|
||||
// solhint-disable-next-line indent
|
||||
(LibOrder.Order[], uint256, bytes[])
|
||||
);
|
||||
}
|
||||
}
|
184
contracts/dev-utils/contracts/src/OrderValidationUtils.sol
Normal file
184
contracts/dev-utils/contracts/src/OrderValidationUtils.sol
Normal file
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||
import "./LibAssetData.sol";
|
||||
|
||||
|
||||
contract OrderValidationUtils is
|
||||
LibAssetData,
|
||||
LibMath
|
||||
{
|
||||
using LibBytes for bytes;
|
||||
|
||||
// solhint-disable var-name-mixedcase
|
||||
bytes internal _ZRX_ASSET_DATA;
|
||||
// solhint-enable var-name-mixedcase
|
||||
|
||||
constructor (address _exchange, bytes memory _zrxAssetData)
|
||||
public
|
||||
LibAssetData(_exchange)
|
||||
{
|
||||
_ZRX_ASSET_DATA = _zrxAssetData;
|
||||
}
|
||||
|
||||
/// @dev Fetches all order-relevant information needed to validate if the supplied order is fillable.
|
||||
/// @param order The order structure.
|
||||
/// @param signature Signature provided by maker that proves the order's authenticity.
|
||||
/// `0x01` can always be provided if the signature does not need to be validated.
|
||||
/// @return The orderInfo (hash, status, and `takerAssetAmount` already filled for the given order),
|
||||
/// fillableTakerAssetAmount (amount of the order's `takerAssetAmount` that is fillable given all on-chain state),
|
||||
/// and isValidSignature (validity of the provided signature).
|
||||
/// NOTE: If the `takerAssetData` encodes data for multiple assets, `fillableTakerAssetAmount` will represent a "scaled"
|
||||
/// amount, meaning it must be multiplied by all the individual asset amounts within the `takerAssetData` to get the final
|
||||
/// amount of each asset that can be filled.
|
||||
function getOrderRelevantState(LibOrder.Order memory order, bytes memory signature)
|
||||
public
|
||||
view
|
||||
returns (
|
||||
LibOrder.OrderInfo memory orderInfo,
|
||||
uint256 fillableTakerAssetAmount,
|
||||
bool isValidSignature
|
||||
)
|
||||
{
|
||||
// Get info specific to order
|
||||
orderInfo = _EXCHANGE.getOrderInfo(order);
|
||||
|
||||
// Validate the maker's signature
|
||||
address makerAddress = order.makerAddress;
|
||||
isValidSignature = _EXCHANGE.isValidSignature(
|
||||
orderInfo.orderHash,
|
||||
makerAddress,
|
||||
signature
|
||||
);
|
||||
|
||||
// Get the transferable amount of the `makerAsset`
|
||||
uint256 transferableMakerAssetAmount = getTransferableAssetAmount(makerAddress, order.makerAssetData);
|
||||
|
||||
// Assign to stack variables to reduce redundant mloads/sloads
|
||||
uint256 takerAssetAmount = order.takerAssetAmount;
|
||||
uint256 makerFee = order.makerFee;
|
||||
bytes memory zrxAssetData = _ZRX_ASSET_DATA;
|
||||
|
||||
// Get the amount of `takerAsset` that is transferable to maker given the transferability of `makerAsset`, `makerFeeAsset`,
|
||||
// and the total amounts specified in the order
|
||||
uint256 transferableTakerAssetAmount;
|
||||
if (order.makerAssetData.equals(zrxAssetData)) {
|
||||
// If `makerAsset` equals `makerFeeAsset`, the % that can be filled is
|
||||
// transferableMakerAssetAmount / (makerAssetAmount + makerFee)
|
||||
transferableTakerAssetAmount = getPartialAmountFloor(
|
||||
transferableMakerAssetAmount,
|
||||
safeAdd(order.makerAssetAmount, makerFee),
|
||||
takerAssetAmount
|
||||
);
|
||||
} else {
|
||||
// Get the transferable amount of the `makerFeeAsset`
|
||||
uint256 transferableMakerFeeAssetAmount = getTransferableAssetAmount(makerAddress, zrxAssetData);
|
||||
|
||||
// If `makerFee` is 0, the % that can be filled is (transferableMakerAssetAmount / makerAssetAmount)
|
||||
if (makerFee == 0) {
|
||||
transferableTakerAssetAmount = getPartialAmountFloor(
|
||||
transferableMakerAssetAmount,
|
||||
order.makerAssetAmount,
|
||||
takerAssetAmount
|
||||
);
|
||||
|
||||
// If `makerAsset` does not equal `makerFeeAsset`, the % that can be filled is the lower of
|
||||
// (transferableMakerAssetAmount / makerAssetAmount) and (transferableMakerAssetFeeAmount / makerFee)
|
||||
} else {
|
||||
uint256 transferableMakerToTakerAmount = getPartialAmountFloor(
|
||||
transferableMakerAssetAmount,
|
||||
order.makerAssetAmount,
|
||||
takerAssetAmount
|
||||
);
|
||||
uint256 transferableMakerFeeToTakerAmount = getPartialAmountFloor(
|
||||
transferableMakerFeeAssetAmount,
|
||||
makerFee,
|
||||
takerAssetAmount
|
||||
);
|
||||
transferableTakerAssetAmount = min256(transferableMakerToTakerAmount, transferableMakerFeeToTakerAmount);
|
||||
}
|
||||
}
|
||||
|
||||
// `fillableTakerAssetAmount` is the lower of the order's remaining `takerAssetAmount` and the `transferableTakerAssetAmount`
|
||||
fillableTakerAssetAmount = min256(
|
||||
safeSub(takerAssetAmount, orderInfo.orderTakerAssetFilledAmount),
|
||||
transferableTakerAssetAmount
|
||||
);
|
||||
|
||||
return (orderInfo, fillableTakerAssetAmount, isValidSignature);
|
||||
}
|
||||
|
||||
/// @dev Fetches all order-relevant information needed to validate if the supplied orders are fillable.
|
||||
/// @param orders Array of order structures.
|
||||
/// @param signatures Array of signatures provided by makers that prove the authenticity of the orders.
|
||||
/// `0x01` can always be provided if a signature does not need to be validated.
|
||||
/// @return The ordersInfo (array of the hash, status, and `takerAssetAmount` already filled for each order),
|
||||
/// fillableTakerAssetAmounts (array of amounts for each order's `takerAssetAmount` that is fillable given all on-chain state),
|
||||
/// and isValidSignature (array containing the validity of each provided signature).
|
||||
/// NOTE: If the `takerAssetData` encodes data for multiple assets, each element of `fillableTakerAssetAmounts`
|
||||
/// will represent a "scaled" amount, meaning it must be multiplied by all the individual asset amounts within
|
||||
/// the `takerAssetData` to get the final amount of each asset that can be filled.
|
||||
function getOrderRelevantStates(LibOrder.Order[] memory orders, bytes[] memory signatures)
|
||||
public
|
||||
view
|
||||
returns (
|
||||
LibOrder.OrderInfo[] memory ordersInfo,
|
||||
uint256[] memory fillableTakerAssetAmounts,
|
||||
bool[] memory isValidSignature
|
||||
)
|
||||
{
|
||||
uint256 length = orders.length;
|
||||
ordersInfo = new LibOrder.OrderInfo[](length);
|
||||
fillableTakerAssetAmounts = new uint256[](length);
|
||||
isValidSignature = new bool[](length);
|
||||
|
||||
for (uint256 i = 0; i != length; i++) {
|
||||
(ordersInfo[i], fillableTakerAssetAmounts[i], isValidSignature[i]) = getOrderRelevantState(
|
||||
orders[i],
|
||||
signatures[i]
|
||||
);
|
||||
}
|
||||
|
||||
return (ordersInfo, fillableTakerAssetAmounts, isValidSignature);
|
||||
}
|
||||
|
||||
/// @dev Gets the amount of an asset transferable by the owner.
|
||||
/// @param ownerAddress Address of the owner of the asset.
|
||||
/// @param assetData Description of tokens, per the AssetProxy contract specification.
|
||||
/// @return The amount of the asset tranferable by the owner.
|
||||
/// NOTE: If the `assetData` encodes data for multiple assets, the `transferableAssetAmount`
|
||||
/// will represent the amount of times the entire `assetData` can be transferred. To calculate
|
||||
/// the total individual transferable amounts, this scaled `transferableAmount` must be multiplied by
|
||||
/// the individual asset amounts located within the `assetData`.
|
||||
function getTransferableAssetAmount(address ownerAddress, bytes memory assetData)
|
||||
public
|
||||
view
|
||||
returns (uint256 transferableAssetAmount)
|
||||
{
|
||||
(uint256 balance, uint256 allowance) = getBalanceAndAssetProxyAllowance(ownerAddress, assetData);
|
||||
transferableAssetAmount = min256(balance, allowance);
|
||||
return transferableAssetAmount;
|
||||
}
|
||||
}
|
91
contracts/dev-utils/package.json
Normal file
91
contracts/dev-utils/package.json
Normal file
@@ -0,0 +1,91 @@
|
||||
{
|
||||
"name": "@0x/contracts-dev-utils",
|
||||
"version": "0.0.2",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
"description": "0x protocol specific utility contracts",
|
||||
"main": "lib/src/index.js",
|
||||
"directories": {
|
||||
"test": "test"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "yarn pre_build && tsc -b",
|
||||
"build:ci": "yarn build",
|
||||
"pre_build": "run-s compile generate_contract_wrappers",
|
||||
"test": "yarn run_mocha",
|
||||
"rebuild_and_test": "run-s build test",
|
||||
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
||||
"test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html",
|
||||
"test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha",
|
||||
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
|
||||
"compile": "sol-compiler",
|
||||
"watch": "sol-compiler -w",
|
||||
"clean": "shx rm -rf lib generated-artifacts generated-wrappers",
|
||||
"generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../../node_modules/@0x/abi-gen-templates/contract.handlebars --partials '../../node_modules/@0x/abi-gen-templates/partials/**/*.handlebars' --output generated-wrappers --backend ethers",
|
||||
"lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||
"fix": "tslint --fix --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||
"coverage:report:text": "istanbul report text",
|
||||
"coverage:report:html": "istanbul report html && open coverage/index.html",
|
||||
"profiler:report:html": "istanbul report html && open coverage/index.html",
|
||||
"coverage:report:lcov": "istanbul report lcov",
|
||||
"test:circleci": "yarn test",
|
||||
"contracts:gen": "contracts-gen",
|
||||
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol"
|
||||
},
|
||||
"config": {
|
||||
"abis": "./generated-artifacts/@(DevUtils|LibAssetData|LibTransactionDecoder|EthBalanceChecker).json",
|
||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/0xProject/0x-monorepo.git"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/0xProject/0x-monorepo/issues"
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/dev-utils/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^2.1.0",
|
||||
"@0x/contract-wrappers": "^9.1.5",
|
||||
"@0x/contracts-gen": "^1.0.10",
|
||||
"@0x/contracts-test-utils": "^3.1.8",
|
||||
"@0x/dev-utils": "^2.2.4",
|
||||
"@0x/sol-compiler": "^3.1.9",
|
||||
"@0x/tslint-config": "^3.0.1",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/node": "*",
|
||||
"chai": "^4.0.1",
|
||||
"chai-as-promised": "^7.1.0",
|
||||
"chai-bignumber": "^3.0.0",
|
||||
"dirty-chai": "^2.0.1",
|
||||
"make-promises-safe": "^1.1.0",
|
||||
"mocha": "^4.1.0",
|
||||
"npm-run-all": "^4.1.2",
|
||||
"shx": "^0.2.2",
|
||||
"solhint": "^1.4.1",
|
||||
"tslint": "5.11.0",
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^5.1.1",
|
||||
"@0x/contracts-asset-proxy": "^2.2.0",
|
||||
"@0x/contracts-erc1155": "^1.1.7",
|
||||
"@0x/contracts-erc20": "^2.2.6",
|
||||
"@0x/contracts-erc721": "^2.1.7",
|
||||
"@0x/contracts-exchange": "^2.1.6",
|
||||
"@0x/contracts-exchange-libs": "^3.0.0",
|
||||
"@0x/contracts-utils": "^3.1.7",
|
||||
"@0x/order-utils": "^8.2.0",
|
||||
"@0x/types": "^2.4.0",
|
||||
"@0x/typescript-typings": "^4.2.3",
|
||||
"@0x/utils": "^4.4.0",
|
||||
"@0x/web3-wrapper": "^6.0.7",
|
||||
"ethereum-types": "^2.1.3",
|
||||
"ethereumjs-util": "^5.1.1"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
15
contracts/dev-utils/src/artifacts.ts
Normal file
15
contracts/dev-utils/src/artifacts.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* -----------------------------------------------------------------------------
|
||||
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
import { ContractArtifact } from 'ethereum-types';
|
||||
|
||||
import * as DevUtils from '../generated-artifacts/DevUtils.json';
|
||||
import * as LibAssetData from '../generated-artifacts/LibAssetData.json';
|
||||
import * as LibTransactionDecoder from '../generated-artifacts/LibTransactionDecoder.json';
|
||||
export const artifacts = {
|
||||
DevUtils: DevUtils as ContractArtifact,
|
||||
LibTransactionDecoder: LibTransactionDecoder as ContractArtifact,
|
||||
LibAssetData: LibAssetData as ContractArtifact,
|
||||
};
|
2
contracts/dev-utils/src/index.ts
Normal file
2
contracts/dev-utils/src/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './artifacts';
|
||||
export * from './wrappers';
|
8
contracts/dev-utils/src/wrappers.ts
Normal file
8
contracts/dev-utils/src/wrappers.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
* -----------------------------------------------------------------------------
|
||||
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
export * from '../generated-wrappers/dev_utils';
|
||||
export * from '../generated-wrappers/lib_asset_data';
|
||||
export * from '../generated-wrappers/lib_transaction_decoder';
|
19
contracts/dev-utils/test/global_hooks.ts
Normal file
19
contracts/dev-utils/test/global_hooks.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { env, EnvVars } from '@0x/dev-utils';
|
||||
|
||||
import { coverage, profiler, provider } from '@0x/contracts-test-utils';
|
||||
import { providerUtils } from '@0x/utils';
|
||||
|
||||
before('start web3 provider', () => {
|
||||
providerUtils.startProviderEngine(provider);
|
||||
});
|
||||
after('generate coverage report', async () => {
|
||||
if (env.parseBoolean(EnvVars.SolidityCoverage)) {
|
||||
const coverageSubprovider = coverage.getCoverageSubproviderSingleton();
|
||||
await coverageSubprovider.writeCoverageAsync();
|
||||
}
|
||||
if (env.parseBoolean(EnvVars.SolidityProfiler)) {
|
||||
const profilerSubprovider = profiler.getProfilerSubproviderSingleton();
|
||||
await profilerSubprovider.writeProfilerOutputAsync();
|
||||
}
|
||||
provider.stop();
|
||||
});
|
527
contracts/dev-utils/test/lib_asset_data.ts
Normal file
527
contracts/dev-utils/test/lib_asset_data.ts
Normal file
@@ -0,0 +1,527 @@
|
||||
import * as chai from 'chai';
|
||||
import { LogWithDecodedArgs } from 'ethereum-types';
|
||||
|
||||
import {
|
||||
artifacts as proxyArtifacts,
|
||||
ERC1155ProxyContract,
|
||||
ERC20ProxyContract,
|
||||
ERC721ProxyContract,
|
||||
MultiAssetProxyContract,
|
||||
StaticCallProxyContract,
|
||||
TestStaticCallTargetContract,
|
||||
} from '@0x/contracts-asset-proxy';
|
||||
import {
|
||||
artifacts as erc1155Artifacts,
|
||||
ERC1155MintableContract,
|
||||
ERC1155TransferSingleEventArgs,
|
||||
} from '@0x/contracts-erc1155';
|
||||
import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contracts-erc20';
|
||||
import { artifacts as erc721Artifacts, DummyERC721TokenContract } from '@0x/contracts-erc721';
|
||||
import { artifacts as exchangeArtifacts, ExchangeContract } from '@0x/contracts-exchange';
|
||||
import { chaiSetup, constants, LogDecoder, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils';
|
||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||
import { assetDataUtils } from '@0x/order-utils';
|
||||
import { AssetProxyId } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as ethUtil from 'ethereumjs-util';
|
||||
|
||||
import { artifacts, LibAssetDataContract } from '../src';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
|
||||
const KNOWN_ERC20_ENCODING = {
|
||||
address: '0x1dc4c1cefef38a777b15aa20260a54e584b16c48',
|
||||
assetData: '0xf47261b00000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48',
|
||||
};
|
||||
const KNOWN_ERC721_ENCODING = {
|
||||
address: '0x1dc4c1cefef38a777b15aa20260a54e584b16c48',
|
||||
tokenId: new BigNumber(1),
|
||||
assetData:
|
||||
'0x025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c480000000000000000000000000000000000000000000000000000000000000001',
|
||||
};
|
||||
const KNOWN_ERC1155_ENCODING = {
|
||||
tokenAddress: '0x1dc4c1cefef38a777b15aa20260a54e584b16c48',
|
||||
tokenIds: [new BigNumber(100), new BigNumber(1001), new BigNumber(10001)],
|
||||
tokenValues: [new BigNumber(200), new BigNumber(2001), new BigNumber(20001)],
|
||||
callbackData:
|
||||
'0x025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c480000000000000000000000000000000000000000000000000000000000000001',
|
||||
assetData:
|
||||
'0xa7cb5fb70000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c480000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000003e90000000000000000000000000000000000000000000000000000000000002711000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000c800000000000000000000000000000000000000000000000000000000000007d10000000000000000000000000000000000000000000000000000000000004e210000000000000000000000000000000000000000000000000000000000000044025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000',
|
||||
};
|
||||
const KNOWN_MULTI_ASSET_ENCODING = {
|
||||
amounts: [new BigNumber(70), new BigNumber(1), new BigNumber(18)],
|
||||
nestedAssetData: [
|
||||
KNOWN_ERC20_ENCODING.assetData,
|
||||
KNOWN_ERC721_ENCODING.assetData,
|
||||
KNOWN_ERC1155_ENCODING.assetData,
|
||||
],
|
||||
assetData:
|
||||
'0x94cfcdd7000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000046000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000024f47261b00000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c480000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000204a7cb5fb70000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c480000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000003e90000000000000000000000000000000000000000000000000000000000002711000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000c800000000000000000000000000000000000000000000000000000000000007d10000000000000000000000000000000000000000000000000000000000004e210000000000000000000000000000000000000000000000000000000000000044025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c4800000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
|
||||
};
|
||||
|
||||
describe('LibAssetData', () => {
|
||||
let exchange: ExchangeContract;
|
||||
let erc20Proxy: ERC20ProxyContract;
|
||||
let erc721Proxy: ERC721ProxyContract;
|
||||
let erc1155Proxy: ERC1155ProxyContract;
|
||||
let multiAssetProxy: MultiAssetProxyContract;
|
||||
let staticCallProxy: StaticCallProxyContract;
|
||||
let staticCallTarget: TestStaticCallTargetContract;
|
||||
let libAssetData: LibAssetDataContract;
|
||||
|
||||
let tokenOwnerAddress: string;
|
||||
|
||||
let erc20Token: DummyERC20TokenContract;
|
||||
let erc721Token: DummyERC721TokenContract;
|
||||
let erc1155Token: ERC1155MintableContract;
|
||||
|
||||
const erc20TokenTotalSupply = new BigNumber(1);
|
||||
|
||||
const firstERC721TokenId = new BigNumber(1);
|
||||
const numberOfERC721Tokens = 10;
|
||||
|
||||
let erc1155TokenId: BigNumber;
|
||||
|
||||
before(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
|
||||
exchange = await ExchangeContract.deployFrom0xArtifactAsync(
|
||||
exchangeArtifacts.Exchange,
|
||||
provider,
|
||||
txDefaults,
|
||||
constants.NULL_BYTES,
|
||||
);
|
||||
|
||||
erc20Proxy = await ERC20ProxyContract.deployFrom0xArtifactAsync(
|
||||
proxyArtifacts.ERC20Proxy,
|
||||
provider,
|
||||
txDefaults,
|
||||
);
|
||||
erc721Proxy = await ERC721ProxyContract.deployFrom0xArtifactAsync(
|
||||
proxyArtifacts.ERC721Proxy,
|
||||
provider,
|
||||
txDefaults,
|
||||
);
|
||||
erc1155Proxy = await ERC1155ProxyContract.deployFrom0xArtifactAsync(
|
||||
proxyArtifacts.ERC1155Proxy,
|
||||
provider,
|
||||
txDefaults,
|
||||
);
|
||||
multiAssetProxy = await MultiAssetProxyContract.deployFrom0xArtifactAsync(
|
||||
proxyArtifacts.MultiAssetProxy,
|
||||
provider,
|
||||
txDefaults,
|
||||
);
|
||||
staticCallProxy = await StaticCallProxyContract.deployFrom0xArtifactAsync(
|
||||
proxyArtifacts.StaticCallProxy,
|
||||
provider,
|
||||
txDefaults,
|
||||
);
|
||||
|
||||
await exchange.registerAssetProxy.awaitTransactionSuccessAsync(erc20Proxy.address);
|
||||
await exchange.registerAssetProxy.awaitTransactionSuccessAsync(erc721Proxy.address);
|
||||
await exchange.registerAssetProxy.awaitTransactionSuccessAsync(erc1155Proxy.address);
|
||||
await exchange.registerAssetProxy.awaitTransactionSuccessAsync(multiAssetProxy.address);
|
||||
await exchange.registerAssetProxy.awaitTransactionSuccessAsync(staticCallProxy.address);
|
||||
|
||||
libAssetData = await LibAssetDataContract.deployFrom0xArtifactAsync(
|
||||
artifacts.LibAssetData,
|
||||
provider,
|
||||
txDefaults,
|
||||
exchange.address,
|
||||
);
|
||||
|
||||
staticCallTarget = await TestStaticCallTargetContract.deployFrom0xArtifactAsync(
|
||||
proxyArtifacts.TestStaticCallTarget,
|
||||
provider,
|
||||
txDefaults,
|
||||
);
|
||||
|
||||
[tokenOwnerAddress] = await web3Wrapper.getAvailableAddressesAsync();
|
||||
|
||||
erc20Token = await DummyERC20TokenContract.deployFrom0xArtifactAsync(
|
||||
erc20Artifacts.DummyERC20Token,
|
||||
provider,
|
||||
txDefaults,
|
||||
'Dummy',
|
||||
'DUM',
|
||||
new BigNumber(1),
|
||||
erc20TokenTotalSupply,
|
||||
);
|
||||
|
||||
erc721Token = await DummyERC721TokenContract.deployFrom0xArtifactAsync(
|
||||
erc721Artifacts.DummyERC721Token,
|
||||
provider,
|
||||
txDefaults,
|
||||
'Dummy',
|
||||
'DUM',
|
||||
);
|
||||
// mint `numberOfERC721Tokens` tokens
|
||||
const transactionMinedPromises = [];
|
||||
for (let i = 0; i < numberOfERC721Tokens; i++) {
|
||||
transactionMinedPromises.push(
|
||||
erc721Token.mint.awaitTransactionSuccessAsync(tokenOwnerAddress, firstERC721TokenId.plus(i - 1)),
|
||||
);
|
||||
}
|
||||
await Promise.all(transactionMinedPromises);
|
||||
|
||||
erc1155Token = await ERC1155MintableContract.deployFrom0xArtifactAsync(
|
||||
erc1155Artifacts.ERC1155Mintable,
|
||||
provider,
|
||||
txDefaults,
|
||||
);
|
||||
|
||||
const logDecoder = new LogDecoder(web3Wrapper, erc1155Artifacts);
|
||||
const transactionReceipt = await logDecoder.getTxWithDecodedLogsAsync(
|
||||
await erc1155Token.create.sendTransactionAsync('uri:Dummy', /*isNonFungible:*/ false),
|
||||
);
|
||||
|
||||
// tslint:disable-next-line no-unnecessary-type-assertion
|
||||
erc1155TokenId = (transactionReceipt.logs[0] as LogWithDecodedArgs<ERC1155TransferSingleEventArgs>).args.id;
|
||||
await erc1155Token.mintFungible.awaitTransactionSuccessAsync(
|
||||
erc1155TokenId,
|
||||
[tokenOwnerAddress],
|
||||
[new BigNumber(1)],
|
||||
);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
afterEach(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
|
||||
it('should have a deployed-to address', () => {
|
||||
expect(libAssetData.address.slice(0, 2)).to.equal('0x');
|
||||
});
|
||||
|
||||
describe('encoding and decoding', () => {
|
||||
it('should encode ERC20 asset data', async () => {
|
||||
expect(await libAssetData.encodeERC20AssetData.callAsync(KNOWN_ERC20_ENCODING.address)).to.equal(
|
||||
KNOWN_ERC20_ENCODING.assetData,
|
||||
);
|
||||
});
|
||||
|
||||
it('should decode ERC20 asset data', async () => {
|
||||
expect(await libAssetData.decodeERC20AssetData.callAsync(KNOWN_ERC20_ENCODING.assetData)).to.deep.equal([
|
||||
AssetProxyId.ERC20,
|
||||
KNOWN_ERC20_ENCODING.address,
|
||||
]);
|
||||
});
|
||||
|
||||
it('should encode ERC721 asset data', async () => {
|
||||
expect(
|
||||
await libAssetData.encodeERC721AssetData.callAsync(
|
||||
KNOWN_ERC721_ENCODING.address,
|
||||
KNOWN_ERC721_ENCODING.tokenId,
|
||||
),
|
||||
).to.equal(KNOWN_ERC721_ENCODING.assetData);
|
||||
});
|
||||
|
||||
it('should decode ERC721 asset data', async () => {
|
||||
expect(await libAssetData.decodeERC721AssetData.callAsync(KNOWN_ERC721_ENCODING.assetData)).to.deep.equal([
|
||||
AssetProxyId.ERC721,
|
||||
KNOWN_ERC721_ENCODING.address,
|
||||
KNOWN_ERC721_ENCODING.tokenId,
|
||||
]);
|
||||
});
|
||||
|
||||
it('should encode ERC1155 asset data', async () => {
|
||||
expect(
|
||||
await libAssetData.encodeERC1155AssetData.callAsync(
|
||||
KNOWN_ERC1155_ENCODING.tokenAddress,
|
||||
KNOWN_ERC1155_ENCODING.tokenIds,
|
||||
KNOWN_ERC1155_ENCODING.tokenValues,
|
||||
KNOWN_ERC1155_ENCODING.callbackData,
|
||||
),
|
||||
).to.equal(KNOWN_ERC1155_ENCODING.assetData);
|
||||
});
|
||||
|
||||
it('should decode ERC1155 asset data', async () => {
|
||||
expect(await libAssetData.decodeERC1155AssetData.callAsync(KNOWN_ERC1155_ENCODING.assetData)).to.deep.equal(
|
||||
[
|
||||
AssetProxyId.ERC1155,
|
||||
KNOWN_ERC1155_ENCODING.tokenAddress,
|
||||
KNOWN_ERC1155_ENCODING.tokenIds,
|
||||
KNOWN_ERC1155_ENCODING.tokenValues,
|
||||
KNOWN_ERC1155_ENCODING.callbackData,
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
it('should encode multiasset data', async () => {
|
||||
expect(
|
||||
await libAssetData.encodeMultiAssetData.callAsync(
|
||||
KNOWN_MULTI_ASSET_ENCODING.amounts,
|
||||
KNOWN_MULTI_ASSET_ENCODING.nestedAssetData,
|
||||
),
|
||||
).to.equal(KNOWN_MULTI_ASSET_ENCODING.assetData);
|
||||
});
|
||||
|
||||
it('should decode multiasset data', async () => {
|
||||
expect(
|
||||
await libAssetData.decodeMultiAssetData.callAsync(KNOWN_MULTI_ASSET_ENCODING.assetData),
|
||||
).to.deep.equal([
|
||||
AssetProxyId.MultiAsset,
|
||||
KNOWN_MULTI_ASSET_ENCODING.amounts,
|
||||
KNOWN_MULTI_ASSET_ENCODING.nestedAssetData,
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getBalance', () => {
|
||||
it('should query ERC20 balance by asset data', async () => {
|
||||
const assetData = assetDataUtils.encodeERC20AssetData(erc20Token.address);
|
||||
expect(await libAssetData.getBalance.callAsync(tokenOwnerAddress, assetData)).to.bignumber.equal(
|
||||
erc20TokenTotalSupply,
|
||||
);
|
||||
});
|
||||
|
||||
it('should return 0 if ERC20 token does not exist', async () => {
|
||||
const assetData = assetDataUtils.encodeERC20AssetData(constants.NULL_ADDRESS);
|
||||
const balance = await libAssetData.getBalance.callAsync(tokenOwnerAddress, assetData);
|
||||
expect(balance).to.bignumber.equal(constants.ZERO_AMOUNT);
|
||||
});
|
||||
|
||||
it('should query ERC721 balance by asset data', async () => {
|
||||
const assetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, firstERC721TokenId);
|
||||
expect(await libAssetData.getBalance.callAsync(tokenOwnerAddress, assetData)).to.bignumber.equal(1);
|
||||
});
|
||||
|
||||
it('should return 0 if ERC721 token does not exist', async () => {
|
||||
const assetData = assetDataUtils.encodeERC721AssetData(constants.NULL_ADDRESS, firstERC721TokenId);
|
||||
const balance = await libAssetData.getBalance.callAsync(tokenOwnerAddress, assetData);
|
||||
expect(balance).to.bignumber.equal(constants.ZERO_AMOUNT);
|
||||
});
|
||||
|
||||
it('should query ERC1155 balances by asset data', async () => {
|
||||
const assetData = assetDataUtils.encodeERC1155AssetData(
|
||||
erc1155Token.address,
|
||||
[erc1155TokenId],
|
||||
[new BigNumber(1)],
|
||||
constants.NULL_BYTES,
|
||||
);
|
||||
expect(await libAssetData.getBalance.callAsync(tokenOwnerAddress, assetData)).to.bignumber.equal(1);
|
||||
});
|
||||
|
||||
it('should return 0 if ERC1155 token does not exist', async () => {
|
||||
const assetData = assetDataUtils.encodeERC1155AssetData(
|
||||
constants.NULL_ADDRESS,
|
||||
[erc1155TokenId],
|
||||
[new BigNumber(1)],
|
||||
constants.NULL_BYTES,
|
||||
);
|
||||
const balance = await libAssetData.getBalance.callAsync(tokenOwnerAddress, assetData);
|
||||
expect(balance).to.bignumber.equal(constants.ZERO_AMOUNT);
|
||||
});
|
||||
|
||||
it('should query multi-asset batch balance by asset data', async () => {
|
||||
const assetData = assetDataUtils.encodeMultiAssetData(
|
||||
[new BigNumber(1), new BigNumber(1)],
|
||||
[
|
||||
assetDataUtils.encodeERC20AssetData(erc20Token.address),
|
||||
assetDataUtils.encodeERC721AssetData(erc721Token.address, firstERC721TokenId),
|
||||
],
|
||||
);
|
||||
expect(await libAssetData.getBalance.callAsync(tokenOwnerAddress, assetData)).to.bignumber.equal(
|
||||
Math.min(erc20TokenTotalSupply.toNumber(), numberOfERC721Tokens),
|
||||
);
|
||||
});
|
||||
|
||||
it('should return a balance of 0 if the assetData does not correspond to an AssetProxy contract', async () => {
|
||||
const fakeAssetData = '0x01020304';
|
||||
const balance = await libAssetData.getBalance.callAsync(tokenOwnerAddress, fakeAssetData);
|
||||
expect(balance).to.bignumber.equal(constants.ZERO_AMOUNT);
|
||||
});
|
||||
|
||||
it('should return a balance of MAX_UINT256 if the the StaticCallProxy assetData contains data for a successful staticcall', async () => {
|
||||
const staticCallData = staticCallTarget.isOddNumber.getABIEncodedTransactionData(new BigNumber(1));
|
||||
const trueAsBuffer = ethUtil.toBuffer('0x0000000000000000000000000000000000000000000000000000000000000001');
|
||||
const expectedResultHash = ethUtil.bufferToHex(ethUtil.sha3(trueAsBuffer));
|
||||
const assetData = assetDataUtils.encodeStaticCallAssetData(
|
||||
staticCallTarget.address,
|
||||
staticCallData,
|
||||
expectedResultHash,
|
||||
);
|
||||
const balance = await libAssetData.getBalance.callAsync(tokenOwnerAddress, assetData);
|
||||
expect(balance).to.bignumber.equal(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
|
||||
});
|
||||
|
||||
it('should return a balance of 0 if the the StaticCallProxy assetData contains data for an unsuccessful staticcall', async () => {
|
||||
const staticCallData = staticCallTarget.isOddNumber.getABIEncodedTransactionData(new BigNumber(0));
|
||||
const trueAsBuffer = ethUtil.toBuffer('0x0000000000000000000000000000000000000000000000000000000000000001');
|
||||
const expectedResultHash = ethUtil.bufferToHex(ethUtil.sha3(trueAsBuffer));
|
||||
const assetData = assetDataUtils.encodeStaticCallAssetData(
|
||||
staticCallTarget.address,
|
||||
staticCallData,
|
||||
expectedResultHash,
|
||||
);
|
||||
const balance = await libAssetData.getBalance.callAsync(tokenOwnerAddress, assetData);
|
||||
expect(balance).to.bignumber.equal(constants.ZERO_AMOUNT);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAssetProxyAllowance', () => {
|
||||
it('should query ERC20 allowances by asset data', async () => {
|
||||
const allowance = new BigNumber(1);
|
||||
await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, allowance, {
|
||||
from: tokenOwnerAddress,
|
||||
});
|
||||
const assetData = assetDataUtils.encodeERC20AssetData(erc20Token.address);
|
||||
expect(
|
||||
await libAssetData.getAssetProxyAllowance.callAsync(tokenOwnerAddress, assetData),
|
||||
).to.bignumber.equal(allowance);
|
||||
});
|
||||
|
||||
it('should query ERC721 approval by asset data', async () => {
|
||||
await erc721Token.approve.awaitTransactionSuccessAsync(erc721Proxy.address, firstERC721TokenId, {
|
||||
from: tokenOwnerAddress,
|
||||
});
|
||||
const assetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, firstERC721TokenId);
|
||||
expect(
|
||||
await libAssetData.getAssetProxyAllowance.callAsync(tokenOwnerAddress, assetData),
|
||||
).to.bignumber.equal(1);
|
||||
});
|
||||
|
||||
it('should query ERC721 approvalForAll by assetData', async () => {
|
||||
await erc721Token.setApprovalForAll.awaitTransactionSuccessAsync(erc721Proxy.address, true, {
|
||||
from: tokenOwnerAddress,
|
||||
});
|
||||
const assetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, firstERC721TokenId);
|
||||
expect(
|
||||
await libAssetData.getAssetProxyAllowance.callAsync(tokenOwnerAddress, assetData),
|
||||
).to.bignumber.equal(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
|
||||
});
|
||||
|
||||
it('should query ERC1155 allowances by asset data', async () => {
|
||||
await erc1155Token.setApprovalForAll.awaitTransactionSuccessAsync(erc1155Proxy.address, true, {
|
||||
from: tokenOwnerAddress,
|
||||
});
|
||||
const assetData = assetDataUtils.encodeERC1155AssetData(
|
||||
erc1155Token.address,
|
||||
[erc1155TokenId],
|
||||
[new BigNumber(1)],
|
||||
constants.NULL_BYTES,
|
||||
);
|
||||
expect(
|
||||
await libAssetData.getAssetProxyAllowance.callAsync(tokenOwnerAddress, assetData),
|
||||
).to.bignumber.equal(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
|
||||
});
|
||||
|
||||
it('should query multi-asset allowances by asset data', async () => {
|
||||
const allowance = new BigNumber(1);
|
||||
await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, allowance, {
|
||||
from: tokenOwnerAddress,
|
||||
});
|
||||
await erc721Token.approve.awaitTransactionSuccessAsync(erc721Proxy.address, firstERC721TokenId, {
|
||||
from: tokenOwnerAddress,
|
||||
});
|
||||
const assetData = assetDataUtils.encodeMultiAssetData(
|
||||
[new BigNumber(1), new BigNumber(1)],
|
||||
[
|
||||
assetDataUtils.encodeERC20AssetData(erc20Token.address),
|
||||
assetDataUtils.encodeERC721AssetData(erc721Token.address, firstERC721TokenId),
|
||||
],
|
||||
);
|
||||
expect(
|
||||
await libAssetData.getAssetProxyAllowance.callAsync(tokenOwnerAddress, assetData),
|
||||
).to.bignumber.equal(1);
|
||||
return;
|
||||
});
|
||||
|
||||
it('should return an allowance of 0 if the assetData does not correspond to an AssetProxy contract', async () => {
|
||||
const fakeAssetData = '0x01020304';
|
||||
const allowance = await libAssetData.getAssetProxyAllowance.callAsync(tokenOwnerAddress, fakeAssetData);
|
||||
expect(allowance).to.bignumber.equal(constants.ZERO_AMOUNT);
|
||||
});
|
||||
|
||||
it('should return an allowance of MAX_UINT256 for any staticCallAssetData', async () => {
|
||||
const staticCallData = AssetProxyId.StaticCall;
|
||||
const assetData = assetDataUtils.encodeStaticCallAssetData(
|
||||
staticCallTarget.address,
|
||||
staticCallData,
|
||||
constants.KECCAK256_NULL,
|
||||
);
|
||||
const allowance = await libAssetData.getAssetProxyAllowance.callAsync(tokenOwnerAddress, assetData);
|
||||
expect(allowance).to.bignumber.equal(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getBatchBalances', () => {
|
||||
it('should query balances for a batch of asset data strings', async () => {
|
||||
const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20Token.address);
|
||||
const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, firstERC721TokenId);
|
||||
expect(
|
||||
await libAssetData.getBatchBalances.callAsync(tokenOwnerAddress, [erc20AssetData, erc721AssetData]),
|
||||
).to.deep.equal([new BigNumber(erc20TokenTotalSupply), new BigNumber(1)]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getERC721TokenOwner', async () => {
|
||||
it('should return the null address when tokenId is not owned', async () => {
|
||||
const nonexistentTokenId = new BigNumber(1234567890);
|
||||
expect(
|
||||
await libAssetData.getERC721TokenOwner.callAsync(erc721Token.address, nonexistentTokenId),
|
||||
).to.be.equal(constants.NULL_ADDRESS);
|
||||
});
|
||||
it('should return the owner address when tokenId is owned', async () => {
|
||||
expect(
|
||||
await libAssetData.getERC721TokenOwner.callAsync(erc721Token.address, firstERC721TokenId),
|
||||
).to.be.equal(tokenOwnerAddress);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getBalanceAndAllowance', () => {
|
||||
it('should query balance and allowance together, from asset data', async () => {
|
||||
const allowance = new BigNumber(1);
|
||||
await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, allowance, {
|
||||
from: tokenOwnerAddress,
|
||||
});
|
||||
const assetData = assetDataUtils.encodeERC20AssetData(erc20Token.address);
|
||||
expect(
|
||||
await libAssetData.getBalanceAndAssetProxyAllowance.callAsync(tokenOwnerAddress, assetData),
|
||||
).to.deep.equal([new BigNumber(erc20TokenTotalSupply), allowance]);
|
||||
});
|
||||
});
|
||||
describe('getBatchBalancesAndAllowances', () => {
|
||||
it('should query balances and allowances together, from an asset data array', async () => {
|
||||
const allowance = new BigNumber(1);
|
||||
await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, allowance, {
|
||||
from: tokenOwnerAddress,
|
||||
});
|
||||
const assetData = assetDataUtils.encodeERC20AssetData(erc20Token.address);
|
||||
expect(
|
||||
await libAssetData.getBatchBalancesAndAssetProxyAllowances.callAsync(tokenOwnerAddress, [assetData]),
|
||||
).to.deep.equal([[new BigNumber(erc20TokenTotalSupply)], [allowance]]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getBatchAssetProxyAllowances', () => {
|
||||
it('should query allowances for a batch of asset data strings', async () => {
|
||||
const allowance = new BigNumber(1);
|
||||
await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, allowance, {
|
||||
from: tokenOwnerAddress,
|
||||
});
|
||||
await erc721Token.approve.awaitTransactionSuccessAsync(erc721Proxy.address, firstERC721TokenId, {
|
||||
from: tokenOwnerAddress,
|
||||
});
|
||||
const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20Token.address);
|
||||
const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, firstERC721TokenId);
|
||||
expect(
|
||||
await libAssetData.getBatchAssetProxyAllowances.callAsync(tokenOwnerAddress, [
|
||||
erc20AssetData,
|
||||
erc721AssetData,
|
||||
]),
|
||||
).to.deep.equal([new BigNumber(1), new BigNumber(1)]);
|
||||
});
|
||||
});
|
||||
});
|
||||
// tslint:disable:max-file-line-count
|
139
contracts/dev-utils/test/lib_transaction_decoder.ts
Normal file
139
contracts/dev-utils/test/lib_transaction_decoder.ts
Normal file
@@ -0,0 +1,139 @@
|
||||
import { IExchangeContract } from '@0x/contracts-exchange';
|
||||
import { chaiSetup, constants, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils';
|
||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as chai from 'chai';
|
||||
|
||||
import { artifacts, LibTransactionDecoderContract } from '../src';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
|
||||
const order = {
|
||||
makerAddress: '0xe36ea790bc9d7ab70c55260c66d52b1eca985f84',
|
||||
takerAddress: '0x0000000000000000000000000000000000000000',
|
||||
feeRecipientAddress: '0x78dc5d2d739606d31509c31d654056a45185ecb6',
|
||||
senderAddress: '0x6ecbe1db9ef729cbe972c83fb886247691fb6beb',
|
||||
makerAssetAmount: new BigNumber('100000000000000000000'),
|
||||
takerAssetAmount: new BigNumber('200000000000000000000'),
|
||||
makerFee: new BigNumber('1000000000000000000'),
|
||||
takerFee: new BigNumber('1000000000000000000'),
|
||||
expirationTimeSeconds: new BigNumber('1552396423'),
|
||||
salt: new BigNumber('66097384406870180066678463045003379626790660770396923976862707230261946348951'),
|
||||
makerAssetData: '0xf47261b000000000000000000000000034d402f14d58e001d8efbe6585051bf9706aa064',
|
||||
takerAssetData: '0xf47261b000000000000000000000000025b8fe1de9daf8ba351890744ff28cf7dfa8f5e3',
|
||||
};
|
||||
const takerAssetFillAmount = new BigNumber('100000000000000000000');
|
||||
const signature =
|
||||
'0x1ce8e3c600d933423172b5021158a6be2e818613ff8e762d70ef490c752fd98a626a215f09f169668990414de75a53da221c294a3002f796d004827258b641876e03';
|
||||
|
||||
describe('LibTransactionDecoder', () => {
|
||||
let libTxDecoder: LibTransactionDecoderContract;
|
||||
const exchangeInterface = new IExchangeContract(constants.NULL_ADDRESS, provider, txDefaults);
|
||||
before(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
libTxDecoder = await LibTransactionDecoderContract.deployFrom0xArtifactAsync(
|
||||
artifacts.LibTransactionDecoder,
|
||||
provider,
|
||||
txDefaults,
|
||||
);
|
||||
});
|
||||
after(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
|
||||
it('should decode an Exchange.batchCancelOrders() transaction', async () => {
|
||||
const input = exchangeInterface.batchCancelOrders.getABIEncodedTransactionData([order, order]);
|
||||
expect(await libTxDecoder.decodeZeroExTransactionData.callAsync(input)).to.deep.equal([
|
||||
'batchCancelOrders',
|
||||
[order, order],
|
||||
[],
|
||||
[],
|
||||
]);
|
||||
});
|
||||
|
||||
for (const func of ['batchFillOrders', 'batchFillOrdersNoThrow', 'batchFillOrKillOrders']) {
|
||||
const input = (exchangeInterface as any)[func].getABIEncodedTransactionData(
|
||||
[order, order],
|
||||
[takerAssetFillAmount, takerAssetFillAmount],
|
||||
[signature, signature],
|
||||
);
|
||||
it(`should decode an Exchange.${func}() transaction`, async () => {
|
||||
expect(await libTxDecoder.decodeZeroExTransactionData.callAsync(input)).to.deep.equal([
|
||||
func,
|
||||
[order, order],
|
||||
[takerAssetFillAmount, takerAssetFillAmount],
|
||||
[signature, signature],
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
it('should decode an Exchange.cancelOrder() transaction', async () => {
|
||||
const input = exchangeInterface.cancelOrder.getABIEncodedTransactionData(order);
|
||||
expect(await libTxDecoder.decodeZeroExTransactionData.callAsync(input)).to.deep.equal([
|
||||
'cancelOrder',
|
||||
[order],
|
||||
[],
|
||||
[],
|
||||
]);
|
||||
});
|
||||
|
||||
for (const func of ['fillOrder', 'fillOrderNoThrow', 'fillOrKillOrder']) {
|
||||
const input = (exchangeInterface as any)[func].getABIEncodedTransactionData(
|
||||
order,
|
||||
takerAssetFillAmount,
|
||||
signature,
|
||||
);
|
||||
it(`should decode an Exchange.${func}() transaction`, async () => {
|
||||
expect(await libTxDecoder.decodeZeroExTransactionData.callAsync(input)).to.deep.equal([
|
||||
func,
|
||||
[order],
|
||||
[takerAssetFillAmount],
|
||||
[signature],
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
for (const func of ['marketBuyOrders', 'marketBuyOrdersNoThrow', 'marketSellOrders', 'marketSellOrdersNoThrow']) {
|
||||
const input = (exchangeInterface as any)[func].getABIEncodedTransactionData(
|
||||
[order, order],
|
||||
takerAssetFillAmount,
|
||||
[signature, signature],
|
||||
);
|
||||
it(`should decode an Exchange.${func}() transaction`, async () => {
|
||||
expect(await libTxDecoder.decodeZeroExTransactionData.callAsync(input)).to.deep.equal([
|
||||
func,
|
||||
[order, order],
|
||||
[takerAssetFillAmount],
|
||||
[signature, signature],
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
it('should decode an Exchange.matchOrders() transaction', async () => {
|
||||
const complementaryOrder = {
|
||||
...order,
|
||||
makerAddress: order.takerAddress,
|
||||
takerAddress: order.makerAddress,
|
||||
makerAssetData: order.takerAssetData,
|
||||
takerAssetData: order.makerAssetData,
|
||||
makerAssetAmount: order.takerAssetAmount,
|
||||
takerAssetAmount: order.makerAssetAmount,
|
||||
makerFee: order.takerFee,
|
||||
takerFee: order.makerFee,
|
||||
};
|
||||
const input = exchangeInterface.matchOrders.getABIEncodedTransactionData(
|
||||
order,
|
||||
complementaryOrder,
|
||||
signature,
|
||||
signature,
|
||||
);
|
||||
expect(await libTxDecoder.decodeZeroExTransactionData.callAsync(input)).to.deep.equal([
|
||||
'matchOrders',
|
||||
[order, complementaryOrder],
|
||||
[order.takerAssetAmount, complementaryOrder.takerAssetAmount],
|
||||
[signature, signature],
|
||||
]);
|
||||
});
|
||||
});
|
442
contracts/dev-utils/test/order_validation_utils.ts
Normal file
442
contracts/dev-utils/test/order_validation_utils.ts
Normal file
@@ -0,0 +1,442 @@
|
||||
import {
|
||||
artifacts as proxyArtifacts,
|
||||
ERC20ProxyContract,
|
||||
ERC20Wrapper,
|
||||
ERC721ProxyContract,
|
||||
ERC721Wrapper,
|
||||
MultiAssetProxyContract,
|
||||
} from '@0x/contracts-asset-proxy';
|
||||
import { DummyERC20TokenContract } from '@0x/contracts-erc20';
|
||||
import { DummyERC721TokenContract } from '@0x/contracts-erc721';
|
||||
import { artifacts as exchangeArtifacts, ExchangeContract, ExchangeWrapper } from '@0x/contracts-exchange';
|
||||
import {
|
||||
chaiSetup,
|
||||
constants,
|
||||
OrderFactory,
|
||||
OrderStatus,
|
||||
provider,
|
||||
txDefaults,
|
||||
web3Wrapper,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||
import { assetDataUtils, orderHashUtils } from '@0x/order-utils';
|
||||
import { SignedOrder } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as chai from 'chai';
|
||||
|
||||
import { artifacts, DevUtilsContract } from '../src';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
|
||||
describe('OrderValidationUtils', () => {
|
||||
let makerAddress: string;
|
||||
let takerAddress: string;
|
||||
let owner: string;
|
||||
let erc20AssetData: string;
|
||||
let erc20AssetData2: string;
|
||||
let erc721AssetData: string;
|
||||
let zrxAssetData: string;
|
||||
|
||||
let erc20Token: DummyERC20TokenContract;
|
||||
let erc20Token2: DummyERC20TokenContract;
|
||||
let zrxToken: DummyERC20TokenContract;
|
||||
let erc721Token: DummyERC721TokenContract;
|
||||
let exchange: ExchangeContract;
|
||||
let devUtils: DevUtilsContract;
|
||||
let erc20Proxy: ERC20ProxyContract;
|
||||
let erc721Proxy: ERC721ProxyContract;
|
||||
let multiAssetProxy: MultiAssetProxyContract;
|
||||
|
||||
let signedOrder: SignedOrder;
|
||||
let orderFactory: OrderFactory;
|
||||
|
||||
const tokenId = new BigNumber(123456789);
|
||||
|
||||
before(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
after(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
|
||||
before(async () => {
|
||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
||||
const usedAddresses = ([owner, makerAddress, takerAddress] = accounts.slice(0, 3));
|
||||
|
||||
const erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner);
|
||||
const erc721Wrapper = new ERC721Wrapper(provider, usedAddresses, owner);
|
||||
|
||||
const numDummyErc20ToDeploy = 3;
|
||||
[erc20Token, zrxToken, erc20Token2] = await erc20Wrapper.deployDummyTokensAsync(
|
||||
numDummyErc20ToDeploy,
|
||||
constants.DUMMY_TOKEN_DECIMALS,
|
||||
);
|
||||
erc20Proxy = await erc20Wrapper.deployProxyAsync();
|
||||
|
||||
[erc721Token] = await erc721Wrapper.deployDummyTokensAsync();
|
||||
erc721Proxy = await erc721Wrapper.deployProxyAsync();
|
||||
|
||||
zrxAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address);
|
||||
exchange = await ExchangeContract.deployFrom0xArtifactAsync(
|
||||
exchangeArtifacts.Exchange,
|
||||
provider,
|
||||
txDefaults,
|
||||
zrxAssetData,
|
||||
);
|
||||
|
||||
multiAssetProxy = await MultiAssetProxyContract.deployFrom0xArtifactAsync(
|
||||
proxyArtifacts.MultiAssetProxy,
|
||||
provider,
|
||||
txDefaults,
|
||||
);
|
||||
const exchangeWrapper = new ExchangeWrapper(exchange, provider);
|
||||
await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, owner);
|
||||
await exchangeWrapper.registerAssetProxyAsync(erc721Proxy.address, owner);
|
||||
await exchangeWrapper.registerAssetProxyAsync(multiAssetProxy.address, owner);
|
||||
await erc20Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(exchange.address, { from: owner });
|
||||
await erc721Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(exchange.address, { from: owner });
|
||||
|
||||
devUtils = await DevUtilsContract.deployFrom0xArtifactAsync(
|
||||
artifacts.DevUtils,
|
||||
provider,
|
||||
txDefaults,
|
||||
exchange.address,
|
||||
zrxAssetData,
|
||||
);
|
||||
|
||||
erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20Token.address);
|
||||
erc20AssetData2 = assetDataUtils.encodeERC20AssetData(erc20Token2.address);
|
||||
erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, tokenId);
|
||||
const defaultOrderParams = {
|
||||
...constants.STATIC_ORDER_PARAMS,
|
||||
exchangeAddress: exchange.address,
|
||||
makerAddress,
|
||||
feeRecipientAddress: constants.NULL_ADDRESS,
|
||||
makerAssetData: erc20AssetData,
|
||||
takerAssetData: erc20AssetData2,
|
||||
};
|
||||
const privateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)];
|
||||
orderFactory = new OrderFactory(privateKey, defaultOrderParams);
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
afterEach(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
|
||||
describe('getTransferableAssetAmount', () => {
|
||||
it('should return the balance when balance < allowance', async () => {
|
||||
const balance = new BigNumber(123);
|
||||
const allowance = new BigNumber(456);
|
||||
await erc20Token.setBalance.awaitTransactionSuccessAsync(makerAddress, balance);
|
||||
await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, allowance, {
|
||||
from: makerAddress,
|
||||
});
|
||||
const transferableAmount = await devUtils.getTransferableAssetAmount.callAsync(
|
||||
makerAddress,
|
||||
erc20AssetData,
|
||||
);
|
||||
expect(transferableAmount).to.bignumber.equal(balance);
|
||||
});
|
||||
it('should return the allowance when allowance < balance', async () => {
|
||||
const balance = new BigNumber(456);
|
||||
const allowance = new BigNumber(123);
|
||||
await erc20Token.setBalance.awaitTransactionSuccessAsync(makerAddress, balance);
|
||||
await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, allowance, {
|
||||
from: makerAddress,
|
||||
});
|
||||
const transferableAmount = await devUtils.getTransferableAssetAmount.callAsync(
|
||||
makerAddress,
|
||||
erc20AssetData,
|
||||
);
|
||||
expect(transferableAmount).to.bignumber.equal(allowance);
|
||||
});
|
||||
it('should return the correct transferable amount for multiAssetData', async () => {
|
||||
const multiAssetData = assetDataUtils.encodeMultiAssetData(
|
||||
[new BigNumber(1), new BigNumber(1)],
|
||||
[erc20AssetData, erc20AssetData2],
|
||||
);
|
||||
const transferableAmount1 = new BigNumber(10);
|
||||
const transferableAmount2 = new BigNumber(5);
|
||||
await erc20Token.setBalance.awaitTransactionSuccessAsync(makerAddress, transferableAmount1);
|
||||
await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, transferableAmount1, {
|
||||
from: makerAddress,
|
||||
});
|
||||
await erc20Token2.setBalance.awaitTransactionSuccessAsync(makerAddress, transferableAmount2);
|
||||
await erc20Token2.approve.awaitTransactionSuccessAsync(erc20Proxy.address, transferableAmount2, {
|
||||
from: makerAddress,
|
||||
});
|
||||
const transferableAmount = await devUtils.getTransferableAssetAmount.callAsync(
|
||||
makerAddress,
|
||||
multiAssetData,
|
||||
);
|
||||
expect(transferableAmount).to.bignumber.equal(transferableAmount2);
|
||||
});
|
||||
});
|
||||
describe('getOrderRelevantState', () => {
|
||||
beforeEach(async () => {
|
||||
signedOrder = await orderFactory.newSignedOrderAsync();
|
||||
});
|
||||
it('should return the correct orderInfo when the order is valid', async () => {
|
||||
const [orderInfo] = await devUtils.getOrderRelevantState.callAsync(signedOrder, signedOrder.signature);
|
||||
expect(orderInfo.orderHash).to.equal(orderHashUtils.getOrderHashHex(signedOrder));
|
||||
expect(orderInfo.orderStatus).to.equal(OrderStatus.Fillable);
|
||||
expect(orderInfo.orderTakerAssetFilledAmount).to.bignumber.equal(constants.ZERO_AMOUNT);
|
||||
});
|
||||
it('should return isValidSignature=true when the signature is valid', async () => {
|
||||
const [, , isValidSignature] = await devUtils.getOrderRelevantState.callAsync(
|
||||
signedOrder,
|
||||
signedOrder.signature,
|
||||
);
|
||||
expect(isValidSignature).to.equal(true);
|
||||
});
|
||||
it('should return isValidSignature=false when the signature is invalid', async () => {
|
||||
const invalidSignature = '0x01';
|
||||
const [, , isValidSignature] = await devUtils.getOrderRelevantState.callAsync(
|
||||
signedOrder,
|
||||
invalidSignature,
|
||||
);
|
||||
expect(isValidSignature).to.equal(false);
|
||||
});
|
||||
it('should return a fillableTakerAssetAmount of 0 when balances or allowances are insufficient', async () => {
|
||||
const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState.callAsync(
|
||||
signedOrder,
|
||||
signedOrder.signature,
|
||||
);
|
||||
expect(fillableTakerAssetAmount).to.bignumber.equal(constants.ZERO_AMOUNT);
|
||||
});
|
||||
it('should return a fillableTakerAssetAmount of 0 when fee balances/allowances are insufficient', async () => {
|
||||
await erc20Token.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerAssetAmount);
|
||||
await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerAssetAmount, {
|
||||
from: makerAddress,
|
||||
});
|
||||
const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState.callAsync(
|
||||
signedOrder,
|
||||
signedOrder.signature,
|
||||
);
|
||||
expect(fillableTakerAssetAmount).to.bignumber.equal(constants.ZERO_AMOUNT);
|
||||
});
|
||||
it('should return a fillableTakerAssetAmount of 0 when balances/allowances of one asset within a multiAssetData are insufficient', async () => {
|
||||
const multiAssetData = assetDataUtils.encodeMultiAssetData(
|
||||
[new BigNumber(1), new BigNumber(1)],
|
||||
[erc20AssetData, erc20AssetData2],
|
||||
);
|
||||
signedOrder = await orderFactory.newSignedOrderAsync({ makerAssetData: multiAssetData });
|
||||
await erc20Token.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerAssetAmount);
|
||||
await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerAssetAmount, {
|
||||
from: makerAddress,
|
||||
});
|
||||
const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState.callAsync(
|
||||
signedOrder,
|
||||
signedOrder.signature,
|
||||
);
|
||||
expect(fillableTakerAssetAmount).to.bignumber.equal(constants.ZERO_AMOUNT);
|
||||
});
|
||||
it('should return the correct fillableTakerAssetAmount when fee balances/allowances are partially sufficient', async () => {
|
||||
await erc20Token.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerAssetAmount);
|
||||
await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerAssetAmount, {
|
||||
from: makerAddress,
|
||||
});
|
||||
const divisor = 4;
|
||||
await zrxToken.setBalance.awaitTransactionSuccessAsync(
|
||||
makerAddress,
|
||||
signedOrder.makerFee.dividedToIntegerBy(divisor),
|
||||
);
|
||||
await zrxToken.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerFee, {
|
||||
from: makerAddress,
|
||||
});
|
||||
const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState.callAsync(
|
||||
signedOrder,
|
||||
signedOrder.signature,
|
||||
);
|
||||
expect(fillableTakerAssetAmount).to.bignumber.equal(
|
||||
signedOrder.takerAssetAmount.dividedToIntegerBy(divisor),
|
||||
);
|
||||
});
|
||||
it('should return the correct fillableTakerAssetAmount when non-fee balances/allowances are partially sufficient', async () => {
|
||||
const divisor = 4;
|
||||
await erc20Token.setBalance.awaitTransactionSuccessAsync(
|
||||
makerAddress,
|
||||
signedOrder.makerAssetAmount.dividedToIntegerBy(divisor),
|
||||
);
|
||||
await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerAssetAmount, {
|
||||
from: makerAddress,
|
||||
});
|
||||
await zrxToken.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerFee);
|
||||
await zrxToken.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerFee, {
|
||||
from: makerAddress,
|
||||
});
|
||||
const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState.callAsync(
|
||||
signedOrder,
|
||||
signedOrder.signature,
|
||||
);
|
||||
expect(fillableTakerAssetAmount).to.bignumber.equal(
|
||||
signedOrder.takerAssetAmount.dividedToIntegerBy(divisor),
|
||||
);
|
||||
});
|
||||
it('should return the correct fillableTakerAssetAmount when balances/allowances of one asset within a multiAssetData are partially sufficient', async () => {
|
||||
const multiAssetData = assetDataUtils.encodeMultiAssetData(
|
||||
[new BigNumber(1), new BigNumber(1)],
|
||||
[erc20AssetData, erc20AssetData2],
|
||||
);
|
||||
signedOrder = await orderFactory.newSignedOrderAsync({ makerAssetData: multiAssetData });
|
||||
await erc20Token.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerAssetAmount);
|
||||
await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerAssetAmount, {
|
||||
from: makerAddress,
|
||||
});
|
||||
await zrxToken.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerFee);
|
||||
await zrxToken.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerFee, {
|
||||
from: makerAddress,
|
||||
});
|
||||
const divisor = 4;
|
||||
await erc20Token2.setBalance.awaitTransactionSuccessAsync(
|
||||
makerAddress,
|
||||
signedOrder.makerAssetAmount.dividedToIntegerBy(divisor),
|
||||
);
|
||||
await erc20Token2.approve.awaitTransactionSuccessAsync(
|
||||
erc20Proxy.address,
|
||||
signedOrder.makerAssetAmount.dividedToIntegerBy(divisor),
|
||||
{
|
||||
from: makerAddress,
|
||||
},
|
||||
);
|
||||
const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState.callAsync(
|
||||
signedOrder,
|
||||
signedOrder.signature,
|
||||
);
|
||||
expect(fillableTakerAssetAmount).to.bignumber.equal(
|
||||
signedOrder.takerAssetAmount.dividedToIntegerBy(divisor),
|
||||
);
|
||||
});
|
||||
it('should return a fillableTakerAssetAmount of 0 when non-fee balances/allowances are insufficient', async () => {
|
||||
await zrxToken.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerFee);
|
||||
await zrxToken.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerFee, {
|
||||
from: makerAddress,
|
||||
});
|
||||
const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState.callAsync(
|
||||
signedOrder,
|
||||
signedOrder.signature,
|
||||
);
|
||||
expect(fillableTakerAssetAmount).to.bignumber.equal(constants.ZERO_AMOUNT);
|
||||
});
|
||||
it('should return a fillableTakerAssetAmount equal to the takerAssetAmount when the order is unfilled and balances/allowances are sufficient', async () => {
|
||||
await erc20Token.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerAssetAmount);
|
||||
await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerAssetAmount, {
|
||||
from: makerAddress,
|
||||
});
|
||||
await zrxToken.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerFee);
|
||||
await zrxToken.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerFee, {
|
||||
from: makerAddress,
|
||||
});
|
||||
const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState.callAsync(
|
||||
signedOrder,
|
||||
signedOrder.signature,
|
||||
);
|
||||
expect(fillableTakerAssetAmount).to.bignumber.equal(signedOrder.takerAssetAmount);
|
||||
});
|
||||
it('should return the correct fillableTakerAssetAmount when balances/allowances are partially sufficient and makerAsset=makerFeeAsset', async () => {
|
||||
signedOrder = await orderFactory.newSignedOrderAsync({
|
||||
makerAssetData: zrxAssetData,
|
||||
makerAssetAmount: new BigNumber(10),
|
||||
takerAssetAmount: new BigNumber(20),
|
||||
makerFee: new BigNumber(40),
|
||||
});
|
||||
const transferableMakerAssetAmount = new BigNumber(10);
|
||||
await zrxToken.setBalance.awaitTransactionSuccessAsync(makerAddress, transferableMakerAssetAmount);
|
||||
await zrxToken.approve.awaitTransactionSuccessAsync(erc20Proxy.address, transferableMakerAssetAmount, {
|
||||
from: makerAddress,
|
||||
});
|
||||
const expectedFillableTakerAssetAmount = transferableMakerAssetAmount
|
||||
.times(signedOrder.takerAssetAmount)
|
||||
.dividedToIntegerBy(signedOrder.makerAssetAmount.plus(signedOrder.makerFee));
|
||||
const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState.callAsync(
|
||||
signedOrder,
|
||||
signedOrder.signature,
|
||||
);
|
||||
expect(fillableTakerAssetAmount).to.bignumber.equal(expectedFillableTakerAssetAmount);
|
||||
});
|
||||
it('should return the correct fillabeTakerassetAmount when makerAsset balances/allowances are sufficient and there are no maker fees', async () => {
|
||||
signedOrder = await orderFactory.newSignedOrderAsync({ makerFee: constants.ZERO_AMOUNT });
|
||||
await erc20Token.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerAssetAmount);
|
||||
await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerAssetAmount, {
|
||||
from: makerAddress,
|
||||
});
|
||||
const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState.callAsync(
|
||||
signedOrder,
|
||||
signedOrder.signature,
|
||||
);
|
||||
expect(fillableTakerAssetAmount).to.bignumber.equal(signedOrder.takerAssetAmount);
|
||||
});
|
||||
it('should return a fillableTakerAssetAmount when the remaining takerAssetAmount is less than the transferable amount', async () => {
|
||||
await erc20Token.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerAssetAmount);
|
||||
await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerAssetAmount, {
|
||||
from: makerAddress,
|
||||
});
|
||||
await zrxToken.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerFee);
|
||||
await zrxToken.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerFee, {
|
||||
from: makerAddress,
|
||||
});
|
||||
await erc20Token2.setBalance.awaitTransactionSuccessAsync(takerAddress, signedOrder.takerAssetAmount);
|
||||
await erc20Token2.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.takerAssetAmount, {
|
||||
from: takerAddress,
|
||||
});
|
||||
await zrxToken.setBalance.awaitTransactionSuccessAsync(takerAddress, signedOrder.takerFee);
|
||||
|
||||
await zrxToken.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.takerFee, {
|
||||
from: takerAddress,
|
||||
});
|
||||
const takerAssetFillAmount = signedOrder.takerAssetAmount.dividedToIntegerBy(4);
|
||||
await exchange.fillOrder.awaitTransactionSuccessAsync(
|
||||
signedOrder,
|
||||
takerAssetFillAmount,
|
||||
signedOrder.signature,
|
||||
{ from: takerAddress },
|
||||
);
|
||||
const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState.callAsync(
|
||||
signedOrder,
|
||||
signedOrder.signature,
|
||||
);
|
||||
expect(fillableTakerAssetAmount).to.bignumber.equal(
|
||||
signedOrder.takerAssetAmount.minus(takerAssetFillAmount),
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('getOrderRelevantStates', async () => {
|
||||
it('should return the correct information for multiple orders', async () => {
|
||||
await erc20Token.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerAssetAmount);
|
||||
await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerAssetAmount, {
|
||||
from: makerAddress,
|
||||
});
|
||||
await zrxToken.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerFee);
|
||||
await zrxToken.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerFee, {
|
||||
from: makerAddress,
|
||||
});
|
||||
const signedOrder2 = await orderFactory.newSignedOrderAsync({ makerAssetData: erc721AssetData });
|
||||
const invalidSignature = '0x01';
|
||||
await exchange.cancelOrder.awaitTransactionSuccessAsync(signedOrder2, { from: makerAddress });
|
||||
const [
|
||||
ordersInfo,
|
||||
fillableTakerAssetAmounts,
|
||||
isValidSignature,
|
||||
] = await devUtils.getOrderRelevantStates.callAsync(
|
||||
[signedOrder, signedOrder2],
|
||||
[signedOrder.signature, invalidSignature],
|
||||
);
|
||||
expect(ordersInfo[0].orderHash).to.equal(orderHashUtils.getOrderHashHex(signedOrder));
|
||||
expect(ordersInfo[1].orderHash).to.equal(orderHashUtils.getOrderHashHex(signedOrder2));
|
||||
expect(ordersInfo[0].orderStatus).to.equal(OrderStatus.Fillable);
|
||||
expect(ordersInfo[1].orderStatus).to.equal(OrderStatus.Cancelled);
|
||||
expect(ordersInfo[0].orderTakerAssetFilledAmount).to.bignumber.equal(constants.ZERO_AMOUNT);
|
||||
expect(ordersInfo[1].orderTakerAssetFilledAmount).to.bignumber.equal(constants.ZERO_AMOUNT);
|
||||
expect(fillableTakerAssetAmounts[0]).to.bignumber.equal(signedOrder.takerAssetAmount);
|
||||
expect(fillableTakerAssetAmounts[1]).to.bignumber.equal(constants.ZERO_AMOUNT);
|
||||
expect(isValidSignature[0]).to.equal(true);
|
||||
expect(isValidSignature[1]).to.equal(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
// tslint:disable:max-file-line-count
|
12
contracts/dev-utils/tsconfig.json
Normal file
12
contracts/dev-utils/tsconfig.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"extends": "../../tsconfig",
|
||||
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
|
||||
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
|
||||
"files": [
|
||||
"generated-artifacts/DevUtils.json",
|
||||
"generated-artifacts/LibAssetData.json",
|
||||
"generated-artifacts/LibTransactionDecoder.json",
|
||||
"generated-artifacts/EthBalanceChecker.json"
|
||||
],
|
||||
"exclude": ["./deploy/solc/solc_bin"]
|
||||
}
|
6
contracts/dev-utils/tslint.json
Normal file
6
contracts/dev-utils/tslint.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"extends": ["@0x/tslint-config"],
|
||||
"rules": {
|
||||
"custom-no-magic-numbers": false
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user