Compare commits
	
		
			325 Commits
		
	
	
		
			@0x/dev-to
			...
			monorepo@d
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					d4c37ecfa3 | ||
| 
						 | 
					3f9fd7c060 | ||
| 
						 | 
					0490ef5900 | ||
| 
						 | 
					fa36c91bd6 | ||
| 
						 | 
					5b8f294aaf | ||
| 
						 | 
					aad75840d4 | ||
| 
						 | 
					31faef7030 | ||
| 
						 | 
					941877a05a | ||
| 
						 | 
					4fd4c1e8e1 | ||
| 
						 | 
					7cd27cd9c8 | ||
| 
						 | 
					54dbef2b4f | ||
| 
						 | 
					05424c9f33 | ||
| 
						 | 
					d29ed6cd49 | ||
| 
						 | 
					8c3abf3473 | ||
| 
						 | 
					384114d3c7 | ||
| 
						 | 
					e8be5a0a8f | ||
| 
						 | 
					98c6fa10e6 | ||
| 
						 | 
					30d0bdec07 | ||
| 
						 | 
					7024f29865 | ||
| 
						 | 
					29ebed9514 | ||
| 
						 | 
					c0260bc44a | ||
| 
						 | 
					6992bff0e0 | ||
| 
						 | 
					6957e6e8f7 | ||
| 
						 | 
					31dbca7efc | ||
| 
						 | 
					82b6dad1ba | ||
| 
						 | 
					5063c17e6b | ||
| 
						 | 
					3fff3d9c60 | ||
| 
						 | 
					2675833b0d | ||
| 
						 | 
					939a5b477a | ||
| 
						 | 
					dc57e7a5b3 | ||
| 
						 | 
					a401b8f475 | ||
| 
						 | 
					b008fabdac | ||
| 
						 | 
					caf286b8cb | ||
| 
						 | 
					aea278c022 | ||
| 
						 | 
					4e20fe1602 | ||
| 
						 | 
					d3ab612a89 | ||
| 
						 | 
					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 | ||
| 
						 | 
					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 | ||
| 
						 | 
					fa6db9411b | ||
| 
						 | 
					644f54bfba | ||
| 
						 | 
					755c4da8cc | ||
| 
						 | 
					483c77fba8 | ||
| 
						 | 
					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 | ||
| 
						 | 
					cf65d4a909 | ||
| 
						 | 
					079f627b34 | ||
| 
						 | 
					f6c6cbc343 | ||
| 
						 | 
					c6cdea77b6 | ||
| 
						 | 
					3191de68b8 | ||
| 
						 | 
					f3da56773e | ||
| 
						 | 
					5197758579 | ||
| 
						 | 
					d48af7c4c2 | ||
| 
						 | 
					bbafe0fc46 | ||
| 
						 | 
					9a91f917e0 | ||
| 
						 | 
					1f681f02ae | ||
| 
						 | 
					778a86e7eb | ||
| 
						 | 
					6eb923d22f | ||
| 
						 | 
					e602afcd5f | ||
| 
						 | 
					bf1115d417 | ||
| 
						 | 
					f5d668af31 | ||
| 
						 | 
					62373f969c | ||
| 
						 | 
					30f2e3b606 | ||
| 
						 | 
					7d3d997083 | ||
| 
						 | 
					3955e2c84a | ||
| 
						 | 
					6a6c41df26 | ||
| 
						 | 
					1dfd2aec50 | ||
| 
						 | 
					8885f543ae | ||
| 
						 | 
					8804e6c2ca | ||
| 
						 | 
					88b7c214f7 | ||
| 
						 | 
					9a7ccc20e8 | 
@@ -47,10 +47,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 +69,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:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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,53 @@
 | 
			
		||||
[
 | 
			
		||||
    {
 | 
			
		||||
        "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",
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,25 @@ Edit the package's CHANGELOG.json file only.
 | 
			
		||||
 | 
			
		||||
CHANGELOG
 | 
			
		||||
 | 
			
		||||
## 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
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,12 @@
 | 
			
		||||
    "useDockerisedSolc": true,
 | 
			
		||||
    "isOfflineMode": false,
 | 
			
		||||
    "compilerSettings": {
 | 
			
		||||
        "optimizer": { "enabled": true, "runs": 1000000 },
 | 
			
		||||
        "evmVersion": "constantinople",
 | 
			
		||||
        "optimizer": {
 | 
			
		||||
            "enabled": true,
 | 
			
		||||
            "runs": 1000000,
 | 
			
		||||
            "details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
 | 
			
		||||
        },
 | 
			
		||||
        "outputSelection": {
 | 
			
		||||
            "*": {
 | 
			
		||||
                "*": [
 | 
			
		||||
@@ -18,11 +23,7 @@
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    "contracts": [
 | 
			
		||||
        "@0x/contracts-erc20/contracts/test/DummyERC20Token.sol",
 | 
			
		||||
        "@0x/contracts-erc20/contracts/test/DummyMultipleReturnERC20Token.sol",
 | 
			
		||||
        "@0x/contracts-erc20/contracts/test/DummyNoReturnERC20Token.sol",
 | 
			
		||||
        "@0x/contracts-erc721/contracts/test/DummyERC721Receiver.sol",
 | 
			
		||||
        "@0x/contracts-erc721/contracts/test/DummyERC721Token.sol",
 | 
			
		||||
        "src/ERC1155Proxy.sol",
 | 
			
		||||
        "src/ERC20Proxy.sol",
 | 
			
		||||
        "src/ERC721Proxy.sol",
 | 
			
		||||
        "src/MixinAuthorizable.sol",
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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("ERC1155Token(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.6",
 | 
			
		||||
    "version": "2.0.0",
 | 
			
		||||
    "engines": {
 | 
			
		||||
        "node": ">=6.12"
 | 
			
		||||
    },
 | 
			
		||||
@@ -33,7 +33,7 @@
 | 
			
		||||
        "lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol"
 | 
			
		||||
    },
 | 
			
		||||
    "config": {
 | 
			
		||||
        "abis": "./generated-artifacts/@(DummyERC20Token|DummyERC721Receiver|DummyERC721Token|DummyMultipleReturnERC20Token|DummyNoReturnERC20Token|ERC20Proxy|ERC721Proxy|IAssetData|IAssetProxy|IAuthorizable|MixinAuthorizable|MultiAssetProxy).json",
 | 
			
		||||
        "abis": "./generated-artifacts/@(ERC1155Proxy|ERC20Proxy|ERC721Proxy|IAssetData|IAssetProxy|IAuthorizable|MixinAuthorizable|MultiAssetProxy).json",
 | 
			
		||||
        "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
 | 
			
		||||
    },
 | 
			
		||||
    "repository": {
 | 
			
		||||
@@ -46,10 +46,11 @@
 | 
			
		||||
    },
 | 
			
		||||
    "homepage": "https://github.com/0xProject/0x-monorepo/contracts/protocol/README.md",
 | 
			
		||||
    "devDependencies": {
 | 
			
		||||
        "@0x/abi-gen": "^2.0.4",
 | 
			
		||||
        "@0x/contracts-gen": "^1.0.3",
 | 
			
		||||
        "@0x/dev-utils": "^2.1.1",
 | 
			
		||||
        "@0x/sol-compiler": "^3.1.0",
 | 
			
		||||
        "@0x/abi-gen": "^2.0.7",
 | 
			
		||||
        "@0x/contracts-gen": "^1.0.6",
 | 
			
		||||
        "@0x/contracts-test-utils": "^3.1.0",
 | 
			
		||||
        "@0x/dev-utils": "^2.1.4",
 | 
			
		||||
        "@0x/sol-compiler": "^3.1.4",
 | 
			
		||||
        "@0x/tslint-config": "^3.0.0",
 | 
			
		||||
        "@types/lodash": "4.14.104",
 | 
			
		||||
        "@types/node": "*",
 | 
			
		||||
@@ -66,17 +67,17 @@
 | 
			
		||||
        "typescript": "3.0.1"
 | 
			
		||||
    },
 | 
			
		||||
    "dependencies": {
 | 
			
		||||
        "@0x/base-contract": "^5.0.0",
 | 
			
		||||
        "@0x/contracts-erc20": "1.0.2",
 | 
			
		||||
        "@0x/contracts-erc721": "1.0.2",
 | 
			
		||||
        "@0x/contracts-test-utils": "^3.0.5",
 | 
			
		||||
        "@0x/contracts-utils": "2.0.1",
 | 
			
		||||
        "@0x/order-utils": "^7.0.0",
 | 
			
		||||
        "@0x/types": "^2.1.0",
 | 
			
		||||
        "@0x/typescript-typings": "^4.1.0",
 | 
			
		||||
        "@0x/utils": "^4.2.0",
 | 
			
		||||
        "@0x/web3-wrapper": "^6.0.0",
 | 
			
		||||
        "ethereum-types": "^2.1.0",
 | 
			
		||||
        "@0x/base-contract": "^5.0.3",
 | 
			
		||||
        "@0x/contracts-erc1155": "^1.0.1",
 | 
			
		||||
        "@0x/contracts-erc20": "^2.0.0",
 | 
			
		||||
        "@0x/contracts-erc721": "^2.0.0",
 | 
			
		||||
        "@0x/contracts-utils": "^3.0.0",
 | 
			
		||||
        "@0x/order-utils": "^7.1.0",
 | 
			
		||||
        "@0x/types": "^2.2.0",
 | 
			
		||||
        "@0x/typescript-typings": "^4.2.0",
 | 
			
		||||
        "@0x/utils": "^4.2.3",
 | 
			
		||||
        "@0x/web3-wrapper": "^6.0.3",
 | 
			
		||||
        "ethereum-types": "^2.1.1",
 | 
			
		||||
        "lodash": "^4.17.11"
 | 
			
		||||
    },
 | 
			
		||||
    "publishConfig": {
 | 
			
		||||
 
 | 
			
		||||
@@ -5,11 +5,7 @@
 | 
			
		||||
 */
 | 
			
		||||
import { ContractArtifact } from 'ethereum-types';
 | 
			
		||||
 | 
			
		||||
import * as DummyERC20Token from '../generated-artifacts/DummyERC20Token.json';
 | 
			
		||||
import * as DummyERC721Receiver from '../generated-artifacts/DummyERC721Receiver.json';
 | 
			
		||||
import * as DummyERC721Token from '../generated-artifacts/DummyERC721Token.json';
 | 
			
		||||
import * as DummyMultipleReturnERC20Token from '../generated-artifacts/DummyMultipleReturnERC20Token.json';
 | 
			
		||||
import * as DummyNoReturnERC20Token from '../generated-artifacts/DummyNoReturnERC20Token.json';
 | 
			
		||||
import * as ERC1155Proxy from '../generated-artifacts/ERC1155Proxy.json';
 | 
			
		||||
import * as ERC20Proxy from '../generated-artifacts/ERC20Proxy.json';
 | 
			
		||||
import * as ERC721Proxy from '../generated-artifacts/ERC721Proxy.json';
 | 
			
		||||
import * as IAssetData from '../generated-artifacts/IAssetData.json';
 | 
			
		||||
@@ -18,13 +14,9 @@ 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 = {
 | 
			
		||||
    DummyERC20Token: DummyERC20Token as ContractArtifact,
 | 
			
		||||
    DummyMultipleReturnERC20Token: DummyMultipleReturnERC20Token as ContractArtifact,
 | 
			
		||||
    DummyNoReturnERC20Token: DummyNoReturnERC20Token as ContractArtifact,
 | 
			
		||||
    DummyERC721Receiver: DummyERC721Receiver as ContractArtifact,
 | 
			
		||||
    DummyERC721Token: DummyERC721Token as ContractArtifact,
 | 
			
		||||
    ERC20Proxy: ERC20Proxy as ContractArtifact,
 | 
			
		||||
    ERC721Proxy: ERC721Proxy as ContractArtifact,
 | 
			
		||||
    ERC1155Proxy: ERC1155Proxy as ContractArtifact,
 | 
			
		||||
    MixinAuthorizable: MixinAuthorizable as ContractArtifact,
 | 
			
		||||
    MultiAssetProxy: MultiAssetProxy as ContractArtifact,
 | 
			
		||||
    IAssetData: IAssetData as ContractArtifact,
 | 
			
		||||
 
 | 
			
		||||
@@ -3,11 +3,7 @@
 | 
			
		||||
 * Warning: This file is auto-generated by contracts-gen. Don't edit manually.
 | 
			
		||||
 * -----------------------------------------------------------------------------
 | 
			
		||||
 */
 | 
			
		||||
export * from '../generated-wrappers/dummy_erc20_token';
 | 
			
		||||
export * from '../generated-wrappers/dummy_erc721_receiver';
 | 
			
		||||
export * from '../generated-wrappers/dummy_erc721_token';
 | 
			
		||||
export * from '../generated-wrappers/dummy_multiple_return_erc20_token';
 | 
			
		||||
export * from '../generated-wrappers/dummy_no_return_erc20_token';
 | 
			
		||||
export * from '../generated-wrappers/erc1155_proxy';
 | 
			
		||||
export * from '../generated-wrappers/erc20_proxy';
 | 
			
		||||
export * from '../generated-wrappers/erc721_proxy';
 | 
			
		||||
export * from '../generated-wrappers/i_asset_data';
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										834
									
								
								contracts/asset-proxy/test/erc1155_proxy.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										834
									
								
								contracts/asset-proxy/test/erc1155_proxy.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,834 @@
 | 
			
		||||
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 { 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();
 | 
			
		||||
            // proxy computed using -- bytes4(keccak256("erc1155Token(address,uint256[],uint256[],bytes)"));
 | 
			
		||||
            const expectedProxyId = '0x9645780d';
 | 
			
		||||
            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,3 +1,16 @@
 | 
			
		||||
import { ERC1155MintableContract, Erc1155Wrapper } from '@0x/contracts-erc1155';
 | 
			
		||||
import {
 | 
			
		||||
    artifacts as erc20Artifacts,
 | 
			
		||||
    DummyERC20TokenContract,
 | 
			
		||||
    DummyERC20TokenTransferEventArgs,
 | 
			
		||||
    DummyMultipleReturnERC20TokenContract,
 | 
			
		||||
    DummyNoReturnERC20TokenContract,
 | 
			
		||||
} from '@0x/contracts-erc20';
 | 
			
		||||
import {
 | 
			
		||||
    artifacts as erc721Artifacts,
 | 
			
		||||
    DummyERC721ReceiverContract,
 | 
			
		||||
    DummyERC721TokenContract,
 | 
			
		||||
} from '@0x/contracts-erc721';
 | 
			
		||||
import {
 | 
			
		||||
    chaiSetup,
 | 
			
		||||
    constants,
 | 
			
		||||
@@ -18,12 +31,7 @@ import * as _ from 'lodash';
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
    artifacts,
 | 
			
		||||
    DummyERC20TokenContract,
 | 
			
		||||
    DummyERC20TokenTransferEventArgs,
 | 
			
		||||
    DummyERC721ReceiverContract,
 | 
			
		||||
    DummyERC721TokenContract,
 | 
			
		||||
    DummyMultipleReturnERC20TokenContract,
 | 
			
		||||
    DummyNoReturnERC20TokenContract,
 | 
			
		||||
    ERC1155ProxyWrapper,
 | 
			
		||||
    ERC20ProxyContract,
 | 
			
		||||
    ERC20Wrapper,
 | 
			
		||||
    ERC721ProxyContract,
 | 
			
		||||
@@ -71,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();
 | 
			
		||||
    });
 | 
			
		||||
@@ -121,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, {
 | 
			
		||||
@@ -140,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;
 | 
			
		||||
@@ -148,7 +187,7 @@ describe('Asset Transfer Proxies', () => {
 | 
			
		||||
            constants.DUMMY_TOKEN_DECIMALS,
 | 
			
		||||
        );
 | 
			
		||||
        noReturnErc20Token = await DummyNoReturnERC20TokenContract.deployFrom0xArtifactAsync(
 | 
			
		||||
            artifacts.DummyNoReturnERC20Token,
 | 
			
		||||
            erc20Artifacts.DummyNoReturnERC20Token,
 | 
			
		||||
            provider,
 | 
			
		||||
            txDefaults,
 | 
			
		||||
            constants.DUMMY_TOKEN_NAME,
 | 
			
		||||
@@ -157,7 +196,7 @@ describe('Asset Transfer Proxies', () => {
 | 
			
		||||
            constants.DUMMY_TOKEN_TOTAL_SUPPLY,
 | 
			
		||||
        );
 | 
			
		||||
        multipleReturnErc20Token = await DummyMultipleReturnERC20TokenContract.deployFrom0xArtifactAsync(
 | 
			
		||||
            artifacts.DummyMultipleReturnERC20Token,
 | 
			
		||||
            erc20Artifacts.DummyMultipleReturnERC20Token,
 | 
			
		||||
            provider,
 | 
			
		||||
            txDefaults,
 | 
			
		||||
            constants.DUMMY_TOKEN_NAME,
 | 
			
		||||
@@ -198,7 +237,7 @@ describe('Asset Transfer Proxies', () => {
 | 
			
		||||
        // Deploy and configure ERC721 tokens and receiver
 | 
			
		||||
        [erc721TokenA, erc721TokenB] = await erc721Wrapper.deployDummyTokensAsync();
 | 
			
		||||
        erc721Receiver = await DummyERC721ReceiverContract.deployFrom0xArtifactAsync(
 | 
			
		||||
            artifacts.DummyERC721Receiver,
 | 
			
		||||
            erc721Artifacts.DummyERC721Receiver,
 | 
			
		||||
            provider,
 | 
			
		||||
            txDefaults,
 | 
			
		||||
        );
 | 
			
		||||
@@ -207,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();
 | 
			
		||||
@@ -562,7 +617,7 @@ describe('Asset Transfer Proxies', () => {
 | 
			
		||||
                    erc721Receiver.address,
 | 
			
		||||
                    amount,
 | 
			
		||||
                );
 | 
			
		||||
                const logDecoder = new LogDecoder(web3Wrapper, artifacts);
 | 
			
		||||
                const logDecoder = new LogDecoder(web3Wrapper, { ...artifacts, ...erc721Artifacts });
 | 
			
		||||
                const tx = await logDecoder.getTxWithDecodedLogsAsync(
 | 
			
		||||
                    await web3Wrapper.sendTransactionAsync({
 | 
			
		||||
                        to: erc721Proxy.address,
 | 
			
		||||
@@ -754,7 +809,7 @@ describe('Asset Transfer Proxies', () => {
 | 
			
		||||
                    inputAmount,
 | 
			
		||||
                );
 | 
			
		||||
                const erc20Balances = await erc20Wrapper.getBalancesAsync();
 | 
			
		||||
                const logDecoder = new LogDecoder(web3Wrapper, artifacts);
 | 
			
		||||
                const logDecoder = new LogDecoder(web3Wrapper, { ...artifacts, ...erc20Artifacts });
 | 
			
		||||
                const tx = await logDecoder.getTxWithDecodedLogsAsync(
 | 
			
		||||
                    await web3Wrapper.sendTransactionAsync({
 | 
			
		||||
                        to: multiAssetProxy.address,
 | 
			
		||||
@@ -938,6 +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);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										383
									
								
								contracts/asset-proxy/test/utils/erc1155_proxy_wrapper.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										383
									
								
								contracts/asset-proxy/test/utils/erc1155_proxy_wrapper.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,383 @@
 | 
			
		||||
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 (!_.isUndefined(extraData)) {
 | 
			
		||||
            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 (_.isUndefined(fungibleHoldingsByOwner[tokenOwnerAddress])) {
 | 
			
		||||
                        fungibleHoldingsByOwner[tokenOwnerAddress] = {};
 | 
			
		||||
                    }
 | 
			
		||||
                    if (_.isUndefined(fungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress])) {
 | 
			
		||||
                        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 (_.isUndefined(nonFungibleHoldingsByOwner[tokenOwnerAddress])) {
 | 
			
		||||
                        nonFungibleHoldingsByOwner[tokenOwnerAddress] = {};
 | 
			
		||||
                    }
 | 
			
		||||
                    if (_.isUndefined(nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress])) {
 | 
			
		||||
                        nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress] = {};
 | 
			
		||||
                    }
 | 
			
		||||
                    if (_.isUndefined(nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress][tokenIdAsString])) {
 | 
			
		||||
                        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 (_.isUndefined(tokenHoldingsByOwner[tokenOwnerAddress])) {
 | 
			
		||||
                        tokenHoldingsByOwner[tokenOwnerAddress] = {};
 | 
			
		||||
                    }
 | 
			
		||||
                    if (_.isUndefined(tokenHoldingsByOwner[tokenOwnerAddress][tokenAddress])) {
 | 
			
		||||
                        tokenHoldingsByOwner[tokenOwnerAddress][tokenAddress] = {};
 | 
			
		||||
                    }
 | 
			
		||||
                    tokenHoldingsByOwner[tokenOwnerAddress][tokenAddress][tokenId] = balances[i++];
 | 
			
		||||
                }
 | 
			
		||||
                // Non-fungible tokens
 | 
			
		||||
                for (const nft of this._nfts) {
 | 
			
		||||
                    if (_.isUndefined(nonFungibleHoldingsByOwner[tokenOwnerAddress])) {
 | 
			
		||||
                        nonFungibleHoldingsByOwner[tokenOwnerAddress] = {};
 | 
			
		||||
                    }
 | 
			
		||||
                    if (_.isUndefined(nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress])) {
 | 
			
		||||
                        nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress] = {};
 | 
			
		||||
                    }
 | 
			
		||||
                    if (
 | 
			
		||||
                        _.isUndefined(
 | 
			
		||||
                            nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress][nft.tokenId.toString()],
 | 
			
		||||
                        )
 | 
			
		||||
                    ) {
 | 
			
		||||
                        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 (_.isUndefined(tokenWrapper)) {
 | 
			
		||||
            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 (_.isUndefined(tokenContractIfExists)) {
 | 
			
		||||
            throw new Error(`Token: ${tokenAddress} was not deployed through ERC1155ProxyWrapper`);
 | 
			
		||||
        }
 | 
			
		||||
        return tokenContractIfExists.getContract();
 | 
			
		||||
    }
 | 
			
		||||
    private _validateDummyTokenContractsExistOrThrow(): void {
 | 
			
		||||
        if (_.isUndefined(this._dummyTokenWrappers)) {
 | 
			
		||||
            throw new Error('Dummy ERC1155 tokens not yet deployed, please call "deployDummyTokensAsync"');
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    private _validateProxyContractExistsOrThrow(): void {
 | 
			
		||||
        if (_.isUndefined(this._proxyContract)) {
 | 
			
		||||
            throw new Error('ERC1155 proxy contract not yet deployed, please call "deployProxyAsync"');
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    private _validateBalancesAndAllowancesSetOrThrow(): void {
 | 
			
		||||
        if (
 | 
			
		||||
            _.keys(this._initialTokenIdsByOwner.fungible).length === 0 ||
 | 
			
		||||
            _.keys(this._initialTokenIdsByOwner.nonFungible).length === 0
 | 
			
		||||
        ) {
 | 
			
		||||
            throw new Error(
 | 
			
		||||
                'Dummy ERC1155 balances and allowances not yet set, please call "setBalancesAndAllowancesAsync"',
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,16 +1,18 @@
 | 
			
		||||
import { constants, ERC20BalancesByOwner, txDefaults, Web3ProviderEngine } from '@0x/contracts-test-utils';
 | 
			
		||||
import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contracts-erc20';
 | 
			
		||||
import { constants, ERC20BalancesByOwner, txDefaults } from '@0x/contracts-test-utils';
 | 
			
		||||
import { assetDataUtils } from '@0x/order-utils';
 | 
			
		||||
import { BigNumber } from '@0x/utils';
 | 
			
		||||
import { Web3Wrapper } from '@0x/web3-wrapper';
 | 
			
		||||
import { ZeroExProvider } from 'ethereum-types';
 | 
			
		||||
import * as _ from 'lodash';
 | 
			
		||||
 | 
			
		||||
import { artifacts, DummyERC20TokenContract, ERC20ProxyContract } from '../../src';
 | 
			
		||||
import { artifacts, ERC20ProxyContract } from '../../src';
 | 
			
		||||
 | 
			
		||||
export class ERC20Wrapper {
 | 
			
		||||
    private readonly _tokenOwnerAddresses: string[];
 | 
			
		||||
    private readonly _contractOwnerAddress: string;
 | 
			
		||||
    private readonly _web3Wrapper: Web3Wrapper;
 | 
			
		||||
    private readonly _provider: Web3ProviderEngine;
 | 
			
		||||
    private readonly _provider: ZeroExProvider;
 | 
			
		||||
    private readonly _dummyTokenContracts: DummyERC20TokenContract[];
 | 
			
		||||
    private _proxyContract?: ERC20ProxyContract;
 | 
			
		||||
    private _proxyIdIfExists?: string;
 | 
			
		||||
@@ -21,7 +23,7 @@ export class ERC20Wrapper {
 | 
			
		||||
     * @param contractOwnerAddress Desired owner of the contract
 | 
			
		||||
     * Instance of ERC20Wrapper
 | 
			
		||||
     */
 | 
			
		||||
    constructor(provider: Web3ProviderEngine, tokenOwnerAddresses: string[], contractOwnerAddress: string) {
 | 
			
		||||
    constructor(provider: ZeroExProvider, tokenOwnerAddresses: string[], contractOwnerAddress: string) {
 | 
			
		||||
        this._dummyTokenContracts = [];
 | 
			
		||||
        this._web3Wrapper = new Web3Wrapper(provider);
 | 
			
		||||
        this._provider = provider;
 | 
			
		||||
@@ -35,7 +37,7 @@ export class ERC20Wrapper {
 | 
			
		||||
        for (let i = 0; i < numberToDeploy; i++) {
 | 
			
		||||
            this._dummyTokenContracts.push(
 | 
			
		||||
                await DummyERC20TokenContract.deployFrom0xArtifactAsync(
 | 
			
		||||
                    artifacts.DummyERC20Token,
 | 
			
		||||
                    erc20Artifacts.DummyERC20Token,
 | 
			
		||||
                    this._provider,
 | 
			
		||||
                    txDefaults,
 | 
			
		||||
                    constants.DUMMY_TOKEN_NAME,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,21 +1,23 @@
 | 
			
		||||
import { constants, ERC721TokenIdsByOwner, txDefaults, Web3ProviderEngine } from '@0x/contracts-test-utils';
 | 
			
		||||
import { artifacts as erc721Artifacts, DummyERC721TokenContract } from '@0x/contracts-erc721';
 | 
			
		||||
import { constants, ERC721TokenIdsByOwner, txDefaults } from '@0x/contracts-test-utils';
 | 
			
		||||
import { generatePseudoRandomSalt } from '@0x/order-utils';
 | 
			
		||||
import { BigNumber } from '@0x/utils';
 | 
			
		||||
import { Web3Wrapper } from '@0x/web3-wrapper';
 | 
			
		||||
import { ZeroExProvider } from 'ethereum-types';
 | 
			
		||||
import * as _ from 'lodash';
 | 
			
		||||
 | 
			
		||||
import { artifacts, DummyERC721TokenContract, ERC721ProxyContract } from '../../src';
 | 
			
		||||
import { artifacts, ERC721ProxyContract } from '../../src';
 | 
			
		||||
 | 
			
		||||
export class ERC721Wrapper {
 | 
			
		||||
    private readonly _tokenOwnerAddresses: string[];
 | 
			
		||||
    private readonly _contractOwnerAddress: string;
 | 
			
		||||
    private readonly _web3Wrapper: Web3Wrapper;
 | 
			
		||||
    private readonly _provider: Web3ProviderEngine;
 | 
			
		||||
    private readonly _provider: ZeroExProvider;
 | 
			
		||||
    private readonly _dummyTokenContracts: DummyERC721TokenContract[];
 | 
			
		||||
    private _proxyContract?: ERC721ProxyContract;
 | 
			
		||||
    private _proxyIdIfExists?: string;
 | 
			
		||||
    private _initialTokenIdsByOwner: ERC721TokenIdsByOwner = {};
 | 
			
		||||
    constructor(provider: Web3ProviderEngine, tokenOwnerAddresses: string[], contractOwnerAddress: string) {
 | 
			
		||||
    constructor(provider: ZeroExProvider, tokenOwnerAddresses: string[], contractOwnerAddress: string) {
 | 
			
		||||
        this._web3Wrapper = new Web3Wrapper(provider);
 | 
			
		||||
        this._provider = provider;
 | 
			
		||||
        this._dummyTokenContracts = [];
 | 
			
		||||
@@ -27,7 +29,7 @@ export class ERC721Wrapper {
 | 
			
		||||
        for (const i of _.times(constants.NUM_DUMMY_ERC721_TO_DEPLOY)) {
 | 
			
		||||
            this._dummyTokenContracts.push(
 | 
			
		||||
                await DummyERC721TokenContract.deployFrom0xArtifactAsync(
 | 
			
		||||
                    artifacts.DummyERC721Token,
 | 
			
		||||
                    erc721Artifacts.DummyERC721Token,
 | 
			
		||||
                    this._provider,
 | 
			
		||||
                    txDefaults,
 | 
			
		||||
                    constants.DUMMY_TOKEN_NAME,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,2 +1,3 @@
 | 
			
		||||
export * from './erc20_wrapper';
 | 
			
		||||
export * from './erc721_wrapper';
 | 
			
		||||
export * from './erc1155_proxy_wrapper';
 | 
			
		||||
 
 | 
			
		||||
@@ -3,11 +3,7 @@
 | 
			
		||||
    "compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
 | 
			
		||||
    "include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
 | 
			
		||||
    "files": [
 | 
			
		||||
        "generated-artifacts/DummyERC20Token.json",
 | 
			
		||||
        "generated-artifacts/DummyERC721Receiver.json",
 | 
			
		||||
        "generated-artifacts/DummyERC721Token.json",
 | 
			
		||||
        "generated-artifacts/DummyMultipleReturnERC20Token.json",
 | 
			
		||||
        "generated-artifacts/DummyNoReturnERC20Token.json",
 | 
			
		||||
        "generated-artifacts/ERC1155Proxy.json",
 | 
			
		||||
        "generated-artifacts/ERC20Proxy.json",
 | 
			
		||||
        "generated-artifacts/ERC721Proxy.json",
 | 
			
		||||
        "generated-artifacts/IAssetData.json",
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										23
									
								
								contracts/coordinator/CHANGELOG.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								contracts/coordinator/CHANGELOG.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
[
 | 
			
		||||
    {
 | 
			
		||||
        "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
 | 
			
		||||
    }
 | 
			
		||||
]
 | 
			
		||||
							
								
								
									
										13
									
								
								contracts/coordinator/CHANGELOG.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								contracts/coordinator/CHANGELOG.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
<!--
 | 
			
		||||
changelogUtils.file is auto-generated using the monorepo-scripts package. Don't edit directly.
 | 
			
		||||
Edit the package's CHANGELOG.json file only.
 | 
			
		||||
-->
 | 
			
		||||
 | 
			
		||||
CHANGELOG
 | 
			
		||||
 | 
			
		||||
## 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).
 | 
			
		||||
							
								
								
									
										30
									
								
								contracts/coordinator/compiler.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								contracts/coordinator/compiler.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
{
 | 
			
		||||
    "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",
 | 
			
		||||
        "test/TestLibs.sol",
 | 
			
		||||
        "test/TestMixins.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 = decodeFillDataOrders(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 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
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        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"
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Decodes the orders from Exchange calldata representing any fill method.
 | 
			
		||||
    /// @param data Exchange calldata representing a fill method.
 | 
			
		||||
    /// @return The orders from the Exchange calldata.
 | 
			
		||||
    function decodeFillDataOrders(bytes memory data)
 | 
			
		||||
        internal
 | 
			
		||||
        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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										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,62 @@
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  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 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
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view;
 | 
			
		||||
}
 | 
			
		||||
@@ -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,96 @@
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  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 "./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, using the domain separator of this contract.
 | 
			
		||||
        bytes transactionSignature;             // Signature of the 0x transaction.
 | 
			
		||||
        uint256 approvalExpirationTimeSeconds;  // Timestamp in seconds for which the signature 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)
 | 
			
		||||
        internal
 | 
			
		||||
        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.transactionHash,
 | 
			
		||||
        //     keccak256(approval.transactionSignature)
 | 
			
		||||
        //     approval.expiration,
 | 
			
		||||
        // ));
 | 
			
		||||
 | 
			
		||||
        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,94 @@
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  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 "./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 this contract.
 | 
			
		||||
    /// @param transaction 0x transaction containing salt, signerAddress, and data.
 | 
			
		||||
    /// @return EIP712 hash of the transaction with the domain separator of this contract.
 | 
			
		||||
    function getTransactionHash(ZeroExTransaction memory transaction)
 | 
			
		||||
        internal
 | 
			
		||||
        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,36 @@
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  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 Decodes the orders from Exchange calldata representing any fill method.
 | 
			
		||||
    /// @param data Exchange calldata representing a fill method.
 | 
			
		||||
    /// @return The orders from the Exchange calldata.
 | 
			
		||||
    function decodeFillDataOrders(bytes memory data)
 | 
			
		||||
        internal
 | 
			
		||||
        pure
 | 
			
		||||
        returns (LibOrder.Order[] memory orders);
 | 
			
		||||
}
 | 
			
		||||
@@ -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);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										61
									
								
								contracts/coordinator/contracts/test/TestLibs.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								contracts/coordinator/contracts/test/TestLibs.sol
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,61 @@
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  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 "../src/libs/LibConstants.sol";
 | 
			
		||||
import "../src/libs/LibCoordinatorApproval.sol";
 | 
			
		||||
import "../src/libs/LibZeroExTransaction.sol";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// solhint-disable no-empty-blocks
 | 
			
		||||
contract TestLibs is
 | 
			
		||||
    LibConstants,
 | 
			
		||||
    LibCoordinatorApproval,
 | 
			
		||||
    LibZeroExTransaction
 | 
			
		||||
{
 | 
			
		||||
    constructor (address _exchange)
 | 
			
		||||
        public
 | 
			
		||||
        LibConstants(_exchange)
 | 
			
		||||
    {}
 | 
			
		||||
 | 
			
		||||
    /// @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 publicGetCoordinatorApprovalHash(CoordinatorApproval memory approval)
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (bytes32 approvalHash)
 | 
			
		||||
    {
 | 
			
		||||
        approvalHash = getCoordinatorApprovalHash(approval);
 | 
			
		||||
        return approvalHash;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @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 the Exchange contract.
 | 
			
		||||
    function publicGetTransactionHash(ZeroExTransaction memory transaction)
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (bytes32 transactionHash)
 | 
			
		||||
    {
 | 
			
		||||
        transactionHash = getTransactionHash(transaction);
 | 
			
		||||
        return transactionHash;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										37
									
								
								contracts/coordinator/contracts/test/TestMixins.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								contracts/coordinator/contracts/test/TestMixins.sol
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  Copyright 2018 ZeroEx Intl.
 | 
			
		||||
 | 
			
		||||
  Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
  you may not use this file except in compliance with the License.
 | 
			
		||||
  You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
  Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
  distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
  See the License for the specific language governing permissions and
 | 
			
		||||
  limitations under the License.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
pragma solidity ^0.5.5;
 | 
			
		||||
pragma experimental "ABIEncoderV2";
 | 
			
		||||
 | 
			
		||||
import "../src/libs/LibConstants.sol";
 | 
			
		||||
import "../src/MixinSignatureValidator.sol";
 | 
			
		||||
import "../src/MixinCoordinatorApprovalVerifier.sol";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// solhint-disable no-empty-blocks
 | 
			
		||||
contract TestMixins is
 | 
			
		||||
    LibConstants,
 | 
			
		||||
    MixinSignatureValidator,
 | 
			
		||||
    MixinCoordinatorApprovalVerifier
 | 
			
		||||
{
 | 
			
		||||
    constructor (address _exchange)
 | 
			
		||||
        public
 | 
			
		||||
        LibConstants(_exchange)
 | 
			
		||||
    {}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										88
									
								
								contracts/coordinator/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								contracts/coordinator/package.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,88 @@
 | 
			
		||||
{
 | 
			
		||||
    "name": "@0x/contracts-coordinator",
 | 
			
		||||
    "version": "1.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",
 | 
			
		||||
        "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|TestLibs|TestMixins).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.7",
 | 
			
		||||
        "@0x/contracts-gen": "^1.0.6",
 | 
			
		||||
        "@0x/contracts-test-utils": "^3.1.0",
 | 
			
		||||
        "@0x/dev-utils": "^2.1.4",
 | 
			
		||||
        "@0x/sol-compiler": "^3.1.4",
 | 
			
		||||
        "@0x/tslint-config": "^3.0.0",
 | 
			
		||||
        "@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.3",
 | 
			
		||||
        "@0x/contracts-asset-proxy": "^2.0.0",
 | 
			
		||||
        "@0x/contracts-erc20": "^2.0.0",
 | 
			
		||||
        "@0x/contracts-exchange": "1.0.2",
 | 
			
		||||
        "@0x/contracts-exchange-libs": "^2.0.0",
 | 
			
		||||
        "@0x/contracts-utils": "^3.0.0",
 | 
			
		||||
        "@0x/order-utils": "^7.1.0",
 | 
			
		||||
        "@0x/types": "^2.2.0",
 | 
			
		||||
        "@0x/typescript-typings": "^4.2.0",
 | 
			
		||||
        "@0x/utils": "^4.2.3",
 | 
			
		||||
        "@0x/web3-wrapper": "^6.0.3",
 | 
			
		||||
        "ethereum-types": "^2.1.1",
 | 
			
		||||
        "ethereumjs-util": "^5.1.1",
 | 
			
		||||
        "lodash": "^4.17.11"
 | 
			
		||||
    },
 | 
			
		||||
    "publishConfig": {
 | 
			
		||||
        "access": "public"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										17
									
								
								contracts/coordinator/src/artifacts.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								contracts/coordinator/src/artifacts.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
/*
 | 
			
		||||
 * -----------------------------------------------------------------------------
 | 
			
		||||
 * 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';
 | 
			
		||||
import * as TestLibs from '../generated-artifacts/TestLibs.json';
 | 
			
		||||
import * as TestMixins from '../generated-artifacts/TestMixins.json';
 | 
			
		||||
export const artifacts = {
 | 
			
		||||
    Coordinator: Coordinator as ContractArtifact,
 | 
			
		||||
    CoordinatorRegistry: CoordinatorRegistry as ContractArtifact,
 | 
			
		||||
    TestLibs: TestLibs as ContractArtifact,
 | 
			
		||||
    TestMixins: TestMixins 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';
 | 
			
		||||
							
								
								
									
										9
									
								
								contracts/coordinator/src/wrappers.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								contracts/coordinator/src/wrappers.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
/*
 | 
			
		||||
 * -----------------------------------------------------------------------------
 | 
			
		||||
 * Warning: This file is auto-generated by contracts-gen. Don't edit manually.
 | 
			
		||||
 * -----------------------------------------------------------------------------
 | 
			
		||||
 */
 | 
			
		||||
export * from '../generated-wrappers/coordinator';
 | 
			
		||||
export * from '../generated-wrappers/coordinator_registry';
 | 
			
		||||
export * from '../generated-wrappers/test_libs';
 | 
			
		||||
export * from '../generated-wrappers/test_mixins';
 | 
			
		||||
							
								
								
									
										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_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(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);
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										17
									
								
								contracts/coordinator/test/global_hooks.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								contracts/coordinator/test/global_hooks.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
import { env, EnvVars } from '@0x/dev-utils';
 | 
			
		||||
 | 
			
		||||
import { coverage, profiler, provider } from '@0x/contracts-test-utils';
 | 
			
		||||
before('start web3 provider', () => {
 | 
			
		||||
    provider.start();
 | 
			
		||||
});
 | 
			
		||||
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, hashUtils, TestLibsContract } from '../src';
 | 
			
		||||
 | 
			
		||||
chaiSetup.configure();
 | 
			
		||||
const expect = chai.expect;
 | 
			
		||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
 | 
			
		||||
 | 
			
		||||
describe('Libs tests', () => {
 | 
			
		||||
    let testLibs: TestLibsContract;
 | 
			
		||||
    const exchangeAddress = addressUtils.generatePseudoRandomAddress();
 | 
			
		||||
 | 
			
		||||
    before(async () => {
 | 
			
		||||
        await blockchainLifecycle.startAsync();
 | 
			
		||||
    });
 | 
			
		||||
    after(async () => {
 | 
			
		||||
        await blockchainLifecycle.revertAsync();
 | 
			
		||||
    });
 | 
			
		||||
    before(async () => {
 | 
			
		||||
        testLibs = await TestLibsContract.deployFrom0xArtifactAsync(
 | 
			
		||||
            artifacts.TestLibs,
 | 
			
		||||
            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 testLibs.publicGetTransactionHash.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,
 | 
			
		||||
                testLibs.address,
 | 
			
		||||
                txOrigin,
 | 
			
		||||
                approvalExpirationTimeSeconds,
 | 
			
		||||
            );
 | 
			
		||||
            const approvalHash = await testLibs.publicGetCoordinatorApprovalHash.callAsync(approval);
 | 
			
		||||
            expect(expectedApprovalHash).to.eq(approvalHash);
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										895
									
								
								contracts/coordinator/test/mixins.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										895
									
								
								contracts/coordinator/test/mixins.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,895 @@
 | 
			
		||||
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, exchangeDataEncoder, TestMixinsContract } 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: TestMixinsContract;
 | 
			
		||||
    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 TestMixinsContract.deployFrom0xArtifactAsync(
 | 
			
		||||
            artifacts.TestMixins,
 | 
			
		||||
            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('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.assertValidTransactionOrdersApproval.callAsync(
 | 
			
		||||
                    transaction,
 | 
			
		||||
                    orders,
 | 
			
		||||
                    transactionSignerAddress,
 | 
			
		||||
                    transaction.signature,
 | 
			
		||||
                    [approvalExpirationTimeSeconds],
 | 
			
		||||
                    [approval.signature],
 | 
			
		||||
                    { from: transactionSignerAddress },
 | 
			
		||||
                );
 | 
			
		||||
                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.assertValidTransactionOrdersApproval.callAsync(
 | 
			
		||||
                    transaction,
 | 
			
		||||
                    orders,
 | 
			
		||||
                    transactionSignerAddress,
 | 
			
		||||
                    transaction.signature,
 | 
			
		||||
                    [approvalExpirationTimeSeconds],
 | 
			
		||||
                    [approval.signature],
 | 
			
		||||
                    { from: transactionSignerAddress },
 | 
			
		||||
                );
 | 
			
		||||
                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.assertValidTransactionOrdersApproval.callAsync(
 | 
			
		||||
                    transaction,
 | 
			
		||||
                    orders,
 | 
			
		||||
                    approvalSignerAddress1,
 | 
			
		||||
                    transaction.signature,
 | 
			
		||||
                    [],
 | 
			
		||||
                    [],
 | 
			
		||||
                    { from: approvalSignerAddress1 },
 | 
			
		||||
                );
 | 
			
		||||
                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.assertValidTransactionOrdersApproval.callAsync(
 | 
			
		||||
                    transaction,
 | 
			
		||||
                    orders,
 | 
			
		||||
                    approvalSignerAddress1,
 | 
			
		||||
                    transaction.signature,
 | 
			
		||||
                    [approvalExpirationTimeSeconds],
 | 
			
		||||
                    [approval.signature],
 | 
			
		||||
                    { from: approvalSignerAddress1 },
 | 
			
		||||
                );
 | 
			
		||||
                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.assertValidTransactionOrdersApproval.callAsync(
 | 
			
		||||
                    transaction,
 | 
			
		||||
                    orders,
 | 
			
		||||
                    approvalSignerAddress1,
 | 
			
		||||
                    transaction.signature,
 | 
			
		||||
                    [],
 | 
			
		||||
                    [],
 | 
			
		||||
                    { from: approvalSignerAddress1 },
 | 
			
		||||
                );
 | 
			
		||||
                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.assertValidTransactionOrdersApproval.callAsync(
 | 
			
		||||
                        transaction,
 | 
			
		||||
                        orders,
 | 
			
		||||
                        transactionSignerAddress,
 | 
			
		||||
                        transaction.signature,
 | 
			
		||||
                        [approvalExpirationTimeSeconds],
 | 
			
		||||
                        [signature],
 | 
			
		||||
                        { from: transactionSignerAddress },
 | 
			
		||||
                    ),
 | 
			
		||||
                    RevertReason.InvalidApprovalSignature,
 | 
			
		||||
                );
 | 
			
		||||
                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.assertValidTransactionOrdersApproval.callAsync(
 | 
			
		||||
                        transaction,
 | 
			
		||||
                        orders,
 | 
			
		||||
                        transactionSignerAddress,
 | 
			
		||||
                        transaction.signature,
 | 
			
		||||
                        [approvalExpirationTimeSeconds],
 | 
			
		||||
                        [approval.signature],
 | 
			
		||||
                        { from: transactionSignerAddress },
 | 
			
		||||
                    ),
 | 
			
		||||
                    RevertReason.ApprovalExpired,
 | 
			
		||||
                );
 | 
			
		||||
                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.assertValidTransactionOrdersApproval.callAsync(
 | 
			
		||||
                        transaction,
 | 
			
		||||
                        orders,
 | 
			
		||||
                        transactionSignerAddress,
 | 
			
		||||
                        transaction.signature,
 | 
			
		||||
                        [approvalExpirationTimeSeconds],
 | 
			
		||||
                        [approval.signature],
 | 
			
		||||
                        { from: approvalSignerAddress2 },
 | 
			
		||||
                    ),
 | 
			
		||||
                    RevertReason.InvalidOrigin,
 | 
			
		||||
                );
 | 
			
		||||
                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.assertValidTransactionOrdersApproval.callAsync(
 | 
			
		||||
                    transaction,
 | 
			
		||||
                    orders,
 | 
			
		||||
                    transactionSignerAddress,
 | 
			
		||||
                    transaction.signature,
 | 
			
		||||
                    [approvalExpirationTimeSeconds],
 | 
			
		||||
                    [approval.signature],
 | 
			
		||||
                    { from: transactionSignerAddress },
 | 
			
		||||
                );
 | 
			
		||||
                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.assertValidTransactionOrdersApproval.callAsync(
 | 
			
		||||
                    transaction,
 | 
			
		||||
                    orders,
 | 
			
		||||
                    transactionSignerAddress,
 | 
			
		||||
                    transaction.signature,
 | 
			
		||||
                    [approvalExpirationTimeSeconds],
 | 
			
		||||
                    [approval.signature],
 | 
			
		||||
                    { from: transactionSignerAddress },
 | 
			
		||||
                );
 | 
			
		||||
                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.assertValidTransactionOrdersApproval.callAsync(
 | 
			
		||||
                    transaction,
 | 
			
		||||
                    orders,
 | 
			
		||||
                    transactionSignerAddress,
 | 
			
		||||
                    transaction.signature,
 | 
			
		||||
                    [],
 | 
			
		||||
                    [],
 | 
			
		||||
                    { from: transactionSignerAddress },
 | 
			
		||||
                );
 | 
			
		||||
                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.assertValidTransactionOrdersApproval.callAsync(
 | 
			
		||||
                    transaction,
 | 
			
		||||
                    orders,
 | 
			
		||||
                    transactionSignerAddress,
 | 
			
		||||
                    transaction.signature,
 | 
			
		||||
                    [approvalExpirationTimeSeconds],
 | 
			
		||||
                    [approval.signature],
 | 
			
		||||
                    { from: transactionSignerAddress },
 | 
			
		||||
                );
 | 
			
		||||
                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.assertValidTransactionOrdersApproval.callAsync(
 | 
			
		||||
                    transaction,
 | 
			
		||||
                    orders,
 | 
			
		||||
                    transactionSignerAddress,
 | 
			
		||||
                    transaction.signature,
 | 
			
		||||
                    [approvalExpirationTimeSeconds, approvalExpirationTimeSeconds],
 | 
			
		||||
                    [approval1.signature, approval2.signature],
 | 
			
		||||
                    { from: transactionSignerAddress },
 | 
			
		||||
                );
 | 
			
		||||
                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.assertValidTransactionOrdersApproval.callAsync(
 | 
			
		||||
                    transaction,
 | 
			
		||||
                    orders,
 | 
			
		||||
                    approvalSignerAddress1,
 | 
			
		||||
                    transaction.signature,
 | 
			
		||||
                    [],
 | 
			
		||||
                    [],
 | 
			
		||||
                    { from: approvalSignerAddress1 },
 | 
			
		||||
                );
 | 
			
		||||
                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.assertValidTransactionOrdersApproval.callAsync(
 | 
			
		||||
                        transaction,
 | 
			
		||||
                        orders,
 | 
			
		||||
                        transactionSignerAddress,
 | 
			
		||||
                        transaction.signature,
 | 
			
		||||
                        [approvalExpirationTimeSeconds],
 | 
			
		||||
                        [approval2.signature],
 | 
			
		||||
                        { from: approvalSignerAddress1 },
 | 
			
		||||
                    ),
 | 
			
		||||
                    RevertReason.InvalidOrigin,
 | 
			
		||||
                );
 | 
			
		||||
                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.assertValidTransactionOrdersApproval.callAsync(
 | 
			
		||||
                        transaction,
 | 
			
		||||
                        orders,
 | 
			
		||||
                        transactionSignerAddress,
 | 
			
		||||
                        transaction.signature,
 | 
			
		||||
                        [],
 | 
			
		||||
                        [],
 | 
			
		||||
                        { from: transactionSignerAddress },
 | 
			
		||||
                    ),
 | 
			
		||||
                    RevertReason.InvalidApprovalSignature,
 | 
			
		||||
                );
 | 
			
		||||
                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.assertValidTransactionOrdersApproval.callAsync(
 | 
			
		||||
                        transaction,
 | 
			
		||||
                        orders,
 | 
			
		||||
                        transactionSignerAddress,
 | 
			
		||||
                        transaction.signature,
 | 
			
		||||
                        [approvalExpirationTimeSeconds],
 | 
			
		||||
                        [signature],
 | 
			
		||||
                        { from: transactionSignerAddress },
 | 
			
		||||
                    ),
 | 
			
		||||
                    RevertReason.InvalidApprovalSignature,
 | 
			
		||||
                );
 | 
			
		||||
                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.assertValidTransactionOrdersApproval.callAsync(
 | 
			
		||||
                        transaction,
 | 
			
		||||
                        orders,
 | 
			
		||||
                        transactionSignerAddress,
 | 
			
		||||
                        transaction.signature,
 | 
			
		||||
                        [approvalExpirationTimeSeconds, approvalExpirationTimeSeconds],
 | 
			
		||||
                        [approval1.signature, approvalSignature2],
 | 
			
		||||
                        { from: transactionSignerAddress },
 | 
			
		||||
                    ),
 | 
			
		||||
                    RevertReason.InvalidApprovalSignature,
 | 
			
		||||
                );
 | 
			
		||||
                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.assertValidTransactionOrdersApproval.callAsync(
 | 
			
		||||
                        transaction,
 | 
			
		||||
                        orders,
 | 
			
		||||
                        approvalSignerAddress1,
 | 
			
		||||
                        transaction.signature,
 | 
			
		||||
                        [approvalExpirationTimeSeconds],
 | 
			
		||||
                        [approvalSignature2],
 | 
			
		||||
                        { from: approvalSignerAddress1 },
 | 
			
		||||
                    ),
 | 
			
		||||
                    RevertReason.InvalidApprovalSignature,
 | 
			
		||||
                );
 | 
			
		||||
                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.assertValidTransactionOrdersApproval.callAsync(
 | 
			
		||||
                        transaction,
 | 
			
		||||
                        orders,
 | 
			
		||||
                        transactionSignerAddress,
 | 
			
		||||
                        transaction.signature,
 | 
			
		||||
                        [approvalExpirationTimeSeconds1, approvalExpirationTimeSeconds2],
 | 
			
		||||
                        [approval1.signature, approval2.signature],
 | 
			
		||||
                        { from: transactionSignerAddress },
 | 
			
		||||
                    ),
 | 
			
		||||
                    RevertReason.ApprovalExpired,
 | 
			
		||||
                );
 | 
			
		||||
                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.assertValidTransactionOrdersApproval.callAsync(
 | 
			
		||||
                        transaction,
 | 
			
		||||
                        orders,
 | 
			
		||||
                        approvalSignerAddress1,
 | 
			
		||||
                        transaction.signature,
 | 
			
		||||
                        [approvalExpirationTimeSeconds],
 | 
			
		||||
                        [approval2.signature],
 | 
			
		||||
                        { from: approvalSignerAddress1 },
 | 
			
		||||
                    ),
 | 
			
		||||
                    RevertReason.ApprovalExpired,
 | 
			
		||||
                );
 | 
			
		||||
                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.assertValidTransactionOrdersApproval.callAsync(
 | 
			
		||||
                        transaction,
 | 
			
		||||
                        orders,
 | 
			
		||||
                        transactionSignerAddress,
 | 
			
		||||
                        transaction.signature,
 | 
			
		||||
                        [approvalExpirationTimeSeconds],
 | 
			
		||||
                        [approval1.signature],
 | 
			
		||||
                        { from: approvalSignerAddress2 },
 | 
			
		||||
                    ),
 | 
			
		||||
                    RevertReason.InvalidOrigin,
 | 
			
		||||
                );
 | 
			
		||||
                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 `cancelOrders` without approval', async () => {
 | 
			
		||||
            const orders = [defaultOrder];
 | 
			
		||||
            const data = exchangeDataEncoder.encodeOrdersToExchangeData(constants.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 `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_ORDERS: 'cancelOrders',
 | 
			
		||||
    BATCH_CANCEL_ORDERS: 'batchCancelOrders',
 | 
			
		||||
    CANCEL_ORDERS_UP_TO: 'cancelOrdersUpTo',
 | 
			
		||||
    TIME_BUFFER: new BigNumber(1000),
 | 
			
		||||
};
 | 
			
		||||
@@ -0,0 +1,65 @@
 | 
			
		||||
import { LogDecoder, txDefaults } from '@0x/contracts-test-utils';
 | 
			
		||||
import { Web3Wrapper } from '@0x/web3-wrapper';
 | 
			
		||||
import { TransactionReceiptWithDecodedLogs, ZeroExProvider } from 'ethereum-types';
 | 
			
		||||
import * as _ from 'lodash';
 | 
			
		||||
 | 
			
		||||
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 (_.isUndefined(this._coordinatorRegistryContract)) {
 | 
			
		||||
            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 (_.isUndefined(this._coordinatorRegistryContract)) {
 | 
			
		||||
            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_ORDERS) {
 | 
			
		||||
            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;
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										48
									
								
								contracts/coordinator/test/utils/hash_utils.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								contracts/coordinator/test/utils/hash_utils.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,48 @@
 | 
			
		||||
import { eip712Utils, transactionHashUtils } from '@0x/order-utils';
 | 
			
		||||
import { constants } from '@0x/order-utils/lib/src/constants';
 | 
			
		||||
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 domain = {
 | 
			
		||||
            name: constants.COORDINATOR_DOMAIN_NAME,
 | 
			
		||||
            version: constants.COORDINATOR_DOMAIN_VERSION,
 | 
			
		||||
            verifyingContractAddress,
 | 
			
		||||
        };
 | 
			
		||||
        const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
 | 
			
		||||
        const approval = {
 | 
			
		||||
            txOrigin,
 | 
			
		||||
            transactionHash,
 | 
			
		||||
            transactionSignature: transaction.signature,
 | 
			
		||||
            approvalExpirationTimeSeconds: approvalExpirationTimeSeconds.toString(),
 | 
			
		||||
        };
 | 
			
		||||
        const typedData = eip712Utils.createTypedData(
 | 
			
		||||
            constants.COORDINATOR_APPROVAL_SCHEMA.name,
 | 
			
		||||
            {
 | 
			
		||||
                CoordinatorApproval: constants.COORDINATOR_APPROVAL_SCHEMA.parameters,
 | 
			
		||||
            },
 | 
			
		||||
            approval,
 | 
			
		||||
            domain,
 | 
			
		||||
        );
 | 
			
		||||
        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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								contracts/coordinator/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								contracts/coordinator/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
{
 | 
			
		||||
    "extends": "../../tsconfig",
 | 
			
		||||
    "compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
 | 
			
		||||
    "include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
 | 
			
		||||
    "files": [
 | 
			
		||||
        "generated-artifacts/Coordinator.json",
 | 
			
		||||
        "generated-artifacts/CoordinatorRegistry.json",
 | 
			
		||||
        "generated-artifacts/TestLibs.json",
 | 
			
		||||
        "generated-artifacts/TestMixins.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
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										20
									
								
								contracts/erc1155/CHANGELOG.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								contracts/erc1155/CHANGELOG.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
[
 | 
			
		||||
    {
 | 
			
		||||
        "timestamp": 1553091633,
 | 
			
		||||
        "version": "1.0.1",
 | 
			
		||||
        "changes": [
 | 
			
		||||
            {
 | 
			
		||||
                "note": "Dependencies updated"
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "version": "1.0.0",
 | 
			
		||||
        "changes": [
 | 
			
		||||
            {
 | 
			
		||||
                "note": "Created ERC1155 contracts package",
 | 
			
		||||
                "pr": 1657
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
]
 | 
			
		||||
							
								
								
									
										14
									
								
								contracts/erc1155/CHANGELOG.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								contracts/erc1155/CHANGELOG.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
<!--
 | 
			
		||||
changelogUtils.file is auto-generated using the monorepo-scripts package. Don't edit directly.
 | 
			
		||||
Edit the package's CHANGELOG.json file only.
 | 
			
		||||
-->
 | 
			
		||||
 | 
			
		||||
CHANGELOG
 | 
			
		||||
 | 
			
		||||
## 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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										82
									
								
								contracts/erc1155/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								contracts/erc1155/package.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,82 @@
 | 
			
		||||
{
 | 
			
		||||
    "name": "@0x/contracts-erc1155",
 | 
			
		||||
    "version": "1.0.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",
 | 
			
		||||
        "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.7",
 | 
			
		||||
        "@0x/contracts-gen": "^1.0.6",
 | 
			
		||||
        "@0x/contracts-test-utils": "^3.1.0",
 | 
			
		||||
        "@0x/dev-utils": "^2.1.4",
 | 
			
		||||
        "@0x/sol-compiler": "^3.1.4",
 | 
			
		||||
        "@0x/tslint-config": "^3.0.0",
 | 
			
		||||
        "@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.3",
 | 
			
		||||
        "@0x/contracts-utils": "^3.0.0",
 | 
			
		||||
        "@0x/types": "^2.2.0",
 | 
			
		||||
        "@0x/typescript-typings": "^4.2.0",
 | 
			
		||||
        "@0x/utils": "^4.2.3",
 | 
			
		||||
        "@0x/web3-wrapper": "^6.0.3",
 | 
			
		||||
        "ethereum-types": "^2.1.1",
 | 
			
		||||
        "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
 | 
			
		||||
							
								
								
									
										17
									
								
								contracts/erc1155/test/global_hooks.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								contracts/erc1155/test/global_hooks.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
import { env, EnvVars } from '@0x/dev-utils';
 | 
			
		||||
 | 
			
		||||
import { coverage, profiler, provider } from '@0x/contracts-test-utils';
 | 
			
		||||
before('start web3 provider', () => {
 | 
			
		||||
    provider.start();
 | 
			
		||||
});
 | 
			
		||||
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 = _.isUndefined(delegatedSpender) ? from : delegatedSpender;
 | 
			
		||||
        const callbackDataHex = _.isUndefined(callbackData) ? '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 = _.isUndefined(delegatedSpender) ? from : delegatedSpender;
 | 
			
		||||
        const callbackDataHex = _.isUndefined(callbackData) ? '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,41 @@
 | 
			
		||||
[
 | 
			
		||||
    {
 | 
			
		||||
        "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",
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,22 @@ Edit the package's CHANGELOG.json file only.
 | 
			
		||||
 | 
			
		||||
CHANGELOG
 | 
			
		||||
 | 
			
		||||
## 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
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,12 @@
 | 
			
		||||
    "useDockerisedSolc": true,
 | 
			
		||||
    "isOfflineMode": false,
 | 
			
		||||
    "compilerSettings": {
 | 
			
		||||
        "optimizer": { "enabled": true, "runs": 1000000 },
 | 
			
		||||
        "evmVersion": "constantinople",
 | 
			
		||||
        "optimizer": {
 | 
			
		||||
            "enabled": true,
 | 
			
		||||
            "runs": 1000000,
 | 
			
		||||
            "details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
 | 
			
		||||
        },
 | 
			
		||||
        "outputSelection": {
 | 
			
		||||
            "*": {
 | 
			
		||||
                "*": [
 | 
			
		||||
@@ -27,7 +32,6 @@
 | 
			
		||||
        "src/interfaces/IEtherToken.sol",
 | 
			
		||||
        "test/DummyERC20Token.sol",
 | 
			
		||||
        "test/DummyMultipleReturnERC20Token.sol",
 | 
			
		||||
        "test/DummyNoReturnERC20Token.sol",
 | 
			
		||||
        "test/ReentrantERC20Token.sol"
 | 
			
		||||
        "test/DummyNoReturnERC20Token.sol"
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,7 @@
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
pragma solidity ^0.4.24;
 | 
			
		||||
pragma solidity ^0.5.5;
 | 
			
		||||
 | 
			
		||||
import "./interfaces/IERC20Token.sol";
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,7 @@
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
pragma solidity ^0.4.24;
 | 
			
		||||
pragma solidity ^0.5.5;
 | 
			
		||||
 | 
			
		||||
import "@0x/contracts-utils/contracts/src/SafeMath.sol";
 | 
			
		||||
import "./UnlimitedAllowanceERC20Token.sol";
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user