Compare commits
	
		
			832 Commits
		
	
	
		
			@0x/abi-ge
			...
			@0x/abi-ge
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | c78a602990 | ||
|  | 0acec57ba9 | ||
|  | 7423028fea | ||
|  | 49d951b7be | ||
|  | a554c9518d | ||
|  | f3c7ba445e | ||
|  | 3abff41385 | ||
|  | 6c36832f0e | ||
|  | 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 | ||
|  | e043735362 | ||
|  | 7010b1adb9 | ||
|  | 17ff262729 | ||
|  | 1c18838cd8 | ||
|  | 55b87ae78d | ||
|  | e446e902f3 | ||
|  | 095b52e0b2 | ||
|  | 4f25ff6a50 | ||
|  | 6242e0aeec | ||
|  | fde9fc9dd4 | ||
|  | c481e42673 | ||
|  | 5e228d7232 | ||
|  | 6b8e40fdc9 | ||
|  | bca44bf9e3 | ||
|  | 08e49dcf2e | ||
|  | 0f45409b4d | ||
|  | 5469d3ec13 | ||
|  | d12f8410b9 | ||
|  | 704c52d229 | ||
|  | 7dcdda14f5 | ||
|  | 0f59256ca7 | ||
|  | ddee04e98c | ||
|  | 50f5002b71 | ||
|  | e866add4b0 | ||
|  | 39b93b88c5 | ||
|  | deb7e95567 | ||
|  | 91500501ce | ||
|  | 6a1caeb9a1 | ||
|  | cee7803c37 | ||
|  | 4e8d0ac7cb | ||
|  | 47bbcb9935 | ||
|  | c0288c5f26 | ||
|  | 18c2013625 | ||
|  | 548089888d | ||
|  | 180c65cfeb | ||
|  | 6a28e41bc8 | ||
|  | f9ef942a98 | ||
|  | 8272c7a74e | ||
|  | 4e745489db | ||
|  | fc2625a0c0 | ||
|  | d0e43ebaf1 | ||
|  | 879805e316 | ||
|  | 6845d2e0ef | ||
|  | 542d1c1c41 | ||
|  | 31568e7abb | ||
|  | 4b2488b124 | ||
|  | d4c37ecfa3 | ||
|  | 3f9fd7c060 | ||
|  | 6ed1412bdd | ||
|  | 78b9a45158 | ||
|  | b8925baa88 | ||
|  | 87fd3f2a82 | ||
|  | 0490ef5900 | ||
|  | 8af164dbd2 | ||
|  | c994afbf3c | ||
|  | fa36c91bd6 | ||
|  | 843caf86fb | ||
|  | 5b8f294aaf | ||
|  | 44eef5b0e0 | ||
|  | aad75840d4 | ||
|  | 31faef7030 | ||
|  | 941877a05a | ||
|  | 4fd4c1e8e1 | ||
|  | 7cd27cd9c8 | ||
|  | 54dbef2b4f | ||
|  | 05424c9f33 | ||
|  | 346c6fc590 | ||
|  | 7ed3afe9f0 | ||
|  | d29ed6cd49 | ||
|  | 8c3abf3473 | ||
|  | 384114d3c7 | ||
|  | e8be5a0a8f | ||
|  | 98c6fa10e6 | ||
|  | 30d0bdec07 | ||
|  | 7024f29865 | ||
|  | 29ebed9514 | ||
|  | c0260bc44a | ||
|  | 6992bff0e0 | ||
|  | 6957e6e8f7 | ||
|  | 31dbca7efc | ||
|  | 82b6dad1ba | ||
|  | 5063c17e6b | ||
|  | 3fff3d9c60 | ||
|  | 2fbe0aed32 | ||
|  | 3bdc1802cb | ||
|  | 2675833b0d | ||
|  | 939a5b477a | ||
|  | dc57e7a5b3 | ||
|  | a401b8f475 | ||
|  | b008fabdac | ||
|  | caf286b8cb | ||
|  | aea278c022 | ||
|  | 4e20fe1602 | ||
|  | 849ca58228 | ||
|  | 9991fca2e5 | ||
|  | 9c7bdcfeef | ||
|  | b5463d522b | ||
|  | af0d830103 | ||
|  | d4187dffa3 | ||
|  | a7f06f2be5 | ||
|  | 7ff7d1a185 | ||
|  | d3ab612a89 | ||
|  | e3bc80e027 | ||
|  | a8720806f1 | ||
|  | 5aac4c2e5d | ||
|  | 6fa645aab9 | ||
|  | 842ea4645b | ||
|  | e1cfbcd4f6 | ||
|  | 6f17ff55fa | ||
|  | d88af4dfa6 | ||
|  | bb0ba21e92 | ||
|  | f0753c8e58 | ||
|  | 4c55004b08 | ||
|  | ae24119c09 | ||
|  | b8753d8f20 | ||
|  | bab368b956 | ||
|  | 22bc1fb21e | ||
|  | 63ba764de8 | ||
|  | 6a7530d741 | ||
|  | eb9bf7c4f9 | ||
|  | b6571d0ca3 | ||
|  | 9207d1c680 | ||
|  | f783c9bb25 | ||
|  | df5786deda | ||
|  | de971e6c46 | ||
|  | 2a6ed0c96e | ||
|  | 9ec380777a | ||
|  | 8916d0d367 | ||
|  | 1e5648111e | ||
|  | ae51cfe8b9 | ||
|  | 05ef250ab4 | ||
|  | 38e4871f32 | ||
|  | a86ba7af2e | ||
|  | 5704afc54c | ||
|  | 55c4fc9aca | ||
|  | 888c17353b | ||
|  | 054c0e91a3 | ||
|  | 4dfb610eba | ||
|  | 008eb8dd8b | ||
|  | 2882c4bb89 | ||
|  | 4473851f5b | ||
|  | 15d9e2d3d5 | ||
|  | d5d9df383e | ||
|  | 243a04b756 | ||
|  | b77dcbd39b | ||
|  | ad5d4bdfc5 | ||
|  | 54c17b0068 | ||
|  | 616907eff8 | ||
|  | afaabb3673 | ||
|  | 1c1f625352 | ||
|  | d3f45d2148 | ||
|  | 7326dbd108 | ||
|  | 4bf311a282 | ||
|  | 7f5a3f12ca | ||
|  | 6a9b71466d | ||
|  | 70e550a25f | ||
|  | 1328882ab6 | ||
|  | b378a0608d | ||
|  | e28c6d6f9c | ||
|  | f57f29e426 | ||
|  | f2857452e3 | ||
|  | af04c294b9 | ||
|  | 65c8630534 | ||
|  | 095882d016 | ||
|  | 1693506f80 | ||
|  | 667c22169f | ||
|  | c53edf6bd9 | ||
|  | b601220845 | ||
|  | e77a608f45 | ||
|  | 2822e77716 | ||
|  | 98227928af | ||
|  | 430afbdc80 | ||
|  | dfdb48ce7d | ||
|  | 257d1b2b52 | ||
|  | 07200437b6 | ||
|  | ebfa00d555 | ||
|  | 421f555d57 | ||
|  | 4b60d941cc | ||
|  | 60d24ada62 | ||
|  | a1c121e2fe | ||
|  | b3775a3ca5 | ||
|  | 5d36c97a46 | ||
|  | 4bdd412c15 | ||
|  | 46e3bcd6b9 | ||
|  | 68a85ddf90 | ||
|  | 6eba9273cb | ||
|  | 9de151cc14 | ||
|  | b8c9a5dd1f | ||
|  | 4dfb3507c4 | ||
|  | 0cac2d407b | ||
|  | b3106cd932 | ||
|  | c4a467fa96 | ||
|  | cf7afbe7f6 | ||
|  | a8d58aeac4 | ||
|  | 28209e9413 | ||
|  | 68ed56f2d9 | ||
|  | 0c03747807 | ||
|  | dfe7ecbb5b | ||
|  | 3039d8edfa | ||
|  | 7c850cc082 | ||
|  | 0e07ee3d81 | ||
|  | 14b820f2c1 | ||
|  | 9366fa3b45 | ||
|  | d356e9e65f | ||
|  | 230ebffd0e | ||
|  | 5eedc1edca | ||
|  | fa6db9411b | ||
|  | b88e42a52d | ||
|  | 8d1b27d130 | ||
|  | 1148d37102 | ||
|  | 88704ce417 | ||
|  | 9f69d2cb76 | ||
|  | 644f54bfba | ||
|  | 755c4da8cc | ||
|  | 483c77fba8 | ||
|  | 55fd71c5e1 | ||
|  | 1991bd437f | ||
|  | 22af796302 | ||
|  | a70931ffbf | ||
|  | 2f7dd177c1 | ||
|  | d35a053efd | ||
|  | 87cc1f9415 | ||
|  | e7bb524362 | ||
|  | 9e03e1c742 | ||
|  | 6fbfcef1fa | ||
|  | 9471510086 | ||
|  | 56320468fd | ||
|  | 53a70bbffb | ||
|  | 4f2e547bd6 | ||
|  | af82fa7b62 | ||
|  | 6bd1a5ab80 | ||
|  | 93568f6fd0 | ||
|  | 1a3bf81c19 | ||
|  | 2eeabe3998 | ||
|  | b1d86a7a2d | ||
|  | 6cfc9ba47b | ||
|  | 350474540b | ||
|  | 1136e58de7 | ||
|  | 558ce4713c | ||
|  | 69c6f50dfb | ||
|  | babe01321c | ||
|  | ff61dc4391 | ||
|  | 4da6ede9d0 | ||
|  | 0b1bab873e | ||
|  | 6c60341ce8 | ||
|  | e1c1878130 | ||
|  | fea5a39740 | ||
|  | 9f2221a885 | ||
|  | 977cd2505e | ||
|  | e3f85a7c0f | ||
|  | d81339722e | ||
|  | f5c09e02de | ||
|  | 5e102526ec | ||
|  | 3c2efd4b67 | ||
|  | 7bb93a7c32 | ||
|  | ac8f4ae2f9 | ||
|  | 1ff49188b1 | ||
|  | 2c329023c2 | ||
|  | 4457a300bf | ||
|  | 397496aff0 | ||
|  | 7458fe0d81 | ||
|  | 453c81f634 | ||
|  | eebce4b54d | ||
|  | f7976e18f1 | ||
|  | d951fe9988 | ||
|  | fd4da78075 | ||
|  | a025ae3f54 | ||
|  | a75ba0d903 | ||
|  | 99318ae2ba | ||
|  | 8369bcb605 | ||
|  | 338cc69034 | ||
|  | 4526c52fe8 | ||
|  | 81f9bda502 | ||
|  | 46bc5463ca | ||
|  | bb346537ba | ||
|  | 3b5f0d5c30 | ||
|  | 808ce969d9 | ||
|  | 16f8339f3c | ||
|  | 25d68c3904 | ||
|  | 77ad8e1a80 | ||
|  | 9e8c18075a | ||
|  | 4278cdfd29 | ||
|  | 142c2bd0f0 | ||
|  | 13ee8686bb | ||
|  | 5c06df2635 | ||
|  | d69bf76341 | ||
|  | d0d1b295b4 | ||
|  | e450191548 | ||
|  | 3aee83f3d8 | ||
|  | 3610a2bc8d | ||
|  | ab559d4620 | ||
|  | 15d308d4c5 | ||
|  | e7ea66afb5 | ||
|  | d9a1d8bde6 | ||
|  | 9ac4486403 | ||
|  | 428afabaa3 | ||
|  | 9162189fa6 | ||
|  | 154ca9b760 | ||
|  | 9fcead3973 | ||
|  | 2fd9d0359c | ||
|  | 4cf9e030a2 | ||
|  | 5570d14179 | ||
|  | fd3c546994 | ||
|  | e81ae05df9 | ||
|  | cb394f3a1c | ||
|  | 03ed057ff6 | ||
|  | 807290ff38 | ||
|  | 40a4b4fa7a | ||
|  | b7d2ad3651 | ||
|  | faac286f70 | ||
|  | c6c7f6f907 | ||
|  | ccd0da58cb | ||
|  | 500e5f1b5d | ||
|  | ebb6177271 | ||
|  | 951c256980 | ||
|  | a134ef03dd | ||
|  | f21a9d16d0 | ||
|  | 42c3bb00ec | ||
|  | 0d0fcfe49a | ||
|  | 3f0db92be6 | ||
|  | ec24c79da1 | ||
|  | f5fffbea04 | ||
|  | fb0a2ef248 | ||
|  | 240f482e8e | ||
|  | 885031d3ce | ||
|  | eb212de70e | ||
|  | 68323d6def | ||
|  | 7c492071f1 | ||
|  | 8fbdef2a1d | ||
|  | 8f64784781 | ||
|  | 1bd6095c60 | ||
|  | e133a5f0f3 | ||
|  | b47886416e | ||
|  | c522d611d1 | ||
|  | f6d9b6b7aa | ||
|  | 1de6bca12d | ||
|  | 2de9b862d8 | ||
|  | 3c649df3df | ||
|  | 9bf38d9e4d | ||
|  | dee40f038d | ||
|  | d0aa907418 | ||
|  | 8dbdffc9b4 | ||
|  | f409780455 | ||
|  | bebcd99b3b | ||
|  | 99aeaddf42 | ||
|  | 793398216f | ||
|  | b353ed3157 | ||
|  | 8637212a17 | ||
|  | c42ce38e1c | ||
|  | 76d228a603 | ||
|  | 5cb52faa10 | ||
|  | e67e822845 | ||
|  | a52686ca3b | ||
|  | 7a1e6cccfd | ||
|  | 1166b6c2fb | ||
|  | 7a6693694c | ||
|  | 6ed423d1af | ||
|  | 8b69444602 | ||
|  | 7de5e8d9c8 | ||
|  | 844e3d1934 | ||
|  | 9eafbbc0ae | ||
|  | 74677e3d54 | ||
|  | 57f4638742 | ||
|  | c7a32f2d56 | ||
|  | d2dc64aa2d | ||
|  | edb2a34c51 | ||
|  | dbd9b1c5c4 | ||
|  | c3b758845d | ||
|  | 4d770549fc | ||
|  | a67674bae1 | ||
|  | 462a61da73 | ||
|  | ae8a7f6320 | ||
|  | a23c6a0996 | ||
|  | e6594cecce | ||
|  | 85ed5d27f5 | ||
|  | 6ac9e11245 | ||
|  | 9fbd809344 | ||
|  | 63a098d757 | ||
|  | b68d9ed672 | ||
|  | 5934eb0559 | ||
|  | b998d719a6 | ||
|  | 33832244cf | ||
|  | df8ea3b65c | ||
|  | 7067d4a499 | ||
|  | 3e764eafd9 | ||
|  | 4169a41eda | ||
|  | 379eab19ad | ||
|  | 6cba1f3b61 | ||
|  | 5dad2404d3 | ||
|  | a1b20eea2c | ||
|  | af075e7b8b | ||
|  | ef0a699919 | ||
|  | 8279ef0e05 | ||
|  | c1cee8e951 | ||
|  | 4c17373311 | ||
|  | c20972de3f | ||
|  | 64401f1031 | ||
|  | 1f895d0f27 | ||
|  | 5aa8ea10c3 | ||
|  | cf65d4a909 | ||
|  | 079f627b34 | ||
|  | f6c6cbc343 | ||
|  | c6cdea77b6 | ||
|  | b1fd3429ae | ||
|  | dc161ef08d | ||
|  | 9502e789b3 | ||
|  | a71aed0233 | ||
|  | eda8dc278b | ||
|  | 23a7ffb167 | ||
|  | ab11aaa694 | ||
|  | 83b5a2709d | ||
|  | f67f94fd6b | ||
|  | 4e8ae037bb | ||
|  | 04d8f46ff3 | ||
|  | 3191de68b8 | ||
|  | 047de370d6 | ||
|  | e21dc5f026 | ||
|  | f3da56773e | ||
|  | 5197758579 | ||
|  | d48af7c4c2 | ||
|  | bbafe0fc46 | ||
|  | 9a91f917e0 | ||
|  | 1f681f02ae | ||
|  | 778a86e7eb | ||
|  | 78da033441 | ||
|  | 6eb923d22f | ||
|  | e602afcd5f | ||
|  | bf1115d417 | ||
|  | b51038b9e7 | ||
|  | 44e0b58c66 | ||
|  | 3f7d5274a2 | ||
|  | c8f5f6045e | ||
|  | 6bcc2064a2 | ||
|  | 3f9872d8b3 | ||
|  | 5814cd5fae | ||
|  | 44f8c7c548 | ||
|  | c2549308fe | ||
|  | cf1a3bf4e7 | ||
|  | f5d668af31 | ||
|  | b7bd4e5c51 | ||
|  | 62373f969c | ||
|  | ceb651fdd1 | ||
|  | 8fa0f4a232 | ||
|  | 30f2e3b606 | ||
|  | 7d3d997083 | ||
|  | 3955e2c84a | ||
|  | 6a6c41df26 | ||
|  | 1dfd2aec50 | ||
|  | 8885f543ae | ||
|  | 8804e6c2ca | ||
|  | 88b7c214f7 | ||
|  | 297f3da5db | ||
|  | ec4d634677 | ||
|  | 0ddec99704 | ||
|  | 64cec1de3b | ||
|  | fdaf5d1fc8 | ||
|  | 6baf71dca5 | ||
|  | 9d75a72a02 | ||
|  | 4bf66d6a63 | ||
|  | 8d9a550107 | ||
|  | a076278026 | ||
|  | 998dac44ee | ||
|  | 871e81bd71 | ||
|  | d892d16b51 | ||
|  | 4cbf2781e5 | ||
|  | 69653b1c7c | ||
|  | 39390865da | ||
|  | 4952cd5afb | ||
|  | e86df96bfe | ||
|  | ef5195db90 | ||
|  | bc7e62db9f | ||
|  | 9a7ccc20e8 | ||
|  | 154e7a6a8a | ||
|  | ba56019f2d | ||
|  | 31d289c504 | ||
|  | a42b22966a | ||
|  | 17db62d463 | ||
|  | d6d40ea839 | ||
|  | eca8410dbb | ||
|  | eb3a530e6c | ||
|  | 26e17074c2 | ||
|  | 6b775628d5 | ||
|  | 13eb9103a7 | ||
|  | 1fa82c1077 | ||
|  | 426190181b | ||
|  | 1232a9a03d | ||
|  | e643c13292 | ||
|  | 522736e367 | ||
|  | 12fd9c29f0 | ||
|  | 0c871b67ca | ||
|  | 379e828fc1 | ||
|  | fe1e8575ea | ||
|  | 8f14099adc | ||
|  | 852c1396ae | ||
|  | b0f9195b57 | ||
|  | 98236736e0 | ||
|  | 8b93a2816c | ||
|  | 76d8a991c0 | ||
|  | 9186af1433 | ||
|  | 6a473f4c57 | ||
|  | 42224ab534 | ||
|  | 20bcf09459 | ||
|  | 5d1dd536e9 | ||
|  | 169b8807f7 | ||
|  | 5acdab849e | ||
|  | 2417084145 | ||
|  | c16e62b5bf | ||
|  | 6fa7d90f1f | ||
|  | 1a3fd0227d | ||
|  | 2259b7dfcf | ||
|  | 33fd70d0e1 | ||
|  | fc906bcaae | ||
|  | 184e111e39 | ||
|  | ab62f50f5c | ||
|  | 3eac854356 | ||
|  | f7892cd86b | ||
|  | 6e0260385c | ||
|  | 44300dabb0 | ||
|  | 580c3038c8 | ||
|  | 2a9175e964 | ||
|  | 5db20bbbe8 | ||
|  | ae144dff39 | ||
|  | 9f533efc92 | ||
|  | 76e15c4166 | ||
|  | 62bb2daf16 | ||
|  | 59e292030a | ||
|  | 533fc7e444 | ||
|  | e5c5338eb6 | ||
|  | 9463acb131 | ||
|  | e69749350b | ||
|  | 42749187ff | ||
|  | d5c2038d58 | ||
|  | 5843a61c9f | ||
|  | ecde2c9d7f | ||
|  | abd597c7a6 | ||
|  | b35e1d350b | ||
|  | 918399458d | ||
|  | 72d9b7028b | ||
|  | ed9790c840 | ||
|  | fd04bc4820 | ||
|  | 58a213b7f9 | ||
|  | 7f8bbfb4bd | ||
|  | 8a3e64b3cd | ||
|  | 572de86d06 | ||
|  | 88c33fa4d4 | ||
|  | ee1c6a1ce1 | ||
|  | 09e90bea8b | ||
|  | bf758412c1 | ||
|  | 0bbbf5b431 | ||
|  | 87b2236cfb | ||
|  | 855d2bb87f | ||
|  | 13b54a3b4d | ||
|  | 5d24ef4849 | ||
|  | b2da451e8d | ||
|  | 139b66fa76 | ||
|  | 390ed5d7f1 | ||
|  | 16b5fb6b56 | ||
|  | 6205d6c885 | ||
|  | e5ea65da82 | ||
|  | 9caa5feff5 | ||
|  | 7f616356d2 | ||
|  | 155b5dd42f | ||
|  | 41aaf428bf | ||
|  | a17cdf33ea | ||
|  | 3d5e879d5f | ||
|  | 1a73ad3a32 | ||
|  | abb5e7cd39 | ||
|  | cdae6de52e | ||
|  | 910fe77ca3 | ||
|  | b9a93c82d7 | ||
|  | 1c040bd616 | ||
|  | 166c537938 | ||
|  | 79c8de5fd0 | ||
|  | 7e6d2102a5 | ||
|  | 4f7d6132fc | ||
|  | b7fcc70cd9 | ||
|  | 43ad27931c | ||
|  | 68dc3f75eb | ||
|  | e67eb6d5b4 | ||
|  | 2e060bc85f | ||
|  | 22f80e4b19 | ||
|  | 1ef434f95e | ||
|  | 5676702da6 | ||
|  | 2fe0539600 | ||
|  | 13df5adecd | ||
|  | f33f808dc7 | ||
|  | 82ed9ef25e | ||
|  | 0aa26d791d | ||
|  | 255f278f5e | ||
|  | 1c76f9a0ff | ||
|  | 9a308e25a3 | ||
|  | 4b52f8c823 | ||
|  | 8f7145f80a | ||
|  | eb17f053f7 | ||
|  | 304e714331 | ||
|  | 525685f942 | ||
|  | e2477b66c6 | ||
|  | 58d51e039c | ||
|  | cb5b5167fc | ||
|  | 7d68378c3c | ||
|  | 87da6a947d | ||
|  | 0e644da6ac | ||
|  | d61a7c360e | ||
|  | ab286d851d | ||
|  | e8b60ab292 | ||
|  | 1cb7e70b42 | ||
|  | 25b9c22845 | ||
|  | 47166104fa | ||
|  | d0e0622058 | ||
|  | 87c75a1e43 | ||
|  | d1cc08f1d9 | ||
|  | 6619626486 | ||
|  | 1681ba090c | ||
|  | c36e1ad056 | ||
|  | d79c7f70be | ||
|  | b7eb2e4746 | ||
|  | 4db9b8b0e3 | ||
|  | f3a537d5c2 | ||
|  | 6a704b8a77 | ||
|  | df896877f8 | ||
|  | 3cb2d3dfff | ||
|  | d5f5e79661 | ||
|  | 82dffe9d0e | ||
|  | d7825dd7db | ||
|  | c0a2f429b6 | ||
|  | e909faa3ef | ||
|  | 28d9fdf798 | ||
|  | e9bfc44b90 | ||
|  | 2a772b71f1 | ||
|  | 233869ed80 | ||
|  | a9aaae7f97 | ||
|  | 500b4940a3 | ||
|  | 6d0dc47157 | ||
|  | 56e7e7d644 | ||
|  | 93c128ee21 | ||
|  | c72e3667b8 | ||
|  | 6406126ae3 | ||
|  | 6bde77bb57 | ||
|  | 831a628379 | ||
|  | 3d2babd059 | ||
|  | d9c4c74a56 | ||
|  | 5a231fb057 | ||
|  | 63f41df327 | ||
|  | 171618d32b | ||
|  | f7b58e7f64 | ||
|  | 436d87da98 | ||
|  | 896874fb99 | ||
|  | a9f8e80b1c | ||
|  | f93cd1bb48 | ||
|  | 8fc3a6b828 | ||
|  | 100840b743 | ||
|  | 8c8cab9309 | ||
|  | 629a8d6328 | ||
|  | ffdaec9f9f | ||
|  | c794153221 | ||
|  | d01837f42c | ||
|  | 7ad4cb0078 | ||
|  | ffd8349889 | ||
|  | f3716fd813 | ||
|  | 1170cf1896 | ||
|  | b9ee9d2bd5 | ||
|  | 21c3f75efc | ||
|  | eb5f7e36e9 | ||
|  | 4079563f5d | ||
|  | 6dabed5938 | ||
|  | 3939d516e6 | ||
|  | 5aba9d698b | ||
|  | 3dd652e7cf | ||
|  | f003298ce4 | ||
|  | 5f5ee0041f | ||
|  | 88497ddc52 | ||
|  | e921d5ec13 | ||
|  | c55974a485 | ||
|  | 2ff0f545f5 | ||
|  | 7f2a011c3e | ||
|  | 8af9a38c8e | ||
|  | b54bafe1c5 | ||
|  | 7a9ff3846e | ||
|  | 53fe08a348 | ||
|  | f118e86cfe | ||
|  | 2e60d5341e | ||
|  | 2ef3af0003 | ||
|  | 2b7699c26a | ||
|  | d567c58d3b | ||
|  | bede3d7e29 | ||
|  | 64d6dae672 | ||
|  | b001fe2b08 | ||
|  | c20285dd36 | ||
|  | 7e19c944b9 | ||
|  | fd4d3cff88 | ||
|  | 5ff646ca8e | ||
|  | 88432a5e67 | ||
|  | 7519d60fb7 | ||
|  | de23428069 | ||
|  | 9089bf2c5f | ||
|  | 2a344e26cc | ||
|  | af26403a86 | ||
|  | 3af6c8fe1d | ||
|  | b883b183a6 | ||
|  | ac58ace8cf | ||
|  | a50eefc44e | ||
|  | dbb3c5e32b | ||
|  | 265df31750 | ||
|  | 9bbe56df0b | ||
|  | 879c795fc4 | ||
|  | 8b5bb97537 | ||
|  | 3d7a635543 | ||
|  | b3ae9e6e25 | ||
|  | 023ea5b3a1 | ||
|  | 2ad6bd3945 | ||
|  | 2326dcdb12 | ||
|  | d95af455f1 | ||
|  | 8de955f3d8 | ||
|  | b5eb47f609 | ||
|  | af4ed0f39c | ||
|  | eac254f925 | ||
|  | 8246dec843 | ||
|  | 4514da0646 | ||
|  | 9abd6f5695 | ||
|  | a394967268 | ||
|  | c7c4cb9bc6 | ||
|  | 1ada679663 | ||
|  | 9552676783 | ||
|  | 69c7c03fb3 | ||
|  | 224a58bdf2 | ||
|  | 98427b715d | ||
|  | a8078aa151 | ||
|  | 3cb6744369 | ||
|  | e021460f03 | ||
|  | 0a93f3346f | ||
|  | 332cc14dcd | ||
|  | 7885b2e3f8 | ||
|  | 2447c72a5c | ||
|  | d7c9d73349 | ||
|  | a1bf5e8261 | ||
|  | 59f48d6d57 | ||
|  | 52f22aad81 | ||
|  | c03e24e10b | ||
|  | 4d75681357 | ||
|  | 1a88eac9c7 | ||
|  | 47357ddec8 | ||
|  | 129ae3a737 | ||
|  | 31d1390039 | ||
|  | 6943bbcacb | 
| @@ -11,12 +11,17 @@ 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 | ||||
|                   command: yarn --frozen-lockfile --ignore-engines install || yarn --frozen-lockfile --ignore-engines install | ||||
|             - setup_remote_docker | ||||
|             - run: yarn build:ci:no_website | ||||
|             - run: yarn build:ts | ||||
| @@ -47,10 +52,12 @@ jobs: | ||||
|             - 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-erc1155 | ||||
|             - 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-coordinator | ||||
|     test-contracts-geth: | ||||
|         docker: | ||||
|             - image: circleci/node:9-browsers | ||||
| @@ -67,10 +74,12 @@ jobs: | ||||
|             - 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-erc1155 | ||||
|             - 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-coordinator | ||||
|     test-publish: | ||||
|         resource_class: medium+ | ||||
|         docker: | ||||
| @@ -115,6 +124,7 @@ 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/contract-artifacts | ||||
|             - run: yarn wsrun test:circleci @0x/assert | ||||
|             - run: yarn wsrun test:circleci @0x/base-contract | ||||
|             - run: yarn wsrun test:circleci @0x/connect | ||||
| @@ -197,8 +207,11 @@ 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 | ||||
| @@ -386,9 +399,11 @@ workflows: | ||||
|             - test-contracts-ganache: | ||||
|                   requires: | ||||
|                       - build | ||||
|             - test-contracts-geth: | ||||
|                   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-pipeline: | ||||
|                   requires: | ||||
|                       - build | ||||
|   | ||||
							
								
								
									
										1
									
								
								.github/autolabeler.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/autolabeler.yml
									
									
									
									
										vendored
									
									
								
							| @@ -2,6 +2,7 @@ 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'] | ||||
|   | ||||
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -83,6 +83,7 @@ packages/react-docs/example/public/bundle* | ||||
| packages/testnet-faucets/server/ | ||||
|  | ||||
| # generated contract artifacts/ | ||||
| contracts/coordinator/generated-artifacts/ | ||||
| contracts/exchange/generated-artifacts/ | ||||
| contracts/asset-proxy/generated-artifacts/ | ||||
| contracts/multisig/generated-artifacts/ | ||||
| @@ -90,6 +91,7 @@ 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/ | ||||
| packages/sol-tracing-utils/test/fixtures/artifacts/ | ||||
| @@ -97,6 +99,7 @@ packages/metacoin/artifacts/ | ||||
|  | ||||
| # generated contract wrappers | ||||
| packages/abi-gen-wrappers/wrappers | ||||
| contracts/coordinator/generated-wrappers/ | ||||
| contracts/exchange/generated-wrappers/ | ||||
| contracts/asset-proxy/generated-wrappers/ | ||||
| contracts/multisig/generated-wrappers/ | ||||
| @@ -104,6 +107,7 @@ 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/ | ||||
| packages/metacoin/src/contract_wrappers | ||||
|   | ||||
| @@ -1,5 +1,7 @@ | ||||
| lib | ||||
| .nyc_output | ||||
| /contracts/coordinator/generated-wrappers | ||||
| /contracts/coordinator/generated-artifacts | ||||
| /contracts/exchange/generated-wrappers | ||||
| /contracts/exchange/generated-artifacts | ||||
| /contracts/asset-proxy/generated-wrappers | ||||
| @@ -14,6 +16,8 @@ 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 | ||||
|   | ||||
| @@ -33,6 +33,8 @@ 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 | ||||
|   | ||||
| @@ -34,18 +34,22 @@ 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 Llbraries used within the [`Exchange`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#exchange) contract                                                                                 | | ||||
| | [`@0x/contracts-exchange-libs`](/contracts/exchange-libs)           | [](https://www.npmjs.com/package/@0x/contracts-exchange-libs)           | Protocol specific libraries used within the [`Exchange`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#exchange) contract                                                                                 | | ||||
| | [`@0x/contracts-extensions`](/contracts/extensions)                 | [](https://www.npmjs.com/package/@0x/contracts-extensions)                 | Contracts that interact with and extend the functionality of the core protocol                                                                                                                                                                        | | ||||
| | [`@0x/contracts-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-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                                                                                                                                                            | | ||||
|  | ||||
| ### Typescript/Javascript Packages | ||||
|  | ||||
|   | ||||
							
								
								
									
										12
									
								
								contracts/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								contracts/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| #### 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)                           | | ||||
| @@ -1,4 +1,117 @@ | ||||
| [ | ||||
|     { | ||||
|         "version": "2.1.1", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ], | ||||
|         "timestamp": 1554997931 | ||||
|     }, | ||||
|     { | ||||
|         "version": "2.1.0", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Run Web3ProviderEngine without excess block polling", | ||||
|                 "pr": 1695 | ||||
|             } | ||||
|         ], | ||||
|         "timestamp": 1553183790 | ||||
|     }, | ||||
|     { | ||||
|         "version": "2.0.0", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Do not reexport external dependencies", | ||||
|                 "pr": 1682 | ||||
|             }, | ||||
|             { | ||||
|                 "note": "Add ERC1155Proxy", | ||||
|                 "pr": 1661 | ||||
|             }, | ||||
|             { | ||||
|                 "note": "Bumped solidity version to ^0.5.5", | ||||
|                 "pr": 1701 | ||||
|             }, | ||||
|             { | ||||
|                 "note": "Integration testing for ERC1155Proxy", | ||||
|                 "pr": 1673 | ||||
|             } | ||||
|         ], | ||||
|         "timestamp": 1553091633 | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1551479279, | ||||
|         "version": "1.0.9", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1551299797, | ||||
|         "version": "1.0.8", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1551220833, | ||||
|         "version": "1.0.7", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1551130135, | ||||
|         "version": "1.0.6", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1549733923, | ||||
|         "version": "1.0.5", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "version": "1.0.4", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ], | ||||
|         "timestamp": 1549547375 | ||||
|     }, | ||||
|     { | ||||
|         "version": "1.0.3", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Fake publish to enable pinning" | ||||
|             } | ||||
|         ], | ||||
|         "timestamp": 1549504360 | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1549452781, | ||||
|         "version": "1.0.2", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1549373905, | ||||
|         "version": "1.0.1", | ||||
|   | ||||
| @@ -5,6 +5,53 @@ Edit the package's CHANGELOG.json file only. | ||||
|  | ||||
| CHANGELOG | ||||
|  | ||||
| ## v2.1.1 - _April 11, 2019_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v2.1.0 - _March 21, 2019_ | ||||
|  | ||||
|     * Run Web3ProviderEngine without excess block polling (#1695) | ||||
|  | ||||
| ## v2.0.0 - _March 20, 2019_ | ||||
|  | ||||
|     * Do not reexport external dependencies (#1682) | ||||
|     * Add ERC1155Proxy (#1661) | ||||
|     * Bumped solidity version to ^0.5.5 (#1701) | ||||
|     * Integration testing for ERC1155Proxy (#1673) | ||||
|  | ||||
| ## v1.0.9 - _March 1, 2019_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v1.0.8 - _February 27, 2019_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v1.0.7 - _February 26, 2019_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v1.0.6 - _February 25, 2019_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v1.0.5 - _February 9, 2019_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v1.0.4 - _February 7, 2019_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v1.0.3 - _February 7, 2019_ | ||||
|  | ||||
|     * Fake publish to enable pinning | ||||
|  | ||||
| ## v1.0.2 - _February 6, 2019_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v1.0.1 - _February 5, 2019_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|   | ||||
| @@ -2,10 +2,13 @@ | ||||
|     "artifactsDir": "./generated-artifacts", | ||||
|     "contractsDir": "./contracts", | ||||
|     "useDockerisedSolc": true, | ||||
|     "isOfflineMode": false, | ||||
|     "compilerSettings": { | ||||
|         "evmVersion": "constantinople", | ||||
|         "optimizer": { | ||||
|             "enabled": true, | ||||
|             "runs": 1000000 | ||||
|             "runs": 1000000, | ||||
|             "details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true } | ||||
|         }, | ||||
|         "outputSelection": { | ||||
|             "*": { | ||||
| @@ -20,12 +23,13 @@ | ||||
|         } | ||||
|     }, | ||||
|     "contracts": [ | ||||
|         "IAssetData", | ||||
|         "IAssetProxy", | ||||
|         "IAuthorizable", | ||||
|         "ERC20Proxy", | ||||
|         "ERC721Proxy", | ||||
|         "MixinAuthorizable", | ||||
|         "MultiAssetProxy" | ||||
|         "src/ERC1155Proxy.sol", | ||||
|         "src/ERC20Proxy.sol", | ||||
|         "src/ERC721Proxy.sol", | ||||
|         "src/MixinAuthorizable.sol", | ||||
|         "src/MultiAssetProxy.sol", | ||||
|         "src/interfaces/IAssetData.sol", | ||||
|         "src/interfaces/IAssetProxy.sol", | ||||
|         "src/interfaces/IAuthorizable.sol" | ||||
|     ] | ||||
| } | ||||
|   | ||||
							
								
								
									
										267
									
								
								contracts/asset-proxy/contracts/src/ERC1155Proxy.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										267
									
								
								contracts/asset-proxy/contracts/src/ERC1155Proxy.sol
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,267 @@ | ||||
| /* | ||||
|  | ||||
|   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 "./MixinAuthorizable.sol"; | ||||
|  | ||||
|  | ||||
| contract ERC1155Proxy is | ||||
|     MixinAuthorizable | ||||
| { | ||||
|  | ||||
|     // Id of this proxy. | ||||
|     bytes4 constant internal PROXY_ID = bytes4(keccak256("ERC1155Assets(address,uint256[],uint256[],bytes)")); | ||||
|  | ||||
|     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 exists 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 asset data from Table #1 and Table #2 to Table #3, while | ||||
|                 // scaling the `values` (Table #2) by `amount` (Table #1). Once Table #3 has | ||||
|                 // been constructed in memory, the destination erc1155 contract is called using this  | ||||
|                 // as its calldata. This process is divided into four steps, below. | ||||
|  | ||||
|                 ////////// STEP 1/4 ////////// | ||||
|                 // 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 offsets in these rows are offset by 32 bytes in Table #3. | ||||
|                 // Strategy: | ||||
|                 // 1. Copy the assetData into memory at offset 32 | ||||
|                 // 2. Increment by 32 the offsets to `ids`, `values`, and `data` | ||||
|  | ||||
|                 // Load offset to `assetData` | ||||
|                 let assetDataOffset := calldataload(4) | ||||
|  | ||||
|                 // Load length in bytes of `assetData`, computed by: | ||||
|                 // 4 (function selector) | ||||
|                 // + assetDataOffset | ||||
|                 let assetDataLength := calldataload(add(4, assetDataOffset)) | ||||
|  | ||||
|                 // This corresponds to the beginning of the Data Area for Table #3. | ||||
|                 // Computed by: | ||||
|                 // 4 (function selector) | ||||
|                 // + assetDataOffset | ||||
|                 // + 32 (length of assetData) | ||||
|                 calldatacopy( | ||||
|                     32,                         // aligned such that "offset to ids" is at the correct location for Table #3 | ||||
|                     add(36, assetDataOffset),   // beginning of asset data contents | ||||
|                     assetDataLength             // length of asset data | ||||
|                 ) | ||||
|  | ||||
|                 // Increment by 32 the offsets to `ids`, `values`, and `data` | ||||
|                 mstore(68, add(mload(68), 32)) | ||||
|                 mstore(100, add(mload(100), 32)) | ||||
|                 mstore(132, add(mload(132), 32)) | ||||
|  | ||||
|                 // Record the address of the destination erc1155 asset for later. | ||||
|                 let assetAddress := and( | ||||
|                     mload(36), | ||||
|                     0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff | ||||
|                 ) | ||||
|  | ||||
|                 ////////// STEP 2/4 ////////// | ||||
|                 let amount := calldataload(100) | ||||
|                 let valuesOffset := add(mload(100), 4) // add 4 for calldata offset | ||||
|                 let valuesLengthInBytes := mul( | ||||
|                     mload(valuesOffset), | ||||
|                     32 | ||||
|                 ) | ||||
|                 let valuesBegin := add(valuesOffset, 32) | ||||
|                 let valuesEnd := add(valuesBegin, valuesLengthInBytes) | ||||
|                 for { let tokenValueOffset := valuesBegin } | ||||
|                     lt(tokenValueOffset, valuesEnd) | ||||
|                     { tokenValueOffset := add(tokenValueOffset, 32) } | ||||
|                 { | ||||
|                     // Load token value and generate scaled value | ||||
|                     let tokenValue := mload(tokenValueOffset) | ||||
|                     let scaledTokenValue := mul(tokenValue, amount) | ||||
|  | ||||
|                     // Revert if `amount` != 0 and multiplication resulted in an overflow | ||||
|                     if iszero(or( | ||||
|                         iszero(amount), | ||||
|                         eq(div(scaledTokenValue, amount), tokenValue) | ||||
|                     )) { | ||||
|                         // Revert with `Error("UINT256_OVERFLOW")` | ||||
|                         mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) | ||||
|                         mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) | ||||
|                         mstore(64, 0x0000001055494e543235365f4f564552464c4f57000000000000000000000000) | ||||
|                         mstore(96, 0) | ||||
|                         revert(0, 100) | ||||
|                     } | ||||
|  | ||||
|                     // There was no overflow, update `tokenValue` with its scaled counterpart | ||||
|                     mstore(tokenValueOffset, scaledTokenValue) | ||||
|                 } | ||||
|  | ||||
|                 ////////// STEP 3/4 ////////// | ||||
|                 // Store the safeBatchTransferFrom function selector, | ||||
|                 // and copy `from`/`to` fields from Table #1 to Table #3. | ||||
|  | ||||
|                 // The function selector is computed using: | ||||
|                 // bytes4(keccak256("safeBatchTransferFrom(address,address,uint256[],uint256[],bytes)")) | ||||
|                 mstore(0, 0x2eb2c2d600000000000000000000000000000000000000000000000000000000) | ||||
|  | ||||
|                 // Copy `from` and `to` fields from Table #1 to 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 4/4 ////////// | ||||
|                 // 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 | ||||
|                     add(assetDataLength, 32),               // length of input (Table #3) is 32 bytes longer than `assetData` (Table #2) | ||||
|                     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"; | ||||
|   | ||||
| @@ -17,7 +17,7 @@ | ||||
| */ | ||||
|  | ||||
| // solhint-disable | ||||
| pragma solidity ^0.4.24; | ||||
| pragma solidity ^0.5.5; | ||||
| pragma experimental ABIEncoderV2; | ||||
|  | ||||
|  | ||||
| @@ -36,8 +36,8 @@ interface IAssetData { | ||||
|         external; | ||||
|  | ||||
|     function MultiAsset( | ||||
|         uint256[] amounts, | ||||
|         bytes[] nestedAssetData | ||||
|         uint256[] calldata amounts, | ||||
|         bytes[] calldata nestedAssetData | ||||
|     ) | ||||
|         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"; | ||||
|  | ||||
|   | ||||
| @@ -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"; | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "@0x/contracts-asset-proxy", | ||||
|     "version": "1.0.1", | ||||
|     "version": "2.1.1", | ||||
|     "engines": { | ||||
|         "node": ">=6.12" | ||||
|     }, | ||||
| @@ -24,15 +24,18 @@ | ||||
|         "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/@(ERC20Proxy|ERC721Proxy|IAssetData|IAssetProxy|IAuthorizable|MixinAuthorizable|MultiAssetProxy).json" | ||||
|         "abis": "./generated-artifacts/@(ERC1155Proxy|ERC20Proxy|ERC721Proxy|IAssetData|IAssetProxy|IAuthorizable|MixinAuthorizable|MultiAssetProxy).json", | ||||
|         "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually." | ||||
|     }, | ||||
|     "repository": { | ||||
|         "type": "git", | ||||
| @@ -44,10 +47,12 @@ | ||||
|     }, | ||||
|     "homepage": "https://github.com/0xProject/0x-monorepo/contracts/protocol/README.md", | ||||
|     "devDependencies": { | ||||
|         "@0x/abi-gen": "^2.0.0", | ||||
|         "@0x/dev-utils": "^2.0.0", | ||||
|         "@0x/sol-compiler": "^3.0.0", | ||||
|         "@0x/tslint-config": "^3.0.0", | ||||
|         "@0x/abi-gen": "^2.0.9", | ||||
|         "@0x/contracts-gen": "^1.0.8", | ||||
|         "@0x/contracts-test-utils": "^3.1.2", | ||||
|         "@0x/dev-utils": "^2.2.1", | ||||
|         "@0x/sol-compiler": "^3.1.6", | ||||
|         "@0x/tslint-config": "^3.0.1", | ||||
|         "@types/lodash": "4.14.104", | ||||
|         "@types/node": "*", | ||||
|         "chai": "^4.0.1", | ||||
| @@ -63,18 +68,18 @@ | ||||
|         "typescript": "3.0.1" | ||||
|     }, | ||||
|     "dependencies": { | ||||
|         "@0x/base-contract": "^4.0.0", | ||||
|         "@0x/contracts-erc20": "^1.0.1", | ||||
|         "@0x/contracts-erc721": "^1.0.1", | ||||
|         "@0x/contracts-test-utils": "^3.0.0", | ||||
|         "@0x/contracts-utils": "^2.0.0", | ||||
|         "@0x/order-utils": "^4.0.0", | ||||
|         "@0x/types": "^2.0.0", | ||||
|         "@0x/typescript-typings": "^4.0.0", | ||||
|         "@0x/utils": "^4.0.1", | ||||
|         "@0x/web3-wrapper": "^4.0.0", | ||||
|         "ethereum-types": "^2.0.0", | ||||
|         "lodash": "^4.17.5" | ||||
|         "@0x/base-contract": "^5.0.5", | ||||
|         "@0x/contracts-erc1155": "^1.1.1", | ||||
|         "@0x/contracts-erc20": "^2.2.0", | ||||
|         "@0x/contracts-erc721": "^2.1.1", | ||||
|         "@0x/contracts-utils": "^3.1.1", | ||||
|         "@0x/order-utils": "^7.2.0", | ||||
|         "@0x/types": "^2.2.2", | ||||
|         "@0x/typescript-typings": "^4.2.2", | ||||
|         "@0x/utils": "^4.3.1", | ||||
|         "@0x/web3-wrapper": "^6.0.5", | ||||
|         "ethereum-types": "^2.1.2", | ||||
|         "lodash": "^4.17.11" | ||||
|     }, | ||||
|     "publishConfig": { | ||||
|         "access": "public" | ||||
|   | ||||
							
								
								
									
										25
									
								
								contracts/asset-proxy/src/artifacts.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								contracts/asset-proxy/src/artifacts.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| /* | ||||
|  * ----------------------------------------------------------------------------- | ||||
|  * Warning: This file is auto-generated by contracts-gen. Don't edit manually. | ||||
|  * ----------------------------------------------------------------------------- | ||||
|  */ | ||||
| import { ContractArtifact } from 'ethereum-types'; | ||||
|  | ||||
| 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'; | ||||
| 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'; | ||||
| export const artifacts = { | ||||
|     ERC20Proxy: ERC20Proxy as ContractArtifact, | ||||
|     ERC721Proxy: ERC721Proxy as ContractArtifact, | ||||
|     ERC1155Proxy: ERC1155Proxy as ContractArtifact, | ||||
|     MixinAuthorizable: MixinAuthorizable as ContractArtifact, | ||||
|     MultiAssetProxy: MultiAssetProxy as ContractArtifact, | ||||
|     IAssetData: IAssetData as ContractArtifact, | ||||
|     IAssetProxy: IAssetProxy as ContractArtifact, | ||||
|     IAuthorizable: IAuthorizable as ContractArtifact, | ||||
| }; | ||||
| @@ -1,19 +0,0 @@ | ||||
| import { ContractArtifact } from 'ethereum-types'; | ||||
|  | ||||
| import * as ERC20Proxy from '../../generated-artifacts/ERC20Proxy.json'; | ||||
| import * as ERC721Proxy from '../../generated-artifacts/ERC721Proxy.json'; | ||||
| import * as IAssetData from '../../generated-artifacts/IAssetData.json'; | ||||
| import * as IAssetProxy from '../../generated-artifacts/IAssetProxy.json'; | ||||
| import * as IAuthorizable from '../../generated-artifacts/IAuthorizable.json'; | ||||
| import * as MixinAuthorizable from '../../generated-artifacts/MixinAuthorizable.json'; | ||||
| import * as MultiAssetProxy from '../../generated-artifacts/MultiAssetProxy.json'; | ||||
|  | ||||
| export const artifacts = { | ||||
|     IAuthorizable: IAuthorizable as ContractArtifact, | ||||
|     IAssetData: IAssetData as ContractArtifact, | ||||
|     IAssetProxy: IAssetProxy as ContractArtifact, | ||||
|     ERC20Proxy: ERC20Proxy as ContractArtifact, | ||||
|     ERC721Proxy: ERC721Proxy as ContractArtifact, | ||||
|     MixinAuthorizable: MixinAuthorizable as ContractArtifact, | ||||
|     MultiAssetProxy: MultiAssetProxy as ContractArtifact, | ||||
| }; | ||||
							
								
								
									
										13
									
								
								contracts/asset-proxy/src/wrappers.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								contracts/asset-proxy/src/wrappers.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| /* | ||||
|  * ----------------------------------------------------------------------------- | ||||
|  * Warning: This file is auto-generated by contracts-gen. Don't edit manually. | ||||
|  * ----------------------------------------------------------------------------- | ||||
|  */ | ||||
| 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'; | ||||
| 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'; | ||||
| @@ -1,7 +0,0 @@ | ||||
| export * from '../../generated-wrappers/i_asset_data'; | ||||
| export * from '../../generated-wrappers/i_asset_proxy'; | ||||
| export * from '../../generated-wrappers/erc20_proxy'; | ||||
| export * from '../../generated-wrappers/erc721_proxy'; | ||||
| export * from '../../generated-wrappers/mixin_authorizable'; | ||||
| export * from '../../generated-wrappers/multi_asset_proxy'; | ||||
| export * from '../../generated-wrappers/i_authorizable'; | ||||
							
								
								
									
										833
									
								
								contracts/asset-proxy/test/erc1155_proxy.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										833
									
								
								contracts/asset-proxy/test/erc1155_proxy.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,833 @@ | ||||
| import { | ||||
|     artifacts as erc1155Artifacts, | ||||
|     DummyERC1155ReceiverBatchTokenReceivedEventArgs, | ||||
|     DummyERC1155ReceiverContract, | ||||
|     ERC1155MintableContract, | ||||
|     Erc1155Wrapper, | ||||
| } from '@0x/contracts-erc1155'; | ||||
| import { | ||||
|     chaiSetup, | ||||
|     constants, | ||||
|     expectTransactionFailedAsync, | ||||
|     expectTransactionFailedWithoutReasonAsync, | ||||
|     provider, | ||||
|     txDefaults, | ||||
|     web3Wrapper, | ||||
| } from '@0x/contracts-test-utils'; | ||||
| import { BlockchainLifecycle } from '@0x/dev-utils'; | ||||
| import { AssetProxyId, RevertReason } from '@0x/types'; | ||||
| import { BigNumber } from '@0x/utils'; | ||||
| import * as chai from 'chai'; | ||||
| import { LogWithDecodedArgs } from 'ethereum-types'; | ||||
| import * as _ from 'lodash'; | ||||
|  | ||||
| import { ERC1155ProxyWrapper, ERC721ProxyContract } from '../src'; | ||||
|  | ||||
| chaiSetup.configure(); | ||||
| const expect = chai.expect; | ||||
| const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); | ||||
|  | ||||
| // tslint:disable:no-unnecessary-type-assertion | ||||
| describe('ERC1155Proxy', () => { | ||||
|     // constant values used in transfer tests | ||||
|     const nftOwnerBalance = new BigNumber(1); | ||||
|     const nftNotOwnerBalance = new BigNumber(0); | ||||
|     const spenderInitialFungibleBalance = constants.INITIAL_ERC1155_FUNGIBLE_BALANCE; | ||||
|     const receiverInitialFungibleBalance = constants.INITIAL_ERC1155_FUNGIBLE_BALANCE; | ||||
|     const receiverContractInitialFungibleBalance = new BigNumber(0); | ||||
|     const fungibleValueToTransferSmall = spenderInitialFungibleBalance.div(100); | ||||
|     const fungibleValueToTransferLarge = spenderInitialFungibleBalance.div(4); | ||||
|     const valueMultiplierSmall = new BigNumber(2); | ||||
|     const valueMultiplierNft = new BigNumber(1); | ||||
|     const nonFungibleValueToTransfer = nftOwnerBalance; | ||||
|     const receiverCallbackData = '0x01020304'; | ||||
|     // addresses | ||||
|     let owner: string; | ||||
|     let notAuthorized: string; | ||||
|     let authorized: string; | ||||
|     let spender: string; | ||||
|     let receiver: string; | ||||
|     let receiverContract: string; | ||||
|     // contracts & wrappers | ||||
|     let erc1155Proxy: ERC721ProxyContract; | ||||
|     let erc1155Receiver: DummyERC1155ReceiverContract; | ||||
|     let erc1155ProxyWrapper: ERC1155ProxyWrapper; | ||||
|     let erc1155Contract: ERC1155MintableContract; | ||||
|     let erc1155Wrapper: Erc1155Wrapper; | ||||
|     // tokens | ||||
|     let fungibleTokens: BigNumber[]; | ||||
|     let nonFungibleTokensOwnedBySpender: BigNumber[]; | ||||
|     // tests | ||||
|     before(async () => { | ||||
|         await blockchainLifecycle.startAsync(); | ||||
|     }); | ||||
|     after(async () => { | ||||
|         await blockchainLifecycle.revertAsync(); | ||||
|     }); | ||||
|     before(async () => { | ||||
|         /// deploy & configure ERC1155Proxy | ||||
|         const accounts = await web3Wrapper.getAvailableAddressesAsync(); | ||||
|         const usedAddresses = ([owner, notAuthorized, authorized, spender, receiver] = _.slice(accounts, 0, 5)); | ||||
|         erc1155ProxyWrapper = new ERC1155ProxyWrapper(provider, usedAddresses, owner); | ||||
|         erc1155Proxy = await erc1155ProxyWrapper.deployProxyAsync(); | ||||
|         await web3Wrapper.awaitTransactionSuccessAsync( | ||||
|             await erc1155Proxy.addAuthorizedAddress.sendTransactionAsync(authorized, { | ||||
|                 from: owner, | ||||
|             }), | ||||
|             constants.AWAIT_TRANSACTION_MINED_MS, | ||||
|         ); | ||||
|         await web3Wrapper.awaitTransactionSuccessAsync( | ||||
|             await erc1155Proxy.addAuthorizedAddress.sendTransactionAsync(erc1155Proxy.address, { | ||||
|                 from: owner, | ||||
|             }), | ||||
|             constants.AWAIT_TRANSACTION_MINED_MS, | ||||
|         ); | ||||
|         // deploy & configure ERC1155 tokens and receiver | ||||
|         [erc1155Wrapper] = await erc1155ProxyWrapper.deployDummyContractsAsync(); | ||||
|         erc1155Contract = erc1155Wrapper.getContract(); | ||||
|         erc1155Receiver = await DummyERC1155ReceiverContract.deployFrom0xArtifactAsync( | ||||
|             erc1155Artifacts.DummyERC1155Receiver, | ||||
|             provider, | ||||
|             txDefaults, | ||||
|         ); | ||||
|         receiverContract = erc1155Receiver.address; | ||||
|         await erc1155ProxyWrapper.setBalancesAndAllowancesAsync(); | ||||
|         fungibleTokens = erc1155ProxyWrapper.getFungibleTokenIds(); | ||||
|         const nonFungibleTokens = erc1155ProxyWrapper.getNonFungibleTokenIds(); | ||||
|         const tokenBalances = await erc1155ProxyWrapper.getBalancesAsync(); | ||||
|         nonFungibleTokensOwnedBySpender = []; | ||||
|         _.each(nonFungibleTokens, (nonFungibleToken: BigNumber) => { | ||||
|             const nonFungibleTokenAsString = nonFungibleToken.toString(); | ||||
|             const nonFungibleTokenHeldBySpender = | ||||
|                 tokenBalances.nonFungible[spender][erc1155Contract.address][nonFungibleTokenAsString][0]; | ||||
|             nonFungibleTokensOwnedBySpender.push(nonFungibleTokenHeldBySpender); | ||||
|         }); | ||||
|     }); | ||||
|     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: owner, | ||||
|                     to: erc1155Proxy.address, | ||||
|                     value: constants.ZERO_AMOUNT, | ||||
|                     data: undefinedSelector, | ||||
|                 }), | ||||
|             ); | ||||
|         }); | ||||
|         it('should have an id of 0x9645780d', async () => { | ||||
|             const proxyId = await erc1155Proxy.getProxyId.callAsync(); | ||||
|             const expectedProxyId = AssetProxyId.ERC1155; | ||||
|             expect(proxyId).to.equal(expectedProxyId); | ||||
|         }); | ||||
|     }); | ||||
|     describe('transferFrom', () => { | ||||
|         it('should successfully transfer value for a single, fungible token', async () => { | ||||
|             // setup test parameters | ||||
|             const tokenHolders = [spender, receiver]; | ||||
|             const tokensToTransfer = fungibleTokens.slice(0, 1); | ||||
|             const valuesToTransfer = [fungibleValueToTransferLarge]; | ||||
|             const valueMultiplier = valueMultiplierSmall; | ||||
|             // check balances before transfer | ||||
|             const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); | ||||
|             // execute transfer | ||||
|             await erc1155ProxyWrapper.transferFromAsync( | ||||
|                 spender, | ||||
|                 receiver, | ||||
|                 erc1155Contract.address, | ||||
|                 tokensToTransfer, | ||||
|                 valuesToTransfer, | ||||
|                 valueMultiplier, | ||||
|                 receiverCallbackData, | ||||
|                 authorized, | ||||
|             ); | ||||
|             // check balances after transfer | ||||
|             const totalValueTransferred = valuesToTransfer[0].times(valueMultiplier); | ||||
|             const expectedFinalBalances = [ | ||||
|                 spenderInitialFungibleBalance.minus(totalValueTransferred), | ||||
|                 receiverInitialFungibleBalance.plus(totalValueTransferred), | ||||
|             ]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances); | ||||
|         }); | ||||
|         it('should successfully transfer value for the same fungible token several times', async () => { | ||||
|             // setup test parameters | ||||
|             const tokenHolders = [spender, receiver]; | ||||
|             const tokenToTransfer = fungibleTokens[0]; | ||||
|             const tokensToTransfer = [tokenToTransfer, tokenToTransfer, tokenToTransfer]; | ||||
|             const valuesToTransfer = [ | ||||
|                 fungibleValueToTransferSmall.plus(10), | ||||
|                 fungibleValueToTransferSmall.plus(20), | ||||
|                 fungibleValueToTransferSmall.plus(30), | ||||
|             ]; | ||||
|             const valueMultiplier = valueMultiplierSmall; | ||||
|             // check balances before transfer | ||||
|             const expectedInitialBalances = [ | ||||
|                 // spender | ||||
|                 spenderInitialFungibleBalance, | ||||
|                 // receiver | ||||
|                 receiverInitialFungibleBalance, | ||||
|             ]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedInitialBalances); | ||||
|             // execute transfer | ||||
|             await erc1155ProxyWrapper.transferFromAsync( | ||||
|                 spender, | ||||
|                 receiver, | ||||
|                 erc1155Contract.address, | ||||
|                 tokensToTransfer, | ||||
|                 valuesToTransfer, | ||||
|                 valueMultiplier, | ||||
|                 receiverCallbackData, | ||||
|                 authorized, | ||||
|             ); | ||||
|             // check balances after transfer | ||||
|             let totalValueTransferred = _.reduce(valuesToTransfer, (sum: BigNumber, value: BigNumber) => { | ||||
|                 return sum.plus(value); | ||||
|             }) as BigNumber; | ||||
|             totalValueTransferred = totalValueTransferred.times(valueMultiplier); | ||||
|             const expectedFinalBalances = [ | ||||
|                 // spender | ||||
|                 spenderInitialFungibleBalance.minus(totalValueTransferred), | ||||
|                 // receiver | ||||
|                 receiverInitialFungibleBalance.plus(totalValueTransferred), | ||||
|             ]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedFinalBalances); | ||||
|         }); | ||||
|         it('should successfully transfer value for several fungible tokens', async () => { | ||||
|             // setup test parameters | ||||
|             const tokenHolders = [spender, receiver]; | ||||
|             const tokensToTransfer = fungibleTokens.slice(0, 3); | ||||
|             const valuesToTransfer = [ | ||||
|                 fungibleValueToTransferSmall.plus(10), | ||||
|                 fungibleValueToTransferSmall.plus(20), | ||||
|                 fungibleValueToTransferSmall.plus(30), | ||||
|             ]; | ||||
|             const valueMultiplier = valueMultiplierSmall; | ||||
|             // check balances before transfer | ||||
|             const expectedInitialBalances = [ | ||||
|                 // spender | ||||
|                 spenderInitialFungibleBalance, | ||||
|                 spenderInitialFungibleBalance, | ||||
|                 spenderInitialFungibleBalance, | ||||
|                 // receiver | ||||
|                 receiverInitialFungibleBalance, | ||||
|                 receiverInitialFungibleBalance, | ||||
|                 receiverInitialFungibleBalance, | ||||
|             ]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); | ||||
|             // execute transfer | ||||
|             await erc1155ProxyWrapper.transferFromAsync( | ||||
|                 spender, | ||||
|                 receiver, | ||||
|                 erc1155Contract.address, | ||||
|                 tokensToTransfer, | ||||
|                 valuesToTransfer, | ||||
|                 valueMultiplier, | ||||
|                 receiverCallbackData, | ||||
|                 authorized, | ||||
|             ); | ||||
|             // check balances after transfer | ||||
|             const totalValuesTransferred = _.map(valuesToTransfer, (value: BigNumber) => { | ||||
|                 return value.times(valueMultiplier); | ||||
|             }); | ||||
|             const expectedFinalBalances = [ | ||||
|                 // spender | ||||
|                 spenderInitialFungibleBalance.minus(totalValuesTransferred[0]), | ||||
|                 spenderInitialFungibleBalance.minus(totalValuesTransferred[1]), | ||||
|                 spenderInitialFungibleBalance.minus(totalValuesTransferred[2]), | ||||
|                 // receiver | ||||
|                 receiverInitialFungibleBalance.plus(totalValuesTransferred[0]), | ||||
|                 receiverInitialFungibleBalance.plus(totalValuesTransferred[1]), | ||||
|                 receiverInitialFungibleBalance.plus(totalValuesTransferred[2]), | ||||
|             ]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances); | ||||
|         }); | ||||
|         it('should successfully transfer a non-fungible token', async () => { | ||||
|             // setup test parameters | ||||
|             const tokenHolders = [spender, receiver]; | ||||
|             const tokensToTransfer = nonFungibleTokensOwnedBySpender.slice(0, 1); | ||||
|             const valuesToTransfer = [nonFungibleValueToTransfer]; | ||||
|             const valueMultiplier = valueMultiplierNft; | ||||
|             // check balances before transfer | ||||
|             const expectedInitialBalances = [ | ||||
|                 // spender | ||||
|                 nftOwnerBalance, | ||||
|                 // receiver | ||||
|                 nftNotOwnerBalance, | ||||
|             ]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); | ||||
|             // execute transfer | ||||
|             await erc1155ProxyWrapper.transferFromAsync( | ||||
|                 spender, | ||||
|                 receiver, | ||||
|                 erc1155Contract.address, | ||||
|                 tokensToTransfer, | ||||
|                 valuesToTransfer, | ||||
|                 valueMultiplier, | ||||
|                 receiverCallbackData, | ||||
|                 authorized, | ||||
|             ); | ||||
|             // check balances after transfer | ||||
|             const expectedFinalBalances = [ | ||||
|                 // spender | ||||
|                 nftNotOwnerBalance, | ||||
|                 // receiver | ||||
|                 nftOwnerBalance, | ||||
|             ]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances); | ||||
|         }); | ||||
|         it('should successfully transfer multiple non-fungible tokens', async () => { | ||||
|             // setup test parameters | ||||
|             const tokenHolders = [spender, receiver]; | ||||
|             const tokensToTransfer = nonFungibleTokensOwnedBySpender.slice(0, 3); | ||||
|             const valuesToTransfer = [ | ||||
|                 nonFungibleValueToTransfer, | ||||
|                 nonFungibleValueToTransfer, | ||||
|                 nonFungibleValueToTransfer, | ||||
|             ]; | ||||
|             const valueMultiplier = valueMultiplierNft; | ||||
|             // check balances before transfer | ||||
|             const expectedInitialBalances = [ | ||||
|                 // spender | ||||
|                 nftOwnerBalance, | ||||
|                 nftOwnerBalance, | ||||
|                 nftOwnerBalance, | ||||
|                 // receiver | ||||
|                 nftNotOwnerBalance, | ||||
|                 nftNotOwnerBalance, | ||||
|                 nftNotOwnerBalance, | ||||
|             ]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); | ||||
|             // execute transfer | ||||
|             await erc1155ProxyWrapper.transferFromAsync( | ||||
|                 spender, | ||||
|                 receiver, | ||||
|                 erc1155Contract.address, | ||||
|                 tokensToTransfer, | ||||
|                 valuesToTransfer, | ||||
|                 valueMultiplier, | ||||
|                 receiverCallbackData, | ||||
|                 authorized, | ||||
|             ); | ||||
|             // check balances after transfer | ||||
|             const expectedFinalBalances = [ | ||||
|                 // spender | ||||
|                 nftNotOwnerBalance, | ||||
|                 nftNotOwnerBalance, | ||||
|                 nftNotOwnerBalance, | ||||
|                 // receiver | ||||
|                 nftOwnerBalance, | ||||
|                 nftOwnerBalance, | ||||
|                 nftOwnerBalance, | ||||
|             ]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances); | ||||
|         }); | ||||
|         it('should successfully transfer value for a combination of several fungible/non-fungible tokens', async () => { | ||||
|             // setup test parameters | ||||
|             const tokenHolders = [spender, receiver]; | ||||
|             const fungibleTokensToTransfer = fungibleTokens.slice(0, 3); | ||||
|             const nonFungibleTokensToTransfer = nonFungibleTokensOwnedBySpender.slice(0, 2); | ||||
|             const tokensToTransfer = fungibleTokensToTransfer.concat(nonFungibleTokensToTransfer); | ||||
|             const valuesToTransfer = [ | ||||
|                 fungibleValueToTransferLarge, | ||||
|                 fungibleValueToTransferSmall, | ||||
|                 fungibleValueToTransferSmall, | ||||
|                 nonFungibleValueToTransfer, | ||||
|                 nonFungibleValueToTransfer, | ||||
|             ]; | ||||
|             const valueMultiplier = valueMultiplierNft; | ||||
|             // check balances before transfer | ||||
|             const expectedInitialBalances = [ | ||||
|                 // spender | ||||
|                 spenderInitialFungibleBalance, | ||||
|                 spenderInitialFungibleBalance, | ||||
|                 spenderInitialFungibleBalance, | ||||
|                 nftOwnerBalance, | ||||
|                 nftOwnerBalance, | ||||
|                 // receiver | ||||
|                 receiverInitialFungibleBalance, | ||||
|                 receiverInitialFungibleBalance, | ||||
|                 receiverInitialFungibleBalance, | ||||
|                 nftNotOwnerBalance, | ||||
|                 nftNotOwnerBalance, | ||||
|             ]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); | ||||
|             // execute transfer | ||||
|             await erc1155ProxyWrapper.transferFromAsync( | ||||
|                 spender, | ||||
|                 receiver, | ||||
|                 erc1155Contract.address, | ||||
|                 tokensToTransfer, | ||||
|                 valuesToTransfer, | ||||
|                 valueMultiplier, | ||||
|                 receiverCallbackData, | ||||
|                 authorized, | ||||
|             ); | ||||
|             // check balances after transfer | ||||
|             const totalValuesTransferred = _.map(valuesToTransfer, (value: BigNumber) => { | ||||
|                 return value.times(valueMultiplier); | ||||
|             }); | ||||
|             const expectedFinalBalances = [ | ||||
|                 // spender | ||||
|                 expectedInitialBalances[0].minus(totalValuesTransferred[0]), | ||||
|                 expectedInitialBalances[1].minus(totalValuesTransferred[1]), | ||||
|                 expectedInitialBalances[2].minus(totalValuesTransferred[2]), | ||||
|                 expectedInitialBalances[3].minus(totalValuesTransferred[3]), | ||||
|                 expectedInitialBalances[4].minus(totalValuesTransferred[4]), | ||||
|                 // receiver | ||||
|                 expectedInitialBalances[5].plus(totalValuesTransferred[0]), | ||||
|                 expectedInitialBalances[6].plus(totalValuesTransferred[1]), | ||||
|                 expectedInitialBalances[7].plus(totalValuesTransferred[2]), | ||||
|                 expectedInitialBalances[8].plus(totalValuesTransferred[3]), | ||||
|                 expectedInitialBalances[9].plus(totalValuesTransferred[4]), | ||||
|             ]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances); | ||||
|         }); | ||||
|         it('should successfully transfer value to a smart contract and trigger its callback', async () => { | ||||
|             // setup test parameters | ||||
|             const tokenHolders = [spender, receiverContract]; | ||||
|             const tokensToTransfer = fungibleTokens.slice(0, 1); | ||||
|             const valuesToTransfer = [fungibleValueToTransferLarge]; | ||||
|             const valueMultiplier = valueMultiplierSmall; | ||||
|             const totalValuesTransferred = _.map(valuesToTransfer, (value: BigNumber) => { | ||||
|                 return value.times(valueMultiplier); | ||||
|             }); | ||||
|             // check balances before transfer | ||||
|             const expectedInitialBalances = [spenderInitialFungibleBalance, receiverContractInitialFungibleBalance]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); | ||||
|             // execute transfer | ||||
|             const txReceipt = await erc1155ProxyWrapper.transferFromWithLogsAsync( | ||||
|                 spender, | ||||
|                 receiverContract, | ||||
|                 erc1155Contract.address, | ||||
|                 tokensToTransfer, | ||||
|                 valuesToTransfer, | ||||
|                 valueMultiplier, | ||||
|                 receiverCallbackData, | ||||
|                 authorized, | ||||
|             ); | ||||
|             // check receiver log ignored extra asset data | ||||
|             expect(txReceipt.logs.length).to.be.equal(2); | ||||
|             const receiverLog = txReceipt.logs[1] as LogWithDecodedArgs< | ||||
|                 DummyERC1155ReceiverBatchTokenReceivedEventArgs | ||||
|             >; | ||||
|             expect(receiverLog.args.operator).to.be.equal(erc1155Proxy.address); | ||||
|             expect(receiverLog.args.from).to.be.equal(spender); | ||||
|             expect(receiverLog.args.tokenIds.length).to.be.deep.equal(1); | ||||
|             expect(receiverLog.args.tokenIds[0]).to.be.bignumber.equal(tokensToTransfer[0]); | ||||
|             expect(receiverLog.args.tokenValues.length).to.be.deep.equal(1); | ||||
|             expect(receiverLog.args.tokenValues[0]).to.be.bignumber.equal(totalValuesTransferred[0]); | ||||
|             // note - if the `extraData` is ignored then the receiver log should ignore it as well. | ||||
|             expect(receiverLog.args.data).to.be.deep.equal(receiverCallbackData); | ||||
|             // check balances after transfer | ||||
|             const expectedFinalBalances = [ | ||||
|                 expectedInitialBalances[0].minus(totalValuesTransferred[0]), | ||||
|                 expectedInitialBalances[1].plus(totalValuesTransferred[0]), | ||||
|             ]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances); | ||||
|         }); | ||||
|         it('should successfully transfer value to a smart contract and trigger its callback, when callback `data` is NULL', async () => { | ||||
|             // setup test parameters | ||||
|             const tokenHolders = [spender, receiverContract]; | ||||
|             const tokensToTransfer = fungibleTokens.slice(0, 1); | ||||
|             const valuesToTransfer = [fungibleValueToTransferLarge]; | ||||
|             const valueMultiplier = valueMultiplierSmall; | ||||
|             const totalValuesTransferred = _.map(valuesToTransfer, (value: BigNumber) => { | ||||
|                 return value.times(valueMultiplier); | ||||
|             }); | ||||
|             // check balances before transfer | ||||
|             const expectedInitialBalances = [spenderInitialFungibleBalance, receiverContractInitialFungibleBalance]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); | ||||
|             // execute transfer | ||||
|             const nullReceiverCallbackData = '0x'; | ||||
|             const txReceipt = await erc1155ProxyWrapper.transferFromWithLogsAsync( | ||||
|                 spender, | ||||
|                 receiverContract, | ||||
|                 erc1155Contract.address, | ||||
|                 tokensToTransfer, | ||||
|                 valuesToTransfer, | ||||
|                 valueMultiplier, | ||||
|                 nullReceiverCallbackData, | ||||
|                 authorized, | ||||
|             ); | ||||
|             // check receiver log ignored extra asset data | ||||
|             expect(txReceipt.logs.length).to.be.equal(2); | ||||
|             const receiverLog = txReceipt.logs[1] as LogWithDecodedArgs< | ||||
|                 DummyERC1155ReceiverBatchTokenReceivedEventArgs | ||||
|             >; | ||||
|             expect(receiverLog.args.operator).to.be.equal(erc1155Proxy.address); | ||||
|             expect(receiverLog.args.from).to.be.equal(spender); | ||||
|             expect(receiverLog.args.tokenIds.length).to.be.deep.equal(1); | ||||
|             expect(receiverLog.args.tokenIds[0]).to.be.bignumber.equal(tokensToTransfer[0]); | ||||
|             expect(receiverLog.args.tokenValues.length).to.be.deep.equal(1); | ||||
|             expect(receiverLog.args.tokenValues[0]).to.be.bignumber.equal(totalValuesTransferred[0]); | ||||
|             // note - if the `extraData` is ignored then the receiver log should ignore it as well. | ||||
|             expect(receiverLog.args.data).to.be.deep.equal(nullReceiverCallbackData); | ||||
|             // check balances after transfer | ||||
|             const expectedFinalBalances = [ | ||||
|                 expectedInitialBalances[0].minus(totalValuesTransferred[0]), | ||||
|                 expectedInitialBalances[1].plus(totalValuesTransferred[0]), | ||||
|             ]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances); | ||||
|         }); | ||||
|         it('should successfully transfer value and ignore extra assetData', async () => { | ||||
|             // setup test parameters | ||||
|             const tokenHolders = [spender, receiverContract]; | ||||
|             const tokensToTransfer = fungibleTokens.slice(0, 1); | ||||
|             const valuesToTransfer = [fungibleValueToTransferLarge]; | ||||
|             const valueMultiplier = valueMultiplierSmall; | ||||
|             const totalValuesTransferred = _.map(valuesToTransfer, (value: BigNumber) => { | ||||
|                 return value.times(valueMultiplier); | ||||
|             }); | ||||
|             const extraData = '0102030405060708'; | ||||
|             // check balances before transfer | ||||
|             const expectedInitialBalances = [spenderInitialFungibleBalance, receiverContractInitialFungibleBalance]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); | ||||
|             // execute transfer | ||||
|             const txReceipt = await erc1155ProxyWrapper.transferFromWithLogsAsync( | ||||
|                 spender, | ||||
|                 receiverContract, | ||||
|                 erc1155Contract.address, | ||||
|                 tokensToTransfer, | ||||
|                 valuesToTransfer, | ||||
|                 valueMultiplier, | ||||
|                 receiverCallbackData, | ||||
|                 authorized, | ||||
|                 extraData, | ||||
|             ); | ||||
|             // check receiver log ignored extra asset data | ||||
|             expect(txReceipt.logs.length).to.be.equal(2); | ||||
|             const receiverLog = txReceipt.logs[1] as LogWithDecodedArgs< | ||||
|                 DummyERC1155ReceiverBatchTokenReceivedEventArgs | ||||
|             >; | ||||
|             expect(receiverLog.args.operator).to.be.equal(erc1155Proxy.address); | ||||
|             expect(receiverLog.args.from).to.be.equal(spender); | ||||
|             expect(receiverLog.args.tokenIds.length).to.be.deep.equal(1); | ||||
|             expect(receiverLog.args.tokenIds[0]).to.be.bignumber.equal(tokensToTransfer[0]); | ||||
|             expect(receiverLog.args.tokenValues.length).to.be.deep.equal(1); | ||||
|             expect(receiverLog.args.tokenValues[0]).to.be.bignumber.equal(totalValuesTransferred[0]); | ||||
|             // note - if the `extraData` is ignored then the receiver log should ignore it as well. | ||||
|             expect(receiverLog.args.data).to.be.deep.equal(receiverCallbackData); | ||||
|             // check balances after transfer | ||||
|             const expectedFinalBalances = [ | ||||
|                 expectedInitialBalances[0].minus(totalValuesTransferred[0]), | ||||
|                 expectedInitialBalances[1].plus(totalValuesTransferred[0]), | ||||
|             ]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances); | ||||
|         }); | ||||
|         it('should transfer nothing if value is zero', async () => { | ||||
|             // setup test parameters | ||||
|             const tokenHolders = [spender, receiver]; | ||||
|             const tokensToTransfer = fungibleTokens.slice(0, 1); | ||||
|             const valuesToTransfer = [new BigNumber(0)]; | ||||
|             const valueMultiplier = valueMultiplierSmall; | ||||
|             // check balances before transfer | ||||
|             const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); | ||||
|             // execute transfer | ||||
|             await erc1155ProxyWrapper.transferFromAsync( | ||||
|                 spender, | ||||
|                 receiver, | ||||
|                 erc1155Contract.address, | ||||
|                 tokensToTransfer, | ||||
|                 valuesToTransfer, | ||||
|                 valueMultiplier, | ||||
|                 receiverCallbackData, | ||||
|                 authorized, | ||||
|             ); | ||||
|             // check balances after transfer | ||||
|             const expectedFinalBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances); | ||||
|         }); | ||||
|         it('should transfer nothing if value multiplier is zero', async () => { | ||||
|             // setup test parameters | ||||
|             const tokenHolders = [spender, receiver]; | ||||
|             const tokensToTransfer = fungibleTokens.slice(0, 1); | ||||
|             const valuesToTransfer = [fungibleValueToTransferLarge]; | ||||
|             const valueMultiplier = new BigNumber(0); | ||||
|             // check balances before transfer | ||||
|             const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); | ||||
|             // execute transfer | ||||
|             await erc1155ProxyWrapper.transferFromAsync( | ||||
|                 spender, | ||||
|                 receiver, | ||||
|                 erc1155Contract.address, | ||||
|                 tokensToTransfer, | ||||
|                 valuesToTransfer, | ||||
|                 valueMultiplier, | ||||
|                 receiverCallbackData, | ||||
|                 authorized, | ||||
|             ); | ||||
|             // check balances after transfer | ||||
|             const expectedFinalBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances); | ||||
|         }); | ||||
|         it('should transfer nothing if there are no tokens in asset data', async () => { | ||||
|             // setup test parameters | ||||
|             const tokenHolders = [spender, receiver]; | ||||
|             const tokensToTransfer: BigNumber[] = []; | ||||
|             const valuesToTransfer: BigNumber[] = []; | ||||
|             const valueMultiplier = valueMultiplierSmall; | ||||
|             // check balances before transfer | ||||
|             const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); | ||||
|             // execute transfer | ||||
|             await erc1155ProxyWrapper.transferFromAsync( | ||||
|                 spender, | ||||
|                 receiver, | ||||
|                 erc1155Contract.address, | ||||
|                 tokensToTransfer, | ||||
|                 valuesToTransfer, | ||||
|                 valueMultiplier, | ||||
|                 receiverCallbackData, | ||||
|                 authorized, | ||||
|             ); | ||||
|             // check balances after transfer | ||||
|             const expectedFinalBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances); | ||||
|         }); | ||||
|         it('should propagate revert reason from erc1155 contract failure', async () => { | ||||
|             // disable transfers | ||||
|             const shouldRejectTransfer = true; | ||||
|             await web3Wrapper.awaitTransactionSuccessAsync( | ||||
|                 await erc1155Receiver.setRejectTransferFlag.sendTransactionAsync(shouldRejectTransfer), | ||||
|                 constants.AWAIT_TRANSACTION_MINED_MS, | ||||
|             ); | ||||
|             // setup test parameters | ||||
|             const tokenHolders = [spender, receiverContract]; | ||||
|             const tokensToTransfer = fungibleTokens.slice(0, 1); | ||||
|             const valuesToTransfer = [fungibleValueToTransferLarge]; | ||||
|             const valueMultiplier = valueMultiplierSmall; | ||||
|             // check balances before transfer | ||||
|             const expectedInitialBalances = [spenderInitialFungibleBalance, receiverContractInitialFungibleBalance]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); | ||||
|             // execute transfer | ||||
|             await expectTransactionFailedAsync( | ||||
|                 erc1155ProxyWrapper.transferFromAsync( | ||||
|                     spender, | ||||
|                     receiverContract, | ||||
|                     erc1155Contract.address, | ||||
|                     tokensToTransfer, | ||||
|                     valuesToTransfer, | ||||
|                     valueMultiplier, | ||||
|                     receiverCallbackData, | ||||
|                     authorized, | ||||
|                 ), | ||||
|                 RevertReason.TransferRejected, | ||||
|             ); | ||||
|         }); | ||||
|         it('should revert if transferring the same non-fungible token more than once', async () => { | ||||
|             // setup test parameters | ||||
|             const tokenHolders = [spender, receiver]; | ||||
|             const nftToTransfer = nonFungibleTokensOwnedBySpender[0]; | ||||
|             const tokensToTransfer = [nftToTransfer, nftToTransfer]; | ||||
|             const valuesToTransfer = [nonFungibleValueToTransfer, nonFungibleValueToTransfer]; | ||||
|             const valueMultiplier = valueMultiplierNft; | ||||
|             // check balances before transfer | ||||
|             const expectedInitialBalances = [ | ||||
|                 // spender | ||||
|                 nftOwnerBalance, | ||||
|                 nftOwnerBalance, | ||||
|                 // receiver | ||||
|                 nftNotOwnerBalance, | ||||
|                 nftNotOwnerBalance, | ||||
|             ]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); | ||||
|             // execute transfer | ||||
|             await expectTransactionFailedAsync( | ||||
|                 erc1155ProxyWrapper.transferFromAsync( | ||||
|                     spender, | ||||
|                     receiver, | ||||
|                     erc1155Contract.address, | ||||
|                     tokensToTransfer, | ||||
|                     valuesToTransfer, | ||||
|                     valueMultiplier, | ||||
|                     receiverCallbackData, | ||||
|                     authorized, | ||||
|                 ), | ||||
|                 RevertReason.NFTNotOwnedByFromAddress, | ||||
|             ); | ||||
|         }); | ||||
|         it('should revert if there is a multiplication overflow', async () => { | ||||
|             // setup test parameters | ||||
|             const tokenHolders = [spender, receiver]; | ||||
|             const tokensToTransfer = nonFungibleTokensOwnedBySpender.slice(0, 3); | ||||
|             const maxUintValue = new BigNumber(2).pow(256).minus(1); | ||||
|             const valuesToTransfer = [nonFungibleValueToTransfer, maxUintValue, nonFungibleValueToTransfer]; | ||||
|             const valueMultiplier = new BigNumber(2); | ||||
|             // check balances before transfer | ||||
|             const expectedInitialBalances = [ | ||||
|                 // spender | ||||
|                 nftOwnerBalance, | ||||
|                 nftOwnerBalance, | ||||
|                 nftOwnerBalance, | ||||
|                 // receiver | ||||
|                 nftNotOwnerBalance, | ||||
|                 nftNotOwnerBalance, | ||||
|                 nftNotOwnerBalance, | ||||
|             ]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); | ||||
|             // execute transfer | ||||
|             // note - this will overflow because we are trying to transfer `maxUintValue * 2` of the 2nd token | ||||
|             await expectTransactionFailedAsync( | ||||
|                 erc1155ProxyWrapper.transferFromAsync( | ||||
|                     spender, | ||||
|                     receiver, | ||||
|                     erc1155Contract.address, | ||||
|                     tokensToTransfer, | ||||
|                     valuesToTransfer, | ||||
|                     valueMultiplier, | ||||
|                     receiverCallbackData, | ||||
|                     authorized, | ||||
|                 ), | ||||
|                 RevertReason.Uint256Overflow, | ||||
|             ); | ||||
|         }); | ||||
|         it('should revert if transferring > 1 instances of a non-fungible token (valueMultiplier field >1)', async () => { | ||||
|             // setup test parameters | ||||
|             const tokenHolders = [spender, receiver]; | ||||
|             const tokensToTransfer = nonFungibleTokensOwnedBySpender.slice(0, 1); | ||||
|             const valuesToTransfer = [nonFungibleValueToTransfer]; | ||||
|             const valueMultiplier = new BigNumber(2); | ||||
|             // check balances before transfer | ||||
|             const expectedInitialBalances = [ | ||||
|                 // spender | ||||
|                 nftOwnerBalance, | ||||
|                 // receiver | ||||
|                 nftNotOwnerBalance, | ||||
|             ]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); | ||||
|             // execute transfer | ||||
|             await expectTransactionFailedAsync( | ||||
|                 erc1155ProxyWrapper.transferFromAsync( | ||||
|                     spender, | ||||
|                     receiver, | ||||
|                     erc1155Contract.address, | ||||
|                     tokensToTransfer, | ||||
|                     valuesToTransfer, | ||||
|                     valueMultiplier, | ||||
|                     receiverCallbackData, | ||||
|                     authorized, | ||||
|                 ), | ||||
|                 RevertReason.AmountEqualToOneRequired, | ||||
|             ); | ||||
|         }); | ||||
|         it('should revert if transferring > 1 instances of a non-fungible token (`valuesToTransfer` field >1)', async () => { | ||||
|             // setup test parameters | ||||
|             const tokenHolders = [spender, receiver]; | ||||
|             const tokensToTransfer = nonFungibleTokensOwnedBySpender.slice(0, 1); | ||||
|             const valuesToTransfer = [new BigNumber(2)]; | ||||
|             const valueMultiplier = valueMultiplierNft; | ||||
|             // check balances before transfer | ||||
|             const expectedInitialBalances = [ | ||||
|                 // spender | ||||
|                 nftOwnerBalance, | ||||
|                 // receiver | ||||
|                 nftNotOwnerBalance, | ||||
|             ]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); | ||||
|             // execute transfer | ||||
|             await expectTransactionFailedAsync( | ||||
|                 erc1155ProxyWrapper.transferFromAsync( | ||||
|                     spender, | ||||
|                     receiver, | ||||
|                     erc1155Contract.address, | ||||
|                     tokensToTransfer, | ||||
|                     valuesToTransfer, | ||||
|                     valueMultiplier, | ||||
|                     receiverCallbackData, | ||||
|                     authorized, | ||||
|                 ), | ||||
|                 RevertReason.AmountEqualToOneRequired, | ||||
|             ); | ||||
|         }); | ||||
|         it('should revert if sender balance is insufficient', async () => { | ||||
|             // setup test parameters | ||||
|             const tokenHolders = [spender, receiver]; | ||||
|             const tokensToTransfer = fungibleTokens.slice(0, 1); | ||||
|             const valueGreaterThanSpenderBalance = spenderInitialFungibleBalance.plus(1); | ||||
|             const valuesToTransfer = [valueGreaterThanSpenderBalance]; | ||||
|             const valueMultiplier = valueMultiplierSmall; | ||||
|             // check balances before transfer | ||||
|             const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); | ||||
|             // execute transfer | ||||
|             await expectTransactionFailedAsync( | ||||
|                 erc1155ProxyWrapper.transferFromAsync( | ||||
|                     spender, | ||||
|                     receiver, | ||||
|                     erc1155Contract.address, | ||||
|                     tokensToTransfer, | ||||
|                     valuesToTransfer, | ||||
|                     valueMultiplier, | ||||
|                     receiverCallbackData, | ||||
|                     authorized, | ||||
|                 ), | ||||
|                 RevertReason.Uint256Underflow, | ||||
|             ); | ||||
|         }); | ||||
|         it('should revert if sender allowance is insufficient', async () => { | ||||
|             // dremove allowance for ERC1155 proxy | ||||
|             const wrapper = erc1155ProxyWrapper.getContractWrapper(erc1155Contract.address); | ||||
|             const isApproved = false; | ||||
|             await wrapper.setApprovalForAllAsync(spender, erc1155Proxy.address, isApproved); | ||||
|             const isApprovedActualValue = await wrapper.isApprovedForAllAsync(spender, erc1155Proxy.address); | ||||
|             expect(isApprovedActualValue).to.be.equal(isApproved); | ||||
|             // setup test parameters | ||||
|             const tokenHolders = [spender, receiver]; | ||||
|             const tokensToTransfer = fungibleTokens.slice(0, 1); | ||||
|             const valuesToTransfer = [fungibleValueToTransferLarge]; | ||||
|             const valueMultiplier = valueMultiplierSmall; | ||||
|             // check balances before transfer | ||||
|             const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); | ||||
|             // execute transfer | ||||
|             await expectTransactionFailedAsync( | ||||
|                 erc1155ProxyWrapper.transferFromAsync( | ||||
|                     spender, | ||||
|                     receiver, | ||||
|                     erc1155Contract.address, | ||||
|                     tokensToTransfer, | ||||
|                     valuesToTransfer, | ||||
|                     valueMultiplier, | ||||
|                     receiverCallbackData, | ||||
|                     authorized, | ||||
|                 ), | ||||
|                 RevertReason.InsufficientAllowance, | ||||
|             ); | ||||
|         }); | ||||
|         it('should revert if caller is not authorized', async () => { | ||||
|             // setup test parameters | ||||
|             const tokenHolders = [spender, receiver]; | ||||
|             const tokensToTransfer = fungibleTokens.slice(0, 1); | ||||
|             const valuesToTransfer = [fungibleValueToTransferLarge]; | ||||
|             const valueMultiplier = valueMultiplierSmall; | ||||
|             // check balances before transfer | ||||
|             const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); | ||||
|             // execute transfer | ||||
|             await expectTransactionFailedAsync( | ||||
|                 erc1155ProxyWrapper.transferFromAsync( | ||||
|                     spender, | ||||
|                     receiver, | ||||
|                     erc1155Contract.address, | ||||
|                     tokensToTransfer, | ||||
|                     valuesToTransfer, | ||||
|                     valueMultiplier, | ||||
|                     receiverCallbackData, | ||||
|                     notAuthorized, | ||||
|                 ), | ||||
|                 RevertReason.SenderNotAuthorized, | ||||
|             ); | ||||
|         }); | ||||
|     }); | ||||
| }); | ||||
| // tslint:enable:no-unnecessary-type-assertion | ||||
| // tslint:disable:max-file-line-count | ||||
| @@ -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,4 @@ | ||||
| import { ERC1155MintableContract, Erc1155Wrapper } from '@0x/contracts-erc1155'; | ||||
| import { | ||||
|     artifacts as erc20Artifacts, | ||||
|     DummyERC20TokenContract, | ||||
| @@ -30,6 +31,7 @@ import * as _ from 'lodash'; | ||||
|  | ||||
| import { | ||||
|     artifacts, | ||||
|     ERC1155ProxyWrapper, | ||||
|     ERC20ProxyContract, | ||||
|     ERC20Wrapper, | ||||
|     ERC721ProxyContract, | ||||
| @@ -77,6 +79,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(); | ||||
|     }); | ||||
| @@ -127,6 +138,22 @@ describe('Asset Transfer Proxies', () => { | ||||
|             constants.AWAIT_TRANSACTION_MINED_MS, | ||||
|         ); | ||||
|  | ||||
|         // Configure ERC115Proxy | ||||
|         erc1155ProxyWrapper = new ERC1155ProxyWrapper(provider, usedAddresses, owner); | ||||
|         erc1155Proxy = await erc1155ProxyWrapper.deployProxyAsync(); | ||||
|         await web3Wrapper.awaitTransactionSuccessAsync( | ||||
|             await erc1155Proxy.addAuthorizedAddress.sendTransactionAsync(authorized, { | ||||
|                 from: owner, | ||||
|             }), | ||||
|             constants.AWAIT_TRANSACTION_MINED_MS, | ||||
|         ); | ||||
|         await web3Wrapper.awaitTransactionSuccessAsync( | ||||
|             await erc1155Proxy.addAuthorizedAddress.sendTransactionAsync(multiAssetProxy.address, { | ||||
|                 from: owner, | ||||
|             }), | ||||
|             constants.AWAIT_TRANSACTION_MINED_MS, | ||||
|         ); | ||||
|  | ||||
|         // Configure MultiAssetProxy | ||||
|         await web3Wrapper.awaitTransactionSuccessAsync( | ||||
|             await multiAssetProxy.addAuthorizedAddress.sendTransactionAsync(authorized, { | ||||
| @@ -146,6 +173,12 @@ describe('Asset Transfer Proxies', () => { | ||||
|             }), | ||||
|             constants.AWAIT_TRANSACTION_MINED_MS, | ||||
|         ); | ||||
|         await web3Wrapper.awaitTransactionSuccessAsync( | ||||
|             await multiAssetProxy.registerAssetProxy.sendTransactionAsync(erc1155Proxy.address, { | ||||
|                 from: owner, | ||||
|             }), | ||||
|             constants.AWAIT_TRANSACTION_MINED_MS, | ||||
|         ); | ||||
|  | ||||
|         // Deploy and configure ERC20 tokens | ||||
|         const numDummyErc20ToDeploy = 2; | ||||
| @@ -213,6 +246,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(); | ||||
| @@ -944,6 +993,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); | ||||
|   | ||||
							
								
								
									
										382
									
								
								contracts/asset-proxy/test/utils/erc1155_proxy_wrapper.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										382
									
								
								contracts/asset-proxy/test/utils/erc1155_proxy_wrapper.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,382 @@ | ||||
| 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( | ||||
|             artifacts.IAssetProxy.compilerOutput.abi, | ||||
|             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 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, | ||||
|         extraData?: string, | ||||
|     ): Promise<string> { | ||||
|         this._validateProxyContractExistsOrThrow(); | ||||
|         let encodedAssetData = assetDataUtils.encodeERC1155AssetData( | ||||
|             contractAddress, | ||||
|             tokensToTransfer, | ||||
|             valuesToTransfer, | ||||
|             receiverCallbackData, | ||||
|         ); | ||||
|         if (extraData !== undefined) { | ||||
|             encodedAssetData = `${encodedAssetData}${extraData}`; | ||||
|         } | ||||
|         const data = this._assetProxyInterface.transferFrom.getABIEncodedTransactionData( | ||||
|             encodedAssetData, | ||||
|             from, | ||||
|             to, | ||||
|             valueMultiplier, | ||||
|         ); | ||||
|         const txHash = await this._web3Wrapper.sendTransactionAsync({ | ||||
|             to: (this._proxyContract as ERC1155ProxyContract).address, | ||||
|             data, | ||||
|             from: authorizedSender, | ||||
|         }); | ||||
|         return txHash; | ||||
|     } | ||||
|     /** | ||||
|      * @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 receipt with decoded logs. | ||||
|      */ | ||||
|     public async transferFromWithLogsAsync( | ||||
|         from: string, | ||||
|         to: string, | ||||
|         contractAddress: string, | ||||
|         tokensToTransfer: BigNumber[], | ||||
|         valuesToTransfer: BigNumber[], | ||||
|         valueMultiplier: BigNumber, | ||||
|         receiverCallbackData: string, | ||||
|         authorizedSender: string, | ||||
|         extraData?: string, | ||||
|     ): Promise<TransactionReceiptWithDecodedLogs> { | ||||
|         const txReceipt = await this._logDecoder.getTxWithDecodedLogsAsync( | ||||
|             await this.transferFromAsync( | ||||
|                 from, | ||||
|                 to, | ||||
|                 contractAddress, | ||||
|                 tokensToTransfer, | ||||
|                 valuesToTransfer, | ||||
|                 valueMultiplier, | ||||
|                 receiverCallbackData, | ||||
|                 authorizedSender, | ||||
|                 extraData, | ||||
|             ), | ||||
|         ); | ||||
|         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,19 +1,18 @@ | ||||
| import { artifacts as tokensArtifacts, DummyERC20TokenContract } from '@0x/contracts-erc20'; | ||||
| 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 { Provider } from 'ethereum-types'; | ||||
| import { ZeroExProvider } from 'ethereum-types'; | ||||
| import * as _ from 'lodash'; | ||||
|  | ||||
| import { ERC20ProxyContract } from '../../generated-wrappers/erc20_proxy'; | ||||
| import { artifacts } from '../../src/artifacts'; | ||||
| import { artifacts, ERC20ProxyContract } from '../../src'; | ||||
|  | ||||
| export class ERC20Wrapper { | ||||
|     private readonly _tokenOwnerAddresses: string[]; | ||||
|     private readonly _contractOwnerAddress: string; | ||||
|     private readonly _web3Wrapper: Web3Wrapper; | ||||
|     private readonly _provider: Provider; | ||||
|     private readonly _provider: ZeroExProvider; | ||||
|     private readonly _dummyTokenContracts: DummyERC20TokenContract[]; | ||||
|     private _proxyContract?: ERC20ProxyContract; | ||||
|     private _proxyIdIfExists?: string; | ||||
| @@ -24,7 +23,7 @@ export class ERC20Wrapper { | ||||
|      * @param contractOwnerAddress Desired owner of the contract | ||||
|      * Instance of ERC20Wrapper | ||||
|      */ | ||||
|     constructor(provider: Provider, tokenOwnerAddresses: string[], contractOwnerAddress: string) { | ||||
|     constructor(provider: ZeroExProvider, tokenOwnerAddresses: string[], contractOwnerAddress: string) { | ||||
|         this._dummyTokenContracts = []; | ||||
|         this._web3Wrapper = new Web3Wrapper(provider); | ||||
|         this._provider = provider; | ||||
| @@ -38,7 +37,7 @@ export class ERC20Wrapper { | ||||
|         for (let i = 0; i < numberToDeploy; i++) { | ||||
|             this._dummyTokenContracts.push( | ||||
|                 await DummyERC20TokenContract.deployFrom0xArtifactAsync( | ||||
|                     tokensArtifacts.DummyERC20Token, | ||||
|                     erc20Artifacts.DummyERC20Token, | ||||
|                     this._provider, | ||||
|                     txDefaults, | ||||
|                     constants.DUMMY_TOKEN_NAME, | ||||
| @@ -134,7 +133,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); | ||||
| @@ -143,7 +142,7 @@ export class ERC20Wrapper { | ||||
|         return balancesByOwner; | ||||
|     } | ||||
|     public addDummyTokenContract(dummy: DummyERC20TokenContract): void { | ||||
|         if (!_.isUndefined(this._dummyTokenContracts)) { | ||||
|         if (this._dummyTokenContracts !== undefined) { | ||||
|             this._dummyTokenContracts.push(dummy); | ||||
|         } | ||||
|     } | ||||
| @@ -161,18 +160,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,24 +1,23 @@ | ||||
| import { artifacts as tokensArtifacts, DummyERC721TokenContract } from '@0x/contracts-erc721'; | ||||
| 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 { Provider } from 'ethereum-types'; | ||||
| import { ZeroExProvider } from 'ethereum-types'; | ||||
| import * as _ from 'lodash'; | ||||
|  | ||||
| import { ERC721ProxyContract } from '../../generated-wrappers/erc721_proxy'; | ||||
| import { artifacts } from '../../src/artifacts'; | ||||
| import { artifacts, ERC721ProxyContract } from '../../src'; | ||||
|  | ||||
| export class ERC721Wrapper { | ||||
|     private readonly _tokenOwnerAddresses: string[]; | ||||
|     private readonly _contractOwnerAddress: string; | ||||
|     private readonly _web3Wrapper: Web3Wrapper; | ||||
|     private readonly _provider: Provider; | ||||
|     private readonly _provider: ZeroExProvider; | ||||
|     private readonly _dummyTokenContracts: DummyERC721TokenContract[]; | ||||
|     private _proxyContract?: ERC721ProxyContract; | ||||
|     private _proxyIdIfExists?: string; | ||||
|     private _initialTokenIdsByOwner: ERC721TokenIdsByOwner = {}; | ||||
|     constructor(provider: Provider, tokenOwnerAddresses: string[], contractOwnerAddress: string) { | ||||
|     constructor(provider: ZeroExProvider, tokenOwnerAddresses: string[], contractOwnerAddress: string) { | ||||
|         this._web3Wrapper = new Web3Wrapper(provider); | ||||
|         this._provider = provider; | ||||
|         this._dummyTokenContracts = []; | ||||
| @@ -30,7 +29,7 @@ export class ERC721Wrapper { | ||||
|         for (const i of _.times(constants.NUM_DUMMY_ERC721_TO_DEPLOY)) { | ||||
|             this._dummyTokenContracts.push( | ||||
|                 await DummyERC721TokenContract.deployFrom0xArtifactAsync( | ||||
|                     tokensArtifacts.DummyERC721Token, | ||||
|                     erc721Artifacts.DummyERC721Token, | ||||
|                     this._provider, | ||||
|                     txDefaults, | ||||
|                     constants.DUMMY_TOKEN_NAME, | ||||
| @@ -63,12 +62,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); | ||||
| @@ -190,12 +189,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); | ||||
| @@ -211,18 +210,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'; | ||||
|   | ||||
| @@ -1,19 +1,16 @@ | ||||
| { | ||||
|     "extends": "../../tsconfig", | ||||
|     "compilerOptions": { | ||||
|         "outDir": "lib", | ||||
|         "rootDir": ".", | ||||
|         "resolveJsonModule": true | ||||
|     }, | ||||
|     "compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true }, | ||||
|     "include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"], | ||||
|     "files": [ | ||||
|         "./generated-artifacts/IAssetData.json", | ||||
|         "./generated-artifacts/IAssetProxy.json", | ||||
|         "./generated-artifacts/IAuthorizable.json", | ||||
|         "./generated-artifacts/ERC20Proxy.json", | ||||
|         "./generated-artifacts/ERC721Proxy.json", | ||||
|         "./generated-artifacts/MixinAuthorizable.json", | ||||
|         "./generated-artifacts/MultiAssetProxy.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" | ||||
|     ], | ||||
|     "exclude": ["./deploy/solc/solc_bin"] | ||||
| } | ||||
|   | ||||
							
								
								
									
										47
									
								
								contracts/coordinator/CHANGELOG.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								contracts/coordinator/CHANGELOG.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | ||||
| [ | ||||
|     { | ||||
|         "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 | ||||
|     } | ||||
| ] | ||||
							
								
								
									
										22
									
								
								contracts/coordinator/CHANGELOG.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								contracts/coordinator/CHANGELOG.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| <!-- | ||||
| changelogUtils.file is auto-generated using the monorepo-scripts package. Don't edit directly. | ||||
| Edit the package's CHANGELOG.json file only. | ||||
| --> | ||||
|  | ||||
| CHANGELOG | ||||
|  | ||||
| ## v2.0.0 - _April 11, 2019_ | ||||
|  | ||||
|     * Make `decodeOrdersFromFillData`, `getCoordinatorApprovalHash`, and `getTransactionHash` public (#1729) | ||||
|     * Make `assertValidTransactionOrdersApproval` internal (#1729) | ||||
|  | ||||
| ## v1.1.0 - _March 21, 2019_ | ||||
|  | ||||
|     * Run Web3ProviderEngine without excess block polling (#1695) | ||||
|  | ||||
| ## v1.0.0 - _March 20, 2019_ | ||||
|  | ||||
|     * Created Coordinator package | ||||
|     * Use separate EIP712 domains for transactions and approvals (#1705) | ||||
|     * Add `SignatureType.Invalid` (#1705) | ||||
|     * Set `evmVersion` to `constantinople` (#1707) | ||||
							
								
								
									
										1
									
								
								contracts/coordinator/DEPLOYS.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								contracts/coordinator/DEPLOYS.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| [] | ||||
							
								
								
									
										73
									
								
								contracts/coordinator/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								contracts/coordinator/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | ||||
| ## Coordinator | ||||
|  | ||||
| This package contains a contract that allows users to call arbitrary functions on the Exchange contract with permission from one or more Coordinators. Addresses of the deployed contracts can be found in 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-coordinator --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-coordinator yarn build | ||||
| ``` | ||||
|  | ||||
| Or continuously rebuild on change: | ||||
|  | ||||
| ```bash | ||||
| PKG=@0x/contracts-coordinator yarn watch | ||||
| ``` | ||||
|  | ||||
| ### Clean | ||||
|  | ||||
| ```bash | ||||
| yarn clean | ||||
| ``` | ||||
|  | ||||
| ### Lint | ||||
|  | ||||
| ```bash | ||||
| yarn lint | ||||
| ``` | ||||
|  | ||||
| ### Run Tests | ||||
|  | ||||
| ```bash | ||||
| yarn test | ||||
| ``` | ||||
|  | ||||
| #### Testing options | ||||
|  | ||||
| Contracts testing options like coverage, profiling, revert traces or backing node choosing - are described [here](../TESTING.md). | ||||
							
								
								
									
										25
									
								
								contracts/coordinator/compiler.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								contracts/coordinator/compiler.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| { | ||||
|     "artifactsDir": "./generated-artifacts", | ||||
|     "contractsDir": "./contracts", | ||||
|     "useDockerisedSolc": true, | ||||
|     "compilerSettings": { | ||||
|         "evmVersion": "constantinople", | ||||
|         "optimizer": { | ||||
|             "enabled": true, | ||||
|             "runs": 1000000, | ||||
|             "details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true } | ||||
|         }, | ||||
|         "outputSelection": { | ||||
|             "*": { | ||||
|                 "*": [ | ||||
|                     "abi", | ||||
|                     "evm.bytecode.object", | ||||
|                     "evm.bytecode.sourceMap", | ||||
|                     "evm.deployedBytecode.object", | ||||
|                     "evm.deployedBytecode.sourceMap" | ||||
|                 ] | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|     "contracts": ["src/Coordinator.sol", "src/registry/CoordinatorRegistry.sol"] | ||||
| } | ||||
							
								
								
									
										39
									
								
								contracts/coordinator/contracts/src/Coordinator.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								contracts/coordinator/contracts/src/Coordinator.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; | ||||
| pragma experimental "ABIEncoderV2"; | ||||
|  | ||||
| import "./libs/LibConstants.sol"; | ||||
| import "./MixinSignatureValidator.sol"; | ||||
| import "./MixinCoordinatorApprovalVerifier.sol"; | ||||
| import "./MixinCoordinatorCore.sol"; | ||||
|  | ||||
|  | ||||
| // solhint-disable no-empty-blocks | ||||
| contract Coordinator is | ||||
|     LibConstants, | ||||
|     MixinSignatureValidator, | ||||
|     MixinCoordinatorApprovalVerifier, | ||||
|     MixinCoordinatorCore | ||||
| { | ||||
|     constructor (address _exchange) | ||||
|         public | ||||
|         LibConstants(_exchange) | ||||
|     {} | ||||
| } | ||||
| @@ -0,0 +1,203 @@ | ||||
| /* | ||||
|  | ||||
|   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/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/LibCoordinatorApproval.sol"; | ||||
| import "./libs/LibZeroExTransaction.sol"; | ||||
| import "./mixins/MSignatureValidator.sol"; | ||||
| import "./mixins/MCoordinatorApprovalVerifier.sol"; | ||||
|  | ||||
|  | ||||
| // solhint-disable avoid-tx-origin | ||||
| contract MixinCoordinatorApprovalVerifier is | ||||
|     LibExchangeSelectors, | ||||
|     LibCoordinatorApproval, | ||||
|     LibZeroExTransaction, | ||||
|     MSignatureValidator, | ||||
|     MCoordinatorApprovalVerifier | ||||
| { | ||||
|     using LibBytes for bytes; | ||||
|     using LibAddressArray for address[]; | ||||
|  | ||||
|     /// @dev Validates that the 0x transaction has been approved by all of the feeRecipients | ||||
|     ///      that correspond to each order in the transaction's Exchange calldata. | ||||
|     /// @param transaction 0x transaction containing salt, signerAddress, and data. | ||||
|     /// @param txOrigin Required signer of Ethereum transaction calling this function. | ||||
|     /// @param transactionSignature Proof that the transaction has been signed by the signer. | ||||
|     /// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires. | ||||
|     /// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order in the transaction's Exchange calldata. | ||||
|     function assertValidCoordinatorApprovals( | ||||
|         LibZeroExTransaction.ZeroExTransaction memory transaction, | ||||
|         address txOrigin, | ||||
|         bytes memory transactionSignature, | ||||
|         uint256[] memory approvalExpirationTimeSeconds, | ||||
|         bytes[] memory approvalSignatures | ||||
|     ) | ||||
|         public | ||||
|         view | ||||
|     { | ||||
|         // Get the orders from the the Exchange calldata in the 0x transaction | ||||
|         LibOrder.Order[] memory orders = decodeOrdersFromFillData(transaction.data); | ||||
|  | ||||
|         // No approval is required for non-fill methods | ||||
|         if (orders.length > 0) { | ||||
|             // Revert if approval is invalid for transaction orders | ||||
|             assertValidTransactionOrdersApproval( | ||||
|                 transaction, | ||||
|                 orders, | ||||
|                 txOrigin, | ||||
|                 transactionSignature, | ||||
|                 approvalExpirationTimeSeconds, | ||||
|                 approvalSignatures | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// @dev Decodes the orders from Exchange calldata representing any fill method. | ||||
|     /// @param data Exchange calldata representing a fill method. | ||||
|     /// @return The orders from the Exchange calldata. | ||||
|     function decodeOrdersFromFillData(bytes memory data) | ||||
|         public | ||||
|         pure | ||||
|         returns (LibOrder.Order[] memory orders) | ||||
|     { | ||||
|         bytes4 selector = data.readBytes4(0); | ||||
|         if ( | ||||
|             selector == FILL_ORDER_SELECTOR || | ||||
|             selector == FILL_ORDER_NO_THROW_SELECTOR || | ||||
|             selector == FILL_OR_KILL_ORDER_SELECTOR | ||||
|         ) { | ||||
|             // Decode single order | ||||
|             (LibOrder.Order memory order) = abi.decode( | ||||
|                 data.slice(4, data.length), | ||||
|                 (LibOrder.Order) | ||||
|             ); | ||||
|             orders = new LibOrder.Order[](1); | ||||
|             orders[0] = order; | ||||
|         } else if ( | ||||
|             selector == BATCH_FILL_ORDERS_SELECTOR || | ||||
|             selector == BATCH_FILL_ORDERS_NO_THROW_SELECTOR || | ||||
|             selector == BATCH_FILL_OR_KILL_ORDERS_SELECTOR || | ||||
|             selector == MARKET_BUY_ORDERS_SELECTOR || | ||||
|             selector == MARKET_BUY_ORDERS_NO_THROW_SELECTOR || | ||||
|             selector == MARKET_SELL_ORDERS_SELECTOR || | ||||
|             selector == MARKET_SELL_ORDERS_NO_THROW_SELECTOR | ||||
|         ) { | ||||
|             // Decode all orders | ||||
|             // solhint-disable indent | ||||
|             (orders) = abi.decode( | ||||
|                 data.slice(4, data.length), | ||||
|                 (LibOrder.Order[]) | ||||
|             ); | ||||
|         } else if (selector == MATCH_ORDERS_SELECTOR) { | ||||
|             // Decode left and right orders | ||||
|             (LibOrder.Order memory leftOrder, LibOrder.Order memory rightOrder) = abi.decode( | ||||
|                 data.slice(4, data.length), | ||||
|                 (LibOrder.Order, LibOrder.Order) | ||||
|             ); | ||||
|  | ||||
|             // Create array of orders | ||||
|             orders = new LibOrder.Order[](2); | ||||
|             orders[0] = leftOrder; | ||||
|             orders[1] = rightOrder; | ||||
|         } | ||||
|         return orders; | ||||
|     } | ||||
|  | ||||
|     /// @dev Validates that the feeRecipients of a batch of order have approved a 0x transaction. | ||||
|     /// @param transaction 0x transaction containing salt, signerAddress, and data. | ||||
|     /// @param orders Array of order structs containing order specifications. | ||||
|     /// @param txOrigin Required signer of Ethereum transaction calling this function. | ||||
|     /// @param transactionSignature Proof that the transaction has been signed by the signer. | ||||
|     /// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires. | ||||
|     /// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order. | ||||
|     function assertValidTransactionOrdersApproval( | ||||
|         LibZeroExTransaction.ZeroExTransaction memory transaction, | ||||
|         LibOrder.Order[] memory orders, | ||||
|         address txOrigin, | ||||
|         bytes memory transactionSignature, | ||||
|         uint256[] memory approvalExpirationTimeSeconds, | ||||
|         bytes[] memory approvalSignatures | ||||
|     ) | ||||
|         internal | ||||
|         view | ||||
|     { | ||||
|         // Verify that Ethereum tx signer is the same as the approved txOrigin | ||||
|         require( | ||||
|             tx.origin == txOrigin, | ||||
|             "INVALID_ORIGIN" | ||||
|         ); | ||||
|  | ||||
|         // Hash 0x transaction | ||||
|         bytes32 transactionHash = getTransactionHash(transaction); | ||||
|  | ||||
|         // Create empty list of approval signers | ||||
|         address[] memory approvalSignerAddresses = new address[](0); | ||||
|  | ||||
|         uint256 signaturesLength = approvalSignatures.length; | ||||
|         for (uint256 i = 0; i != signaturesLength; i++) { | ||||
|             // Create approval message | ||||
|             uint256 currentApprovalExpirationTimeSeconds = approvalExpirationTimeSeconds[i]; | ||||
|             CoordinatorApproval memory approval = CoordinatorApproval({ | ||||
|                 txOrigin: txOrigin, | ||||
|                 transactionHash: transactionHash, | ||||
|                 transactionSignature: transactionSignature, | ||||
|                 approvalExpirationTimeSeconds: currentApprovalExpirationTimeSeconds | ||||
|             }); | ||||
|  | ||||
|             // Ensure approval has not expired | ||||
|             require( | ||||
|                 // solhint-disable-next-line not-rely-on-time | ||||
|                 currentApprovalExpirationTimeSeconds > block.timestamp, | ||||
|                 "APPROVAL_EXPIRED" | ||||
|             ); | ||||
|  | ||||
|             // Hash approval message and recover signer address | ||||
|             bytes32 approvalHash = getCoordinatorApprovalHash(approval); | ||||
|             address approvalSignerAddress = getSignerAddress(approvalHash, approvalSignatures[i]); | ||||
|  | ||||
|             // Add approval signer to list of signers | ||||
|             approvalSignerAddresses = approvalSignerAddresses.append(approvalSignerAddress); | ||||
|         } | ||||
|  | ||||
|         // Ethereum transaction signer gives implicit signature of approval | ||||
|         approvalSignerAddresses = approvalSignerAddresses.append(tx.origin); | ||||
|  | ||||
|         uint256 ordersLength = orders.length; | ||||
|         for (uint256 i = 0; i != ordersLength; i++) { | ||||
|             // Do not check approval if the order's senderAddress is null | ||||
|             if (orders[i].senderAddress == address(0)) { | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             // Ensure feeRecipient of order has approved this 0x transaction | ||||
|             address approverAddress = orders[i].feeRecipientAddress; | ||||
|             bool isOrderApproved = approvalSignerAddresses.contains(approverAddress); | ||||
|             require( | ||||
|                 isOrderApproved, | ||||
|                 "INVALID_APPROVAL_SIGNATURE" | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										65
									
								
								contracts/coordinator/contracts/src/MixinCoordinatorCore.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								contracts/coordinator/contracts/src/MixinCoordinatorCore.sol
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | ||||
| /* | ||||
|  | ||||
|   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 "./libs/LibZeroExTransaction.sol"; | ||||
| import "./libs/LibConstants.sol"; | ||||
| import "./mixins/MCoordinatorApprovalVerifier.sol"; | ||||
| import "./interfaces/ICoordinatorCore.sol"; | ||||
|  | ||||
|  | ||||
| contract MixinCoordinatorCore is | ||||
|     LibConstants, | ||||
|     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 | ||||
|     ) | ||||
|         public | ||||
|     { | ||||
|         // Validate that the 0x transaction has been approves by each feeRecipient | ||||
|         assertValidCoordinatorApprovals( | ||||
|             transaction, | ||||
|             txOrigin, | ||||
|             transactionSignature, | ||||
|             approvalExpirationTimeSeconds, | ||||
|             approvalSignatures | ||||
|         ); | ||||
|  | ||||
|         // Execute the transaction | ||||
|         EXCHANGE.executeTransaction( | ||||
|             transaction.salt, | ||||
|             transaction.signerAddress, | ||||
|             transaction.data, | ||||
|             transactionSignature | ||||
|         ); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										118
									
								
								contracts/coordinator/contracts/src/MixinSignatureValidator.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								contracts/coordinator/contracts/src/MixinSignatureValidator.sol
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,118 @@ | ||||
| /* | ||||
|  | ||||
|   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 "@0x/contracts-utils/contracts/src/LibBytes.sol"; | ||||
| import "./mixins/MSignatureValidator.sol"; | ||||
|  | ||||
|  | ||||
| contract MixinSignatureValidator is | ||||
|     MSignatureValidator | ||||
| { | ||||
|     using LibBytes for bytes; | ||||
|  | ||||
|     /// @dev Recovers the address of a signer given a hash and signature. | ||||
|     /// @param hash Any 32 byte hash. | ||||
|     /// @param signature Proof that the hash has been signed by signer. | ||||
|     function getSignerAddress(bytes32 hash, bytes memory signature) | ||||
|         public | ||||
|         pure | ||||
|         returns (address signerAddress) | ||||
|     { | ||||
|         require( | ||||
|             signature.length > 0, | ||||
|             "LENGTH_GREATER_THAN_0_REQUIRED" | ||||
|         ); | ||||
|  | ||||
|         // Pop last byte off of signature byte array. | ||||
|         uint8 signatureTypeRaw = uint8(signature.popLastByte()); | ||||
|  | ||||
|         // Ensure signature is supported | ||||
|         require( | ||||
|             signatureTypeRaw < uint8(SignatureType.NSignatureTypes), | ||||
|             "SIGNATURE_UNSUPPORTED" | ||||
|         ); | ||||
|  | ||||
|         SignatureType signatureType = SignatureType(signatureTypeRaw); | ||||
|  | ||||
|         // Always illegal signature. | ||||
|         // This is always an implicit option since a signer can create a | ||||
|         // signature array with invalid type or length. We may as well make | ||||
|         // it an explicit option. This aids testing and analysis. It is | ||||
|         // also the initialization value for the enum type. | ||||
|         if (signatureType == SignatureType.Illegal) { | ||||
|             revert("SIGNATURE_ILLEGAL"); | ||||
|  | ||||
|         // Always invalid signature. | ||||
|         // Like Illegal, this is always implicitly available and therefore | ||||
|         // offered explicitly. It can be implicitly created by providing | ||||
|         // a correctly formatted but incorrect signature. | ||||
|         } else if (signatureType == SignatureType.Invalid) { | ||||
|             require( | ||||
|                 signature.length == 0, | ||||
|                 "LENGTH_0_REQUIRED" | ||||
|             ); | ||||
|             revert("SIGNATURE_INVALID"); | ||||
|  | ||||
|         // Signature using EIP712 | ||||
|         } else if (signatureType == SignatureType.EIP712) { | ||||
|             require( | ||||
|                 signature.length == 65, | ||||
|                 "LENGTH_65_REQUIRED" | ||||
|             ); | ||||
|             uint8 v = uint8(signature[0]); | ||||
|             bytes32 r = signature.readBytes32(1); | ||||
|             bytes32 s = signature.readBytes32(33); | ||||
|             signerAddress = ecrecover( | ||||
|                 hash, | ||||
|                 v, | ||||
|                 r, | ||||
|                 s | ||||
|             ); | ||||
|             return signerAddress; | ||||
|  | ||||
|         // Signed using web3.eth_sign | ||||
|         } else if (signatureType == SignatureType.EthSign) { | ||||
|             require( | ||||
|                 signature.length == 65, | ||||
|                 "LENGTH_65_REQUIRED" | ||||
|             ); | ||||
|             uint8 v = uint8(signature[0]); | ||||
|             bytes32 r = signature.readBytes32(1); | ||||
|             bytes32 s = signature.readBytes32(33); | ||||
|             signerAddress = ecrecover( | ||||
|                 keccak256(abi.encodePacked( | ||||
|                     "\x19Ethereum Signed Message:\n32", | ||||
|                     hash | ||||
|                 )), | ||||
|                 v, | ||||
|                 r, | ||||
|                 s | ||||
|             ); | ||||
|             return signerAddress; | ||||
|         } | ||||
|  | ||||
|         // Anything else is illegal (We do not return false because | ||||
|         // the signature may actually be valid, just not in a format | ||||
|         // that we currently support. In this case returning false | ||||
|         // may lead the caller to incorrectly believe that the | ||||
|         // signature was invalid.) | ||||
|         revert("SIGNATURE_UNSUPPORTED"); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,52 @@ | ||||
| /* | ||||
|  | ||||
|   Copyright 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 "../libs/LibZeroExTransaction.sol"; | ||||
|  | ||||
|  | ||||
| contract ICoordinatorApprovalVerifier { | ||||
|  | ||||
|     /// @dev Validates that the 0x transaction has been approved by all of the feeRecipients | ||||
|     ///      that correspond to each order in the transaction's Exchange calldata. | ||||
|     /// @param transaction 0x transaction containing salt, signerAddress, and data. | ||||
|     /// @param txOrigin Required signer of Ethereum transaction calling this function. | ||||
|     /// @param transactionSignature Proof that the transaction has been signed by the signer. | ||||
|     /// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires. | ||||
|     /// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order in the transaction's Exchange calldata. | ||||
|     function assertValidCoordinatorApprovals( | ||||
|         LibZeroExTransaction.ZeroExTransaction memory transaction, | ||||
|         address txOrigin, | ||||
|         bytes memory transactionSignature, | ||||
|         uint256[] memory approvalExpirationTimeSeconds, | ||||
|         bytes[] memory approvalSignatures | ||||
|     ) | ||||
|         public | ||||
|         view; | ||||
|  | ||||
|     /// @dev Decodes the orders from Exchange calldata representing any fill method. | ||||
|     /// @param data Exchange calldata representing a fill method. | ||||
|     /// @return The orders from the Exchange calldata. | ||||
|     function decodeOrdersFromFillData(bytes memory data) | ||||
|         public | ||||
|         pure | ||||
|         returns (LibOrder.Order[] memory orders); | ||||
| } | ||||
| @@ -0,0 +1,41 @@ | ||||
| /* | ||||
|  | ||||
|   Copyright 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 "../libs/LibZeroExTransaction.sol"; | ||||
|  | ||||
|  | ||||
| contract ICoordinatorCore { | ||||
|  | ||||
|     /// @dev Executes a 0x transaction that has been signed by the feeRecipients that correspond to each order in the transaction's Exchange calldata. | ||||
|     /// @param transaction 0x transaction containing salt, signerAddress, and data. | ||||
|     /// @param txOrigin Required signer of Ethereum transaction calling this function. | ||||
|     /// @param transactionSignature Proof that the transaction has been signed by the signer. | ||||
|     /// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires. | ||||
|     /// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order in the transaction's Exchange calldata. | ||||
|     function executeTransaction( | ||||
|         LibZeroExTransaction.ZeroExTransaction memory transaction, | ||||
|         address txOrigin, | ||||
|         bytes memory transactionSignature, | ||||
|         uint256[] memory approvalExpirationTimeSeconds, | ||||
|         bytes[] memory approvalSignatures | ||||
|     ) | ||||
|         public; | ||||
| } | ||||
| @@ -0,0 +1,31 @@ | ||||
| /* | ||||
|  | ||||
|   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 ISignatureValidator { | ||||
|  | ||||
|     /// @dev Recovers the address of a signer given a hash and signature. | ||||
|     /// @param hash Any 32 byte hash. | ||||
|     /// @param signature Proof that the hash has been signed by signer. | ||||
|     function getSignerAddress(bytes32 hash, bytes memory signature) | ||||
|         public | ||||
|         pure | ||||
|         returns (address signerAddress); | ||||
| } | ||||
| @@ -0,0 +1,35 @@ | ||||
| /* | ||||
|  | ||||
|   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 ITransactions { | ||||
|  | ||||
|     /// @dev Executes an exchange method call in the context of signer. | ||||
|     /// @param salt Arbitrary number to ensure uniqueness of transaction hash. | ||||
|     /// @param signerAddress Address of transaction signer. | ||||
|     /// @param data AbiV2 encoded calldata. | ||||
|     /// @param signature Proof of signer transaction by signer. | ||||
|     function executeTransaction( | ||||
|         uint256 salt, | ||||
|         address signerAddress, | ||||
|         bytes calldata data, | ||||
|         bytes calldata signature | ||||
|     ) | ||||
|         external; | ||||
| } | ||||
							
								
								
									
										34
									
								
								contracts/coordinator/contracts/src/libs/LibConstants.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								contracts/coordinator/contracts/src/libs/LibConstants.sol
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| /* | ||||
|  | ||||
|   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/ITransactions.sol"; | ||||
|  | ||||
|  | ||||
| contract LibConstants { | ||||
|  | ||||
|      // solhint-disable-next-line var-name-mixedcase | ||||
|     ITransactions internal EXCHANGE; | ||||
|  | ||||
|     constructor (address _exchange) | ||||
|         public | ||||
|     { | ||||
|         EXCHANGE = ITransactions(_exchange); | ||||
|     } | ||||
| } | ||||
| @@ -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; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,95 @@ | ||||
| /* | ||||
|  | ||||
|   Copyright 2018 ZeroEx Intl. | ||||
|  | ||||
|   Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|   you may not use this file except in compliance with the License. | ||||
|   You may obtain a copy of the License at | ||||
|  | ||||
|     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
|   Unless required by applicable law or agreed to in writing, software | ||||
|   distributed under the License is distributed on an "AS IS" BASIS, | ||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|   See the License for the specific language governing permissions and | ||||
|   limitations under the License. | ||||
|  | ||||
| */ | ||||
|  | ||||
| pragma solidity ^0.5.5; | ||||
| pragma experimental "ABIEncoderV2"; | ||||
|  | ||||
| import "./LibEIP712Domain.sol"; | ||||
|  | ||||
|  | ||||
| contract LibZeroExTransaction is | ||||
|     LibEIP712Domain | ||||
| { | ||||
|     // Hash for the EIP712 0x transaction schema | ||||
|     // keccak256(abi.encodePacked( | ||||
|     //    "ZeroExTransaction(", | ||||
|     //    "uint256 salt,", | ||||
|     //    "address signerAddress,", | ||||
|     //    "bytes data", | ||||
|     //    ")" | ||||
|     // )); | ||||
|     bytes32 constant internal EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH = 0x213c6f636f3ea94e701c0adf9b2624aa45a6c694f9a292c094f9a81c24b5df4c; | ||||
|  | ||||
|     struct ZeroExTransaction { | ||||
|         uint256 salt;           // Arbitrary number to ensure uniqueness of transaction hash. | ||||
|         address signerAddress;  // Address of transaction signer. | ||||
|         bytes data;             // AbiV2 encoded calldata. | ||||
|     } | ||||
|  | ||||
|     /// @dev Calculates the EIP712 hash of a 0x transaction using the domain separator of the Exchange contract. | ||||
|     /// @param transaction 0x transaction containing salt, signerAddress, and data. | ||||
|     /// @return EIP712 hash of the transaction with the domain separator of this contract. | ||||
|     function getTransactionHash(ZeroExTransaction memory transaction) | ||||
|         public | ||||
|         view | ||||
|         returns (bytes32 transactionHash) | ||||
|     { | ||||
|         // Hash the transaction with the domain separator of the Exchange contract. | ||||
|         transactionHash = hashEIP712ExchangeMessage(hashZeroExTransaction(transaction)); | ||||
|         return transactionHash; | ||||
|     } | ||||
|  | ||||
|     /// @dev Calculates EIP712 hash of the 0x transaction with no domain separator. | ||||
|     /// @param transaction 0x transaction containing salt, signerAddress, and data. | ||||
|     /// @return EIP712 hash of the transaction with no domain separator. | ||||
|     function hashZeroExTransaction(ZeroExTransaction memory transaction) | ||||
|         internal | ||||
|         pure | ||||
|         returns (bytes32 result) | ||||
|     { | ||||
|         bytes32 schemaHash = EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH; | ||||
|         bytes memory data = transaction.data; | ||||
|         uint256 salt = transaction.salt; | ||||
|         address signerAddress = transaction.signerAddress; | ||||
|  | ||||
|         // Assembly for more efficiently computing: | ||||
|         // keccak256(abi.encodePacked( | ||||
|         //     EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH, | ||||
|         //     transaction.salt, | ||||
|         //     uint256(transaction.signerAddress), | ||||
|         //     keccak256(transaction.data) | ||||
|         // )); | ||||
|  | ||||
|         assembly { | ||||
|             // Compute hash of data | ||||
|             let dataHash := keccak256(add(data, 32), mload(data)) | ||||
|  | ||||
|             // Load free memory pointer | ||||
|             let memPtr := mload(64) | ||||
|  | ||||
|             mstore(memPtr, schemaHash)                                                               // hash of schema | ||||
|             mstore(add(memPtr, 32), salt)                                                            // salt | ||||
|             mstore(add(memPtr, 64), and(signerAddress, 0xffffffffffffffffffffffffffffffffffffffff))  // signerAddress | ||||
|             mstore(add(memPtr, 96), dataHash)                                                        // hash of data | ||||
|  | ||||
|             // Compute hash | ||||
|             result := keccak256(memPtr, 128) | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,46 @@ | ||||
| /* | ||||
|  | ||||
|   Copyright 2018 ZeroEx Intl. | ||||
|  | ||||
|   Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|   you may not use this file except in compliance with the License. | ||||
|   You may obtain a copy of the License at | ||||
|  | ||||
|     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
|   Unless required by applicable law or agreed to in writing, software | ||||
|   distributed under the License is distributed on an "AS IS" BASIS, | ||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|   See the License for the specific language governing permissions and | ||||
|   limitations under the License. | ||||
|  | ||||
| */ | ||||
|  | ||||
| pragma solidity ^0.5.5; | ||||
| pragma experimental "ABIEncoderV2"; | ||||
|  | ||||
| import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol"; | ||||
| import "../interfaces/ICoordinatorApprovalVerifier.sol"; | ||||
|  | ||||
|  | ||||
| contract MCoordinatorApprovalVerifier is | ||||
|     ICoordinatorApprovalVerifier | ||||
| { | ||||
|     /// @dev Validates that the feeRecipients of a batch of order have approved a 0x transaction. | ||||
|     /// @param transaction 0x transaction containing salt, signerAddress, and data. | ||||
|     /// @param orders Array of order structs containing order specifications. | ||||
|     /// @param txOrigin Required signer of Ethereum transaction calling this function. | ||||
|     /// @param transactionSignature Proof that the transaction has been signed by the signer. | ||||
|     /// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires. | ||||
|     /// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order. | ||||
|     function assertValidTransactionOrdersApproval( | ||||
|         LibZeroExTransaction.ZeroExTransaction memory transaction, | ||||
|         LibOrder.Order[] memory orders, | ||||
|         address txOrigin, | ||||
|         bytes memory transactionSignature, | ||||
|         uint256[] memory approvalExpirationTimeSeconds, | ||||
|         bytes[] memory approvalSignatures | ||||
|     ) | ||||
|         internal | ||||
|         view; | ||||
| } | ||||
| @@ -0,0 +1,35 @@ | ||||
| /* | ||||
|  | ||||
|   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/ISignatureValidator.sol"; | ||||
|  | ||||
|  | ||||
| contract MSignatureValidator is | ||||
|     ISignatureValidator | ||||
| { | ||||
|     // Allowed signature types. | ||||
|     enum SignatureType { | ||||
|         Illegal,         // 0x00, default value | ||||
|         Invalid,         // 0x01 | ||||
|         EIP712,          // 0x02 | ||||
|         EthSign,         // 0x03 | ||||
|         NSignatureTypes  // 0x04, number of signature types. Always leave at end. | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,32 @@ | ||||
| /* | ||||
|  | ||||
|   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 "./MixinCoordinatorRegistryCore.sol"; | ||||
|  | ||||
|  | ||||
| // solhint-disable no-empty-blocks | ||||
| 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.0", | ||||
|     "engines": { | ||||
|         "node": ">=6.12" | ||||
|     }, | ||||
|     "description": "Smart contract extensions of 0x protocol", | ||||
|     "main": "lib/src/index.js", | ||||
|     "directories": { | ||||
|         "test": "test" | ||||
|     }, | ||||
|     "scripts": { | ||||
|         "build": "yarn pre_build && tsc -b", | ||||
|         "build:ci": "yarn build", | ||||
|         "pre_build": "run-s compile 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.0.9", | ||||
|         "@0x/contracts-gen": "^1.0.8", | ||||
|         "@0x/contracts-test-utils": "^3.1.2", | ||||
|         "@0x/dev-utils": "^2.2.1", | ||||
|         "@0x/sol-compiler": "^3.1.6", | ||||
|         "@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.0.5", | ||||
|         "@0x/contracts-asset-proxy": "^2.1.1", | ||||
|         "@0x/contracts-erc20": "^2.2.0", | ||||
|         "@0x/contracts-exchange": "1.0.2", | ||||
|         "@0x/contracts-exchange-libs": "^2.1.1", | ||||
|         "@0x/contracts-utils": "^3.1.1", | ||||
|         "@0x/order-utils": "^7.2.0", | ||||
|         "@0x/types": "^2.2.2", | ||||
|         "@0x/typescript-typings": "^4.2.2", | ||||
|         "@0x/utils": "^4.3.1", | ||||
|         "@0x/web3-wrapper": "^6.0.5", | ||||
|         "ethereum-types": "^2.1.2", | ||||
|         "ethereumjs-util": "^5.1.1", | ||||
|         "lodash": "^4.17.11" | ||||
|     }, | ||||
|     "publishConfig": { | ||||
|         "access": "public" | ||||
|     } | ||||
| } | ||||
							
								
								
									
										13
									
								
								contracts/coordinator/src/artifacts.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								contracts/coordinator/src/artifacts.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| /* | ||||
|  * ----------------------------------------------------------------------------- | ||||
|  * Warning: This file is auto-generated by contracts-gen. Don't edit manually. | ||||
|  * ----------------------------------------------------------------------------- | ||||
|  */ | ||||
| import { ContractArtifact } from 'ethereum-types'; | ||||
|  | ||||
| import * as Coordinator from '../generated-artifacts/Coordinator.json'; | ||||
| import * as CoordinatorRegistry from '../generated-artifacts/CoordinatorRegistry.json'; | ||||
| export const artifacts = { | ||||
|     Coordinator: Coordinator as ContractArtifact, | ||||
|     CoordinatorRegistry: CoordinatorRegistry as ContractArtifact, | ||||
| }; | ||||
							
								
								
									
										3
									
								
								contracts/coordinator/src/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								contracts/coordinator/src/index.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| export * from './artifacts'; | ||||
| export * from './wrappers'; | ||||
| export * from '../test/utils'; | ||||
							
								
								
									
										7
									
								
								contracts/coordinator/src/wrappers.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								contracts/coordinator/src/wrappers.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| /* | ||||
|  * ----------------------------------------------------------------------------- | ||||
|  * Warning: This file is auto-generated by contracts-gen. Don't edit manually. | ||||
|  * ----------------------------------------------------------------------------- | ||||
|  */ | ||||
| export * from '../generated-wrappers/coordinator'; | ||||
| export * from '../generated-wrappers/coordinator_registry'; | ||||
							
								
								
									
										521
									
								
								contracts/coordinator/test/coordinator.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										521
									
								
								contracts/coordinator/test/coordinator.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,521 @@ | ||||
| import { ERC20ProxyContract, ERC20Wrapper } from '@0x/contracts-asset-proxy'; | ||||
| import { DummyERC20TokenContract } from '@0x/contracts-erc20'; | ||||
| import { | ||||
|     artifacts as exchangeArtifacts, | ||||
|     ExchangeCancelEventArgs, | ||||
|     ExchangeCancelUpToEventArgs, | ||||
|     ExchangeContract, | ||||
|     ExchangeFillEventArgs, | ||||
| } from '@0x/contracts-exchange'; | ||||
| import { | ||||
|     chaiSetup, | ||||
|     constants as devConstants, | ||||
|     expectTransactionFailedAsync, | ||||
|     getLatestBlockTimestampAsync, | ||||
|     OrderFactory, | ||||
|     provider, | ||||
|     TransactionFactory, | ||||
|     txDefaults, | ||||
|     web3Wrapper, | ||||
| } from '@0x/contracts-test-utils'; | ||||
| import { BlockchainLifecycle } from '@0x/dev-utils'; | ||||
| import { assetDataUtils, orderHashUtils } from '@0x/order-utils'; | ||||
| import { RevertReason, SignedOrder } from '@0x/types'; | ||||
| import { BigNumber } from '@0x/utils'; | ||||
| import * as chai from 'chai'; | ||||
| import { LogWithDecodedArgs } from 'ethereum-types'; | ||||
|  | ||||
| import { ApprovalFactory, artifacts, constants, CoordinatorContract, exchangeDataEncoder } from '../src'; | ||||
|  | ||||
| chaiSetup.configure(); | ||||
| const expect = chai.expect; | ||||
| const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); | ||||
| web3Wrapper.abiDecoder.addABI(exchangeArtifacts.Exchange.compilerOutput.abi); | ||||
| // tslint:disable:no-unnecessary-type-assertion | ||||
| describe('Coordinator tests', () => { | ||||
|     let makerAddress: string; | ||||
|     let owner: string; | ||||
|     let takerAddress: string; | ||||
|     let feeRecipientAddress: string; | ||||
|  | ||||
|     let erc20Proxy: ERC20ProxyContract; | ||||
|     let erc20TokenA: DummyERC20TokenContract; | ||||
|     let erc20TokenB: DummyERC20TokenContract; | ||||
|     let zrxToken: DummyERC20TokenContract; | ||||
|     let coordinatorContract: CoordinatorContract; | ||||
|     let exchange: ExchangeContract; | ||||
|  | ||||
|     let erc20Wrapper: ERC20Wrapper; | ||||
|     let orderFactory: OrderFactory; | ||||
|     let takerTransactionFactory: TransactionFactory; | ||||
|     let makerTransactionFactory: TransactionFactory; | ||||
|     let approvalFactory: ApprovalFactory; | ||||
|  | ||||
|     before(async () => { | ||||
|         await blockchainLifecycle.startAsync(); | ||||
|     }); | ||||
|     after(async () => { | ||||
|         await blockchainLifecycle.revertAsync(); | ||||
|     }); | ||||
|     before(async () => { | ||||
|         const accounts = await web3Wrapper.getAvailableAddressesAsync(); | ||||
|         const usedAddresses = ([owner, makerAddress, takerAddress, feeRecipientAddress] = accounts.slice(0, 4)); | ||||
|  | ||||
|         erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner); | ||||
|         erc20Proxy = await erc20Wrapper.deployProxyAsync(); | ||||
|         const numDummyErc20ToDeploy = 3; | ||||
|         [erc20TokenA, erc20TokenB, zrxToken] = await erc20Wrapper.deployDummyTokensAsync( | ||||
|             numDummyErc20ToDeploy, | ||||
|             devConstants.DUMMY_TOKEN_DECIMALS, | ||||
|         ); | ||||
|         await erc20Wrapper.setBalancesAndAllowancesAsync(); | ||||
|  | ||||
|         exchange = await ExchangeContract.deployFrom0xArtifactAsync( | ||||
|             exchangeArtifacts.Exchange, | ||||
|             provider, | ||||
|             txDefaults, | ||||
|             assetDataUtils.encodeERC20AssetData(zrxToken.address), | ||||
|         ); | ||||
|  | ||||
|         await web3Wrapper.awaitTransactionSuccessAsync( | ||||
|             await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { from: owner }), | ||||
|             devConstants.AWAIT_TRANSACTION_MINED_MS, | ||||
|         ); | ||||
|  | ||||
|         await web3Wrapper.awaitTransactionSuccessAsync( | ||||
|             await exchange.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, { from: owner }), | ||||
|             devConstants.AWAIT_TRANSACTION_MINED_MS, | ||||
|         ); | ||||
|  | ||||
|         coordinatorContract = await CoordinatorContract.deployFrom0xArtifactAsync( | ||||
|             artifacts.Coordinator, | ||||
|             provider, | ||||
|             txDefaults, | ||||
|             exchange.address, | ||||
|         ); | ||||
|  | ||||
|         // Configure order defaults | ||||
|         const defaultOrderParams = { | ||||
|             ...devConstants.STATIC_ORDER_PARAMS, | ||||
|             exchangeAddress: exchange.address, | ||||
|             senderAddress: coordinatorContract.address, | ||||
|             makerAddress, | ||||
|             feeRecipientAddress, | ||||
|             makerAssetData: assetDataUtils.encodeERC20AssetData(erc20TokenA.address), | ||||
|             takerAssetData: assetDataUtils.encodeERC20AssetData(erc20TokenB.address), | ||||
|         }; | ||||
|         const makerPrivateKey = devConstants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)]; | ||||
|         const takerPrivateKey = devConstants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(takerAddress)]; | ||||
|         const feeRecipientPrivateKey = devConstants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(feeRecipientAddress)]; | ||||
|         orderFactory = new OrderFactory(makerPrivateKey, defaultOrderParams); | ||||
|         makerTransactionFactory = new TransactionFactory(makerPrivateKey, exchange.address); | ||||
|         takerTransactionFactory = new TransactionFactory(takerPrivateKey, exchange.address); | ||||
|         approvalFactory = new ApprovalFactory(feeRecipientPrivateKey, coordinatorContract.address); | ||||
|     }); | ||||
|     beforeEach(async () => { | ||||
|         await blockchainLifecycle.startAsync(); | ||||
|     }); | ||||
|     afterEach(async () => { | ||||
|         await blockchainLifecycle.revertAsync(); | ||||
|     }); | ||||
|  | ||||
|     describe('single order fills', () => { | ||||
|         for (const fnName of constants.SINGLE_FILL_FN_NAMES) { | ||||
|             it(`${fnName} should fill the order with a signed approval`, async () => { | ||||
|                 const orders = [await orderFactory.newSignedOrderAsync()]; | ||||
|                 const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); | ||||
|                 const transaction = takerTransactionFactory.newSignedTransaction(data); | ||||
|                 const currentTimestamp = await getLatestBlockTimestampAsync(); | ||||
|                 const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER); | ||||
|                 const approval = approvalFactory.newSignedApproval( | ||||
|                     transaction, | ||||
|                     takerAddress, | ||||
|                     approvalExpirationTimeSeconds, | ||||
|                 ); | ||||
|                 const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync( | ||||
|                     await coordinatorContract.executeTransaction.sendTransactionAsync( | ||||
|                         transaction, | ||||
|                         takerAddress, | ||||
|                         transaction.signature, | ||||
|                         [approvalExpirationTimeSeconds], | ||||
|                         [approval.signature], | ||||
|                         { from: takerAddress }, | ||||
|                     ), | ||||
|                     devConstants.AWAIT_TRANSACTION_MINED_MS, | ||||
|                 ); | ||||
|                 const fillLogs = transactionReceipt.logs.filter( | ||||
|                     log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill', | ||||
|                 ); | ||||
|                 expect(fillLogs.length).to.eq(1); | ||||
|                 const fillLogArgs = (fillLogs[0] as LogWithDecodedArgs<ExchangeFillEventArgs>).args; | ||||
|                 expect(fillLogArgs.makerAddress).to.eq(makerAddress); | ||||
|                 expect(fillLogArgs.takerAddress).to.eq(takerAddress); | ||||
|                 expect(fillLogArgs.senderAddress).to.eq(coordinatorContract.address); | ||||
|                 expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress); | ||||
|                 expect(fillLogArgs.makerAssetData).to.eq(orders[0].makerAssetData); | ||||
|                 expect(fillLogArgs.takerAssetData).to.eq(orders[0].takerAssetData); | ||||
|                 expect(fillLogArgs.makerAssetFilledAmount).to.bignumber.eq(orders[0].makerAssetAmount); | ||||
|                 expect(fillLogArgs.takerAssetFilledAmount).to.bignumber.eq(orders[0].takerAssetAmount); | ||||
|                 expect(fillLogArgs.makerFeePaid).to.bignumber.eq(orders[0].makerFee); | ||||
|                 expect(fillLogArgs.takerFeePaid).to.bignumber.eq(orders[0].takerFee); | ||||
|                 expect(fillLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(orders[0])); | ||||
|             }); | ||||
|             it(`${fnName} should fill the order if called by approver`, async () => { | ||||
|                 const orders = [await orderFactory.newSignedOrderAsync()]; | ||||
|                 const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); | ||||
|                 const transaction = takerTransactionFactory.newSignedTransaction(data); | ||||
|                 const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync( | ||||
|                     await coordinatorContract.executeTransaction.sendTransactionAsync( | ||||
|                         transaction, | ||||
|                         feeRecipientAddress, | ||||
|                         transaction.signature, | ||||
|                         [], | ||||
|                         [], | ||||
|                         { from: feeRecipientAddress }, | ||||
|                     ), | ||||
|                     devConstants.AWAIT_TRANSACTION_MINED_MS, | ||||
|                 ); | ||||
|                 const fillLogs = transactionReceipt.logs.filter( | ||||
|                     log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill', | ||||
|                 ); | ||||
|                 expect(fillLogs.length).to.eq(1); | ||||
|                 const fillLogArgs = (fillLogs[0] as LogWithDecodedArgs<ExchangeFillEventArgs>).args; | ||||
|                 expect(fillLogArgs.makerAddress).to.eq(makerAddress); | ||||
|                 expect(fillLogArgs.takerAddress).to.eq(takerAddress); | ||||
|                 expect(fillLogArgs.senderAddress).to.eq(coordinatorContract.address); | ||||
|                 expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress); | ||||
|                 expect(fillLogArgs.makerAssetData).to.eq(orders[0].makerAssetData); | ||||
|                 expect(fillLogArgs.takerAssetData).to.eq(orders[0].takerAssetData); | ||||
|                 expect(fillLogArgs.makerAssetFilledAmount).to.bignumber.eq(orders[0].makerAssetAmount); | ||||
|                 expect(fillLogArgs.takerAssetFilledAmount).to.bignumber.eq(orders[0].takerAssetAmount); | ||||
|                 expect(fillLogArgs.makerFeePaid).to.bignumber.eq(orders[0].makerFee); | ||||
|                 expect(fillLogArgs.takerFeePaid).to.bignumber.eq(orders[0].takerFee); | ||||
|                 expect(fillLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(orders[0])); | ||||
|             }); | ||||
|             it(`${fnName} should revert with no approval signature`, async () => { | ||||
|                 const orders = [await orderFactory.newSignedOrderAsync()]; | ||||
|                 const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); | ||||
|                 const transaction = takerTransactionFactory.newSignedTransaction(data); | ||||
|                 await expectTransactionFailedAsync( | ||||
|                     coordinatorContract.executeTransaction.sendTransactionAsync( | ||||
|                         transaction, | ||||
|                         takerAddress, | ||||
|                         transaction.signature, | ||||
|                         [], | ||||
|                         [], | ||||
|                         { | ||||
|                             from: takerAddress, | ||||
|                             gas: devConstants.MAX_EXECUTE_TRANSACTION_GAS, | ||||
|                         }, | ||||
|                     ), | ||||
|                     RevertReason.InvalidApprovalSignature, | ||||
|                 ); | ||||
|             }); | ||||
|             it(`${fnName} should revert with an invalid approval signature`, async () => { | ||||
|                 const orders = [await orderFactory.newSignedOrderAsync()]; | ||||
|                 const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); | ||||
|                 const transaction = takerTransactionFactory.newSignedTransaction(data); | ||||
|                 const currentTimestamp = await getLatestBlockTimestampAsync(); | ||||
|                 const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER); | ||||
|                 const approval = approvalFactory.newSignedApproval( | ||||
|                     transaction, | ||||
|                     takerAddress, | ||||
|                     approvalExpirationTimeSeconds, | ||||
|                 ); | ||||
|                 const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`; | ||||
|                 await expectTransactionFailedAsync( | ||||
|                     coordinatorContract.executeTransaction.sendTransactionAsync( | ||||
|                         transaction, | ||||
|                         takerAddress, | ||||
|                         transaction.signature, | ||||
|                         [approvalExpirationTimeSeconds], | ||||
|                         [signature], | ||||
|                         { from: takerAddress }, | ||||
|                     ), | ||||
|                     RevertReason.InvalidApprovalSignature, | ||||
|                 ); | ||||
|             }); | ||||
|             it(`${fnName} should revert with an expired approval`, async () => { | ||||
|                 const orders = [await orderFactory.newSignedOrderAsync()]; | ||||
|                 const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); | ||||
|                 const transaction = takerTransactionFactory.newSignedTransaction(data); | ||||
|                 const currentTimestamp = await getLatestBlockTimestampAsync(); | ||||
|                 const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER); | ||||
|                 const approval = approvalFactory.newSignedApproval( | ||||
|                     transaction, | ||||
|                     takerAddress, | ||||
|                     approvalExpirationTimeSeconds, | ||||
|                 ); | ||||
|                 await expectTransactionFailedAsync( | ||||
|                     coordinatorContract.executeTransaction.sendTransactionAsync( | ||||
|                         transaction, | ||||
|                         takerAddress, | ||||
|                         transaction.signature, | ||||
|                         [approvalExpirationTimeSeconds], | ||||
|                         [approval.signature], | ||||
|                         { from: takerAddress }, | ||||
|                     ), | ||||
|                     RevertReason.ApprovalExpired, | ||||
|                 ); | ||||
|             }); | ||||
|             it(`${fnName} should revert if not called by tx signer or approver`, async () => { | ||||
|                 const orders = [await orderFactory.newSignedOrderAsync()]; | ||||
|                 const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); | ||||
|                 const transaction = takerTransactionFactory.newSignedTransaction(data); | ||||
|                 const currentTimestamp = await getLatestBlockTimestampAsync(); | ||||
|                 const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER); | ||||
|                 const approval = approvalFactory.newSignedApproval( | ||||
|                     transaction, | ||||
|                     takerAddress, | ||||
|                     approvalExpirationTimeSeconds, | ||||
|                 ); | ||||
|                 await expectTransactionFailedAsync( | ||||
|                     coordinatorContract.executeTransaction.sendTransactionAsync( | ||||
|                         transaction, | ||||
|                         takerAddress, | ||||
|                         transaction.signature, | ||||
|                         [approvalExpirationTimeSeconds], | ||||
|                         [approval.signature], | ||||
|                         { from: owner }, | ||||
|                     ), | ||||
|                     RevertReason.InvalidOrigin, | ||||
|                 ); | ||||
|             }); | ||||
|         } | ||||
|     }); | ||||
|     describe('batch order fills', () => { | ||||
|         for (const fnName of [...constants.MARKET_FILL_FN_NAMES, ...constants.BATCH_FILL_FN_NAMES]) { | ||||
|             it(`${fnName} should fill the orders with a signed approval`, async () => { | ||||
|                 const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()]; | ||||
|                 const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); | ||||
|                 const transaction = takerTransactionFactory.newSignedTransaction(data); | ||||
|                 const currentTimestamp = await getLatestBlockTimestampAsync(); | ||||
|                 const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER); | ||||
|                 const approval = approvalFactory.newSignedApproval( | ||||
|                     transaction, | ||||
|                     takerAddress, | ||||
|                     approvalExpirationTimeSeconds, | ||||
|                 ); | ||||
|                 const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync( | ||||
|                     await coordinatorContract.executeTransaction.sendTransactionAsync( | ||||
|                         transaction, | ||||
|                         takerAddress, | ||||
|                         transaction.signature, | ||||
|                         [approvalExpirationTimeSeconds], | ||||
|                         [approval.signature], | ||||
|                         { from: takerAddress, gas: devConstants.MAX_EXECUTE_TRANSACTION_GAS }, | ||||
|                     ), | ||||
|                     devConstants.AWAIT_TRANSACTION_MINED_MS, | ||||
|                 ); | ||||
|                 const fillLogs = transactionReceipt.logs.filter( | ||||
|                     log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill', | ||||
|                 ); | ||||
|                 expect(fillLogs.length).to.eq(orders.length); | ||||
|                 orders.forEach((order, index) => { | ||||
|                     const fillLogArgs = (fillLogs[index] as LogWithDecodedArgs<ExchangeFillEventArgs>).args; | ||||
|                     expect(fillLogArgs.makerAddress).to.eq(makerAddress); | ||||
|                     expect(fillLogArgs.takerAddress).to.eq(takerAddress); | ||||
|                     expect(fillLogArgs.senderAddress).to.eq(coordinatorContract.address); | ||||
|                     expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress); | ||||
|                     expect(fillLogArgs.makerAssetData).to.eq(order.makerAssetData); | ||||
|                     expect(fillLogArgs.takerAssetData).to.eq(order.takerAssetData); | ||||
|                     expect(fillLogArgs.makerAssetFilledAmount).to.bignumber.eq(order.makerAssetAmount); | ||||
|                     expect(fillLogArgs.takerAssetFilledAmount).to.bignumber.eq(order.takerAssetAmount); | ||||
|                     expect(fillLogArgs.makerFeePaid).to.bignumber.eq(order.makerFee); | ||||
|                     expect(fillLogArgs.takerFeePaid).to.bignumber.eq(order.takerFee); | ||||
|                     expect(fillLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(order)); | ||||
|                 }); | ||||
|             }); | ||||
|             it(`${fnName} should fill the orders if called by approver`, async () => { | ||||
|                 const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()]; | ||||
|                 const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); | ||||
|                 const transaction = takerTransactionFactory.newSignedTransaction(data); | ||||
|                 const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync( | ||||
|                     await coordinatorContract.executeTransaction.sendTransactionAsync( | ||||
|                         transaction, | ||||
|                         feeRecipientAddress, | ||||
|                         transaction.signature, | ||||
|                         [], | ||||
|                         [], | ||||
|                         { from: feeRecipientAddress, gas: devConstants.MAX_EXECUTE_TRANSACTION_GAS }, | ||||
|                     ), | ||||
|                     devConstants.AWAIT_TRANSACTION_MINED_MS, | ||||
|                 ); | ||||
|                 const fillLogs = transactionReceipt.logs.filter( | ||||
|                     log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill', | ||||
|                 ); | ||||
|                 expect(fillLogs.length).to.eq(orders.length); | ||||
|                 orders.forEach((order, index) => { | ||||
|                     const fillLogArgs = (fillLogs[index] as LogWithDecodedArgs<ExchangeFillEventArgs>).args; | ||||
|                     expect(fillLogArgs.makerAddress).to.eq(makerAddress); | ||||
|                     expect(fillLogArgs.takerAddress).to.eq(takerAddress); | ||||
|                     expect(fillLogArgs.senderAddress).to.eq(coordinatorContract.address); | ||||
|                     expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress); | ||||
|                     expect(fillLogArgs.makerAssetData).to.eq(order.makerAssetData); | ||||
|                     expect(fillLogArgs.takerAssetData).to.eq(order.takerAssetData); | ||||
|                     expect(fillLogArgs.makerAssetFilledAmount).to.bignumber.eq(order.makerAssetAmount); | ||||
|                     expect(fillLogArgs.takerAssetFilledAmount).to.bignumber.eq(order.takerAssetAmount); | ||||
|                     expect(fillLogArgs.makerFeePaid).to.bignumber.eq(order.makerFee); | ||||
|                     expect(fillLogArgs.takerFeePaid).to.bignumber.eq(order.takerFee); | ||||
|                     expect(fillLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(order)); | ||||
|                 }); | ||||
|             }); | ||||
|             it(`${fnName} should revert with an invalid approval signature`, async () => { | ||||
|                 const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()]; | ||||
|                 const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); | ||||
|                 const transaction = takerTransactionFactory.newSignedTransaction(data); | ||||
|                 const currentTimestamp = await getLatestBlockTimestampAsync(); | ||||
|                 const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER); | ||||
|                 const approval = approvalFactory.newSignedApproval( | ||||
|                     transaction, | ||||
|                     takerAddress, | ||||
|                     approvalExpirationTimeSeconds, | ||||
|                 ); | ||||
|                 const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`; | ||||
|                 await expectTransactionFailedAsync( | ||||
|                     coordinatorContract.executeTransaction.sendTransactionAsync( | ||||
|                         transaction, | ||||
|                         takerAddress, | ||||
|                         transaction.signature, | ||||
|                         [approvalExpirationTimeSeconds], | ||||
|                         [signature], | ||||
|                         { from: takerAddress }, | ||||
|                     ), | ||||
|                     RevertReason.InvalidApprovalSignature, | ||||
|                 ); | ||||
|             }); | ||||
|             it(`${fnName} should revert with an expired approval`, async () => { | ||||
|                 const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()]; | ||||
|                 const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); | ||||
|                 const transaction = takerTransactionFactory.newSignedTransaction(data); | ||||
|                 const currentTimestamp = await getLatestBlockTimestampAsync(); | ||||
|                 const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER); | ||||
|                 const approval = approvalFactory.newSignedApproval( | ||||
|                     transaction, | ||||
|                     takerAddress, | ||||
|                     approvalExpirationTimeSeconds, | ||||
|                 ); | ||||
|                 await expectTransactionFailedAsync( | ||||
|                     coordinatorContract.executeTransaction.sendTransactionAsync( | ||||
|                         transaction, | ||||
|                         takerAddress, | ||||
|                         transaction.signature, | ||||
|                         [approvalExpirationTimeSeconds], | ||||
|                         [approval.signature], | ||||
|                         { from: takerAddress }, | ||||
|                     ), | ||||
|                     RevertReason.ApprovalExpired, | ||||
|                 ); | ||||
|             }); | ||||
|             it(`${fnName} should revert if not called by tx signer or approver`, async () => { | ||||
|                 const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()]; | ||||
|                 const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); | ||||
|                 const transaction = takerTransactionFactory.newSignedTransaction(data); | ||||
|                 const currentTimestamp = await getLatestBlockTimestampAsync(); | ||||
|                 const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER); | ||||
|                 const approval = approvalFactory.newSignedApproval( | ||||
|                     transaction, | ||||
|                     takerAddress, | ||||
|                     approvalExpirationTimeSeconds, | ||||
|                 ); | ||||
|                 await expectTransactionFailedAsync( | ||||
|                     coordinatorContract.executeTransaction.sendTransactionAsync( | ||||
|                         transaction, | ||||
|                         takerAddress, | ||||
|                         transaction.signature, | ||||
|                         [approvalExpirationTimeSeconds], | ||||
|                         [approval.signature], | ||||
|                         { from: owner }, | ||||
|                     ), | ||||
|                     RevertReason.InvalidOrigin, | ||||
|                 ); | ||||
|             }); | ||||
|         } | ||||
|     }); | ||||
|     describe('cancels', () => { | ||||
|         it('cancelOrder call should be successful without an approval', async () => { | ||||
|             const orders = [await orderFactory.newSignedOrderAsync()]; | ||||
|             const data = exchangeDataEncoder.encodeOrdersToExchangeData(constants.CANCEL_ORDER, orders); | ||||
|             const transaction = makerTransactionFactory.newSignedTransaction(data); | ||||
|             const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync( | ||||
|                 await coordinatorContract.executeTransaction.sendTransactionAsync( | ||||
|                     transaction, | ||||
|                     makerAddress, | ||||
|                     transaction.signature, | ||||
|                     [], | ||||
|                     [], | ||||
|                     { | ||||
|                         from: makerAddress, | ||||
|                     }, | ||||
|                 ), | ||||
|             ); | ||||
|             const cancelLogs = transactionReceipt.logs.filter( | ||||
|                 log => (log as LogWithDecodedArgs<ExchangeCancelEventArgs>).event === 'Cancel', | ||||
|             ); | ||||
|             expect(cancelLogs.length).to.eq(1); | ||||
|             const cancelLogArgs = (cancelLogs[0] as LogWithDecodedArgs<ExchangeCancelEventArgs>).args; | ||||
|             expect(cancelLogArgs.makerAddress).to.eq(makerAddress); | ||||
|             expect(cancelLogArgs.senderAddress).to.eq(coordinatorContract.address); | ||||
|             expect(cancelLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress); | ||||
|             expect(cancelLogArgs.makerAssetData).to.eq(orders[0].makerAssetData); | ||||
|             expect(cancelLogArgs.takerAssetData).to.eq(orders[0].takerAssetData); | ||||
|             expect(cancelLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(orders[0])); | ||||
|         }); | ||||
|         it('batchCancelOrders call should be successful without an approval', async () => { | ||||
|             const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()]; | ||||
|             const data = exchangeDataEncoder.encodeOrdersToExchangeData(constants.BATCH_CANCEL_ORDERS, orders); | ||||
|             const transaction = makerTransactionFactory.newSignedTransaction(data); | ||||
|             const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync( | ||||
|                 await coordinatorContract.executeTransaction.sendTransactionAsync( | ||||
|                     transaction, | ||||
|                     makerAddress, | ||||
|                     transaction.signature, | ||||
|                     [], | ||||
|                     [], | ||||
|                     { | ||||
|                         from: makerAddress, | ||||
|                     }, | ||||
|                 ), | ||||
|             ); | ||||
|             const cancelLogs = transactionReceipt.logs.filter( | ||||
|                 log => (log as LogWithDecodedArgs<ExchangeCancelEventArgs>).event === 'Cancel', | ||||
|             ); | ||||
|             expect(cancelLogs.length).to.eq(orders.length); | ||||
|             orders.forEach((order, index) => { | ||||
|                 const cancelLogArgs = (cancelLogs[index] as LogWithDecodedArgs<ExchangeCancelEventArgs>).args; | ||||
|                 expect(cancelLogArgs.makerAddress).to.eq(makerAddress); | ||||
|                 expect(cancelLogArgs.senderAddress).to.eq(coordinatorContract.address); | ||||
|                 expect(cancelLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress); | ||||
|                 expect(cancelLogArgs.makerAssetData).to.eq(order.makerAssetData); | ||||
|                 expect(cancelLogArgs.takerAssetData).to.eq(order.takerAssetData); | ||||
|                 expect(cancelLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(order)); | ||||
|             }); | ||||
|         }); | ||||
|         it('cancelOrdersUpTo call should be successful without an approval', async () => { | ||||
|             const orders: SignedOrder[] = []; | ||||
|             const data = exchangeDataEncoder.encodeOrdersToExchangeData(constants.CANCEL_ORDERS_UP_TO, orders); | ||||
|             const transaction = makerTransactionFactory.newSignedTransaction(data); | ||||
|             const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync( | ||||
|                 await coordinatorContract.executeTransaction.sendTransactionAsync( | ||||
|                     transaction, | ||||
|                     makerAddress, | ||||
|                     transaction.signature, | ||||
|                     [], | ||||
|                     [], | ||||
|                     { | ||||
|                         from: makerAddress, | ||||
|                     }, | ||||
|                 ), | ||||
|             ); | ||||
|             const cancelLogs = transactionReceipt.logs.filter( | ||||
|                 log => (log as LogWithDecodedArgs<ExchangeCancelUpToEventArgs>).event === 'CancelUpTo', | ||||
|             ); | ||||
|             expect(cancelLogs.length).to.eq(1); | ||||
|             const cancelLogArgs = (cancelLogs[0] as LogWithDecodedArgs<ExchangeCancelUpToEventArgs>).args; | ||||
|             expect(cancelLogArgs.makerAddress).to.eq(makerAddress); | ||||
|             expect(cancelLogArgs.senderAddress).to.eq(coordinatorContract.address); | ||||
|             expect(cancelLogArgs.orderEpoch).to.bignumber.eq(new BigNumber(1)); | ||||
|         }); | ||||
|     }); | ||||
| }); | ||||
| // tslint:disable:max-file-line-count | ||||
							
								
								
									
										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); | ||||
|         }); | ||||
|     }); | ||||
| }); | ||||
							
								
								
									
										19
									
								
								contracts/coordinator/test/global_hooks.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								contracts/coordinator/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(); | ||||
| }); | ||||
							
								
								
									
										79
									
								
								contracts/coordinator/test/libs.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								contracts/coordinator/test/libs.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,79 @@ | ||||
| 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, CoordinatorContract, hashUtils } from '../src'; | ||||
|  | ||||
| chaiSetup.configure(); | ||||
| const expect = chai.expect; | ||||
| const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); | ||||
|  | ||||
| describe('Libs tests', () => { | ||||
|     let coordinatorContract: CoordinatorContract; | ||||
|     const exchangeAddress = addressUtils.generatePseudoRandomAddress(); | ||||
|  | ||||
|     before(async () => { | ||||
|         await blockchainLifecycle.startAsync(); | ||||
|     }); | ||||
|     after(async () => { | ||||
|         await blockchainLifecycle.revertAsync(); | ||||
|     }); | ||||
|     before(async () => { | ||||
|         coordinatorContract = await CoordinatorContract.deployFrom0xArtifactAsync( | ||||
|             artifacts.Coordinator, | ||||
|             provider, | ||||
|             txDefaults, | ||||
|             exchangeAddress, | ||||
|         ); | ||||
|     }); | ||||
|     beforeEach(async () => { | ||||
|         await blockchainLifecycle.startAsync(); | ||||
|     }); | ||||
|     afterEach(async () => { | ||||
|         await blockchainLifecycle.revertAsync(); | ||||
|     }); | ||||
|  | ||||
|     describe('getTransactionHash', () => { | ||||
|         it('should return the correct transaction hash', async () => { | ||||
|             const tx = { | ||||
|                 verifyingContractAddress: exchangeAddress, | ||||
|                 salt: new BigNumber(0), | ||||
|                 signerAddress: constants.NULL_ADDRESS, | ||||
|                 data: '0x1234', | ||||
|             }; | ||||
|             const expectedTxHash = transactionHashUtils.getTransactionHashHex(tx); | ||||
|             const txHash = await coordinatorContract.getTransactionHash.callAsync(tx); | ||||
|             expect(expectedTxHash).to.eq(txHash); | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
|     describe('getApprovalHash', () => { | ||||
|         it('should return the correct approval hash', async () => { | ||||
|             const signedTx = { | ||||
|                 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 = { | ||||
|                 txOrigin, | ||||
|                 transactionHash: transactionHashUtils.getTransactionHashHex(signedTx), | ||||
|                 transactionSignature: signedTx.signature, | ||||
|                 approvalExpirationTimeSeconds, | ||||
|             }; | ||||
|             const expectedApprovalHash = hashUtils.getApprovalHashHex( | ||||
|                 signedTx, | ||||
|                 coordinatorContract.address, | ||||
|                 txOrigin, | ||||
|                 approvalExpirationTimeSeconds, | ||||
|             ); | ||||
|             const approvalHash = await coordinatorContract.getCoordinatorApprovalHash.callAsync(approval); | ||||
|             expect(expectedApprovalHash).to.eq(approvalHash); | ||||
|         }); | ||||
|     }); | ||||
| }); | ||||
							
								
								
									
										728
									
								
								contracts/coordinator/test/mixins.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										728
									
								
								contracts/coordinator/test/mixins.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,728 @@ | ||||
| import { | ||||
|     addressUtils, | ||||
|     chaiSetup, | ||||
|     constants as devConstants, | ||||
|     expectContractCallFailedAsync, | ||||
|     getLatestBlockTimestampAsync, | ||||
|     provider, | ||||
|     TransactionFactory, | ||||
|     txDefaults, | ||||
|     web3Wrapper, | ||||
| } from '@0x/contracts-test-utils'; | ||||
| import { BlockchainLifecycle } from '@0x/dev-utils'; | ||||
| import { transactionHashUtils } from '@0x/order-utils'; | ||||
| import { RevertReason, SignatureType, SignedOrder } from '@0x/types'; | ||||
| import { BigNumber } from '@0x/utils'; | ||||
| import * as chai from 'chai'; | ||||
| import * as ethUtil from 'ethereumjs-util'; | ||||
|  | ||||
| import { ApprovalFactory, artifacts, constants, CoordinatorContract, exchangeDataEncoder } from '../src'; | ||||
|  | ||||
| chaiSetup.configure(); | ||||
| const expect = chai.expect; | ||||
| const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); | ||||
|  | ||||
| describe('Mixins tests', () => { | ||||
|     let transactionSignerAddress: string; | ||||
|     let approvalSignerAddress1: string; | ||||
|     let approvalSignerAddress2: string; | ||||
|     let mixins: CoordinatorContract; | ||||
|     let transactionFactory: TransactionFactory; | ||||
|     let approvalFactory1: ApprovalFactory; | ||||
|     let approvalFactory2: ApprovalFactory; | ||||
|     let defaultOrder: SignedOrder; | ||||
|     const exchangeAddress = addressUtils.generatePseudoRandomAddress(); | ||||
|  | ||||
|     before(async () => { | ||||
|         await blockchainLifecycle.startAsync(); | ||||
|     }); | ||||
|     after(async () => { | ||||
|         await blockchainLifecycle.revertAsync(); | ||||
|     }); | ||||
|     before(async () => { | ||||
|         mixins = await CoordinatorContract.deployFrom0xArtifactAsync( | ||||
|             artifacts.Coordinator, | ||||
|             provider, | ||||
|             txDefaults, | ||||
|             exchangeAddress, | ||||
|         ); | ||||
|         const accounts = await web3Wrapper.getAvailableAddressesAsync(); | ||||
|         [transactionSignerAddress, approvalSignerAddress1, approvalSignerAddress2] = accounts.slice(0, 3); | ||||
|         defaultOrder = { | ||||
|             exchangeAddress: devConstants.NULL_ADDRESS, | ||||
|             makerAddress: devConstants.NULL_ADDRESS, | ||||
|             takerAddress: devConstants.NULL_ADDRESS, | ||||
|             senderAddress: mixins.address, | ||||
|             feeRecipientAddress: approvalSignerAddress1, | ||||
|             makerAssetData: devConstants.NULL_BYTES, | ||||
|             takerAssetData: devConstants.NULL_BYTES, | ||||
|             makerAssetAmount: devConstants.ZERO_AMOUNT, | ||||
|             takerAssetAmount: devConstants.ZERO_AMOUNT, | ||||
|             makerFee: devConstants.ZERO_AMOUNT, | ||||
|             takerFee: devConstants.ZERO_AMOUNT, | ||||
|             expirationTimeSeconds: devConstants.ZERO_AMOUNT, | ||||
|             salt: devConstants.ZERO_AMOUNT, | ||||
|             signature: devConstants.NULL_BYTES, | ||||
|         }; | ||||
|         const transactionSignerPrivateKey = | ||||
|             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 TransactionFactory(transactionSignerPrivateKey, exchangeAddress); | ||||
|         approvalFactory1 = new ApprovalFactory(approvalSignerPrivateKey1, mixins.address); | ||||
|         approvalFactory2 = new ApprovalFactory(approvalSignerPrivateKey2, mixins.address); | ||||
|     }); | ||||
|     beforeEach(async () => { | ||||
|         await blockchainLifecycle.startAsync(); | ||||
|     }); | ||||
|     afterEach(async () => { | ||||
|         await blockchainLifecycle.revertAsync(); | ||||
|     }); | ||||
|  | ||||
|     describe('getSignerAddress', () => { | ||||
|         it('should return the correct address using the EthSign signature type', async () => { | ||||
|             const data = devConstants.NULL_BYTES; | ||||
|             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.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.newSignedTransaction(data); | ||||
|             const illegalSignatureByte = ethUtil.toBuffer(SignatureType.Illegal).toString('hex'); | ||||
|             transaction.signature = `${transaction.signature.slice( | ||||
|                 0, | ||||
|                 transaction.signature.length - 2, | ||||
|             )}${illegalSignatureByte}`; | ||||
|             const transactionHash = transactionHashUtils.getTransactionHashHex(transaction); | ||||
|             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.newSignedTransaction(data); | ||||
|             const invalidSignatureByte = '04'; | ||||
|             transaction.signature = `${transaction.signature.slice( | ||||
|                 0, | ||||
|                 transaction.signature.length - 2, | ||||
|             )}${invalidSignatureByte}`; | ||||
|             const transactionHash = transactionHashUtils.getTransactionHashHex(transaction); | ||||
|             expectContractCallFailedAsync( | ||||
|                 mixins.getSignerAddress.callAsync(transactionHash, transaction.signature), | ||||
|                 RevertReason.SignatureUnsupported, | ||||
|             ); | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
|     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.newSignedTransaction(data); | ||||
|                 const currentTimestamp = await getLatestBlockTimestampAsync(); | ||||
|                 const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER); | ||||
|                 const approval = approvalFactory1.newSignedApproval( | ||||
|                     transaction, | ||||
|                     transactionSignerAddress, | ||||
|                     approvalExpirationTimeSeconds, | ||||
|                 ); | ||||
|                 await mixins.assertValidCoordinatorApprovals.callAsync( | ||||
|                     transaction, | ||||
|                     transactionSignerAddress, | ||||
|                     transaction.signature, | ||||
|                     [approvalExpirationTimeSeconds], | ||||
|                     [approval.signature], | ||||
|                     { from: transactionSignerAddress }, | ||||
|                 ); | ||||
|             }); | ||||
|             it(`Should be successful: function=${fnName}, caller=tx_signer, senderAddress=[null], approval_sig=[approver1], expiration=[valid]`, async () => { | ||||
|                 const order = { | ||||
|                     ...defaultOrder, | ||||
|                     senderAddress: devConstants.NULL_ADDRESS, | ||||
|                 }; | ||||
|                 const orders = [order]; | ||||
|                 const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); | ||||
|                 const transaction = transactionFactory.newSignedTransaction(data); | ||||
|                 const currentTimestamp = await getLatestBlockTimestampAsync(); | ||||
|                 const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER); | ||||
|                 const approval = approvalFactory1.newSignedApproval( | ||||
|                     transaction, | ||||
|                     transactionSignerAddress, | ||||
|                     approvalExpirationTimeSeconds, | ||||
|                 ); | ||||
|                 await mixins.assertValidCoordinatorApprovals.callAsync( | ||||
|                     transaction, | ||||
|                     transactionSignerAddress, | ||||
|                     transaction.signature, | ||||
|                     [approvalExpirationTimeSeconds], | ||||
|                     [approval.signature], | ||||
|                     { from: transactionSignerAddress }, | ||||
|                 ); | ||||
|             }); | ||||
|             it(`Should be successful: function=${fnName}, caller=approver1, senderAddress=[verifier], approval_sig=[], expiration=[]`, async () => { | ||||
|                 const orders = [defaultOrder]; | ||||
|                 const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); | ||||
|                 const transaction = transactionFactory.newSignedTransaction(data); | ||||
|                 await mixins.assertValidCoordinatorApprovals.callAsync( | ||||
|                     transaction, | ||||
|                     approvalSignerAddress1, | ||||
|                     transaction.signature, | ||||
|                     [], | ||||
|                     [], | ||||
|                     { | ||||
|                         from: approvalSignerAddress1, | ||||
|                     }, | ||||
|                 ); | ||||
|             }); | ||||
|             it(`Should be successful: function=${fnName}, caller=approver1, senderAddress=[verifier], approval_sig=[approver1], expiration=[invalid]`, async () => { | ||||
|                 const orders = [defaultOrder]; | ||||
|                 const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); | ||||
|                 const transaction = transactionFactory.newSignedTransaction(data); | ||||
|                 const currentTimestamp = await getLatestBlockTimestampAsync(); | ||||
|                 const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER); | ||||
|                 const approval = approvalFactory1.newSignedApproval( | ||||
|                     transaction, | ||||
|                     transactionSignerAddress, | ||||
|                     approvalExpirationTimeSeconds, | ||||
|                 ); | ||||
|                 await mixins.assertValidCoordinatorApprovals.callAsync( | ||||
|                     transaction, | ||||
|                     approvalSignerAddress1, | ||||
|                     transaction.signature, | ||||
|                     [approvalExpirationTimeSeconds], | ||||
|                     [approval.signature], | ||||
|                     { from: approvalSignerAddress1 }, | ||||
|                 ); | ||||
|             }); | ||||
|             it(`Should be successful: function=${fnName}, caller=approver1, senderAddress=[verifier], approval_sig=[], expiration=[]`, async () => { | ||||
|                 const orders = [defaultOrder]; | ||||
|                 const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); | ||||
|                 const transaction = transactionFactory.newSignedTransaction(data); | ||||
|                 await mixins.assertValidCoordinatorApprovals.callAsync( | ||||
|                     transaction, | ||||
|                     approvalSignerAddress1, | ||||
|                     transaction.signature, | ||||
|                     [], | ||||
|                     [], | ||||
|                     { | ||||
|                         from: approvalSignerAddress1, | ||||
|                     }, | ||||
|                 ); | ||||
|             }); | ||||
|             it(`Should revert: function=${fnName}, caller=tx_signer, senderAddress=[verifier], approval_sig=[invalid], expiration=[valid]`, async () => { | ||||
|                 const orders = [defaultOrder]; | ||||
|                 const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); | ||||
|                 const transaction = transactionFactory.newSignedTransaction(data); | ||||
|                 const currentTimestamp = await getLatestBlockTimestampAsync(); | ||||
|                 const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER); | ||||
|                 const approval = approvalFactory1.newSignedApproval( | ||||
|                     transaction, | ||||
|                     transactionSignerAddress, | ||||
|                     approvalExpirationTimeSeconds, | ||||
|                 ); | ||||
|                 const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`; | ||||
|                 expectContractCallFailedAsync( | ||||
|                     mixins.assertValidCoordinatorApprovals.callAsync( | ||||
|                         transaction, | ||||
|                         transactionSignerAddress, | ||||
|                         transaction.signature, | ||||
|                         [approvalExpirationTimeSeconds], | ||||
|                         [signature], | ||||
|                         { from: transactionSignerAddress }, | ||||
|                     ), | ||||
|                     RevertReason.InvalidApprovalSignature, | ||||
|                 ); | ||||
|             }); | ||||
|             it(`Should revert: function=${fnName}, caller=tx_signer, senderAddress=[verifier], approval_sig=[approver1], expiration=[invalid]`, async () => { | ||||
|                 const orders = [defaultOrder]; | ||||
|                 const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); | ||||
|                 const transaction = transactionFactory.newSignedTransaction(data); | ||||
|                 const currentTimestamp = await getLatestBlockTimestampAsync(); | ||||
|                 const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER); | ||||
|                 const approval = approvalFactory1.newSignedApproval( | ||||
|                     transaction, | ||||
|                     transactionSignerAddress, | ||||
|                     approvalExpirationTimeSeconds, | ||||
|                 ); | ||||
|                 expectContractCallFailedAsync( | ||||
|                     mixins.assertValidCoordinatorApprovals.callAsync( | ||||
|                         transaction, | ||||
|                         transactionSignerAddress, | ||||
|                         transaction.signature, | ||||
|                         [approvalExpirationTimeSeconds], | ||||
|                         [approval.signature], | ||||
|                         { from: transactionSignerAddress }, | ||||
|                     ), | ||||
|                     RevertReason.ApprovalExpired, | ||||
|                 ); | ||||
|             }); | ||||
|             it(`Should revert: function=${fnName}, caller=approver2, senderAddress=[verifier], approval_sig=[approver1], expiration=[valid]`, async () => { | ||||
|                 const orders = [defaultOrder]; | ||||
|                 const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); | ||||
|                 const transaction = transactionFactory.newSignedTransaction(data); | ||||
|                 const currentTimestamp = await getLatestBlockTimestampAsync(); | ||||
|                 const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER); | ||||
|                 const approval = approvalFactory1.newSignedApproval( | ||||
|                     transaction, | ||||
|                     transactionSignerAddress, | ||||
|                     approvalExpirationTimeSeconds, | ||||
|                 ); | ||||
|                 expectContractCallFailedAsync( | ||||
|                     mixins.assertValidCoordinatorApprovals.callAsync( | ||||
|                         transaction, | ||||
|                         transactionSignerAddress, | ||||
|                         transaction.signature, | ||||
|                         [approvalExpirationTimeSeconds], | ||||
|                         [approval.signature], | ||||
|                         { from: approvalSignerAddress2 }, | ||||
|                     ), | ||||
|                     RevertReason.InvalidOrigin, | ||||
|                 ); | ||||
|             }); | ||||
|         } | ||||
|     }); | ||||
|     describe('Batch order approvals', () => { | ||||
|         for (const fnName of [ | ||||
|             ...constants.BATCH_FILL_FN_NAMES, | ||||
|             ...constants.MARKET_FILL_FN_NAMES, | ||||
|             constants.MATCH_ORDERS, | ||||
|         ]) { | ||||
|             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.newSignedTransaction(data); | ||||
|                 const currentTimestamp = await getLatestBlockTimestampAsync(); | ||||
|                 const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER); | ||||
|                 const approval = approvalFactory1.newSignedApproval( | ||||
|                     transaction, | ||||
|                     transactionSignerAddress, | ||||
|                     approvalExpirationTimeSeconds, | ||||
|                 ); | ||||
|                 await mixins.assertValidCoordinatorApprovals.callAsync( | ||||
|                     transaction, | ||||
|                     transactionSignerAddress, | ||||
|                     transaction.signature, | ||||
|                     [approvalExpirationTimeSeconds], | ||||
|                     [approval.signature], | ||||
|                     { from: transactionSignerAddress }, | ||||
|                 ); | ||||
|             }); | ||||
|             it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[null,null], feeRecipient=[approver1,approver1], approval_sig=[approver1], expiration=[valid]`, async () => { | ||||
|                 const orders = [defaultOrder, defaultOrder].map(order => ({ | ||||
|                     ...order, | ||||
|                     senderAddress: devConstants.NULL_ADDRESS, | ||||
|                 })); | ||||
|                 const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); | ||||
|                 const transaction = transactionFactory.newSignedTransaction(data); | ||||
|                 const currentTimestamp = await getLatestBlockTimestampAsync(); | ||||
|                 const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER); | ||||
|                 const approval = approvalFactory1.newSignedApproval( | ||||
|                     transaction, | ||||
|                     transactionSignerAddress, | ||||
|                     approvalExpirationTimeSeconds, | ||||
|                 ); | ||||
|                 await mixins.assertValidCoordinatorApprovals.callAsync( | ||||
|                     transaction, | ||||
|                     transactionSignerAddress, | ||||
|                     transaction.signature, | ||||
|                     [approvalExpirationTimeSeconds], | ||||
|                     [approval.signature], | ||||
|                     { from: transactionSignerAddress }, | ||||
|                 ); | ||||
|             }); | ||||
|             it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[null,null], feeRecipient=[approver1,approver1], approval_sig=[], expiration=[]`, async () => { | ||||
|                 const orders = [defaultOrder, defaultOrder].map(order => ({ | ||||
|                     ...order, | ||||
|                     senderAddress: devConstants.NULL_ADDRESS, | ||||
|                 })); | ||||
|                 const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); | ||||
|                 const transaction = transactionFactory.newSignedTransaction(data); | ||||
|                 await mixins.assertValidCoordinatorApprovals.callAsync( | ||||
|                     transaction, | ||||
|                     transactionSignerAddress, | ||||
|                     transaction.signature, | ||||
|                     [], | ||||
|                     [], | ||||
|                     { from: transactionSignerAddress }, | ||||
|                 ); | ||||
|             }); | ||||
|             it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[verifier,null], feeRecipient=[approver1,approver1], approval_sig=[approver1], expiration=[valid]`, async () => { | ||||
|                 const orders = [defaultOrder, { ...defaultOrder, senderAddress: devConstants.NULL_ADDRESS }]; | ||||
|                 const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); | ||||
|                 const transaction = transactionFactory.newSignedTransaction(data); | ||||
|                 const currentTimestamp = await getLatestBlockTimestampAsync(); | ||||
|                 const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER); | ||||
|                 const approval = approvalFactory1.newSignedApproval( | ||||
|                     transaction, | ||||
|                     transactionSignerAddress, | ||||
|                     approvalExpirationTimeSeconds, | ||||
|                 ); | ||||
|                 await mixins.assertValidCoordinatorApprovals.callAsync( | ||||
|                     transaction, | ||||
|                     transactionSignerAddress, | ||||
|                     transaction.signature, | ||||
|                     [approvalExpirationTimeSeconds], | ||||
|                     [approval.signature], | ||||
|                     { from: transactionSignerAddress }, | ||||
|                 ); | ||||
|             }); | ||||
|             it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver2], approval_sig=[approver1,approver2], expiration=[valid,valid]`, async () => { | ||||
|                 const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }]; | ||||
|                 const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); | ||||
|                 const transaction = transactionFactory.newSignedTransaction(data); | ||||
|                 const currentTimestamp = await getLatestBlockTimestampAsync(); | ||||
|                 const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER); | ||||
|                 const approval1 = approvalFactory1.newSignedApproval( | ||||
|                     transaction, | ||||
|                     transactionSignerAddress, | ||||
|                     approvalExpirationTimeSeconds, | ||||
|                 ); | ||||
|                 const approval2 = approvalFactory2.newSignedApproval( | ||||
|                     transaction, | ||||
|                     transactionSignerAddress, | ||||
|                     approvalExpirationTimeSeconds, | ||||
|                 ); | ||||
|                 await mixins.assertValidCoordinatorApprovals.callAsync( | ||||
|                     transaction, | ||||
|                     transactionSignerAddress, | ||||
|                     transaction.signature, | ||||
|                     [approvalExpirationTimeSeconds, approvalExpirationTimeSeconds], | ||||
|                     [approval1.signature, approval2.signature], | ||||
|                     { from: transactionSignerAddress }, | ||||
|                 ); | ||||
|             }); | ||||
|             it(`Should be successful: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver1], approval_sig=[], expiration=[]`, async () => { | ||||
|                 const orders = [defaultOrder, defaultOrder]; | ||||
|                 const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); | ||||
|                 const transaction = transactionFactory.newSignedTransaction(data); | ||||
|                 await mixins.assertValidCoordinatorApprovals.callAsync( | ||||
|                     transaction, | ||||
|                     approvalSignerAddress1, | ||||
|                     transaction.signature, | ||||
|                     [], | ||||
|                     [], | ||||
|                     { from: approvalSignerAddress1 }, | ||||
|                 ); | ||||
|             }); | ||||
|             it(`Should revert: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver2], approval_sig=[approver2], expiration=[valid]`, async () => { | ||||
|                 const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }]; | ||||
|                 const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); | ||||
|                 const transaction = transactionFactory.newSignedTransaction(data); | ||||
|                 const currentTimestamp = await getLatestBlockTimestampAsync(); | ||||
|                 const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER); | ||||
|                 const approval2 = approvalFactory2.newSignedApproval( | ||||
|                     transaction, | ||||
|                     transactionSignerAddress, | ||||
|                     approvalExpirationTimeSeconds, | ||||
|                 ); | ||||
|                 expectContractCallFailedAsync( | ||||
|                     mixins.assertValidCoordinatorApprovals.callAsync( | ||||
|                         transaction, | ||||
|                         transactionSignerAddress, | ||||
|                         transaction.signature, | ||||
|                         [approvalExpirationTimeSeconds], | ||||
|                         [approval2.signature], | ||||
|                         { from: approvalSignerAddress1 }, | ||||
|                     ), | ||||
|                     RevertReason.InvalidOrigin, | ||||
|                 ); | ||||
|             }); | ||||
|             it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver1], approval_sig=[], expiration=[]`, async () => { | ||||
|                 const orders = [defaultOrder, defaultOrder]; | ||||
|                 const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); | ||||
|                 const transaction = transactionFactory.newSignedTransaction(data); | ||||
|                 expectContractCallFailedAsync( | ||||
|                     mixins.assertValidCoordinatorApprovals.callAsync( | ||||
|                         transaction, | ||||
|                         transactionSignerAddress, | ||||
|                         transaction.signature, | ||||
|                         [], | ||||
|                         [], | ||||
|                         { from: transactionSignerAddress }, | ||||
|                     ), | ||||
|                     RevertReason.InvalidApprovalSignature, | ||||
|                 ); | ||||
|             }); | ||||
|             it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver1], approval_sig=[invalid], expiration=[valid]`, async () => { | ||||
|                 const orders = [defaultOrder, defaultOrder]; | ||||
|                 const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); | ||||
|                 const transaction = transactionFactory.newSignedTransaction(data); | ||||
|                 const currentTimestamp = await getLatestBlockTimestampAsync(); | ||||
|                 const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER); | ||||
|                 const approval = approvalFactory1.newSignedApproval( | ||||
|                     transaction, | ||||
|                     transactionSignerAddress, | ||||
|                     approvalExpirationTimeSeconds, | ||||
|                 ); | ||||
|                 const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`; | ||||
|                 expectContractCallFailedAsync( | ||||
|                     mixins.assertValidCoordinatorApprovals.callAsync( | ||||
|                         transaction, | ||||
|                         transactionSignerAddress, | ||||
|                         transaction.signature, | ||||
|                         [approvalExpirationTimeSeconds], | ||||
|                         [signature], | ||||
|                         { from: transactionSignerAddress }, | ||||
|                     ), | ||||
|                     RevertReason.InvalidApprovalSignature, | ||||
|                 ); | ||||
|             }); | ||||
|             it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[valid,invalid], expiration=[valid,valid]`, async () => { | ||||
|                 const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }]; | ||||
|                 const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); | ||||
|                 const transaction = transactionFactory.newSignedTransaction(data); | ||||
|                 const currentTimestamp = await getLatestBlockTimestampAsync(); | ||||
|                 const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER); | ||||
|                 const approval1 = approvalFactory1.newSignedApproval( | ||||
|                     transaction, | ||||
|                     transactionSignerAddress, | ||||
|                     approvalExpirationTimeSeconds, | ||||
|                 ); | ||||
|                 const approval2 = approvalFactory2.newSignedApproval( | ||||
|                     transaction, | ||||
|                     transactionSignerAddress, | ||||
|                     approvalExpirationTimeSeconds, | ||||
|                 ); | ||||
|                 const approvalSignature2 = `${approval2.signature.slice(0, 4)}FFFFFFFF${approval2.signature.slice(12)}`; | ||||
|                 expectContractCallFailedAsync( | ||||
|                     mixins.assertValidCoordinatorApprovals.callAsync( | ||||
|                         transaction, | ||||
|                         transactionSignerAddress, | ||||
|                         transaction.signature, | ||||
|                         [approvalExpirationTimeSeconds, approvalExpirationTimeSeconds], | ||||
|                         [approval1.signature, approvalSignature2], | ||||
|                         { from: transactionSignerAddress }, | ||||
|                     ), | ||||
|                     RevertReason.InvalidApprovalSignature, | ||||
|                 ); | ||||
|             }); | ||||
|             it(`Should revert: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[invalid], expiration=[valid]`, async () => { | ||||
|                 const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }]; | ||||
|                 const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); | ||||
|                 const transaction = transactionFactory.newSignedTransaction(data); | ||||
|                 const currentTimestamp = await getLatestBlockTimestampAsync(); | ||||
|                 const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER); | ||||
|                 const approval2 = approvalFactory2.newSignedApproval( | ||||
|                     transaction, | ||||
|                     transactionSignerAddress, | ||||
|                     approvalExpirationTimeSeconds, | ||||
|                 ); | ||||
|                 const approvalSignature2 = `${approval2.signature.slice(0, 4)}FFFFFFFF${approval2.signature.slice(12)}`; | ||||
|                 expectContractCallFailedAsync( | ||||
|                     mixins.assertValidCoordinatorApprovals.callAsync( | ||||
|                         transaction, | ||||
|                         approvalSignerAddress1, | ||||
|                         transaction.signature, | ||||
|                         [approvalExpirationTimeSeconds], | ||||
|                         [approvalSignature2], | ||||
|                         { from: approvalSignerAddress1 }, | ||||
|                     ), | ||||
|                     RevertReason.InvalidApprovalSignature, | ||||
|                 ); | ||||
|             }); | ||||
|             it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[valid,valid], expiration=[valid,invalid]`, async () => { | ||||
|                 const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }]; | ||||
|                 const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); | ||||
|                 const transaction = 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, | ||||
|                     transactionSignerAddress, | ||||
|                     approvalExpirationTimeSeconds1, | ||||
|                 ); | ||||
|                 const approval2 = approvalFactory2.newSignedApproval( | ||||
|                     transaction, | ||||
|                     transactionSignerAddress, | ||||
|                     approvalExpirationTimeSeconds2, | ||||
|                 ); | ||||
|                 expectContractCallFailedAsync( | ||||
|                     mixins.assertValidCoordinatorApprovals.callAsync( | ||||
|                         transaction, | ||||
|                         transactionSignerAddress, | ||||
|                         transaction.signature, | ||||
|                         [approvalExpirationTimeSeconds1, approvalExpirationTimeSeconds2], | ||||
|                         [approval1.signature, approval2.signature], | ||||
|                         { from: transactionSignerAddress }, | ||||
|                     ), | ||||
|                     RevertReason.ApprovalExpired, | ||||
|                 ); | ||||
|             }); | ||||
|             it(`Should revert: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[valid], expiration=[invalid]`, async () => { | ||||
|                 const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }]; | ||||
|                 const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); | ||||
|                 const transaction = transactionFactory.newSignedTransaction(data); | ||||
|                 const currentTimestamp = await getLatestBlockTimestampAsync(); | ||||
|                 const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER); | ||||
|                 const approval2 = approvalFactory2.newSignedApproval( | ||||
|                     transaction, | ||||
|                     transactionSignerAddress, | ||||
|                     approvalExpirationTimeSeconds, | ||||
|                 ); | ||||
|                 expectContractCallFailedAsync( | ||||
|                     mixins.assertValidCoordinatorApprovals.callAsync( | ||||
|                         transaction, | ||||
|                         approvalSignerAddress1, | ||||
|                         transaction.signature, | ||||
|                         [approvalExpirationTimeSeconds], | ||||
|                         [approval2.signature], | ||||
|                         { from: approvalSignerAddress1 }, | ||||
|                     ), | ||||
|                     RevertReason.ApprovalExpired, | ||||
|                 ); | ||||
|             }); | ||||
|             it(`Should revert: function=${fnName} caller=approver2, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver1], approval_sig=[valid], expiration=[valid]`, async () => { | ||||
|                 const orders = [defaultOrder, defaultOrder]; | ||||
|                 const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); | ||||
|                 const transaction = transactionFactory.newSignedTransaction(data); | ||||
|                 const currentTimestamp = await getLatestBlockTimestampAsync(); | ||||
|                 const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER); | ||||
|                 const approval1 = approvalFactory1.newSignedApproval( | ||||
|                     transaction, | ||||
|                     transactionSignerAddress, | ||||
|                     approvalExpirationTimeSeconds, | ||||
|                 ); | ||||
|                 expectContractCallFailedAsync( | ||||
|                     mixins.assertValidCoordinatorApprovals.callAsync( | ||||
|                         transaction, | ||||
|                         transactionSignerAddress, | ||||
|                         transaction.signature, | ||||
|                         [approvalExpirationTimeSeconds], | ||||
|                         [approval1.signature], | ||||
|                         { from: approvalSignerAddress2 }, | ||||
|                     ), | ||||
|                     RevertReason.InvalidOrigin, | ||||
|                 ); | ||||
|             }); | ||||
|         } | ||||
|     }); | ||||
|     describe('cancels', () => { | ||||
|         it('should allow the tx signer to call `cancelOrder` without approval', async () => { | ||||
|             const orders = [defaultOrder]; | ||||
|             const data = exchangeDataEncoder.encodeOrdersToExchangeData(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.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.newSignedTransaction(data); | ||||
|             await mixins.assertValidCoordinatorApprovals.callAsync( | ||||
|                 transaction, | ||||
|                 transactionSignerAddress, | ||||
|                 transaction.signature, | ||||
|                 [], | ||||
|                 [], | ||||
|                 { from: transactionSignerAddress }, | ||||
|             ); | ||||
|         }); | ||||
|     }); | ||||
| }); | ||||
| // tslint:disable:max-file-line-count | ||||
							
								
								
									
										38
									
								
								contracts/coordinator/test/utils/approval_factory.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								contracts/coordinator/test/utils/approval_factory.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | ||||
| import { signingUtils } from '@0x/contracts-test-utils'; | ||||
| import { SignatureType, SignedZeroExTransaction } from '@0x/types'; | ||||
| import { BigNumber } from '@0x/utils'; | ||||
| import * as ethUtil from 'ethereumjs-util'; | ||||
|  | ||||
| import { hashUtils, SignedCoordinatorApproval } from './index'; | ||||
|  | ||||
| export class ApprovalFactory { | ||||
|     private readonly _privateKey: Buffer; | ||||
|     private readonly _verifyingContractAddress: string; | ||||
|  | ||||
|     constructor(privateKey: Buffer, verifyingContractAddress: string) { | ||||
|         this._privateKey = privateKey; | ||||
|         this._verifyingContractAddress = verifyingContractAddress; | ||||
|     } | ||||
|  | ||||
|     public newSignedApproval( | ||||
|         transaction: SignedZeroExTransaction, | ||||
|         txOrigin: string, | ||||
|         approvalExpirationTimeSeconds: BigNumber, | ||||
|         signatureType: SignatureType = SignatureType.EthSign, | ||||
|     ): SignedCoordinatorApproval { | ||||
|         const approvalHashBuff = hashUtils.getApprovalHashBuffer( | ||||
|             transaction, | ||||
|             this._verifyingContractAddress, | ||||
|             txOrigin, | ||||
|             approvalExpirationTimeSeconds, | ||||
|         ); | ||||
|         const signatureBuff = signingUtils.signMessage(approvalHashBuff, this._privateKey, signatureType); | ||||
|         const signedApproval = { | ||||
|             txOrigin, | ||||
|             transaction, | ||||
|             approvalExpirationTimeSeconds, | ||||
|             signature: ethUtil.addHexPrefix(signatureBuff.toString('hex')), | ||||
|         }; | ||||
|         return signedApproval; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										12
									
								
								contracts/coordinator/test/utils/constants.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								contracts/coordinator/test/utils/constants.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| import { BigNumber } from '@0x/utils'; | ||||
|  | ||||
| export const constants = { | ||||
|     SINGLE_FILL_FN_NAMES: ['fillOrder', 'fillOrKillOrder', 'fillOrderNoThrow'], | ||||
|     BATCH_FILL_FN_NAMES: ['batchFillOrders', 'batchFillOrKillOrders', 'batchFillOrdersNoThrow'], | ||||
|     MARKET_FILL_FN_NAMES: ['marketBuyOrders', 'marketBuyOrdersNoThrow', 'marketSellOrders', 'marketSellOrdersNoThrow'], | ||||
|     MATCH_ORDERS: 'matchOrders', | ||||
|     CANCEL_ORDER: 'cancelOrder', | ||||
|     BATCH_CANCEL_ORDERS: 'batchCancelOrders', | ||||
|     CANCEL_ORDERS_UP_TO: 'cancelOrdersUpTo', | ||||
|     TIME_BUFFER: new BigNumber(1000), | ||||
| }; | ||||
| @@ -0,0 +1,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.', | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										51
									
								
								contracts/coordinator/test/utils/exchange_data_encoder.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								contracts/coordinator/test/utils/exchange_data_encoder.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | ||||
| import { artifacts, IExchangeContract } from '@0x/contracts-exchange'; | ||||
| import { constants as devConstants, provider } from '@0x/contracts-test-utils'; | ||||
| import { SignedOrder } from '@0x/types'; | ||||
|  | ||||
| import { constants } from './index'; | ||||
|  | ||||
| export const exchangeDataEncoder = { | ||||
|     encodeOrdersToExchangeData(fnName: string, orders: SignedOrder[]): string { | ||||
|         const exchangeInstance = new IExchangeContract( | ||||
|             artifacts.IExchange.compilerOutput.abi, | ||||
|             devConstants.NULL_ADDRESS, | ||||
|             provider, | ||||
|         ); | ||||
|         let data; | ||||
|         if (constants.SINGLE_FILL_FN_NAMES.indexOf(fnName) !== -1) { | ||||
|             data = (exchangeInstance as any)[fnName].getABIEncodedTransactionData( | ||||
|                 orders[0], | ||||
|                 orders[0].takerAssetAmount, | ||||
|                 orders[0].signature, | ||||
|             ); | ||||
|         } else if (constants.BATCH_FILL_FN_NAMES.indexOf(fnName) !== -1) { | ||||
|             data = (exchangeInstance as any)[fnName].getABIEncodedTransactionData( | ||||
|                 orders, | ||||
|                 orders.map(order => order.takerAssetAmount), | ||||
|                 orders.map(order => order.signature), | ||||
|             ); | ||||
|         } else if (constants.MARKET_FILL_FN_NAMES.indexOf(fnName) !== -1) { | ||||
|             data = (exchangeInstance as any)[fnName].getABIEncodedTransactionData( | ||||
|                 orders, | ||||
|                 orders.map(order => order.takerAssetAmount).reduce((prev, curr) => prev.plus(curr)), | ||||
|                 orders.map(order => order.signature), | ||||
|             ); | ||||
|         } else if (fnName === constants.MATCH_ORDERS) { | ||||
|             data = exchangeInstance.matchOrders.getABIEncodedTransactionData( | ||||
|                 orders[0], | ||||
|                 orders[1], | ||||
|                 orders[0].signature, | ||||
|                 orders[1].signature, | ||||
|             ); | ||||
|         } else if (fnName === constants.CANCEL_ORDER) { | ||||
|             data = exchangeInstance.cancelOrder.getABIEncodedTransactionData(orders[0]); | ||||
|         } else if (fnName === constants.BATCH_CANCEL_ORDERS) { | ||||
|             data = exchangeInstance.batchCancelOrders.getABIEncodedTransactionData(orders); | ||||
|         } else if (fnName === constants.CANCEL_ORDERS_UP_TO) { | ||||
|             data = exchangeInstance.cancelOrdersUpTo.getABIEncodedTransactionData(devConstants.ZERO_AMOUNT); | ||||
|         } else { | ||||
|             throw new Error(`Error: ${fnName} not a supported function`); | ||||
|         } | ||||
|         return data; | ||||
|     }, | ||||
| }; | ||||
							
								
								
									
										33
									
								
								contracts/coordinator/test/utils/hash_utils.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								contracts/coordinator/test/utils/hash_utils.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| import { eip712Utils } from '@0x/order-utils'; | ||||
| import { SignedZeroExTransaction } from '@0x/types'; | ||||
| import { BigNumber, signTypedDataUtils } from '@0x/utils'; | ||||
| import * as _ from 'lodash'; | ||||
|  | ||||
| export const hashUtils = { | ||||
|     getApprovalHashBuffer( | ||||
|         transaction: SignedZeroExTransaction, | ||||
|         verifyingContractAddress: string, | ||||
|         txOrigin: string, | ||||
|         approvalExpirationTimeSeconds: BigNumber, | ||||
|     ): Buffer { | ||||
|         const typedData = eip712Utils.createCoordinatorApprovalTypedData( | ||||
|             transaction, | ||||
|             verifyingContractAddress, | ||||
|             txOrigin, | ||||
|             approvalExpirationTimeSeconds, | ||||
|         ); | ||||
|         const hashBuffer = signTypedDataUtils.generateTypedDataHash(typedData); | ||||
|         return hashBuffer; | ||||
|     }, | ||||
|     getApprovalHashHex( | ||||
|         transaction: SignedZeroExTransaction, | ||||
|         verifyingContractAddress: string, | ||||
|         txOrigin: string, | ||||
|         approvalExpirationTimeSeconds: BigNumber, | ||||
|     ): string { | ||||
|         const hashHex = `0x${hashUtils | ||||
|             .getApprovalHashBuffer(transaction, verifyingContractAddress, txOrigin, approvalExpirationTimeSeconds) | ||||
|             .toString('hex')}`; | ||||
|         return hashHex; | ||||
|     }, | ||||
| }; | ||||
							
								
								
									
										5
									
								
								contracts/coordinator/test/utils/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								contracts/coordinator/test/utils/index.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| export { hashUtils } from './hash_utils'; | ||||
| export { ApprovalFactory } from './approval_factory'; | ||||
| export { constants } from './constants'; | ||||
| export { exchangeDataEncoder } from './exchange_data_encoder'; | ||||
| export * from './types'; | ||||
							
								
								
									
										12
									
								
								contracts/coordinator/test/utils/types.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								contracts/coordinator/test/utils/types.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| import { SignedZeroExTransaction } from '@0x/types'; | ||||
| import { BigNumber } from '@0x/utils'; | ||||
|  | ||||
| export interface CoordinatorApproval { | ||||
|     transaction: SignedZeroExTransaction; | ||||
|     txOrigin: string; | ||||
|     approvalExpirationTimeSeconds: BigNumber; | ||||
| } | ||||
|  | ||||
| export interface SignedCoordinatorApproval extends CoordinatorApproval { | ||||
|     signature: string; | ||||
| } | ||||
							
								
								
									
										7
									
								
								contracts/coordinator/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								contracts/coordinator/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| { | ||||
|     "extends": "../../tsconfig", | ||||
|     "compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true }, | ||||
|     "include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"], | ||||
|     "files": ["generated-artifacts/Coordinator.json", "generated-artifacts/CoordinatorRegistry.json"], | ||||
|     "exclude": ["./deploy/solc/solc_bin"] | ||||
| } | ||||
							
								
								
									
										6
									
								
								contracts/coordinator/tslint.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								contracts/coordinator/tslint.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| { | ||||
|     "extends": ["@0x/tslint-config"], | ||||
|     "rules": { | ||||
|         "custom-no-magic-numbers": false | ||||
|     } | ||||
| } | ||||
							
								
								
									
										39
									
								
								contracts/erc1155/CHANGELOG.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								contracts/erc1155/CHANGELOG.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| [ | ||||
|     { | ||||
|         "version": "1.1.1", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ], | ||||
|         "timestamp": 1554997931 | ||||
|     }, | ||||
|     { | ||||
|         "version": "1.1.0", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Run Web3ProviderEngine without excess block polling", | ||||
|                 "pr": 1695 | ||||
|             } | ||||
|         ], | ||||
|         "timestamp": 1553183790 | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1553091633, | ||||
|         "version": "1.0.1", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "version": "1.0.0", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Created ERC1155 contracts package", | ||||
|                 "pr": 1657 | ||||
|             } | ||||
|         ] | ||||
|     } | ||||
| ] | ||||
							
								
								
									
										22
									
								
								contracts/erc1155/CHANGELOG.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								contracts/erc1155/CHANGELOG.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| <!-- | ||||
| changelogUtils.file is auto-generated using the monorepo-scripts package. Don't edit directly. | ||||
| Edit the package's CHANGELOG.json file only. | ||||
| --> | ||||
|  | ||||
| CHANGELOG | ||||
|  | ||||
| ## v1.1.1 - _April 11, 2019_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v1.1.0 - _March 21, 2019_ | ||||
|  | ||||
|     * Run Web3ProviderEngine without excess block polling (#1695) | ||||
|  | ||||
| ## v1.0.1 - _March 20, 2019_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v1.0.0 - _Invalid date_ | ||||
|  | ||||
|     * Created ERC1155 contracts package (#1657) | ||||
							
								
								
									
										1
									
								
								contracts/erc1155/DEPLOYS.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								contracts/erc1155/DEPLOYS.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| [] | ||||
							
								
								
									
										73
									
								
								contracts/erc1155/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								contracts/erc1155/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | ||||
| ## ERC1155 Tokens | ||||
|  | ||||
| This package contains implementations of various [ERC1155](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1155.md) tokens. 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-erc1155 --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-erc1155 yarn build | ||||
| ``` | ||||
|  | ||||
| Or continuously rebuild on change: | ||||
|  | ||||
| ```bash | ||||
| PKG=@0x/contracts-erc1155 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). | ||||
							
								
								
									
										30
									
								
								contracts/erc1155/compiler.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								contracts/erc1155/compiler.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| { | ||||
|     "artifactsDir": "generated-artifacts", | ||||
|     "contractsDir": "contracts", | ||||
|     "useDockerisedSolc": true, | ||||
|     "compilerSettings": { | ||||
|         "evmVersion": "constantinople", | ||||
|         "optimizer": { "enabled": true, "runs": 1000000 }, | ||||
|         "outputSelection": { | ||||
|             "*": { | ||||
|                 "*": [ | ||||
|                     "abi", | ||||
|                     "evm.bytecode.object", | ||||
|                     "evm.bytecode.sourceMap", | ||||
|                     "evm.deployedBytecode.object", | ||||
|                     "evm.deployedBytecode.sourceMap" | ||||
|                 ] | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|     "contracts": [ | ||||
|         "src/ERC1155.sol", | ||||
|         "src/ERC1155Mintable.sol", | ||||
|         "src/MixinNonFungibleToken.sol", | ||||
|         "src/interfaces/IERC1155.sol", | ||||
|         "src/interfaces/IERC1155Mintable.sol", | ||||
|         "src/interfaces/IERC1155Receiver.sol", | ||||
|         "src/mixins/MNonFungibleToken.sol", | ||||
|         "test/DummyERC1155Receiver.sol" | ||||
|     ] | ||||
| } | ||||
							
								
								
									
										247
									
								
								contracts/erc1155/contracts/src/ERC1155.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										247
									
								
								contracts/erc1155/contracts/src/ERC1155.sol
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,247 @@ | ||||
| /* | ||||
|  | ||||
|   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 "@0x/contracts-utils/contracts/src/SafeMath.sol"; | ||||
| import "@0x/contracts-utils/contracts/src/Address.sol"; | ||||
| import "./interfaces/IERC1155.sol"; | ||||
| import "./interfaces/IERC1155Receiver.sol"; | ||||
| import "./MixinNonFungibleToken.sol"; | ||||
|  | ||||
|  | ||||
| contract ERC1155 is | ||||
|     SafeMath, | ||||
|     IERC1155, | ||||
|     MixinNonFungibleToken | ||||
| { | ||||
|     using Address for address; | ||||
|  | ||||
|     // selectors for receiver callbacks | ||||
|     bytes4 constant public ERC1155_RECEIVED       = 0xf23a6e61; | ||||
|     bytes4 constant public ERC1155_BATCH_RECEIVED = 0xbc197c81; | ||||
|  | ||||
|     // id => (owner => balance) | ||||
|     mapping (uint256 => mapping(address => uint256)) internal balances; | ||||
|  | ||||
|     // owner => (operator => approved) | ||||
|     mapping (address => mapping(address => bool)) internal operatorApproval; | ||||
|  | ||||
|     /// @notice Transfers value amount of an _id from the _from address to the _to address specified. | ||||
|     /// @dev MUST emit TransferSingle event on success. | ||||
|     /// Caller must be approved to manage the _from account's tokens (see isApprovedForAll). | ||||
|     /// MUST throw if `_to` is the zero address. | ||||
|     /// MUST throw if balance of sender for token `_id` is lower than the `_value` sent. | ||||
|     /// MUST throw on any other error. | ||||
|     /// When transfer is complete, this function MUST check if `_to` is a smart contract (code size > 0). | ||||
|     /// If so, it MUST call `onERC1155Received` on `_to` and revert if the return value | ||||
|     /// is not `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`. | ||||
|     /// @param from    Source address | ||||
|     /// @param to      Target address | ||||
|     /// @param id      ID of the token type | ||||
|     /// @param value   Transfer amount | ||||
|     /// @param data    Additional data with no specified format, sent in call to `_to` | ||||
|     function safeTransferFrom( | ||||
|         address from, | ||||
|         address to, | ||||
|         uint256 id, | ||||
|         uint256 value, | ||||
|         bytes calldata data | ||||
|     ) | ||||
|         external | ||||
|     { | ||||
|         // sanity checks | ||||
|         require( | ||||
|             to != address(0x0), | ||||
|             "CANNOT_TRANSFER_TO_ADDRESS_ZERO" | ||||
|         ); | ||||
|         require( | ||||
|             from == msg.sender || operatorApproval[from][msg.sender] == true, | ||||
|             "INSUFFICIENT_ALLOWANCE" | ||||
|         ); | ||||
|  | ||||
|         // perform transfer | ||||
|         if (isNonFungible(id)) { | ||||
|             require( | ||||
|                     value == 1, | ||||
|                     "AMOUNT_EQUAL_TO_ONE_REQUIRED" | ||||
|             ); | ||||
|             require( | ||||
|                 nfOwners[id] == from, | ||||
|                 "NFT_NOT_OWNED_BY_FROM_ADDRESS" | ||||
|             ); | ||||
|             nfOwners[id] = to; | ||||
|             // You could keep balance of NF type in base type id like so: | ||||
|             // uint256 baseType = getNonFungibleBaseType(_id); | ||||
|             // balances[baseType][_from] = balances[baseType][_from].safeSub(_value); | ||||
|             // balances[baseType][_to]   = balances[baseType][_to].safeAdd(_value); | ||||
|         } else { | ||||
|             balances[id][from] = safeSub(balances[id][from], value); | ||||
|             balances[id][to] = safeAdd(balances[id][to], value); | ||||
|         } | ||||
|         emit TransferSingle(msg.sender, from, to, id, value); | ||||
|  | ||||
|         // if `to` is a contract then trigger its callback | ||||
|         if (to.isContract()) { | ||||
|             bytes4 callbackReturnValue = IERC1155Receiver(to).onERC1155Received( | ||||
|                 msg.sender, | ||||
|                 from, | ||||
|                 id, | ||||
|                 value, | ||||
|                 data | ||||
|             ); | ||||
|             require( | ||||
|                 callbackReturnValue == ERC1155_RECEIVED, | ||||
|                 "BAD_RECEIVER_RETURN_VALUE" | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// @notice Send multiple types of Tokens from a 3rd party in one transfer (with safety call). | ||||
|     /// @dev MUST emit TransferBatch event on success. | ||||
|     /// Caller must be approved to manage the _from account's tokens (see isApprovedForAll). | ||||
|     /// MUST throw if `_to` is the zero address. | ||||
|     /// MUST throw if length of `_ids` is not the same as length of `_values`. | ||||
|     ///  MUST throw if any of the balance of sender for token `_ids` is lower than the respective `_values` sent. | ||||
|     /// MUST throw on any other error. | ||||
|     /// When transfer is complete, this function MUST check if `_to` is a smart contract (code size > 0). | ||||
|     /// If so, it MUST call `onERC1155BatchReceived` on `_to` and revert if the return value | ||||
|     /// is not `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`. | ||||
|     /// @param from    Source addresses | ||||
|     /// @param to      Target addresses | ||||
|     /// @param ids     IDs of each token type | ||||
|     /// @param values  Transfer amounts per token type | ||||
|     /// @param data    Additional data with no specified format, sent in call to `_to` | ||||
|     function safeBatchTransferFrom( | ||||
|         address from, | ||||
|         address to, | ||||
|         uint256[] calldata ids, | ||||
|         uint256[] calldata values, | ||||
|         bytes calldata data | ||||
|     ) | ||||
|         external | ||||
|     { | ||||
|         // sanity checks | ||||
|         require( | ||||
|             to != address(0x0), | ||||
|             "CANNOT_TRANSFER_TO_ADDRESS_ZERO" | ||||
|         ); | ||||
|         require( | ||||
|             ids.length == values.length, | ||||
|             "TOKEN_AND_VALUES_LENGTH_MISMATCH" | ||||
|         ); | ||||
|  | ||||
|         // Only supporting a global operator approval allows us to do | ||||
|         // only 1 check and not to touch storage to handle allowances. | ||||
|         require( | ||||
|             from == msg.sender || operatorApproval[from][msg.sender] == true, | ||||
|             "INSUFFICIENT_ALLOWANCE" | ||||
|         ); | ||||
|  | ||||
|         // perform transfers | ||||
|         for (uint256 i = 0; i < ids.length; ++i) { | ||||
|             // Cache value to local variable to reduce read costs. | ||||
|             uint256 id = ids[i]; | ||||
|             uint256 value = values[i]; | ||||
|  | ||||
|             if (isNonFungible(id)) { | ||||
|                 require( | ||||
|                     value == 1, | ||||
|                     "AMOUNT_EQUAL_TO_ONE_REQUIRED" | ||||
|                 ); | ||||
|                 require( | ||||
|                     nfOwners[id] == from, | ||||
|                     "NFT_NOT_OWNED_BY_FROM_ADDRESS" | ||||
|                 ); | ||||
|                 nfOwners[id] = to; | ||||
|             } else { | ||||
|                 balances[id][from] = safeSub(balances[id][from], value); | ||||
|                 balances[id][to] = safeAdd(balances[id][to], value); | ||||
|             } | ||||
|         } | ||||
|         emit TransferBatch(msg.sender, from, to, ids, values); | ||||
|  | ||||
|         // if `to` is a contract then trigger its callback | ||||
|         if (to.isContract()) { | ||||
|             bytes4 callbackReturnValue = IERC1155Receiver(to).onERC1155BatchReceived( | ||||
|                 msg.sender, | ||||
|                 from, | ||||
|                 ids, | ||||
|                 values, | ||||
|                 data | ||||
|             ); | ||||
|             require( | ||||
|                 callbackReturnValue == ERC1155_BATCH_RECEIVED, | ||||
|                 "BAD_RECEIVER_RETURN_VALUE" | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// @notice Enable or disable approval for a third party ("operator") to manage all of the caller's tokens. | ||||
|     /// @dev MUST emit the ApprovalForAll event on success. | ||||
|     /// @param operator  Address to add to the set of authorized operators | ||||
|     /// @param approved  True if the operator is approved, false to revoke approval | ||||
|     function setApprovalForAll(address operator, bool approved) external { | ||||
|         operatorApproval[msg.sender][operator] = approved; | ||||
|         emit ApprovalForAll(msg.sender, operator, approved); | ||||
|     } | ||||
|  | ||||
|     /// @notice Queries the approval status of an operator for a given owner. | ||||
|     /// @param owner     The owner of the Tokens | ||||
|     /// @param operator  Address of authorized operator | ||||
|     /// @return           True if the operator is approved, false if not | ||||
|     function isApprovedForAll(address owner, address operator) external view returns (bool) { | ||||
|         return operatorApproval[owner][operator]; | ||||
|     } | ||||
|  | ||||
|     /// @notice Get the balance of an account's Tokens. | ||||
|     /// @param owner  The address of the token holder | ||||
|     /// @param id     ID of the Token | ||||
|     /// @return        The _owner's balance of the Token type requested | ||||
|     function balanceOf(address owner, uint256 id) external view returns (uint256) { | ||||
|         if (isNonFungibleItem(id)) { | ||||
|             return nfOwners[id] == owner ? 1 : 0; | ||||
|         } | ||||
|         return balances[id][owner]; | ||||
|     } | ||||
|  | ||||
|     /// @notice Get the balance of multiple account/token pairs | ||||
|     /// @param owners The addresses of the token holders | ||||
|     /// @param ids    ID of the Tokens | ||||
|     /// @return        The _owner's balance of the Token types requested | ||||
|     function balanceOfBatch(address[] calldata owners, uint256[] calldata ids) external view returns (uint256[] memory balances_) { | ||||
|         // sanity check | ||||
|         require( | ||||
|             owners.length == ids.length, | ||||
|             "OWNERS_AND_IDS_MUST_HAVE_SAME_LENGTH" | ||||
|         ); | ||||
|  | ||||
|         // get balances | ||||
|         balances_ = new uint256[](owners.length); | ||||
|         for (uint256 i = 0; i < owners.length; ++i) { | ||||
|             uint256 id = ids[i]; | ||||
|             if (isNonFungibleItem(id)) { | ||||
|                 balances_[i] = nfOwners[id] == owners[i] ? 1 : 0; | ||||
|             } else { | ||||
|                 balances_[i] = balances[id][owners[i]]; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return balances_; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										173
									
								
								contracts/erc1155/contracts/src/ERC1155Mintable.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								contracts/erc1155/contracts/src/ERC1155Mintable.sol
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,173 @@ | ||||
| pragma solidity ^0.5.5; | ||||
|  | ||||
| import "@0x/contracts-utils/contracts/src/SafeMath.sol"; | ||||
| import "./ERC1155.sol"; | ||||
| import "./interfaces/IERC1155Mintable.sol"; | ||||
|  | ||||
|  | ||||
| /// @dev Mintable form of ERC1155 | ||||
| /// Shows how easy it is to mint new items | ||||
| contract ERC1155Mintable is | ||||
|     IERC1155Mintable, | ||||
|     ERC1155 | ||||
| { | ||||
|  | ||||
|     /// token nonce | ||||
|     uint256 internal nonce; | ||||
|  | ||||
|     /// mapping from token to creator | ||||
|     mapping (uint256 => address) public creators; | ||||
|  | ||||
|     /// mapping from token to max index | ||||
|     mapping (uint256 => uint256) public maxIndex; | ||||
|  | ||||
|     /// asserts token is owned by msg.sender | ||||
|     modifier creatorOnly(uint256 _id) { | ||||
|         require(creators[_id] == msg.sender); | ||||
|         _; | ||||
|     } | ||||
|  | ||||
|     /// @dev creates a new token | ||||
|     /// @param uri URI of token | ||||
|     /// @param isNF is non-fungible token | ||||
|     /// @return type_ of token (a unique identifier) | ||||
|     function create( | ||||
|         string calldata uri, | ||||
|         bool isNF | ||||
|     ) | ||||
|         external | ||||
|         returns (uint256 type_) | ||||
|     {  | ||||
|         // Store the type in the upper 128 bits | ||||
|         type_ = (++nonce << 128); | ||||
|  | ||||
|         // Set a flag if this is an NFI. | ||||
|         if (isNF) { | ||||
|             type_ = type_ | TYPE_NF_BIT; | ||||
|         } | ||||
|  | ||||
|         // This will allow restricted access to creators. | ||||
|         creators[type_] = msg.sender; | ||||
|  | ||||
|         // emit a Transfer event with Create semantic to help with discovery. | ||||
|         emit TransferSingle( | ||||
|             msg.sender, | ||||
|             address(0x0), | ||||
|             address(0x0), | ||||
|             type_, | ||||
|             0 | ||||
|         ); | ||||
|  | ||||
|         if (bytes(uri).length > 0) { | ||||
|             emit URI(uri, type_); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// @dev mints fungible tokens | ||||
|     /// @param id token type | ||||
|     /// @param to beneficiaries of minted tokens | ||||
|     /// @param quantities amounts of minted tokens | ||||
|     function mintFungible( | ||||
|         uint256 id, | ||||
|         address[] calldata to, | ||||
|         uint256[] calldata quantities | ||||
|     ) | ||||
|         external | ||||
|         creatorOnly(id) | ||||
|     { | ||||
|         // sanity checks | ||||
|         require( | ||||
|             isFungible(id), | ||||
|             "TRIED_TO_MINT_FUNGIBLE_FOR_NON_FUNGIBLE_TOKEN" | ||||
|         ); | ||||
|  | ||||
|         // mint tokens | ||||
|         for (uint256 i = 0; i < to.length; ++i) { | ||||
|             // cache to reduce number of loads | ||||
|             address dst = to[i]; | ||||
|             uint256 quantity = quantities[i]; | ||||
|  | ||||
|             // Grant the items to the caller | ||||
|             balances[id][dst] = safeAdd(quantity, balances[id][dst]); | ||||
|  | ||||
|             // Emit the Transfer/Mint event. | ||||
|             // the 0x0 source address implies a mint | ||||
|             // It will also provide the circulating supply info. | ||||
|             emit TransferSingle( | ||||
|                 msg.sender, | ||||
|                 address(0x0), | ||||
|                 dst, | ||||
|                 id, | ||||
|                 quantity | ||||
|             ); | ||||
|  | ||||
|             // if `to` is a contract then trigger its callback | ||||
|             if (dst.isContract()) { | ||||
|                 bytes4 callbackReturnValue = IERC1155Receiver(dst).onERC1155Received( | ||||
|                     msg.sender, | ||||
|                     msg.sender, | ||||
|                     id, | ||||
|                     quantity, | ||||
|                     "" | ||||
|                 ); | ||||
|                 require( | ||||
|                     callbackReturnValue == ERC1155_RECEIVED, | ||||
|                     "BAD_RECEIVER_RETURN_VALUE" | ||||
|                 ); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// @dev mints a non-fungible token | ||||
|     /// @param type_ token type | ||||
|     /// @param to beneficiaries of minted tokens | ||||
|     function mintNonFungible( | ||||
|         uint256 type_, | ||||
|         address[] calldata to | ||||
|     ) | ||||
|         external | ||||
|         creatorOnly(type_) | ||||
|     { | ||||
|         // No need to check this is a nf type rather than an id since | ||||
|         // creatorOnly() will only let a type pass through. | ||||
|         require( | ||||
|             isNonFungible(type_), | ||||
|             "TRIED_TO_MINT_NON_FUNGIBLE_FOR_FUNGIBLE_TOKEN" | ||||
|         ); | ||||
|  | ||||
|         // Index are 1-based. | ||||
|         uint256 index = maxIndex[type_] + 1; | ||||
|  | ||||
|         for (uint256 i = 0; i < to.length; ++i) { | ||||
|             // cache to reduce number of loads | ||||
|             address dst = to[i]; | ||||
|             uint256 id  = type_ | index + i; | ||||
|  | ||||
|             nfOwners[id] = dst; | ||||
|  | ||||
|             // You could use base-type id to store NF type balances if you wish. | ||||
|             // balances[_type][dst] = quantity.safeAdd(balances[_type][dst]); | ||||
|  | ||||
|             emit TransferSingle(msg.sender, address(0x0), dst, id, 1); | ||||
|  | ||||
|             // if `to` is a contract then trigger its callback | ||||
|             if (dst.isContract()) { | ||||
|                 bytes4 callbackReturnValue = IERC1155Receiver(dst).onERC1155Received( | ||||
|                     msg.sender, | ||||
|                     msg.sender, | ||||
|                     id, | ||||
|                     1, | ||||
|                     "" | ||||
|                 ); | ||||
|                 require( | ||||
|                     callbackReturnValue == ERC1155_RECEIVED, | ||||
|                     "BAD_RECEIVER_RETURN_VALUE" | ||||
|                 ); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // record the `maxIndex` of this nft type | ||||
|         // this allows us to mint more nft's of this type in a subsequent call. | ||||
|         maxIndex[type_] = safeAdd(to.length, maxIndex[type_]); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										76
									
								
								contracts/erc1155/contracts/src/MixinNonFungibleToken.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								contracts/erc1155/contracts/src/MixinNonFungibleToken.sol
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,76 @@ | ||||
| /* | ||||
|  | ||||
|   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 "./mixins/MNonFungibleToken.sol"; | ||||
|  | ||||
|  | ||||
| contract MixinNonFungibleToken is | ||||
|     MNonFungibleToken | ||||
| { | ||||
|     /// Use a split bit implementation. | ||||
|     /// Store the type in the upper 128 bits.. | ||||
|     uint256 constant internal TYPE_MASK = uint256(uint128(~0)) << 128; | ||||
|  | ||||
|     /// ..and the non-fungible index in the lower 128 | ||||
|     uint256 constant internal NF_INDEX_MASK = uint128(~0); | ||||
|  | ||||
|     /// The top bit is a flag to tell if this is a NFI. | ||||
|     uint256 constant internal TYPE_NF_BIT = 1 << 255; | ||||
|  | ||||
|     /// mapping of nft to owner | ||||
|     mapping (uint256 => address) internal nfOwners; | ||||
|  | ||||
|     /// @dev Returns true if token is non-fungible | ||||
|     function isNonFungible(uint256 id) public pure returns(bool) { | ||||
|         return id & TYPE_NF_BIT == TYPE_NF_BIT; | ||||
|     } | ||||
|  | ||||
|     /// @dev Returns true if token is fungible | ||||
|     function isFungible(uint256 id) public pure returns(bool) { | ||||
|         return id & TYPE_NF_BIT == 0; | ||||
|     } | ||||
|  | ||||
|     /// @dev Returns index of non-fungible token | ||||
|     function getNonFungibleIndex(uint256 id) public pure returns(uint256) { | ||||
|         return id & NF_INDEX_MASK; | ||||
|     } | ||||
|  | ||||
|     /// @dev Returns base type of non-fungible token | ||||
|     function getNonFungibleBaseType(uint256 id) public pure returns(uint256) { | ||||
|         return id & TYPE_MASK; | ||||
|     } | ||||
|  | ||||
|     /// @dev Returns true if input is base-type of a non-fungible token | ||||
|     function isNonFungibleBaseType(uint256 id) public pure returns(bool) { | ||||
|         // A base type has the NF bit but does not have an index. | ||||
|         return (id & TYPE_NF_BIT == TYPE_NF_BIT) && (id & NF_INDEX_MASK == 0); | ||||
|     } | ||||
|  | ||||
|     /// @dev Returns true if input is a non-fungible token | ||||
|     function isNonFungibleItem(uint256 id) public pure returns(bool) { | ||||
|         // A base type has the NF bit but does has an index. | ||||
|         return (id & TYPE_NF_BIT == TYPE_NF_BIT) && (id & NF_INDEX_MASK != 0); | ||||
|     } | ||||
|      | ||||
|     /// @dev returns owner of a non-fungible token | ||||
|     function ownerOf(uint256 id) public view returns (address) { | ||||
|         return nfOwners[id]; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										152
									
								
								contracts/erc1155/contracts/src/interfaces/IERC1155.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								contracts/erc1155/contracts/src/interfaces/IERC1155.sol
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,152 @@ | ||||
| /* | ||||
|  | ||||
|   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; | ||||
|  | ||||
|  | ||||
| /// @title ERC-1155 Multi Token Standard | ||||
| /// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1155.md | ||||
| /// Note: The ERC-165 identifier for this interface is 0xd9b67a26. | ||||
| interface IERC1155 { | ||||
|      | ||||
|     /// @dev Either TransferSingle or TransferBatch MUST emit when tokens are transferred, | ||||
|     ///      including zero value transfers as well as minting or burning. | ||||
|     /// Operator will always be msg.sender. | ||||
|     /// Either event from address `0x0` signifies a minting operation. | ||||
|     /// An event to address `0x0` signifies a burning or melting operation. | ||||
|     /// The total value transferred from address 0x0 minus the total value transferred to 0x0 may | ||||
|     /// be used by clients and exchanges to be added to the "circulating supply" for a given token ID. | ||||
|     /// To define a token ID with no initial balance, the contract SHOULD emit the TransferSingle event | ||||
|     /// from `0x0` to `0x0`, with the token creator as `_operator`. | ||||
|     event TransferSingle( | ||||
|         address indexed operator, | ||||
|         address indexed from, | ||||
|         address indexed to, | ||||
|         uint256 id, | ||||
|         uint256 value | ||||
|     ); | ||||
|  | ||||
|     /// @dev Either TransferSingle or TransferBatch MUST emit when tokens are transferred, | ||||
|     ///      including zero value transfers as well as minting or burning. | ||||
|     ///Operator will always be msg.sender. | ||||
|     /// Either event from address `0x0` signifies a minting operation. | ||||
|     /// An event to address `0x0` signifies a burning or melting operation. | ||||
|     /// The total value transferred from address 0x0 minus the total value transferred to 0x0 may | ||||
|     /// be used by clients and exchanges to be added to the "circulating supply" for a given token ID. | ||||
|     /// To define multiple token IDs with no initial balance, this SHOULD emit the TransferBatch event | ||||
|     /// from `0x0` to `0x0`, with the token creator as `_operator`. | ||||
|     event TransferBatch( | ||||
|         address indexed operator, | ||||
|         address indexed from, | ||||
|         address indexed to, | ||||
|         uint256[] ids, | ||||
|         uint256[] values | ||||
|     ); | ||||
|  | ||||
|     /// @dev MUST emit when an approval is updated. | ||||
|     event ApprovalForAll( | ||||
|         address indexed owner, | ||||
|         address indexed operator, | ||||
|         bool approved | ||||
|     ); | ||||
|  | ||||
|     /// @dev MUST emit when the URI is updated for a token ID. | ||||
|     /// URIs are defined in RFC 3986. | ||||
|     /// The URI MUST point a JSON file that conforms to the "ERC-1155 Metadata JSON Schema". | ||||
|     event URI( | ||||
|         string value, | ||||
|         uint256 indexed id | ||||
|     ); | ||||
|  | ||||
|     /// @notice Transfers value amount of an _id from the _from address to the _to address specified. | ||||
|     /// @dev MUST emit TransferSingle event on success. | ||||
|     /// Caller must be approved to manage the _from account's tokens (see isApprovedForAll). | ||||
|     /// MUST throw if `_to` is the zero address. | ||||
|     /// MUST throw if balance of sender for token `_id` is lower than the `_value` sent. | ||||
|     /// MUST throw on any other error. | ||||
|     /// When transfer is complete, this function MUST check if `_to` is a smart contract (code size > 0). | ||||
|     /// If so, it MUST call `onERC1155Received` on `_to` and revert if the return value | ||||
|     /// is not `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`. | ||||
|     /// @param from    Source address | ||||
|     /// @param to      Target address | ||||
|     /// @param id      ID of the token type | ||||
|     /// @param value   Transfer amount | ||||
|     /// @param data    Additional data with no specified format, sent in call to `_to` | ||||
|     function safeTransferFrom( | ||||
|         address from, | ||||
|         address to, | ||||
|         uint256 id, | ||||
|         uint256 value, | ||||
|         bytes calldata data | ||||
|     ) | ||||
|         external; | ||||
|  | ||||
|     /// @notice Send multiple types of Tokens from a 3rd party in one transfer (with safety call). | ||||
|     /// @dev MUST emit TransferBatch event on success. | ||||
|     /// Caller must be approved to manage the _from account's tokens (see isApprovedForAll). | ||||
|     /// MUST throw if `_to` is the zero address. | ||||
|     /// MUST throw if length of `_ids` is not the same as length of `_values`. | ||||
|     ///  MUST throw if any of the balance of sender for token `_ids` is lower than the respective `_values` sent. | ||||
|     /// MUST throw on any other error. | ||||
|     /// When transfer is complete, this function MUST check if `_to` is a smart contract (code size > 0). | ||||
|     /// If so, it MUST call `onERC1155BatchReceived` on `_to` and revert if the return value | ||||
|     /// is not `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`. | ||||
|     /// @param from    Source addresses | ||||
|     /// @param to      Target addresses | ||||
|     /// @param ids     IDs of each token type | ||||
|     /// @param values  Transfer amounts per token type | ||||
|     /// @param data    Additional data with no specified format, sent in call to `_to` | ||||
|     function safeBatchTransferFrom( | ||||
|         address from, | ||||
|         address to, | ||||
|         uint256[] calldata ids, | ||||
|         uint256[] calldata values, | ||||
|         bytes calldata data | ||||
|     ) | ||||
|         external; | ||||
|  | ||||
|     /// @notice Enable or disable approval for a third party ("operator") to manage all of the caller's tokens. | ||||
|     /// @dev MUST emit the ApprovalForAll event on success. | ||||
|     /// @param operator  Address to add to the set of authorized operators | ||||
|     /// @param approved  True if the operator is approved, false to revoke approval | ||||
|     function setApprovalForAll(address operator, bool approved) external; | ||||
|  | ||||
|     /// @notice Queries the approval status of an operator for a given owner. | ||||
|     /// @param owner     The owner of the Tokens | ||||
|     /// @param operator  Address of authorized operator | ||||
|     /// @return           True if the operator is approved, false if not | ||||
|     function isApprovedForAll(address owner, address operator) external view returns (bool); | ||||
|  | ||||
|     /// @notice Get the balance of an account's Tokens. | ||||
|     /// @param owner  The address of the token holder | ||||
|     /// @param id     ID of the Token | ||||
|     /// @return        The _owner's balance of the Token type requested | ||||
|     function balanceOf(address owner, uint256 id) external view returns (uint256); | ||||
|  | ||||
|     /// @notice Get the balance of multiple account/token pairs | ||||
|     /// @param owners The addresses of the token holders | ||||
|     /// @param ids    ID of the Tokens | ||||
|     /// @return        The _owner's balance of the Token types requested | ||||
|     function balanceOfBatch( | ||||
|         address[] calldata owners, | ||||
|         uint256[] calldata ids | ||||
|     ) | ||||
|         external | ||||
|         view | ||||
|         returns (uint256[] memory balances_); | ||||
| } | ||||
| @@ -0,0 +1,42 @@ | ||||
| pragma solidity ^0.5.5; | ||||
|  | ||||
| import "./IERC1155.sol"; | ||||
|  | ||||
|  | ||||
| /// @dev Mintable form of ERC1155 | ||||
| /// Shows how easy it is to mint new items | ||||
| contract IERC1155Mintable is | ||||
|     IERC1155 | ||||
| { | ||||
|  | ||||
|     /// @dev creates a new token | ||||
|     /// @param uri URI of token | ||||
|     /// @param isNF is non-fungible token | ||||
|     /// @return _type of token (a unique identifier) | ||||
|     function create( | ||||
|         string calldata uri, | ||||
|         bool isNF | ||||
|     ) | ||||
|         external | ||||
|         returns (uint256 type_); | ||||
|  | ||||
|     /// @dev mints fungible tokens | ||||
|     /// @param id token type | ||||
|     /// @param to beneficiaries of minted tokens | ||||
|     /// @param quantities amounts of minted tokens | ||||
|     function mintFungible( | ||||
|         uint256 id, | ||||
|         address[] calldata to, | ||||
|         uint256[] calldata quantities | ||||
|     ) | ||||
|         external; | ||||
|  | ||||
|     /// @dev mints a non-fungible token | ||||
|     /// @param type_ token type | ||||
|     /// @param to beneficiaries of minted tokens | ||||
|     function mintNonFungible( | ||||
|         uint256 type_, | ||||
|         address[] calldata to | ||||
|     ) | ||||
|         external; | ||||
| } | ||||
| @@ -0,0 +1,67 @@ | ||||
| /* | ||||
|  | ||||
|   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; | ||||
|  | ||||
|  | ||||
| interface IERC1155Receiver { | ||||
|      | ||||
|     /// @notice Handle the receipt of a single ERC1155 token type | ||||
|     /// @dev The smart contract calls this function on the recipient | ||||
|     /// after a `safeTransferFrom`. This function MAY throw to revert and reject the | ||||
|     /// transfer. Return of other than the magic value MUST result in the | ||||
|     ///transaction being reverted | ||||
|     /// Note: the contract address is always the message sender | ||||
|     /// @param operator  The address which called `safeTransferFrom` function | ||||
|     /// @param from      The address which previously owned the token | ||||
|     /// @param id        An array containing the ids of the token being transferred | ||||
|     /// @param value     An array containing the amount of tokens being transferred | ||||
|     /// @param data      Additional data with no specified format | ||||
|     /// @return          `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` | ||||
|     function onERC1155Received( | ||||
|         address operator, | ||||
|         address from, | ||||
|         uint256 id, | ||||
|         uint256 value, | ||||
|         bytes calldata data | ||||
|     ) | ||||
|         external | ||||
|         returns(bytes4); | ||||
|  | ||||
|     /// @notice Handle the receipt of multiple ERC1155 token types | ||||
|     /// @dev The smart contract calls this function on the recipient | ||||
|     /// after a `safeTransferFrom`. This function MAY throw to revert and reject the | ||||
|     /// transfer. Return of other than the magic value MUST result in the | ||||
|     /// transaction being reverted | ||||
|     /// Note: the contract address is always the message sender | ||||
|     /// @param operator  The address which called `safeTransferFrom` function | ||||
|     /// @param from      The address which previously owned the token | ||||
|     /// @param ids       An array containing ids of each token being transferred | ||||
|     /// @param values    An array containing amounts of each token being transferred | ||||
|     /// @param data      Additional data with no specified format | ||||
|     /// @return           `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` | ||||
|     function onERC1155BatchReceived( | ||||
|         address operator, | ||||
|         address from, | ||||
|         uint256[] calldata ids, | ||||
|         uint256[] calldata values, | ||||
|         bytes calldata data | ||||
|     ) | ||||
|         external | ||||
|         returns(bytes4); | ||||
| } | ||||
							
								
								
									
										44
									
								
								contracts/erc1155/contracts/src/mixins/MNonFungibleToken.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								contracts/erc1155/contracts/src/mixins/MNonFungibleToken.sol
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | ||||
| /* | ||||
|  | ||||
|   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 MNonFungibleToken { | ||||
|  | ||||
|     /// @dev Returns true if token is non-fungible | ||||
|     function isNonFungible(uint256 id) public pure returns(bool); | ||||
|  | ||||
|     /// @dev Returns true if token is fungible | ||||
|     function isFungible(uint256 _d) public pure returns(bool); | ||||
|  | ||||
|     /// @dev Returns index of non-fungible token | ||||
|     function getNonFungibleIndex(uint256 id) public pure returns(uint256); | ||||
|  | ||||
|     /// @dev Returns base type of non-fungible token | ||||
|     function getNonFungibleBaseType(uint256 id) public pure returns(uint256); | ||||
|  | ||||
|     /// @dev Returns true if input is base-type of a non-fungible token | ||||
|     function isNonFungibleBaseType(uint256 id) public pure returns(bool); | ||||
|  | ||||
|     /// @dev Returns true if input is a non-fungible token | ||||
|     function isNonFungibleItem(uint256 id) public pure returns(bool); | ||||
|  | ||||
|     /// @dev returns owner of a non-fungible token | ||||
|     function ownerOf(uint256 id) public view returns (address); | ||||
| } | ||||
							
								
								
									
										126
									
								
								contracts/erc1155/contracts/test/DummyERC1155Receiver.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								contracts/erc1155/contracts/test/DummyERC1155Receiver.sol
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,126 @@ | ||||
| /* | ||||
|  | ||||
|   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 "../src/interfaces/IERC1155Receiver.sol"; | ||||
|  | ||||
|  | ||||
| contract DummyERC1155Receiver is | ||||
|     IERC1155Receiver | ||||
| { | ||||
|  | ||||
|     bytes4 constant public ERC1155_RECEIVED       = 0xf23a6e61; | ||||
|     bytes4 constant public ERC1155_BATCH_RECEIVED = 0xbc197c81; | ||||
|     bool internal shouldRejectTransfer; | ||||
|  | ||||
|     event TokenReceived( | ||||
|         address operator, | ||||
|         address from, | ||||
|         uint256 tokenId, | ||||
|         uint256 tokenValue, | ||||
|         bytes data | ||||
|     ); | ||||
|  | ||||
|     event BatchTokenReceived( | ||||
|         address operator, | ||||
|         address from, | ||||
|         uint256[] tokenIds, | ||||
|         uint256[] tokenValues, | ||||
|         bytes data | ||||
|     ); | ||||
|      | ||||
|     constructor () public { | ||||
|         shouldRejectTransfer = false; | ||||
|     } | ||||
|  | ||||
|     /// @notice Handle the receipt of a single ERC1155 token type | ||||
|     /// @dev The smart contract calls this function on the recipient | ||||
|     /// after a `safeTransferFrom`. This function MAY throw to revert and reject the | ||||
|     /// transfer. Return of other than the magic value MUST result in the | ||||
|     ///transaction being reverted | ||||
|     /// Note: the contract address is always the message sender | ||||
|     /// @param operator  The address which called `safeTransferFrom` function | ||||
|     /// @param from      The address which previously owned the token | ||||
|     /// @param id        An array containing the ids of the token being transferred | ||||
|     /// @param value     An array containing the amount of tokens being transferred | ||||
|     /// @param data      Additional data with no specified format | ||||
|     /// @return           `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` | ||||
|     function onERC1155Received( | ||||
|         address operator, | ||||
|         address from, | ||||
|         uint256 id, | ||||
|         uint256 value, | ||||
|         bytes calldata data | ||||
|     ) | ||||
|         external | ||||
|         returns(bytes4) | ||||
|     { | ||||
|         if (shouldRejectTransfer) { | ||||
|             revert("TRANSFER_REJECTED"); | ||||
|         } | ||||
|         emit TokenReceived( | ||||
|             operator, | ||||
|             from, | ||||
|             id, | ||||
|             value, | ||||
|             data | ||||
|         ); | ||||
|         return ERC1155_RECEIVED; | ||||
|     } | ||||
|  | ||||
|     /// @notice Handle the receipt of multiple ERC1155 token types | ||||
|     /// @dev The smart contract calls this function on the recipient | ||||
|     /// after a `safeTransferFrom`. This function MAY throw to revert and reject the | ||||
|     /// transfer. Return of other than the magic value MUST result in the | ||||
|     /// transaction being reverted | ||||
|     /// Note: the contract address is always the message sender | ||||
|     /// @param operator  The address which called `safeTransferFrom` function | ||||
|     /// @param from      The address which previously owned the token | ||||
|     /// @param ids       An array containing ids of each token being transferred | ||||
|     /// @param values    An array containing amounts of each token being transferred | ||||
|     /// @param data      Additional data with no specified format | ||||
|     /// @return          `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` | ||||
|     function onERC1155BatchReceived( | ||||
|         address operator, | ||||
|         address from, | ||||
|         uint256[] calldata ids, | ||||
|         uint256[] calldata values, | ||||
|         bytes calldata data | ||||
|     ) | ||||
|         external | ||||
|         returns (bytes4) | ||||
|     { | ||||
|         if (shouldRejectTransfer) { | ||||
|             revert("TRANSFER_REJECTED"); | ||||
|         } | ||||
|         emit BatchTokenReceived( | ||||
|             operator, | ||||
|             from, | ||||
|             ids, | ||||
|             values, | ||||
|             data | ||||
|         ); | ||||
|         return ERC1155_BATCH_RECEIVED; | ||||
|     } | ||||
|  | ||||
|     // @dev If set to true then all future transfers will be rejected. | ||||
|     function setRejectTransferFlag(bool _shouldRejectTransfer) external { | ||||
|         shouldRejectTransfer = _shouldRejectTransfer; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										83
									
								
								contracts/erc1155/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								contracts/erc1155/package.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | ||||
| { | ||||
|     "name": "@0x/contracts-erc1155", | ||||
|     "version": "1.1.1", | ||||
|     "engines": { | ||||
|         "node": ">=6.12" | ||||
|     }, | ||||
|     "description": "Token contracts used by 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/@(DummyERC1155Receiver|ERC1155|ERC1155Mintable|IERC1155|IERC1155Mintable|IERC1155Receiver|MNonFungibleToken|MixinNonFungibleToken).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/tokens/README.md", | ||||
|     "devDependencies": { | ||||
|         "@0x/abi-gen": "^2.0.9", | ||||
|         "@0x/contracts-gen": "^1.0.8", | ||||
|         "@0x/dev-utils": "^2.2.1", | ||||
|         "@0x/sol-compiler": "^3.1.6", | ||||
|         "@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.0.5", | ||||
|         "@0x/contracts-test-utils": "^3.1.2", | ||||
|         "@0x/contracts-utils": "^3.1.1", | ||||
|         "@0x/types": "^2.2.2", | ||||
|         "@0x/typescript-typings": "^4.2.2", | ||||
|         "@0x/utils": "^4.3.1", | ||||
|         "@0x/web3-wrapper": "^6.0.5", | ||||
|         "ethereum-types": "^2.1.2", | ||||
|         "lodash": "^4.17.11" | ||||
|     }, | ||||
|     "publishConfig": { | ||||
|         "access": "public" | ||||
|     } | ||||
| } | ||||
							
								
								
									
										25
									
								
								contracts/erc1155/src/artifacts.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								contracts/erc1155/src/artifacts.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| /* | ||||
|  * ----------------------------------------------------------------------------- | ||||
|  * Warning: This file is auto-generated by contracts-gen. Don't edit manually. | ||||
|  * ----------------------------------------------------------------------------- | ||||
|  */ | ||||
| import { ContractArtifact } from 'ethereum-types'; | ||||
|  | ||||
| import * as DummyERC1155Receiver from '../generated-artifacts/DummyERC1155Receiver.json'; | ||||
| import * as ERC1155 from '../generated-artifacts/ERC1155.json'; | ||||
| import * as ERC1155Mintable from '../generated-artifacts/ERC1155Mintable.json'; | ||||
| import * as IERC1155 from '../generated-artifacts/IERC1155.json'; | ||||
| import * as IERC1155Mintable from '../generated-artifacts/IERC1155Mintable.json'; | ||||
| import * as IERC1155Receiver from '../generated-artifacts/IERC1155Receiver.json'; | ||||
| import * as MixinNonFungibleToken from '../generated-artifacts/MixinNonFungibleToken.json'; | ||||
| import * as MNonFungibleToken from '../generated-artifacts/MNonFungibleToken.json'; | ||||
| export const artifacts = { | ||||
|     DummyERC1155Receiver: DummyERC1155Receiver as ContractArtifact, | ||||
|     ERC1155: ERC1155 as ContractArtifact, | ||||
|     MNonFungibleToken: MNonFungibleToken as ContractArtifact, | ||||
|     ERC1155Mintable: ERC1155Mintable as ContractArtifact, | ||||
|     MixinNonFungibleToken: MixinNonFungibleToken as ContractArtifact, | ||||
|     IERC1155Mintable: IERC1155Mintable as ContractArtifact, | ||||
|     IERC1155Receiver: IERC1155Receiver as ContractArtifact, | ||||
|     IERC1155: IERC1155 as ContractArtifact, | ||||
| }; | ||||
							
								
								
									
										3
									
								
								contracts/erc1155/src/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								contracts/erc1155/src/index.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| export * from './wrappers'; | ||||
| export * from './artifacts'; | ||||
| export { Erc1155Wrapper } from '../test/utils/erc1155_wrapper'; | ||||
							
								
								
									
										13
									
								
								contracts/erc1155/src/wrappers.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								contracts/erc1155/src/wrappers.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| /* | ||||
|  * ----------------------------------------------------------------------------- | ||||
|  * Warning: This file is auto-generated by contracts-gen. Don't edit manually. | ||||
|  * ----------------------------------------------------------------------------- | ||||
|  */ | ||||
| export * from '../generated-wrappers/dummy_erc1155_receiver'; | ||||
| export * from '../generated-wrappers/erc1155'; | ||||
| export * from '../generated-wrappers/erc1155_mintable'; | ||||
| export * from '../generated-wrappers/i_erc1155_mintable'; | ||||
| export * from '../generated-wrappers/i_erc1155_receiver'; | ||||
| export * from '../generated-wrappers/ierc1155'; | ||||
| export * from '../generated-wrappers/m_non_fungible_token'; | ||||
| export * from '../generated-wrappers/mixin_non_fungible_token'; | ||||
							
								
								
									
										492
									
								
								contracts/erc1155/test/erc1155_token.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										492
									
								
								contracts/erc1155/test/erc1155_token.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,492 @@ | ||||
| import { | ||||
|     chaiSetup, | ||||
|     constants, | ||||
|     expectTransactionFailedAsync, | ||||
|     provider, | ||||
|     txDefaults, | ||||
|     web3Wrapper, | ||||
| } from '@0x/contracts-test-utils'; | ||||
| import { BlockchainLifecycle } from '@0x/dev-utils'; | ||||
| import { RevertReason } from '@0x/types'; | ||||
| import { BigNumber } from '@0x/utils'; | ||||
| import * as chai from 'chai'; | ||||
| import { LogWithDecodedArgs } from 'ethereum-types'; | ||||
| import * as _ from 'lodash'; | ||||
|  | ||||
| import { | ||||
|     artifacts, | ||||
|     DummyERC1155ReceiverBatchTokenReceivedEventArgs, | ||||
|     DummyERC1155ReceiverContract, | ||||
|     ERC1155MintableContract, | ||||
| } from '../src'; | ||||
|  | ||||
| import { Erc1155Wrapper } from './utils/erc1155_wrapper'; | ||||
|  | ||||
| chaiSetup.configure(); | ||||
| const expect = chai.expect; | ||||
| const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); | ||||
| // tslint:disable:no-unnecessary-type-assertion | ||||
| describe('ERC1155Token', () => { | ||||
|     // constant values used in transfer tests | ||||
|     const nftOwnerBalance = new BigNumber(1); | ||||
|     const nftNotOwnerBalance = new BigNumber(0); | ||||
|     const spenderInitialFungibleBalance = new BigNumber(500); | ||||
|     const receiverInitialFungibleBalance = new BigNumber(0); | ||||
|     const fungibleValueToTransfer = spenderInitialFungibleBalance.div(2); | ||||
|     const nonFungibleValueToTransfer = nftOwnerBalance; | ||||
|     const receiverCallbackData = '0x01020304'; | ||||
|     // tokens & addresses | ||||
|     let owner: string; | ||||
|     let spender: string; | ||||
|     let delegatedSpender: string; | ||||
|     let receiver: string; | ||||
|     let erc1155Contract: ERC1155MintableContract; | ||||
|     let erc1155Receiver: DummyERC1155ReceiverContract; | ||||
|     let nonFungibleToken: BigNumber; | ||||
|     let erc1155Wrapper: Erc1155Wrapper; | ||||
|     let fungibleToken: BigNumber; | ||||
|     // tests | ||||
|     before(async () => { | ||||
|         await blockchainLifecycle.startAsync(); | ||||
|     }); | ||||
|     after(async () => { | ||||
|         await blockchainLifecycle.revertAsync(); | ||||
|     }); | ||||
|     before(async () => { | ||||
|         // deploy erc1155 contract & receiver | ||||
|         const accounts = await web3Wrapper.getAvailableAddressesAsync(); | ||||
|         [owner, spender, delegatedSpender] = accounts; | ||||
|         erc1155Contract = await ERC1155MintableContract.deployFrom0xArtifactAsync( | ||||
|             artifacts.ERC1155Mintable, | ||||
|             provider, | ||||
|             txDefaults, | ||||
|         ); | ||||
|         erc1155Receiver = await DummyERC1155ReceiverContract.deployFrom0xArtifactAsync( | ||||
|             artifacts.DummyERC1155Receiver, | ||||
|             provider, | ||||
|             txDefaults, | ||||
|         ); | ||||
|         receiver = erc1155Receiver.address; | ||||
|         // create wrapper & mint erc1155 tokens | ||||
|         erc1155Wrapper = new Erc1155Wrapper(erc1155Contract, provider, owner); | ||||
|         fungibleToken = await erc1155Wrapper.mintFungibleTokensAsync([spender], spenderInitialFungibleBalance); | ||||
|         let nonFungibleTokens: BigNumber[]; | ||||
|         [, nonFungibleTokens] = await erc1155Wrapper.mintNonFungibleTokensAsync([spender]); | ||||
|         nonFungibleToken = nonFungibleTokens[0]; | ||||
|     }); | ||||
|     beforeEach(async () => { | ||||
|         await blockchainLifecycle.startAsync(); | ||||
|     }); | ||||
|     afterEach(async () => { | ||||
|         await blockchainLifecycle.revertAsync(); | ||||
|     }); | ||||
|     describe('safeTransferFrom', () => { | ||||
|         it('should transfer fungible token if called by token owner', async () => { | ||||
|             // setup test parameters | ||||
|             const tokenHolders = [spender, receiver]; | ||||
|             const tokenToTransfer = fungibleToken; | ||||
|             const valueToTransfer = fungibleValueToTransfer; | ||||
|             // check balances before transfer | ||||
|             const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedInitialBalances); | ||||
|             // execute transfer | ||||
|             await erc1155Wrapper.safeTransferFromAsync( | ||||
|                 spender, | ||||
|                 receiver, | ||||
|                 fungibleToken, | ||||
|                 valueToTransfer, | ||||
|                 receiverCallbackData, | ||||
|             ); | ||||
|             // check balances after transfer | ||||
|             const expectedFinalBalances = [ | ||||
|                 spenderInitialFungibleBalance.minus(valueToTransfer), | ||||
|                 receiverInitialFungibleBalance.plus(valueToTransfer), | ||||
|             ]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedFinalBalances); | ||||
|         }); | ||||
|         it('should transfer non-fungible token if called by token owner', async () => { | ||||
|             // setup test parameters | ||||
|             const tokenHolders = [spender, receiver]; | ||||
|             const tokenToTransfer = nonFungibleToken; | ||||
|             const valueToTransfer = nonFungibleValueToTransfer; | ||||
|             // check balances before transfer | ||||
|             const expectedInitialBalances = [nftOwnerBalance, nftNotOwnerBalance]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedInitialBalances); | ||||
|             // execute transfer | ||||
|             await erc1155Wrapper.safeTransferFromAsync( | ||||
|                 spender, | ||||
|                 receiver, | ||||
|                 tokenToTransfer, | ||||
|                 valueToTransfer, | ||||
|                 receiverCallbackData, | ||||
|             ); | ||||
|             // check balances after transfer | ||||
|             const expectedFinalBalances = [nftNotOwnerBalance, nftOwnerBalance]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedFinalBalances); | ||||
|         }); | ||||
|         it('should trigger callback if transferring to a contract', async () => { | ||||
|             // setup test parameters | ||||
|             const tokenHolders = [spender, receiver]; | ||||
|             const tokenToTransfer = fungibleToken; | ||||
|             const valueToTransfer = fungibleValueToTransfer; | ||||
|             // check balances before transfer | ||||
|             const expectedInitialBalances = [ | ||||
|                 spenderInitialFungibleBalance, | ||||
|                 receiverInitialFungibleBalance, | ||||
|                 nftOwnerBalance, | ||||
|                 nftNotOwnerBalance, | ||||
|             ]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedInitialBalances); | ||||
|             // execute transfer | ||||
|             const tx = await erc1155Wrapper.safeTransferFromAsync( | ||||
|                 spender, | ||||
|                 receiver, | ||||
|                 tokenToTransfer, | ||||
|                 valueToTransfer, | ||||
|                 receiverCallbackData, | ||||
|             ); | ||||
|             expect(tx.logs.length).to.be.equal(2); | ||||
|             const receiverLog = tx.logs[1] as LogWithDecodedArgs<DummyERC1155ReceiverBatchTokenReceivedEventArgs>; | ||||
|             // check callback logs | ||||
|             const expectedCallbackLog = { | ||||
|                 operator: spender, | ||||
|                 from: spender, | ||||
|                 tokenId: tokenToTransfer, | ||||
|                 tokenValue: valueToTransfer, | ||||
|                 data: receiverCallbackData, | ||||
|             }; | ||||
|             expect(receiverLog.args.operator).to.be.equal(expectedCallbackLog.operator); | ||||
|             expect(receiverLog.args.from).to.be.equal(expectedCallbackLog.from); | ||||
|             expect(receiverLog.args.tokenId).to.be.bignumber.equal(expectedCallbackLog.tokenId); | ||||
|             expect(receiverLog.args.tokenValue).to.be.bignumber.equal(expectedCallbackLog.tokenValue); | ||||
|             expect(receiverLog.args.data).to.be.deep.equal(expectedCallbackLog.data); | ||||
|             // check balances after transfer | ||||
|             const expectedFinalBalances = [ | ||||
|                 spenderInitialFungibleBalance.minus(valueToTransfer), | ||||
|                 receiverInitialFungibleBalance.plus(valueToTransfer), | ||||
|             ]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedFinalBalances); | ||||
|         }); | ||||
|         it('should throw if transfer reverts', async () => { | ||||
|             // setup test parameters | ||||
|             const tokenToTransfer = fungibleToken; | ||||
|             const valueToTransfer = spenderInitialFungibleBalance.plus(1); | ||||
|             // execute transfer | ||||
|             await expectTransactionFailedAsync( | ||||
|                 erc1155Contract.safeTransferFrom.sendTransactionAsync( | ||||
|                     spender, | ||||
|                     receiver, | ||||
|                     tokenToTransfer, | ||||
|                     valueToTransfer, | ||||
|                     receiverCallbackData, | ||||
|                     { from: spender }, | ||||
|                 ), | ||||
|                 RevertReason.Uint256Underflow, | ||||
|             ); | ||||
|         }); | ||||
|         it('should throw if callback reverts', async () => { | ||||
|             // setup test parameters | ||||
|             const tokenToTransfer = fungibleToken; | ||||
|             const valueToTransfer = fungibleValueToTransfer; | ||||
|             // set receiver to reject balances | ||||
|             const shouldRejectTransfer = true; | ||||
|             await web3Wrapper.awaitTransactionSuccessAsync( | ||||
|                 await erc1155Receiver.setRejectTransferFlag.sendTransactionAsync(shouldRejectTransfer), | ||||
|                 constants.AWAIT_TRANSACTION_MINED_MS, | ||||
|             ); | ||||
|             // execute transfer | ||||
|             await expectTransactionFailedAsync( | ||||
|                 erc1155Contract.safeTransferFrom.sendTransactionAsync( | ||||
|                     spender, | ||||
|                     receiver, | ||||
|                     tokenToTransfer, | ||||
|                     valueToTransfer, | ||||
|                     receiverCallbackData, | ||||
|                     { from: spender }, | ||||
|                 ), | ||||
|                 RevertReason.TransferRejected, | ||||
|             ); | ||||
|         }); | ||||
|     }); | ||||
|     describe('batchSafeTransferFrom', () => { | ||||
|         it('should transfer fungible tokens if called by token owner', async () => { | ||||
|             // setup test parameters | ||||
|             const tokenHolders = [spender, receiver]; | ||||
|             const tokensToTransfer = [fungibleToken]; | ||||
|             const valuesToTransfer = [fungibleValueToTransfer]; | ||||
|             // check balances before transfer | ||||
|             const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); | ||||
|             // execute transfer | ||||
|             await erc1155Wrapper.safeBatchTransferFromAsync( | ||||
|                 spender, | ||||
|                 receiver, | ||||
|                 tokensToTransfer, | ||||
|                 valuesToTransfer, | ||||
|                 receiverCallbackData, | ||||
|             ); | ||||
|             // check balances after transfer | ||||
|             const expectedFinalBalances = [ | ||||
|                 spenderInitialFungibleBalance.minus(valuesToTransfer[0]), | ||||
|                 receiverInitialFungibleBalance.plus(valuesToTransfer[0]), | ||||
|             ]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances); | ||||
|         }); | ||||
|         it('should transfer non-fungible token if called by token owner', async () => { | ||||
|             // setup test parameters | ||||
|             const tokenHolders = [spender, receiver]; | ||||
|             const tokensToTransfer = [nonFungibleToken]; | ||||
|             const valuesToTransfer = [nonFungibleValueToTransfer]; | ||||
|             // check balances before transfer | ||||
|             const expectedInitialBalances = [nftOwnerBalance, nftNotOwnerBalance]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); | ||||
|             // execute transfer | ||||
|             await erc1155Wrapper.safeBatchTransferFromAsync( | ||||
|                 spender, | ||||
|                 receiver, | ||||
|                 tokensToTransfer, | ||||
|                 valuesToTransfer, | ||||
|                 receiverCallbackData, | ||||
|             ); | ||||
|             // check balances after transfer | ||||
|             const expectedFinalBalances = [nftNotOwnerBalance, nftOwnerBalance]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances); | ||||
|         }); | ||||
|         it('should transfer mix of fungible / non-fungible tokens if called by token owner', async () => { | ||||
|             // setup test parameters | ||||
|             const tokenHolders = [spender, receiver]; | ||||
|             const tokensToTransfer = [fungibleToken, nonFungibleToken]; | ||||
|             const valuesToTransfer = [fungibleValueToTransfer, nonFungibleValueToTransfer]; | ||||
|             // check balances before transfer | ||||
|             const expectedInitialBalances = [ | ||||
|                 // spender | ||||
|                 spenderInitialFungibleBalance, | ||||
|                 nftOwnerBalance, | ||||
|                 // receiver | ||||
|                 receiverInitialFungibleBalance, | ||||
|                 nftNotOwnerBalance, | ||||
|             ]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); | ||||
|             // execute transfer | ||||
|             await erc1155Wrapper.safeBatchTransferFromAsync( | ||||
|                 spender, | ||||
|                 receiver, | ||||
|                 tokensToTransfer, | ||||
|                 valuesToTransfer, | ||||
|                 receiverCallbackData, | ||||
|             ); | ||||
|             // check balances after transfer | ||||
|             const expectedFinalBalances = [ | ||||
|                 // spender | ||||
|                 spenderInitialFungibleBalance.minus(valuesToTransfer[0]), | ||||
|                 nftNotOwnerBalance, | ||||
|                 // receiver | ||||
|                 receiverInitialFungibleBalance.plus(valuesToTransfer[0]), | ||||
|                 nftOwnerBalance, | ||||
|             ]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances); | ||||
|         }); | ||||
|         it('should trigger callback if transferring to a contract', async () => { | ||||
|             // setup test parameters | ||||
|             const tokenHolders = [spender, receiver]; | ||||
|             const tokensToTransfer = [fungibleToken, nonFungibleToken]; | ||||
|             const valuesToTransfer = [fungibleValueToTransfer, nonFungibleValueToTransfer]; | ||||
|             // check balances before transfer | ||||
|             const expectedInitialBalances = [ | ||||
|                 // spender | ||||
|                 spenderInitialFungibleBalance, | ||||
|                 nftOwnerBalance, | ||||
|                 // receiver | ||||
|                 receiverInitialFungibleBalance, | ||||
|                 nftNotOwnerBalance, | ||||
|             ]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); | ||||
|             // execute transfer | ||||
|             const tx = await erc1155Wrapper.safeBatchTransferFromAsync( | ||||
|                 spender, | ||||
|                 receiver, | ||||
|                 tokensToTransfer, | ||||
|                 valuesToTransfer, | ||||
|                 receiverCallbackData, | ||||
|             ); | ||||
|             expect(tx.logs.length).to.be.equal(2); | ||||
|             const receiverLog = tx.logs[1] as LogWithDecodedArgs<DummyERC1155ReceiverBatchTokenReceivedEventArgs>; | ||||
|             // check callback logs | ||||
|             const expectedCallbackLog = { | ||||
|                 operator: spender, | ||||
|                 from: spender, | ||||
|                 tokenIds: tokensToTransfer, | ||||
|                 tokenValues: valuesToTransfer, | ||||
|                 data: receiverCallbackData, | ||||
|             }; | ||||
|             expect(receiverLog.args.operator).to.be.equal(expectedCallbackLog.operator); | ||||
|             expect(receiverLog.args.from).to.be.equal(expectedCallbackLog.from); | ||||
|             expect(receiverLog.args.tokenIds.length).to.be.equal(2); | ||||
|             expect(receiverLog.args.tokenIds[0]).to.be.bignumber.equal(expectedCallbackLog.tokenIds[0]); | ||||
|             expect(receiverLog.args.tokenIds[1]).to.be.bignumber.equal(expectedCallbackLog.tokenIds[1]); | ||||
|             expect(receiverLog.args.tokenValues.length).to.be.equal(2); | ||||
|             expect(receiverLog.args.tokenValues[0]).to.be.bignumber.equal(expectedCallbackLog.tokenValues[0]); | ||||
|             expect(receiverLog.args.tokenValues[1]).to.be.bignumber.equal(expectedCallbackLog.tokenValues[1]); | ||||
|             expect(receiverLog.args.data).to.be.deep.equal(expectedCallbackLog.data); | ||||
|             // check balances after transfer | ||||
|             const expectedFinalBalances = [ | ||||
|                 // spender | ||||
|                 spenderInitialFungibleBalance.minus(valuesToTransfer[0]), | ||||
|                 nftNotOwnerBalance, | ||||
|                 // receiver | ||||
|                 receiverInitialFungibleBalance.plus(valuesToTransfer[0]), | ||||
|                 nftOwnerBalance, | ||||
|             ]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances); | ||||
|         }); | ||||
|         it('should throw if transfer reverts', async () => { | ||||
|             // setup test parameters | ||||
|             const tokensToTransfer = [fungibleToken]; | ||||
|             const valuesToTransfer = [spenderInitialFungibleBalance.plus(1)]; | ||||
|             // execute transfer | ||||
|             await expectTransactionFailedAsync( | ||||
|                 erc1155Contract.safeBatchTransferFrom.sendTransactionAsync( | ||||
|                     spender, | ||||
|                     receiver, | ||||
|                     tokensToTransfer, | ||||
|                     valuesToTransfer, | ||||
|                     receiverCallbackData, | ||||
|                     { from: spender }, | ||||
|                 ), | ||||
|                 RevertReason.Uint256Underflow, | ||||
|             ); | ||||
|         }); | ||||
|         it('should throw if callback reverts', async () => { | ||||
|             // setup test parameters | ||||
|             const tokensToTransfer = [fungibleToken]; | ||||
|             const valuesToTransfer = [fungibleValueToTransfer]; | ||||
|             // set receiver to reject balances | ||||
|             const shouldRejectTransfer = true; | ||||
|             await web3Wrapper.awaitTransactionSuccessAsync( | ||||
|                 await erc1155Receiver.setRejectTransferFlag.sendTransactionAsync(shouldRejectTransfer), | ||||
|                 constants.AWAIT_TRANSACTION_MINED_MS, | ||||
|             ); | ||||
|             // execute transfer | ||||
|             await expectTransactionFailedAsync( | ||||
|                 erc1155Contract.safeBatchTransferFrom.sendTransactionAsync( | ||||
|                     spender, | ||||
|                     receiver, | ||||
|                     tokensToTransfer, | ||||
|                     valuesToTransfer, | ||||
|                     receiverCallbackData, | ||||
|                     { from: spender }, | ||||
|                 ), | ||||
|                 RevertReason.TransferRejected, | ||||
|             ); | ||||
|         }); | ||||
|     }); | ||||
|     describe('setApprovalForAll', () => { | ||||
|         it('should transfer token via safeTransferFrom if called by approved account', async () => { | ||||
|             // set approval | ||||
|             const isApprovedForAll = true; | ||||
|             await erc1155Wrapper.setApprovalForAllAsync(spender, delegatedSpender, isApprovedForAll); | ||||
|             const isApprovedForAllCheck = await erc1155Wrapper.isApprovedForAllAsync(spender, delegatedSpender); | ||||
|             expect(isApprovedForAllCheck).to.be.true(); | ||||
|             // setup test parameters | ||||
|             const tokenHolders = [spender, receiver]; | ||||
|             const tokenToTransfer = fungibleToken; | ||||
|             const valueToTransfer = fungibleValueToTransfer; | ||||
|             // check balances before transfer | ||||
|             const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedInitialBalances); | ||||
|             // execute transfer | ||||
|             await erc1155Wrapper.safeTransferFromAsync( | ||||
|                 spender, | ||||
|                 receiver, | ||||
|                 tokenToTransfer, | ||||
|                 valueToTransfer, | ||||
|                 receiverCallbackData, | ||||
|                 delegatedSpender, | ||||
|             ); | ||||
|             // check balances after transfer | ||||
|             const expectedFinalBalances = [ | ||||
|                 spenderInitialFungibleBalance.minus(valueToTransfer), | ||||
|                 receiverInitialFungibleBalance.plus(valueToTransfer), | ||||
|             ]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedFinalBalances); | ||||
|         }); | ||||
|         it('should throw if trying to transfer tokens via safeTransferFrom by an unapproved account', async () => { | ||||
|             // check approval not set | ||||
|             const isApprovedForAllCheck = await erc1155Wrapper.isApprovedForAllAsync(spender, delegatedSpender); | ||||
|             expect(isApprovedForAllCheck).to.be.false(); | ||||
|             // setup test parameters | ||||
|             const tokenHolders = [spender, receiver]; | ||||
|             const tokenToTransfer = fungibleToken; | ||||
|             const valueToTransfer = fungibleValueToTransfer; | ||||
|             // check balances before transfer | ||||
|             const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedInitialBalances); | ||||
|             // execute transfer | ||||
|             await expectTransactionFailedAsync( | ||||
|                 erc1155Contract.safeTransferFrom.sendTransactionAsync( | ||||
|                     spender, | ||||
|                     receiver, | ||||
|                     tokenToTransfer, | ||||
|                     valueToTransfer, | ||||
|                     receiverCallbackData, | ||||
|                     { from: delegatedSpender }, | ||||
|                 ), | ||||
|                 RevertReason.InsufficientAllowance, | ||||
|             ); | ||||
|         }); | ||||
|         it('should transfer token via safeBatchTransferFrom if called by approved account', async () => { | ||||
|             // set approval | ||||
|             const isApprovedForAll = true; | ||||
|             await erc1155Wrapper.setApprovalForAllAsync(spender, delegatedSpender, isApprovedForAll); | ||||
|             const isApprovedForAllCheck = await erc1155Wrapper.isApprovedForAllAsync(spender, delegatedSpender); | ||||
|             expect(isApprovedForAllCheck).to.be.true(); | ||||
|             // setup test parameters | ||||
|             const tokenHolders = [spender, receiver]; | ||||
|             const tokensToTransfer = [fungibleToken]; | ||||
|             const valuesToTransfer = [fungibleValueToTransfer]; | ||||
|             // check balances before transfer | ||||
|             const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); | ||||
|             // execute transfer | ||||
|             await erc1155Wrapper.safeBatchTransferFromAsync( | ||||
|                 spender, | ||||
|                 receiver, | ||||
|                 tokensToTransfer, | ||||
|                 valuesToTransfer, | ||||
|                 receiverCallbackData, | ||||
|                 delegatedSpender, | ||||
|             ); | ||||
|             // check balances after transfer | ||||
|             const expectedFinalBalances = [ | ||||
|                 spenderInitialFungibleBalance.minus(valuesToTransfer[0]), | ||||
|                 receiverInitialFungibleBalance.plus(valuesToTransfer[0]), | ||||
|             ]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances); | ||||
|         }); | ||||
|         it('should throw if trying to transfer tokens via safeBatchTransferFrom by an unapproved account', async () => { | ||||
|             // check approval not set | ||||
|             const isApprovedForAllCheck = await erc1155Wrapper.isApprovedForAllAsync(spender, delegatedSpender); | ||||
|             expect(isApprovedForAllCheck).to.be.false(); | ||||
|             // setup test parameters | ||||
|             const tokenHolders = [spender, receiver]; | ||||
|             const tokensToTransfer = [fungibleToken]; | ||||
|             const valuesToTransfer = [fungibleValueToTransfer]; | ||||
|             // check balances before transfer | ||||
|             const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance]; | ||||
|             await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); | ||||
|             // execute transfer | ||||
|             await expectTransactionFailedAsync( | ||||
|                 erc1155Contract.safeBatchTransferFrom.sendTransactionAsync( | ||||
|                     spender, | ||||
|                     receiver, | ||||
|                     tokensToTransfer, | ||||
|                     valuesToTransfer, | ||||
|                     receiverCallbackData, | ||||
|                     { from: delegatedSpender }, | ||||
|                 ), | ||||
|                 RevertReason.InsufficientAllowance, | ||||
|             ); | ||||
|         }); | ||||
|     }); | ||||
| }); | ||||
| // tslint:enable:no-unnecessary-type-assertion | ||||
							
								
								
									
										19
									
								
								contracts/erc1155/test/global_hooks.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								contracts/erc1155/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(); | ||||
| }); | ||||
							
								
								
									
										159
									
								
								contracts/erc1155/test/utils/erc1155_wrapper.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										159
									
								
								contracts/erc1155/test/utils/erc1155_wrapper.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,159 @@ | ||||
| import { constants, LogDecoder } from '@0x/contracts-test-utils'; | ||||
| import { BigNumber } from '@0x/utils'; | ||||
| import { Web3Wrapper } from '@0x/web3-wrapper'; | ||||
| import * as chai from 'chai'; | ||||
| import { LogWithDecodedArgs, Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types'; | ||||
| import * as _ from 'lodash'; | ||||
|  | ||||
| import { artifacts, ERC1155MintableContract, ERC1155TransferSingleEventArgs } from '../../src'; | ||||
|  | ||||
| const expect = chai.expect; | ||||
|  | ||||
| export class Erc1155Wrapper { | ||||
|     private readonly _erc1155Contract: ERC1155MintableContract; | ||||
|     private readonly _web3Wrapper: Web3Wrapper; | ||||
|     private readonly _contractOwner: string; | ||||
|     private readonly _logDecoder: LogDecoder; | ||||
|  | ||||
|     constructor(contractInstance: ERC1155MintableContract, provider: Provider, contractOwner: string) { | ||||
|         this._erc1155Contract = contractInstance; | ||||
|         this._web3Wrapper = new Web3Wrapper(provider); | ||||
|         this._contractOwner = contractOwner; | ||||
|         this._logDecoder = new LogDecoder(this._web3Wrapper, artifacts); | ||||
|     } | ||||
|     public getContract(): ERC1155MintableContract { | ||||
|         return this._erc1155Contract; | ||||
|     } | ||||
|     public async getBalancesAsync(owners: string[], tokens: BigNumber[]): Promise<BigNumber[]> { | ||||
|         const balances = await this._erc1155Contract.balanceOfBatch.callAsync(owners, tokens); | ||||
|         return balances; | ||||
|     } | ||||
|     public async safeTransferFromAsync( | ||||
|         from: string, | ||||
|         to: string, | ||||
|         token: BigNumber, | ||||
|         value: BigNumber, | ||||
|         callbackData?: string, | ||||
|         delegatedSpender?: string, | ||||
|     ): Promise<TransactionReceiptWithDecodedLogs> { | ||||
|         const spender = delegatedSpender === undefined ? from : delegatedSpender; | ||||
|         const callbackDataHex = callbackData === undefined ? '0x' : callbackData; | ||||
|         const tx = await this._logDecoder.getTxWithDecodedLogsAsync( | ||||
|             await this._erc1155Contract.safeTransferFrom.sendTransactionAsync(from, to, token, value, callbackDataHex, { | ||||
|                 from: spender, | ||||
|             }), | ||||
|         ); | ||||
|         return tx; | ||||
|     } | ||||
|     public async safeBatchTransferFromAsync( | ||||
|         from: string, | ||||
|         to: string, | ||||
|         tokens: BigNumber[], | ||||
|         values: BigNumber[], | ||||
|         callbackData?: string, | ||||
|         delegatedSpender?: string, | ||||
|     ): Promise<TransactionReceiptWithDecodedLogs> { | ||||
|         const spender = delegatedSpender === undefined ? from : delegatedSpender; | ||||
|         const callbackDataHex = callbackData === undefined ? '0x' : callbackData; | ||||
|         const tx = await this._logDecoder.getTxWithDecodedLogsAsync( | ||||
|             await this._erc1155Contract.safeBatchTransferFrom.sendTransactionAsync( | ||||
|                 from, | ||||
|                 to, | ||||
|                 tokens, | ||||
|                 values, | ||||
|                 callbackDataHex, | ||||
|                 { from: spender }, | ||||
|             ), | ||||
|         ); | ||||
|         return tx; | ||||
|     } | ||||
|     public async mintFungibleTokensAsync( | ||||
|         beneficiaries: string[], | ||||
|         tokenAmounts: BigNumber | BigNumber[], | ||||
|     ): Promise<BigNumber> { | ||||
|         const tokenUri = 'dummyFungibleToken'; | ||||
|         const tokenIsNonFungible = false; | ||||
|         const tx = await this._logDecoder.getTxWithDecodedLogsAsync( | ||||
|             await this._erc1155Contract.create.sendTransactionAsync(tokenUri, tokenIsNonFungible, { | ||||
|                 from: this._contractOwner, | ||||
|             }), | ||||
|         ); | ||||
|         // tslint:disable-next-line no-unnecessary-type-assertion | ||||
|         const createFungibleTokenLog = tx.logs[0] as LogWithDecodedArgs<ERC1155TransferSingleEventArgs>; | ||||
|         const token = createFungibleTokenLog.args.id; | ||||
|         const tokenAmountsAsArray = _.isArray(tokenAmounts) ? tokenAmounts : []; | ||||
|         if (!_.isArray(tokenAmounts)) { | ||||
|             _.each(_.range(0, beneficiaries.length), () => { | ||||
|                 tokenAmountsAsArray.push(tokenAmounts); | ||||
|             }); | ||||
|         } | ||||
|         await this._web3Wrapper.awaitTransactionSuccessAsync( | ||||
|             await this._erc1155Contract.mintFungible.sendTransactionAsync(token, beneficiaries, tokenAmountsAsArray, { | ||||
|                 from: this._contractOwner, | ||||
|             }), | ||||
|             constants.AWAIT_TRANSACTION_MINED_MS, | ||||
|         ); | ||||
|         return token; | ||||
|     } | ||||
|     public async mintNonFungibleTokensAsync(beneficiaries: string[]): Promise<[BigNumber, BigNumber[]]> { | ||||
|         const tokenUri = 'dummyNonFungibleToken'; | ||||
|         const tokenIsNonFungible = true; | ||||
|         const tx = await this._logDecoder.getTxWithDecodedLogsAsync( | ||||
|             await this._erc1155Contract.create.sendTransactionAsync(tokenUri, tokenIsNonFungible, { | ||||
|                 from: this._contractOwner, | ||||
|             }), | ||||
|         ); | ||||
|         // tslint:disable-next-line no-unnecessary-type-assertion | ||||
|         const createFungibleTokenLog = tx.logs[0] as LogWithDecodedArgs<ERC1155TransferSingleEventArgs>; | ||||
|         const token = createFungibleTokenLog.args.id; | ||||
|         await this._web3Wrapper.awaitTransactionSuccessAsync( | ||||
|             await this._erc1155Contract.mintNonFungible.sendTransactionAsync(token, beneficiaries, { | ||||
|                 from: this._contractOwner, | ||||
|             }), | ||||
|             constants.AWAIT_TRANSACTION_MINED_MS, | ||||
|         ); | ||||
|         const encodedNftIds: BigNumber[] = []; | ||||
|         const nftIdBegin = 1; | ||||
|         const nftIdEnd = beneficiaries.length + 1; | ||||
|         const nftIdRange = _.range(nftIdBegin, nftIdEnd); | ||||
|         _.each(nftIdRange, (nftId: number) => { | ||||
|             const encodedNftId = token.plus(nftId); | ||||
|             encodedNftIds.push(encodedNftId); | ||||
|         }); | ||||
|         return [token, encodedNftIds]; | ||||
|     } | ||||
|     public async setApprovalForAllAsync( | ||||
|         owner: string, | ||||
|         beneficiary: string, | ||||
|         isApproved: boolean, | ||||
|     ): Promise<TransactionReceiptWithDecodedLogs> { | ||||
|         const tx = await this._logDecoder.getTxWithDecodedLogsAsync( | ||||
|             await this._erc1155Contract.setApprovalForAll.sendTransactionAsync(beneficiary, isApproved, { | ||||
|                 from: owner, | ||||
|             }), | ||||
|         ); | ||||
|         return tx; | ||||
|     } | ||||
|     public async isApprovedForAllAsync(owner: string, beneficiary: string): Promise<boolean> { | ||||
|         const isApprovedForAll = await this._erc1155Contract.isApprovedForAll.callAsync(owner, beneficiary); | ||||
|         return isApprovedForAll; | ||||
|     } | ||||
|     public async assertBalancesAsync( | ||||
|         owners: string[], | ||||
|         tokens: BigNumber[], | ||||
|         expectedBalances: BigNumber[], | ||||
|     ): Promise<void> { | ||||
|         const ownersExtended: string[] = []; | ||||
|         let tokensExtended: BigNumber[] = []; | ||||
|         _.each(owners, (owner: string) => { | ||||
|             tokensExtended = tokensExtended.concat(tokens); | ||||
|             _.each(_.range(0, tokens.length), () => { | ||||
|                 ownersExtended.push(owner); | ||||
|             }); | ||||
|         }); | ||||
|         const balances = await this.getBalancesAsync(ownersExtended, tokensExtended); | ||||
|         _.each(balances, (balance: BigNumber, i: number) => { | ||||
|             expect(balance, `${ownersExtended[i]}${tokensExtended[i]}`).to.be.bignumber.equal(expectedBalances[i]); | ||||
|         }); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										16
									
								
								contracts/erc1155/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								contracts/erc1155/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| { | ||||
|     "extends": "../../tsconfig", | ||||
|     "compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true }, | ||||
|     "include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"], | ||||
|     "files": [ | ||||
|         "generated-artifacts/DummyERC1155Receiver.json", | ||||
|         "generated-artifacts/ERC1155.json", | ||||
|         "generated-artifacts/ERC1155Mintable.json", | ||||
|         "generated-artifacts/IERC1155.json", | ||||
|         "generated-artifacts/IERC1155Mintable.json", | ||||
|         "generated-artifacts/IERC1155Receiver.json", | ||||
|         "generated-artifacts/MNonFungibleToken.json", | ||||
|         "generated-artifacts/MixinNonFungibleToken.json" | ||||
|     ], | ||||
|     "exclude": ["./deploy/solc/solc_bin"] | ||||
| } | ||||
							
								
								
									
										6
									
								
								contracts/erc1155/tslint.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								contracts/erc1155/tslint.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| { | ||||
|     "extends": ["@0x/tslint-config"], | ||||
|     "rules": { | ||||
|         "custom-no-magic-numbers": false | ||||
|     } | ||||
| } | ||||
| @@ -1,4 +1,106 @@ | ||||
| [ | ||||
|     { | ||||
|         "version": "2.2.0", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Added UntransferrableDummyERC20Token", | ||||
|                 "pr": 1714 | ||||
|             } | ||||
|         ], | ||||
|         "timestamp": 1554997931 | ||||
|     }, | ||||
|     { | ||||
|         "version": "2.1.0", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Run Web3ProviderEngine without excess block polling", | ||||
|                 "pr": 1695 | ||||
|             } | ||||
|         ], | ||||
|         "timestamp": 1553183790 | ||||
|     }, | ||||
|     { | ||||
|         "version": "2.0.0", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Upgrade contracts to Solidity 0.5.5", | ||||
|                 "pr": 1682 | ||||
|             } | ||||
|         ], | ||||
|         "timestamp": 1553091633 | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1551479279, | ||||
|         "version": "1.0.9", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1551299797, | ||||
|         "version": "1.0.8", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1551220833, | ||||
|         "version": "1.0.7", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1551130135, | ||||
|         "version": "1.0.6", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1549733923, | ||||
|         "version": "1.0.5", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "version": "1.0.4", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ], | ||||
|         "timestamp": 1549547375 | ||||
|     }, | ||||
|     { | ||||
|         "version": "1.0.3", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Fake publish to enable pinning" | ||||
|             } | ||||
|         ], | ||||
|         "timestamp": 1549504360 | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1549452781, | ||||
|         "version": "1.0.2", | ||||
|         "changes": [ | ||||
|             { | ||||
|                 "note": "Dependencies updated" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         "timestamp": 1549373905, | ||||
|         "version": "1.0.1", | ||||
|   | ||||
| @@ -5,6 +5,50 @@ Edit the package's CHANGELOG.json file only. | ||||
|  | ||||
| CHANGELOG | ||||
|  | ||||
| ## v2.2.0 - _April 11, 2019_ | ||||
|  | ||||
|     * Added UntransferrableDummyERC20Token (#1714) | ||||
|  | ||||
| ## v2.1.0 - _March 21, 2019_ | ||||
|  | ||||
|     * Run Web3ProviderEngine without excess block polling (#1695) | ||||
|  | ||||
| ## v2.0.0 - _March 20, 2019_ | ||||
|  | ||||
|     * Upgrade contracts to Solidity 0.5.5 (#1682) | ||||
|  | ||||
| ## v1.0.9 - _March 1, 2019_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v1.0.8 - _February 27, 2019_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v1.0.7 - _February 26, 2019_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v1.0.6 - _February 25, 2019_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v1.0.5 - _February 9, 2019_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v1.0.4 - _February 7, 2019_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v1.0.3 - _February 7, 2019_ | ||||
|  | ||||
|     * Fake publish to enable pinning | ||||
|  | ||||
| ## v1.0.2 - _February 6, 2019_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|  | ||||
| ## v1.0.1 - _February 5, 2019_ | ||||
|  | ||||
|     * Dependencies updated | ||||
|   | ||||
| @@ -2,10 +2,13 @@ | ||||
|     "artifactsDir": "./generated-artifacts", | ||||
|     "contractsDir": "./contracts", | ||||
|     "useDockerisedSolc": true, | ||||
|     "isOfflineMode": false, | ||||
|     "compilerSettings": { | ||||
|         "evmVersion": "constantinople", | ||||
|         "optimizer": { | ||||
|             "enabled": true, | ||||
|             "runs": 1000000 | ||||
|             "runs": 1000000, | ||||
|             "details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true } | ||||
|         }, | ||||
|         "outputSelection": { | ||||
|             "*": { | ||||
| @@ -20,16 +23,16 @@ | ||||
|         } | ||||
|     }, | ||||
|     "contracts": [ | ||||
|         "DummyERC20Token", | ||||
|         "DummyMultipleReturnERC20Token", | ||||
|         "DummyNoReturnERC20Token", | ||||
|         "ReentrantERC20Token", | ||||
|         "ERC20Token", | ||||
|         "IERC20Token", | ||||
|         "MintableERC20Token", | ||||
|         "UnlimitedAllowanceERC20Token", | ||||
|         "IEtherToken", | ||||
|         "WETH9", | ||||
|         "ZRXToken" | ||||
|         "src/ERC20Token.sol", | ||||
|         "src/MintableERC20Token.sol", | ||||
|         "src/UnlimitedAllowanceERC20Token.sol", | ||||
|         "src/WETH9.sol", | ||||
|         "src/ZRXToken.sol", | ||||
|         "src/interfaces/IERC20Token.sol", | ||||
|         "src/interfaces/IEtherToken.sol", | ||||
|         "test/DummyERC20Token.sol", | ||||
|         "test/DummyMultipleReturnERC20Token.sol", | ||||
|         "test/DummyNoReturnERC20Token.sol", | ||||
|         "test/UntransferrableDummyERC20Token.sol" | ||||
|     ] | ||||
| } | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user