Compare commits
	
		
			280 Commits
		
	
	
		
			@0x/contra
			...
			shawn/ftm
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 238fe28bd7 | ||
|  | 1f0e518de0 | ||
|  | 3362be3e11 | ||
|  | 0e48886797 | ||
|  | a97641901f | ||
|  | 466df9fee7 | ||
|  | 543a3dd8a4 | ||
|  | 6b1cdd9bd1 | ||
|  | dbf899b20d | ||
|  | 341e1162e0 | ||
|  | 126b3c2079 | ||
|  | 9b9ed535c7 | ||
|  | 17f81d8609 | ||
|  | d7cff52e75 | ||
|  | cf1f29a37d | ||
|  | 9af22110b4 | ||
|  | 2c187c7e85 | ||
|  | 225907b359 | ||
|  | eb464f0cef | ||
|  | 23571a0b84 | ||
|  | c6a138a158 | ||
|  | d46756ae2e | ||
|  | d9a16ed1f9 | ||
|  | 57a1120997 | ||
|  | 0945d4cef2 | ||
|  | 8f6f7ad453 | ||
|  | bb4fad37fa | ||
|  | d06daf2957 | ||
|  | 64110cfb96 | ||
|  | 34314960ef | ||
|  | bd833b38b7 | ||
|  | 832ba737ec | ||
|  | 6950fdbebb | ||
|  | 1849b1bb9a | ||
|  | a883139220 | ||
|  | f3b3ab05ea | ||
|  | bb2c26cb94 | ||
|  | ba09a0b2bf | ||
|  | b2d54f0238 | ||
|  | 4a3096495b | ||
|  | 23f6e9e53c | ||
|  | d7dbc0576d | ||
|  | 15fb00e958 | ||
|  | 1d9295cc94 | ||
|  | 79f36cf6fb | ||
|  | 6ce4458a5d | ||
|  | fad6e65c07 | ||
|  | 840c85373e | ||
|  | 0479bb5fe1 | ||
|  | 9ecc31ed54 | ||
|  | eb29abd36c | ||
|  | d202e01522 | ||
|  | e838a6801b | ||
|  | 7439871aa0 | ||
|  | 317f2138c5 | ||
|  | e5834f1901 | ||
|  | 5063446f93 | ||
|  | 0caf495a1a | ||
|  | a20de0fc69 | ||
|  | 9aa0065d2d | ||
|  | c24855e627 | ||
|  | 6bb72dd775 | ||
|  | 77d1ed257c | ||
|  | 5d265360c4 | ||
|  | c9097f6e8b | ||
|  | d3df985a42 | ||
|  | 7267420874 | ||
|  | 17e81432f1 | ||
|  | 57c767c3b1 | ||
|  | dbb1c88ad9 | ||
|  | 254b850a8b | ||
|  | e2242e5955 | ||
|  | 7a0255fee7 | ||
|  | ec6522cfbf | ||
|  | 1bf96e91bb | ||
|  | eb733cc58f | ||
|  | d9fdcf813b | ||
|  | 88bd1c9cea | ||
|  | 9e759e82b4 | ||
|  | da433854ac | ||
|  | ed3524e0d7 | ||
|  | 20e23bf3f6 | ||
|  | be4f85690e | ||
|  | e061dfa59b | ||
|  | cb4f42dfea | ||
|  | 9727f0cebe | ||
|  | 68656b4a4d | ||
|  | aae46bef84 | ||
|  | a0bb36954e | ||
|  | 4f32f3174f | ||
|  | b84107d142 | ||
|  | b46eeadc64 | ||
|  | 692231c2ea | ||
|  | 3a1becc3e4 | ||
|  | 3653e2d7f9 | ||
|  | 65def38d98 | ||
|  | 859c36cb10 | ||
|  | 3e34386812 | ||
|  | a35af11981 | ||
|  | 59eabec71e | ||
|  | bf4c7e7d50 | ||
|  | fd098ca4df | ||
|  | c360f8d8fd | ||
|  | b358559421 | ||
|  | df5aad8e8e | ||
|  | 8dbf79db59 | ||
|  | 1839608e84 | ||
|  | 6e3e795b8b | ||
|  | d9c410a7e3 | ||
|  | 609727afe8 | ||
|  | 8a5c74c0b4 | ||
|  | cd93f3b07e | ||
|  | 8397b12de6 | ||
|  | 3f85acec3a | ||
|  | d6a9e3d600 | ||
|  | 361569ac2f | ||
|  | 719664c145 | ||
|  | f800d6c24c | ||
|  | a9a81bcafb | ||
|  | 4280307a15 | ||
|  | 7b57d3ae51 | ||
|  | 8a8a5bbda0 | ||
|  | 76987c8db1 | ||
|  | 6f8971cc42 | ||
|  | 71ab882143 | ||
|  | 5a4961c8d9 | ||
|  | 4c4fb99d87 | ||
|  | 872abf09e9 | ||
|  | f10bfe7d04 | ||
|  | a74d8deff3 | ||
|  | 835ee4e8de | ||
|  | 63ec42303f | ||
|  | f789aebddc | ||
|  | efd83be779 | ||
|  | 603bc1d51c | ||
|  | 32a930a7fc | ||
|  | f464bf68d7 | ||
|  | ebdc4fb509 | ||
|  | 7580719586 | ||
|  | aba9db2be7 | ||
|  | a6680411c8 | ||
|  | 0d0e87de94 | ||
|  | ccf2000c09 | ||
|  | 3eb2e0f56a | ||
|  | d07c7d5b69 | ||
|  | adf6684c29 | ||
|  | 9bf889aa30 | ||
|  | e81c88564e | ||
|  | 901d400d62 | ||
|  | 289474e2ce | ||
|  | 407ca21168 | ||
|  | 5c68fc24d2 | ||
|  | 548800e0a9 | ||
|  | bde3d6dc6a | ||
|  | 56550a6acc | ||
|  | e51b83accc | ||
|  | d5ae971f1c | ||
|  | 5a2f5f9a42 | ||
|  | 75a3b70cef | ||
|  | 803cf65ba1 | ||
|  | 5d3947b838 | ||
|  | 4397a59008 | ||
|  | 966d54c935 | ||
|  | 234ddb495d | ||
|  | a744acc7bc | ||
|  | 27c624633c | ||
|  | 7ef75101b4 | ||
|  | 6f8aace00d | ||
|  | 6c264b2f18 | ||
|  | df055e1958 | ||
|  | 70d2117470 | ||
|  | 2c173ccaf3 | ||
|  | d2f4a0c5f3 | ||
|  | 0d6021e5e3 | ||
|  | bb04726e7f | ||
|  | 220ca370c2 | ||
|  | 63af4e3e98 | ||
|  | 9754e12d82 | ||
|  | d72ebed246 | ||
|  | 587fc71058 | ||
|  | 7d34e09a12 | ||
|  | 7d15baad0f | ||
|  | 1e6476ada7 | ||
|  | 1d6ca5f6b5 | ||
|  | fb249f02fc | ||
|  | fdf04ef275 | ||
|  | b0f5f634f2 | ||
|  | 6ee0108565 | ||
|  | c73097e688 | ||
|  | a2d42b07b5 | ||
|  | f9a794af93 | ||
|  | a2643674ca | ||
|  | c00ce9daac | ||
|  | c68b5d7844 | ||
|  | 09ed106d4c | ||
|  | a6b92fc658 | ||
|  | 4be4a1a30b | ||
|  | 9bede5d331 | ||
|  | b50d4aee6d | ||
|  | 55bc367bd6 | ||
|  | 7a59b7eafe | ||
|  | 9e59d41e44 | ||
|  | 475e6c7bca | ||
|  | dbc5a5293e | ||
|  | f4bd2bd0d8 | ||
|  | f1782a83ba | ||
|  | cbade0d558 | ||
|  | fe0c26387c | ||
|  | c03a014740 | ||
|  | 84e6d788aa | ||
|  | cd296b8767 | ||
|  | 5946d32a7d | ||
|  | 842dd8572b | ||
|  | 33e260f9db | ||
|  | c44f8d0060 | ||
|  | 411548a33e | ||
|  | 9a17ce1383 | ||
|  | 2b120d0669 | ||
|  | 6d877d5242 | ||
|  | e4abd690e7 | ||
|  | 2a194384b6 | ||
|  | 1e069e6f8a | ||
|  | a019bb913d | ||
|  | 9ce73931f7 | ||
|  | 97020df178 | ||
|  | dfb7b3de8f | ||
|  | 9e152912fe | ||
|  | b2c2f1e1aa | ||
|  | 629c7d8e92 | ||
|  | 62f24d4356 | ||
|  | ae281c33ca | ||
|  | 5d034dd106 | ||
|  | c1f8df0eca | ||
|  | 76dda9eeda | ||
|  | a9b84a92ac | ||
|  | 0f7e881899 | ||
|  | 6045f777ab | ||
|  | 5a15044ead | ||
|  | a69d76e487 | ||
|  | 3adfcdffa8 | ||
|  | 164a5d44d9 | ||
|  | 70ddab0231 | ||
|  | 7bf009fbf6 | ||
|  | 525bc8197b | ||
|  | 24397c51a8 | ||
|  | 06b3464756 | ||
|  | bbaa90bd9a | ||
|  | 5c683cbc0f | ||
|  | 95345f18bc | ||
|  | 4c3fbe83ac | ||
|  | 7caa43d02c | ||
|  | 3d4c03c9df | ||
|  | 22e1ed35d3 | ||
|  | dabe6fd793 | ||
|  | 3cc639c8d0 | ||
|  | 22c8e0b6db | ||
|  | f3ca4293bc | ||
|  | db3e076d03 | ||
|  | 1a6759820a | ||
|  | 61c5e7b948 | ||
|  | 5fd78ef32f | ||
|  | 14ff9b827c | ||
|  | 598dc2cd71 | ||
|  | 08e1c5109f | ||
|  | 6f7a843742 | ||
|  | c9c9615bb5 | ||
|  | f98609686d | ||
|  | 5b8bbc34e8 | ||
|  | 49cb00a9ab | ||
|  | 74b240fb88 | ||
|  | 514f9d2621 | ||
|  | fa78d1092a | ||
|  | 297342092b | ||
|  | 076f263a86 | ||
|  | 0c56207abc | ||
|  | 23953d8a5a | ||
|  | c6919eb25a | ||
|  | d509604b52 | ||
|  | a74a3450eb | ||
|  | 72c5399b9d | 
| @@ -1,25 +1,25 @@ | |||||||
| version: 2 | version: 2.1 | ||||||
|  |  | ||||||
| jobs: | jobs: | ||||||
|     build: |     build: | ||||||
|         resource_class: medium+ |         resource_class: xlarge | ||||||
|         docker: |         docker: | ||||||
|             - image: nikolaik/python-nodejs:python3.7-nodejs10 |             - image: node:12 | ||||||
|         environment: |         environment: | ||||||
|             CONTRACTS_COMMIT_HASH: '9ed05f5' |             NODE_OPTIONS: '--max-old-space-size=16384' | ||||||
|         working_directory: ~/repo |         working_directory: ~/repo | ||||||
|         steps: |         steps: | ||||||
|             - checkout |             - checkout | ||||||
|             - run: echo 'export PATH=$HOME/CIRCLE_PROJECT_REPONAME/node_modules/.bin:$PATH' >> $BASH_ENV |             - run: echo 'export PATH=$HOME/CIRCLE_PROJECT_REPONAME/node_modules/.bin:$PATH' >> $BASH_ENV | ||||||
|             - run: |             - run: | ||||||
|                   name: install-yarn |                   name: install-yarn | ||||||
|                   command: npm install --force --global yarn@1.17.0 |                   command: npm install --force --global yarn@1.22.0 | ||||||
|             - run: |             - run: | ||||||
|                   name: yarn |                   name: yarn | ||||||
|                   command: yarn --frozen-lockfile --ignore-engines install || yarn --frozen-lockfile --ignore-engines install |                   command: yarn --frozen-lockfile --ignore-engines install || yarn --frozen-lockfile --ignore-engines install | ||||||
|             - setup_remote_docker |             - setup_remote_docker | ||||||
|             - run: yarn build:ci |             - run: yarn build:ci || yarn build:ci || yarn build:ci || yarn build:ci || yarn build:ci || yarn build:ci | ||||||
|             - run: yarn build:ts |             - run: yarn build:ts || yarn build:ts || yarn build:ts || yarn build:ts || yarn build:ts || yarn build:ts | ||||||
|             - save_cache: |             - save_cache: | ||||||
|                   key: repo-{{ .Environment.CIRCLE_SHA1 }} |                   key: repo-{{ .Environment.CIRCLE_SHA1 }} | ||||||
|                   paths: |                   paths: | ||||||
| @@ -31,57 +31,59 @@ jobs: | |||||||
|     test-exchange-ganache: |     test-exchange-ganache: | ||||||
|         resource_class: medium+ |         resource_class: medium+ | ||||||
|         docker: |         docker: | ||||||
|             - image: nikolaik/python-nodejs:python3.7-nodejs10 |             - image: node:12 | ||||||
|         working_directory: ~/repo |         working_directory: ~/repo | ||||||
|         steps: |         steps: | ||||||
|             - restore_cache: |             - restore_cache: | ||||||
|                   keys: |                   keys: | ||||||
|                       - repo-{{ .Environment.CIRCLE_SHA1 }} |                       - repo-{{ .Environment.CIRCLE_SHA1 }} | ||||||
|             - run: yarn wsrun test:circleci @0x/contracts-exchange |             - run: yarn wsrun -p @0x/contracts-exchange -m --serial -c test:circleci | ||||||
|     test-integrations-ganache: |     test-integrations-ganache: | ||||||
|         resource_class: medium+ |         resource_class: medium+ | ||||||
|         docker: |         docker: | ||||||
|             - image: nikolaik/python-nodejs:python3.7-nodejs10 |             - image: node:12 | ||||||
|         working_directory: ~/repo |         working_directory: ~/repo | ||||||
|         steps: |         steps: | ||||||
|             - restore_cache: |             - restore_cache: | ||||||
|                   keys: |                   keys: | ||||||
|                       - repo-{{ .Environment.CIRCLE_SHA1 }} |                       - repo-{{ .Environment.CIRCLE_SHA1 }} | ||||||
|             - run: yarn wsrun test:circleci @0x/contracts-integrations |             - run: yarn wsrun -p @0x/contracts-integrations -m --serial -c test:circleci | ||||||
|     test-contracts-staking-ganache: |     test-contracts-staking-ganache: | ||||||
|         resource_class: medium+ |         resource_class: medium+ | ||||||
|         docker: |         docker: | ||||||
|             - image: nikolaik/python-nodejs:python3.7-nodejs10 |             - image: node:12 | ||||||
|         working_directory: ~/repo |         working_directory: ~/repo | ||||||
|         steps: |         steps: | ||||||
|             - restore_cache: |             - restore_cache: | ||||||
|                   keys: |                   keys: | ||||||
|                       - repo-{{ .Environment.CIRCLE_SHA1 }} |                       - repo-{{ .Environment.CIRCLE_SHA1 }} | ||||||
|             - run: yarn wsrun test:circleci @0x/contracts-staking |             - run: yarn wsrun -p @0x/contracts-staking -m --serial -c test:circleci | ||||||
|     test-contracts-extra-ganache: |     test-contracts-extra-ganache: | ||||||
|         resource_class: medium+ |         resource_class: medium+ | ||||||
|         docker: |         docker: | ||||||
|             - image: nikolaik/python-nodejs:python3.7-nodejs10 |             - image: node:12 | ||||||
|         working_directory: ~/repo |         working_directory: ~/repo | ||||||
|         steps: |         steps: | ||||||
|             - restore_cache: |             - restore_cache: | ||||||
|                   keys: |                   keys: | ||||||
|                       - repo-{{ .Environment.CIRCLE_SHA1 }} |                       - repo-{{ .Environment.CIRCLE_SHA1 }} | ||||||
|             - run: yarn wsrun test:circleci @0x/contracts-exchange-forwarder @0x/contracts-coordinator |             - run: yarn wsrun -p @0x/contracts-exchange-forwarder -p @0x/contracts-coordinator -m --serial -c test:circleci | ||||||
|     test-contracts-rest-ganache: |     test-contracts-rest-ganache: | ||||||
|         resource_class: medium+ |         resource_class: medium+ | ||||||
|         docker: |         docker: | ||||||
|             - image: nikolaik/python-nodejs:python3.7-nodejs10 |             - image: node:12 | ||||||
|         working_directory: ~/repo |         working_directory: ~/repo | ||||||
|         steps: |         steps: | ||||||
|             - restore_cache: |             - restore_cache: | ||||||
|                   keys: |                   keys: | ||||||
|                       - repo-{{ .Environment.CIRCLE_SHA1 }} |                       - repo-{{ .Environment.CIRCLE_SHA1 }} | ||||||
|             - run: yarn wsrun test:circleci @0x/contracts-multisig @0x/contracts-utils @0x/contracts-exchange-libs @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-asset-proxy @0x/contracts-broker @0x/contracts-zero-ex |             - run: yarn wsrun -p @0x/contracts-multisig -p @0x/contracts-utils -p @0x/contracts-exchange-libs -p  @0x/contracts-erc20 -p @0x/contracts-erc721 -p @0x/contracts-erc1155 -p @0x/contracts-asset-proxy -p @0x/contracts-broker -p @0x/contracts-zero-ex -m --serial -c test:circleci | ||||||
|     test-publish: |     test-publish: | ||||||
|         resource_class: medium+ |         resource_class: large | ||||||
|  |         environment: | ||||||
|  |             NODE_OPTIONS: '--max-old-space-size=6442' | ||||||
|         docker: |         docker: | ||||||
|             - image: nikolaik/python-nodejs:python3.7-nodejs10 |             - image: node:12 | ||||||
|             - image: 0xorg/verdaccio |             - image: 0xorg/verdaccio | ||||||
|         working_directory: ~/repo |         working_directory: ~/repo | ||||||
|         steps: |         steps: | ||||||
| @@ -95,7 +97,7 @@ jobs: | |||||||
|                   path: ~/.npm/_logs |                   path: ~/.npm/_logs | ||||||
|     test-doc-generation: |     test-doc-generation: | ||||||
|         docker: |         docker: | ||||||
|             - image: nikolaik/python-nodejs:python3.7-nodejs10 |             - image: node:12 | ||||||
|         working_directory: ~/repo |         working_directory: ~/repo | ||||||
|         steps: |         steps: | ||||||
|             - restore_cache: |             - restore_cache: | ||||||
| @@ -106,18 +108,18 @@ jobs: | |||||||
|                   no_output_timeout: 1200 |                   no_output_timeout: 1200 | ||||||
|     test-rest: |     test-rest: | ||||||
|         docker: |         docker: | ||||||
|             - image: nikolaik/python-nodejs:python3.7-nodejs10 |             - image: node:12 | ||||||
|         working_directory: ~/repo |         working_directory: ~/repo | ||||||
|         steps: |         steps: | ||||||
|             - restore_cache: |             - restore_cache: | ||||||
|                   keys: |                   keys: | ||||||
|                       - repo-{{ .Environment.CIRCLE_SHA1 }} |                       - repo-{{ .Environment.CIRCLE_SHA1 }} | ||||||
|             - run: yarn wsrun test:circleci @0x/contracts-test-utils |             - run: yarn wsrun -p @0x/contracts-test-utils -m --serial -c test:circleci | ||||||
|             - run: yarn wsrun test:circleci @0x/contract-artifacts |             - run: yarn wsrun -p @0x/contract-artifacts -m --serial -c test:circleci | ||||||
|             - run: yarn wsrun test:circleci @0x/contract-wrappers-test |             - run: yarn wsrun -p @0x/contract-wrappers-test -m --serial -c test:circleci | ||||||
|             - run: yarn wsrun test:circleci @0x/migrations |             - run: yarn wsrun -p @0x/migrations -m --serial -c test:circleci | ||||||
|             - run: yarn wsrun test:circleci @0x/order-utils |             - run: yarn wsrun -p @0x/order-utils -m --serial -c test:circleci | ||||||
|             - run: yarn wsrun test:circleci @0x/asset-swapper |             - run: yarn wsrun -p @0x/asset-swapper -m --serial -c test:circleci | ||||||
|             - save_cache: |             - save_cache: | ||||||
|                   key: coverage-contract-wrappers-test-{{ .Environment.CIRCLE_SHA1 }} |                   key: coverage-contract-wrappers-test-{{ .Environment.CIRCLE_SHA1 }} | ||||||
|                   paths: |                   paths: | ||||||
| @@ -134,7 +136,7 @@ jobs: | |||||||
|         resource_class: large |         resource_class: large | ||||||
|         working_directory: ~/repo |         working_directory: ~/repo | ||||||
|         docker: |         docker: | ||||||
|             - image: nikolaik/python-nodejs:python3.7-nodejs10 |             - image: node:12 | ||||||
|         steps: |         steps: | ||||||
|             - restore_cache: |             - restore_cache: | ||||||
|                   keys: |                   keys: | ||||||
| @@ -145,7 +147,7 @@ jobs: | |||||||
|             - run: yarn diff_md_docs:ci |             - run: yarn diff_md_docs:ci | ||||||
|     submit-coverage: |     submit-coverage: | ||||||
|         docker: |         docker: | ||||||
|             - image: nikolaik/python-nodejs:python3.7-nodejs10 |             - image: node:12 | ||||||
|         working_directory: ~/repo |         working_directory: ~/repo | ||||||
|         steps: |         steps: | ||||||
|             - restore_cache: |             - restore_cache: | ||||||
|   | |||||||
							
								
								
									
										7
									
								
								.github/workflows/publish.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								.github/workflows/publish.yml
									
									
									
									
										vendored
									
									
								
							| @@ -7,6 +7,9 @@ on: | |||||||
|               description: 'required CI status' |               description: 'required CI status' | ||||||
|               default: 'success' |               default: 'success' | ||||||
|               required: true |               required: true | ||||||
|  |           prerelease: | ||||||
|  |               description: 'prerelease name' | ||||||
|  |               required: false | ||||||
|  |  | ||||||
| jobs: | jobs: | ||||||
|     publish: |     publish: | ||||||
| @@ -21,7 +24,7 @@ jobs: | |||||||
|                   (echo "::error ::${{ github.ref }} does not have a successful CI status" && false) |                   (echo "::error ::${{ github.ref }} does not have a successful CI status" && false) | ||||||
|             - uses: actions/checkout@v2 |             - uses: actions/checkout@v2 | ||||||
|               with: |               with: | ||||||
|                 ref: 'development' |                 ref: ${{ github.ref }} | ||||||
|                 fetch-depth: 0 |                 fetch-depth: 0 | ||||||
|             - uses: actions/setup-node@v1 |             - uses: actions/setup-node@v1 | ||||||
|               with: |               with: | ||||||
| @@ -41,7 +44,9 @@ jobs: | |||||||
|               env: |               env: | ||||||
|                   NPM_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }} |                   NPM_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }} | ||||||
|                   GITHUB_TOKEN: ${{ github.token }} |                   GITHUB_TOKEN: ${{ github.token }} | ||||||
|  |                   PUBLISH_PRERELEASE: ${{ github.event.inputs.prerelease }} | ||||||
|             - name: 'merge into main branch' |             - name: 'merge into main branch' | ||||||
|  |               if: github.event.inputs.prerelease == '' # unless it's a prerelease | ||||||
|               run: | |               run: | | ||||||
|                   git checkout main && \ |                   git checkout main && \ | ||||||
|                   git merge ${{ github.ref }} && \ |                   git merge ${{ github.ref }} && \ | ||||||
|   | |||||||
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -75,8 +75,9 @@ generated_docs/ | |||||||
|  |  | ||||||
| TODO.md | TODO.md | ||||||
|  |  | ||||||
| # VSCode file | # IDE file | ||||||
| .vscode | .vscode | ||||||
|  | .idea | ||||||
|  |  | ||||||
| # generated contract artifacts/ | # generated contract artifacts/ | ||||||
| contracts/broker/generated-artifacts/ | contracts/broker/generated-artifacts/ | ||||||
|   | |||||||
| @@ -3,5 +3,6 @@ | |||||||
|     "tabWidth": 4, |     "tabWidth": 4, | ||||||
|     "singleQuote": true, |     "singleQuote": true, | ||||||
|     "trailingComma": "all", |     "trailingComma": "all", | ||||||
|     "bracketSpacing": true |     "bracketSpacing": true, | ||||||
|  |     "arrowParens": "avoid" | ||||||
| } | } | ||||||
|   | |||||||
| @@ -49,7 +49,6 @@ | |||||||
| | Package | Version | | | Package | Version | | ||||||
| | ------: | :------ | | | ------: | :------ | | ||||||
|  |  | ||||||
|  |  | ||||||
| <!-- For example: | <!-- For example: | ||||||
| |             `0x.js` | 2.0.4   | | |             `0x.js` | 2.0.4   | | ||||||
| | `Exchange Contract` | v2      | | | `Exchange Contract` | v2      | | ||||||
|   | |||||||
							
								
								
									
										41
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								README.md
									
									
									
									
									
								
							| @@ -2,7 +2,7 @@ | |||||||
|  |  | ||||||
| --- | --- | ||||||
|  |  | ||||||
| [0x][website-url] is an open protocol that facilitates trustless, low friction exchange of Ethereum-based assets. For more information on how it works, check out the [0x protocol specification](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md). | [0x][website-url] is an open protocol that facilitates trustless, low friction exchange of Ethereum-based assets. For more information on how it works, check out the [0x protocol specification](https://protocol.0x.org/). | ||||||
|  |  | ||||||
| This repository is a monorepo including the 0x protocol smart contracts and numerous developer tools. Each public sub-package is independently published to NPM. | This repository is a monorepo including the 0x protocol smart contracts and numerous developer tools. Each public sub-package is independently published to NPM. | ||||||
|  |  | ||||||
| @@ -15,28 +15,18 @@ This repository is a monorepo including the 0x protocol smart contracts and nume | |||||||
|  |  | ||||||
| ## Packages | ## Packages | ||||||
|  |  | ||||||
| Visit our [developer portal](https://0x.org/docs/tools/order-utils) for a comprehensive list of core & community maintained packages. All packages maintained with this monorepo are listed below. | Visit our [developer portal](https://0x.org/docs/) for a comprehensive list of core & community maintained packages. All packages maintained with this monorepo are listed below. | ||||||
|  |  | ||||||
| ### Solidity Packages | ### Solidity Packages | ||||||
|  |  | ||||||
| These packages are all under development. See [/contracts/README.md](/contracts/README.md) for a list of deployed packages. | These packages are all under development. See [/contracts/README.md](/contracts/README.md) for a list of deployed packages. | ||||||
|  |  | ||||||
| | Package                                                             | Version                                                                                                                                     | Description                                                                                                                                                                                                                                           | | | 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-zero-ex`](/contracts/zero-ex)       | [](https://www.npmjs.com/package/@0x/contracts-zero-ex)       | The contracts used for settling trades within the protocol           | | ||||||
| | [`@0x/contracts-erc20`](/contracts/erc20)                           | [](https://www.npmjs.com/package/@0x/contracts-erc20)                           | Implementations of various ERC20 tokens                                                                                                                                                                                                               | | | [`@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-test-utils`](/contracts/test-utils) | [](https://www.npmjs.com/package/@0x/contracts-test-utils) | TypeScript/Javascript shared utilities used for testing contracts    | | ||||||
| | [`@0x/contracts-erc1155`](/contracts/erc1155)                       | [](https://www.npmjs.com/package/@0x/contracts-erc1155)                       | Implementations of various ERC1155 tokens                                                                                                                                                                                                             | | | [`@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-exchange`](/contracts/exchange)                     | [](https://www.npmjs.com/package/@0x/contracts-exchange)                     | The [`Exchange`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#exchange) contract used for settling trades within the protocol                                                                            | |  | ||||||
| | [`@0x/contracts-exchange-forwarder`](/contracts/exchange-forwarder) | [](https://www.npmjs.com/package/@0x/contracts-exchange-forwarder) | A [`Forwarder`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/forwarder-specification.md) contract used to simplify UX for interacting with the protocol                                                                      | |  | ||||||
| | [`@0x/contracts-exchange-libs`](/contracts/exchange-libs)           | [](https://www.npmjs.com/package/@0x/contracts-exchange-libs)           | Protocol specific libraries used within the [`Exchange`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#exchange) contract                                                                                 | |  | ||||||
| | [`@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                                                                                                                                                            | |  | ||||||
| | [`@0x/contracts-dev-utils`](/contracts/dev-utils)                   | [](https://www.npmjs.com/package/@0x/contracts-dev-utils)                   | A contract contains utility functions for developers (such as validating many orders using a single eth_call)                                                                                                                                         | |  | ||||||
| | [`@0x/contracts-staking`](/contracts/staking)                       | [](https://www.npmjs.com/package/@0x/contracts-staking)                       | Implements the stake-based liquidity incentives defined by [`ZEIP-31`](https://github.com/0xProject/ZEIPs/issues/31)                                                                                                                                  | |  | ||||||
|  |  | ||||||
| ### TypeScript/Javascript Packages | ### TypeScript/Javascript Packages | ||||||
|  |  | ||||||
| @@ -44,9 +34,10 @@ These packages are all under development. See [/contracts/README.md](/contracts/ | |||||||
|  |  | ||||||
| | Package                                                  | Version                                                                                                                 | Description                                                                                    | | | Package                                                  | Version                                                                                                                 | Description                                                                                    | | ||||||
| | -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | | | -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | | ||||||
|  | | [`@0x/asset-swapper`](/packages/asset-swapper)           | [](https://www.npmjs.com/package/@0x/asset-swapper)           | Package used to find and create aggregated swaps                                               | | ||||||
|  | | [`@0x/protocol-utils`](/packages/protocol-utils)         | [](https://www.npmjs.com/package/@0x/protocol-utils)         | A set of utilities for generating, parsing, signing and validating 0x orders                   | | ||||||
| | [`@0x/contract-addresses`](/packages/contract-addresses) | [](https://www.npmjs.com/package/@0x/contract-addresses) | A tiny utility library for getting known deployed contract addresses for a particular network. | | | [`@0x/contract-addresses`](/packages/contract-addresses) | [](https://www.npmjs.com/package/@0x/contract-addresses) | A tiny utility library for getting known deployed contract addresses for a particular network. | | ||||||
| | [`@0x/contract-wrappers`](/packages/contract-wrappers)   | [](https://www.npmjs.com/package/@0x/contract-wrappers)   | JS/TS wrappers for interacting with the 0x smart contracts                                     | | | [`@0x/contract-wrappers`](/packages/contract-wrappers)   | [](https://www.npmjs.com/package/@0x/contract-wrappers)   | JS/TS wrappers for interacting with the 0x smart contracts                                     | | ||||||
| | [`@0x/order-utils`](/packages/order-utils)               | [](https://www.npmjs.com/package/@0x/order-utils)               | A set of utilities for generating, parsing, signing and validating 0x orders                   | |  | ||||||
| | [`@0x/migrations`](/packages/migrations)                 | [](https://www.npmjs.com/package/@0x/migrations)                 | Migration tool for deploying 0x smart contracts on private testnets                            | | | [`@0x/migrations`](/packages/migrations)                 | [](https://www.npmjs.com/package/@0x/migrations)                 | Migration tool for deploying 0x smart contracts on private testnets                            | | ||||||
| | [`@0x/contract-artifacts`](/packages/contract-artifacts) | [](https://www.npmjs.com/package/@0x/contract-artifacts) | 0x smart contract compilation artifacts                                                        |  | | | [`@0x/contract-artifacts`](/packages/contract-artifacts) | [](https://www.npmjs.com/package/@0x/contract-artifacts) | 0x smart contract compilation artifacts                                                        |  | | ||||||
|  |  | ||||||
| @@ -92,7 +83,7 @@ yarn build | |||||||
| To build a specific package: | To build a specific package: | ||||||
|  |  | ||||||
| ```bash | ```bash | ||||||
| PKG=@0x/contract-wrappers yarn build | PKG=@0x/asset-swapper yarn build | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| To build all contracts packages: | To build all contracts packages: | ||||||
| @@ -115,7 +106,7 @@ To watch a specific package and all it's dependent packages: | |||||||
| PKG=[NPM_PACKAGE_NAME] yarn watch | PKG=[NPM_PACKAGE_NAME] yarn watch | ||||||
|  |  | ||||||
| e.g | e.g | ||||||
| PKG=@0x/contract-wrappers yarn watch | PKG=@0x/asset-swapper yarn watch | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| ### Clean | ### Clean | ||||||
| @@ -129,7 +120,7 @@ yarn clean | |||||||
| Clean a specific package | Clean a specific package | ||||||
|  |  | ||||||
| ```bash | ```bash | ||||||
| PKG=0x.js yarn clean | PKG=@0x/asset-swapper yarn clean | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| ### Rebuild | ### Rebuild | ||||||
| @@ -143,7 +134,7 @@ yarn rebuild | |||||||
| To re-build (clean & build) a specific package & it's deps: | To re-build (clean & build) a specific package & it's deps: | ||||||
|  |  | ||||||
| ```bash | ```bash | ||||||
| PKG=@0x/contract-wrappers yarn rebuild | PKG=@0x/asset-swapper yarn rebuild | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| ### Lint | ### Lint | ||||||
| @@ -157,7 +148,7 @@ yarn lint | |||||||
| Lint a specific package: | Lint a specific package: | ||||||
|  |  | ||||||
| ```bash | ```bash | ||||||
| PKG=@0x/contract-wrappers yarn lint | PKG=@0x/asset-swapper yarn lint | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| ### Run Tests | ### Run Tests | ||||||
| @@ -171,7 +162,7 @@ yarn test | |||||||
| Run a specific package's test: | Run a specific package's test: | ||||||
|  |  | ||||||
| ```bash | ```bash | ||||||
| PKG=@0x/contract-wrappers yarn test | PKG=@0x/asset-swapper yarn test | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| Run all contracts packages tests: | Run all contracts packages tests: | ||||||
|   | |||||||
| @@ -1,16 +1,3 @@ | |||||||
| #### Deployed Contract Packages |  | ||||||
|  |  | ||||||
| | Contract        | Package                                                             | Version                                                                          | Git Tag                                                                                                                                | |  | ||||||
| | --------------- | ------------------------------------------------------------------- | -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | |  | ||||||
| | AssetProxyOwner | [`@0x/contracts-multisig`](/contracts/multisig)                     | [v1.0.2](https://www.npmjs.com/package/@0x/contracts-multisig/v/1.0.2)           | [@0x/contracts-multisig@1.0.2](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-multisig@1.0.2)                     | |  | ||||||
| | ERC20Proxy      | [`@0x/contracts-asset-proxy`](/contracts/asset-proxy)               | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-asset-proxy/v/1.0.1)        | [@0x/contracts-asset-proxy@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-asset-proxy@1.0.1)               | |  | ||||||
| | ERC721Proxy     | [`@0x/contracts-asset-proxy`](/contracts/asset-proxy)               | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-asset-proxy/v/1.0.1)        | [@0x/contracts-asset-proxy@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-asset-proxy@1.0.1)               | |  | ||||||
| | Exchange        | [`@0x/contracts-exchange`](/contracts/exchange)                     | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-exchange/v/1.0.1)           | [@0x/contracts-exchange@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-exchange@1.0.1)                     | |  | ||||||
| | DutchAuction    | [`@0x/contracts-extensions`](/contracts/extensions)                 | [v1.0.2](https://www.npmjs.com/package/@0x/contracts-extensions/v/1.0.2)         | [@0x/contracts-extensions@1.0.2](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-extensions@1.0.2)                 | |  | ||||||
| | Forwarder       | [`@0x/contracts-exchange-forwarder`](/contracts/exchange-forwarder) | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-exchange-forwarder/v/1.0.1) | [@0x/contracts-exchange-forwarder@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-exchange-forwarder@1.0.1) | |  | ||||||
| | MultiAssetProxy | [`@0x/contracts-asset-proxy`](/contracts/asset-proxy)               | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-asset-proxy/v/1.0.1)        | [@0x/contracts-asset-proxy@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-asset-proxy@1.0.1)               | |  | ||||||
| | ZRXToken        | [`@0x/contracts-erc20`](/contracts/erc20)                           | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-erc20/v/1.0.1)              | [@0x/contracts-erc20@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-erc20@1.0.1)                           | |  | ||||||
|  |  | ||||||
| #### Development | #### Development | ||||||
|  |  | ||||||
| Building solidity files will update the contract artifact in `{package-name}/generated-artifacts/{contract}.json`, but does not automatically update the `contract-artifacts` or `contract-wrappers` packages, which are generated from the artifact JSON. See `contract-artifacts/README.md` for instructions on updating these packages. | Building solidity files will update the contract artifact in `{package-name}/generated-artifacts/{contract}.json`, but does not automatically update the `contract-artifacts` or `contract-wrappers` packages, which are generated from the artifact JSON. See `contract-artifacts/README.md` for instructions on updating these packages. | ||||||
|   | |||||||
| @@ -1,48 +0,0 @@ | |||||||
| # Contracts testing options |  | ||||||
|  |  | ||||||
| ## Revert stack traces |  | ||||||
|  |  | ||||||
| If you want to see helpful stack traces (incl. line number, code snippet) for smart contract reverts, run the tests with: |  | ||||||
|  |  | ||||||
| ``` |  | ||||||
| yarn test:trace |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| **Note:** This currently slows down the test runs and is therefore not enabled by default. |  | ||||||
|  |  | ||||||
| ## Backing Ethereum node |  | ||||||
|  |  | ||||||
| By default, our tests run against an in-process [Ganache](https://github.com/trufflesuite/ganache-core) instance. In order to run the tests against [Geth](https://github.com/ethereum/go-ethereum), first follow the instructions in the README for the devnet package to start the devnet Geth node. Then run: |  | ||||||
|  |  | ||||||
| ```bash |  | ||||||
| TEST_PROVIDER=geth yarn test |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| ## Code coverage |  | ||||||
|  |  | ||||||
| In order to see the Solidity code coverage output generated by `@0x/sol-coverage`, run: |  | ||||||
|  |  | ||||||
| ``` |  | ||||||
| yarn test:coverage |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| ## Gas profiler |  | ||||||
|  |  | ||||||
| In order to profile the gas costs for a specific smart contract call/transaction, you can run the tests in `profiler` mode. |  | ||||||
|  |  | ||||||
| **Note:** Traces emitted by ganache have incorrect gas costs so we recommend using Geth for profiling. |  | ||||||
|  |  | ||||||
| ``` |  | ||||||
| TEST_PROVIDER=geth yarn test:profiler |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| You'll see a warning that you need to explicitly enable and disable the profiler before and after the block of code you want to profile. |  | ||||||
|  |  | ||||||
| ```typescript |  | ||||||
| import { profiler } from './utils/profiler'; |  | ||||||
| profiler.start(); |  | ||||||
| // Some call to a smart contract |  | ||||||
| profiler.stop(); |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| Without explicitly starting and stopping the profiler, the profiler output will be too busy, and therefore unusable. |  | ||||||
| @@ -1,10 +0,0 @@ | |||||||
| # Blacklist all files |  | ||||||
| .* |  | ||||||
| * |  | ||||||
| # Whitelist lib |  | ||||||
| !lib/**/* |  | ||||||
| # Whitelist Solidity contracts |  | ||||||
| !contracts/src/**/* |  | ||||||
| # Blacklist tests in lib |  | ||||||
| /lib/test/* |  | ||||||
| # Package specific ignore |  | ||||||
| @@ -1,2 +0,0 @@ | |||||||
| # solhint can't parse `abi.decode` syntax. |  | ||||||
| contracts/src/ERC1155Proxy.sol |  | ||||||
| @@ -1,787 +0,0 @@ | |||||||
| [ |  | ||||||
|     { |  | ||||||
|         "timestamp": 1612950500, |  | ||||||
|         "version": "3.7.6", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "timestamp": 1611648096, |  | ||||||
|         "version": "3.7.5", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "timestamp": 1610510890, |  | ||||||
|         "version": "3.7.4", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "timestamp": 1609802516, |  | ||||||
|         "version": "3.7.3", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "timestamp": 1608692071, |  | ||||||
|         "version": "3.7.2", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "timestamp": 1608245516, |  | ||||||
|         "version": "3.7.1", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "version": "3.7.0", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Fix Bancor support of ETH", |  | ||||||
|                 "pr": 88 |  | ||||||
|             } |  | ||||||
|         ], |  | ||||||
|         "timestamp": 1608105788 |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "timestamp": 1607485227, |  | ||||||
|         "version": "3.6.9", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "timestamp": 1607381756, |  | ||||||
|         "version": "3.6.8", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "timestamp": 1606961263, |  | ||||||
|         "version": "3.6.7", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "timestamp": 1605763885, |  | ||||||
|         "version": "3.6.6", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "timestamp": 1605302002, |  | ||||||
|         "version": "3.6.5", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "timestamp": 1604385937, |  | ||||||
|         "version": "3.6.4", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "timestamp": 1604376968, |  | ||||||
|         "version": "3.6.3", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "timestamp": 1604355662, |  | ||||||
|         "version": "3.6.2", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "timestamp": 1603851023, |  | ||||||
|         "version": "3.6.1", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "version": "3.6.0", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Add `SwerveBridge` and `SnowSwapBridge` (duplicate of `CurveBridge`)", |  | ||||||
|                 "pr": 2707 |  | ||||||
|             } |  | ||||||
|         ], |  | ||||||
|         "timestamp": 1603833198 |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "version": "3.5.0", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Update `CurveBridge` to support more varied curves", |  | ||||||
|                 "pr": 2633 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Export DexForwarderBridgeContract", |  | ||||||
|                 "pr": 2656 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Add BancorBridge and IBancorNetwork, ", |  | ||||||
|                 "pr": 2650 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Added `MStableBridge`", |  | ||||||
|                 "pr": 2662 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Added `MooniswapBridge`", |  | ||||||
|                 "pr": 2675 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Reworked `KyberBridge`", |  | ||||||
|                 "pr": 2683 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Added `CreamBridge`", |  | ||||||
|                 "pr": 2715 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Added `ShellBridge`", |  | ||||||
|                 "pr": 2722 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Added `DODOBridge`", |  | ||||||
|                 "pr": 2701 |  | ||||||
|             } |  | ||||||
|         ], |  | ||||||
|         "timestamp": 1603265572 |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "version": "3.4.0", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Fix instability with DFB.", |  | ||||||
|                 "pr": 2616 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Add `BalancerBridge`", |  | ||||||
|                 "pr": 2613 |  | ||||||
|             } |  | ||||||
|         ], |  | ||||||
|         "timestamp": 1594788383 |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "version": "3.3.0", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Use `LibERC20Token.approveIfBelow()` in DEX bridges for for approvals.", |  | ||||||
|                 "pr": 2512 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Emit `ERC20BridgeTransfer` events in bridges.", |  | ||||||
|                 "pr": 2512 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Change names of `ERC20BridgeTransfer` args to be less ambiguous.", |  | ||||||
|                 "pr": 2524 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Added `MixinGasToken` allowing Gas Tokens to be freed", |  | ||||||
|                 "pr": 2523 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Add `DexForwaderBridge` bridge contract.", |  | ||||||
|                 "pr": 2525 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Add `UniswapV2Bridge` bridge contract.", |  | ||||||
|                 "pr": 2590 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Add Gas Token freeing to `DexForwarderBridge` contract.", |  | ||||||
|                 "pr": 2536 |  | ||||||
|             } |  | ||||||
|         ], |  | ||||||
|         "timestamp": 1592969527 |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "timestamp": 1583220306, |  | ||||||
|         "version": "3.2.5", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "timestamp": 1582837861, |  | ||||||
|         "version": "3.2.4", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "timestamp": 1582677073, |  | ||||||
|         "version": "3.2.3", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "timestamp": 1582623685, |  | ||||||
|         "version": "3.2.2", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "timestamp": 1581748629, |  | ||||||
|         "version": "3.2.1", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "version": "3.2.0", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Add more types and functions to `IDydx`", |  | ||||||
|                 "pr": 2466 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Rename `DydxBrigeAction.accountId` to `DydxBridgeAction.accountIdx`", |  | ||||||
|                 "pr": 2466 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Fix broken tests.", |  | ||||||
|                 "pr": 2462 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Remove dependency on `@0x/contracts-dev-utils`", |  | ||||||
|                 "pr": 2462 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Add asset data decoding functions", |  | ||||||
|                 "pr": 2462 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Add `setOperators()` to `IDydx`", |  | ||||||
|                 "pr": 2462 |  | ||||||
|             } |  | ||||||
|         ], |  | ||||||
|         "timestamp": 1581204851 |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "timestamp": 1580988106, |  | ||||||
|         "version": "3.1.3", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "timestamp": 1580811564, |  | ||||||
|         "version": "3.1.2", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "timestamp": 1579682890, |  | ||||||
|         "version": "3.1.1", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "version": "3.1.0", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Integration tests for DydxBridge with ERC20BridgeProxy.", |  | ||||||
|                 "pr": 2401 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Fix `UniswapBridge` token -> token transfer call.", |  | ||||||
|                 "pr": 2412 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Fix `KyberBridge` incorrect `minConversionRate` calculation.", |  | ||||||
|                 "pr": 2412 |  | ||||||
|             } |  | ||||||
|         ], |  | ||||||
|         "timestamp": 1578272714 |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "timestamp": 1576540892, |  | ||||||
|         "version": "3.0.2", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "timestamp": 1575931811, |  | ||||||
|         "version": "3.0.1", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "version": "3.0.0", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Implement `KyberBridge`.", |  | ||||||
|                 "pr": 2352 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils", |  | ||||||
|                 "pr": 2330 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "ERC20Wrapper and ERC1155ProxyWrapper constructors now require an instance of DevUtilsContract", |  | ||||||
|                 "pr": 2034 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Disallow the zero address from being made an authorized address in MixinAuthorizable, and created an archive directory that includes an old version of Ownable", |  | ||||||
|                 "pr": 2019 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Remove `LibAssetProxyIds` contract", |  | ||||||
|                 "pr": 2055 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Compile and export all contracts, artifacts, and wrappers by default", |  | ||||||
|                 "pr": 2055 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Remove unused dependency on IAuthorizable in IAssetProxy", |  | ||||||
|                 "pr": 1910 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Add `ERC20BridgeProxy`", |  | ||||||
|                 "pr": 2220 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Add `Eth2DaiBridge`", |  | ||||||
|                 "pr": 2221 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Add `UniswapBridge`", |  | ||||||
|                 "pr": 2233 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Replaced `SafeMath` with `LibSafeMath`", |  | ||||||
|                 "pr": 2254 |  | ||||||
|             } |  | ||||||
|         ], |  | ||||||
|         "timestamp": 1575296764 |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "version": "2.3.0-beta.4", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Implement `KyberBridge`.", |  | ||||||
|                 "pr": 2352 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Implement `DydxBridge`.", |  | ||||||
|                 "pr": 2365 |  | ||||||
|             } |  | ||||||
|         ], |  | ||||||
|         "timestamp": 1575290197 |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "version": "2.3.0-beta.3", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ], |  | ||||||
|         "timestamp": 1574238768 |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "version": "2.3.0-beta.2", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils", |  | ||||||
|                 "pr": 2330 |  | ||||||
|             } |  | ||||||
|         ], |  | ||||||
|         "timestamp": 1574030254 |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "version": "2.3.0-beta.1", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "ERC20Wrapper and ERC1155ProxyWrapper constructors now require an instance of DevUtilsContract", |  | ||||||
|                 "pr": 2034 |  | ||||||
|             } |  | ||||||
|         ], |  | ||||||
|         "timestamp": 1573159180 |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "version": "2.3.0-beta.0", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Disallow the zero address from being made an authorized address in MixinAuthorizable, and created an archive directory that includes an old version of Ownable", |  | ||||||
|                 "pr": 2019 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Remove `LibAssetProxyIds` contract", |  | ||||||
|                 "pr": 2055 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Compile and export all contracts, artifacts, and wrappers by default", |  | ||||||
|                 "pr": 2055 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Remove unused dependency on IAuthorizable in IAssetProxy", |  | ||||||
|                 "pr": 1910 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Add `ERC20BridgeProxy`", |  | ||||||
|                 "pr": 2220 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Add `Eth2DaiBridge`", |  | ||||||
|                 "pr": 2221 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Add `UniswapBridge`", |  | ||||||
|                 "pr": 2233 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Replaced `SafeMath` with `LibSafeMath`", |  | ||||||
|                 "pr": 2254 |  | ||||||
|             } |  | ||||||
|         ], |  | ||||||
|         "timestamp": 1570135330 |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "timestamp": 1568744790, |  | ||||||
|         "version": "2.2.8", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "timestamp": 1567521715, |  | ||||||
|         "version": "2.2.7", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "timestamp": 1566446343, |  | ||||||
|         "version": "2.2.6", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "timestamp": 1565296576, |  | ||||||
|         "version": "2.2.5", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "version": "2.2.4", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Updated calls to <contract wrapper>.deployFrom0xArtifactAsync to include artifact dependencies.", |  | ||||||
|                 "pr": 1995 |  | ||||||
|             } |  | ||||||
|         ], |  | ||||||
|         "timestamp": 1564607468 |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "timestamp": 1563957393, |  | ||||||
|         "version": "2.2.3", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "timestamp": 1563193019, |  | ||||||
|         "version": "2.2.2", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "timestamp": 1563047529, |  | ||||||
|         "version": "2.2.1", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "version": "2.2.0", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Add `LibAssetProxyIds` contract", |  | ||||||
|                 "pr": 1835 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Updated ERC1155 Asset Proxy. Less optimization. More explicit handling of edge cases.", |  | ||||||
|                 "pr": 1852 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Implement StaticCallProxy", |  | ||||||
|                 "pr": 1863 |  | ||||||
|             } |  | ||||||
|         ], |  | ||||||
|         "timestamp": 1563006338 |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "timestamp": 1558712885, |  | ||||||
|         "version": "2.1.5", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "timestamp": 1557961111, |  | ||||||
|         "version": "2.1.4", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "timestamp": 1557799313, |  | ||||||
|         "version": "2.1.3", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "version": "2.1.2", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Update tests to use contract-built-in `awaitTransactionSuccessAsync`", |  | ||||||
|                 "pr": 1797 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Make `ERC721Wrapper.setApprovalForAll()` take an owner address instead of a token ID", |  | ||||||
|                 "pr": 1819 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Automatically set unlimited proxy allowances in `ERC721.setBalancesAndAllowancesAsync()`", |  | ||||||
|                 "pr": 1819 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Add `setProxyAllowanceForAllAsync()` to `ERC1155ProxyWrapper`.", |  | ||||||
|                 "pr": 1819 |  | ||||||
|             } |  | ||||||
|         ], |  | ||||||
|         "timestamp": 1557507213 |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "version": "2.1.1", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ], |  | ||||||
|         "timestamp": 1554997931 |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "version": "2.1.0", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Run Web3ProviderEngine without excess block polling", |  | ||||||
|                 "pr": 1695 |  | ||||||
|             } |  | ||||||
|         ], |  | ||||||
|         "timestamp": 1553183790 |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "version": "2.0.0", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Do not reexport external dependencies", |  | ||||||
|                 "pr": 1682 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Add ERC1155Proxy", |  | ||||||
|                 "pr": 1661 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Bumped solidity version to ^0.5.5", |  | ||||||
|                 "pr": 1701 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "note": "Integration testing for ERC1155Proxy", |  | ||||||
|                 "pr": 1673 |  | ||||||
|             } |  | ||||||
|         ], |  | ||||||
|         "timestamp": 1553091633 |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "timestamp": 1551479279, |  | ||||||
|         "version": "1.0.9", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "timestamp": 1551299797, |  | ||||||
|         "version": "1.0.8", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "timestamp": 1551220833, |  | ||||||
|         "version": "1.0.7", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "timestamp": 1551130135, |  | ||||||
|         "version": "1.0.6", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "timestamp": 1549733923, |  | ||||||
|         "version": "1.0.5", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "version": "1.0.4", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ], |  | ||||||
|         "timestamp": 1549547375 |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "version": "1.0.3", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Fake publish to enable pinning" |  | ||||||
|             } |  | ||||||
|         ], |  | ||||||
|         "timestamp": 1549504360 |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "timestamp": 1549452781, |  | ||||||
|         "version": "1.0.2", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "timestamp": 1549373905, |  | ||||||
|         "version": "1.0.1", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Dependencies updated" |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "version": "1.0.0", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Move all AssetProxy contracts out of contracts-protocol to new package", |  | ||||||
|                 "pr": 1539 |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     } |  | ||||||
| ] |  | ||||||
| @@ -1,310 +0,0 @@ | |||||||
| <!-- |  | ||||||
| changelogUtils.file is auto-generated using the monorepo-scripts package. Don't edit directly. |  | ||||||
| Edit the package's CHANGELOG.json file only. |  | ||||||
| --> |  | ||||||
|  |  | ||||||
| CHANGELOG |  | ||||||
|  |  | ||||||
| ## v3.7.6 - _February 10, 2021_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v3.7.5 - _January 26, 2021_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v3.7.4 - _January 13, 2021_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v3.7.3 - _January 4, 2021_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v3.7.2 - _December 23, 2020_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v3.7.1 - _December 17, 2020_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v3.7.0 - _December 16, 2020_ |  | ||||||
|  |  | ||||||
|     * Fix Bancor support of ETH (#88) |  | ||||||
|  |  | ||||||
| ## v3.6.9 - _December 9, 2020_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v3.6.8 - _December 7, 2020_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v3.6.7 - _December 3, 2020_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v3.6.6 - _November 19, 2020_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v3.6.5 - _November 13, 2020_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v3.6.4 - _November 3, 2020_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v3.6.3 - _November 3, 2020_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v3.6.2 - _November 2, 2020_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v3.6.1 - _October 28, 2020_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v3.6.0 - _October 27, 2020_ |  | ||||||
|  |  | ||||||
|     * Add `SwerveBridge` and `SnowSwapBridge` (duplicate of `CurveBridge`) (#2707) |  | ||||||
|  |  | ||||||
| ## v3.5.0 - _October 21, 2020_ |  | ||||||
|  |  | ||||||
|     * Update `CurveBridge` to support more varied curves (#2633) |  | ||||||
|     * Export DexForwarderBridgeContract (#2656) |  | ||||||
|     * Add BancorBridge and IBancorNetwork,  (#2650) |  | ||||||
|     * Added `MStableBridge` (#2662) |  | ||||||
|     * Added `MooniswapBridge` (#2675) |  | ||||||
|     * Reworked `KyberBridge` (#2683) |  | ||||||
|     * Added `CreamBridge` (#2715) |  | ||||||
|     * Added `ShellBridge` (#2722) |  | ||||||
|     * Added `DODOBridge` (#2701) |  | ||||||
|  |  | ||||||
| ## v3.4.0 - _July 15, 2020_ |  | ||||||
|  |  | ||||||
|     * Fix instability with DFB. (#2616) |  | ||||||
|     * Add `BalancerBridge` (#2613) |  | ||||||
|  |  | ||||||
| ## v3.3.0 - _June 24, 2020_ |  | ||||||
|  |  | ||||||
|     * Use `LibERC20Token.approveIfBelow()` in DEX bridges for for approvals. (#2512) |  | ||||||
|     * Emit `ERC20BridgeTransfer` events in bridges. (#2512) |  | ||||||
|     * Change names of `ERC20BridgeTransfer` args to be less ambiguous. (#2524) |  | ||||||
|     * Added `MixinGasToken` allowing Gas Tokens to be freed (#2523) |  | ||||||
|     * Add `DexForwaderBridge` bridge contract. (#2525) |  | ||||||
|     * Add `UniswapV2Bridge` bridge contract. (#2590) |  | ||||||
|     * Add Gas Token freeing to `DexForwarderBridge` contract. (#2536) |  | ||||||
|  |  | ||||||
| ## v3.2.5 - _March 3, 2020_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v3.2.4 - _February 27, 2020_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v3.2.3 - _February 26, 2020_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v3.2.2 - _February 25, 2020_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v3.2.1 - _February 15, 2020_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v3.2.0 - _February 8, 2020_ |  | ||||||
|  |  | ||||||
|     * Add more types and functions to `IDydx` (#2466) |  | ||||||
|     * Rename `DydxBrigeAction.accountId` to `DydxBridgeAction.accountIdx` (#2466) |  | ||||||
|     * Fix broken tests. (#2462) |  | ||||||
|     * Remove dependency on `@0x/contracts-dev-utils` (#2462) |  | ||||||
|     * Add asset data decoding functions (#2462) |  | ||||||
|     * Add `setOperators()` to `IDydx` (#2462) |  | ||||||
|  |  | ||||||
| ## v3.1.3 - _February 6, 2020_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v3.1.2 - _February 4, 2020_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v3.1.1 - _January 22, 2020_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v3.1.0 - _January 6, 2020_ |  | ||||||
|  |  | ||||||
|     * Integration tests for DydxBridge with ERC20BridgeProxy. (#2401) |  | ||||||
|     * Fix `UniswapBridge` token -> token transfer call. (#2412) |  | ||||||
|     * Fix `KyberBridge` incorrect `minConversionRate` calculation. (#2412) |  | ||||||
|  |  | ||||||
| ## v3.0.2 - _December 17, 2019_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v3.0.1 - _December 9, 2019_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v3.0.0 - _December 2, 2019_ |  | ||||||
|  |  | ||||||
|     * Implement `KyberBridge`. (#2352) |  | ||||||
|     * Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330) |  | ||||||
|     * ERC20Wrapper and ERC1155ProxyWrapper constructors now require an instance of DevUtilsContract (#2034) |  | ||||||
|     * Disallow the zero address from being made an authorized address in MixinAuthorizable, and created an archive directory that includes an old version of Ownable (#2019) |  | ||||||
|     * Remove `LibAssetProxyIds` contract (#2055) |  | ||||||
|     * Compile and export all contracts, artifacts, and wrappers by default (#2055) |  | ||||||
|     * Remove unused dependency on IAuthorizable in IAssetProxy (#1910) |  | ||||||
|     * Add `ERC20BridgeProxy` (#2220) |  | ||||||
|     * Add `Eth2DaiBridge` (#2221) |  | ||||||
|     * Add `UniswapBridge` (#2233) |  | ||||||
|     * Replaced `SafeMath` with `LibSafeMath` (#2254) |  | ||||||
|  |  | ||||||
| ## v2.3.0-beta.4 - _December 2, 2019_ |  | ||||||
|  |  | ||||||
|     * Implement `KyberBridge`. (#2352) |  | ||||||
|     * Implement `DydxBridge`. (#2365) |  | ||||||
|  |  | ||||||
| ## v2.3.0-beta.3 - _November 20, 2019_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v2.3.0-beta.2 - _November 17, 2019_ |  | ||||||
|  |  | ||||||
|     * Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330) |  | ||||||
|  |  | ||||||
| ## v2.3.0-beta.1 - _November 7, 2019_ |  | ||||||
|  |  | ||||||
|     * ERC20Wrapper and ERC1155ProxyWrapper constructors now require an instance of DevUtilsContract (#2034) |  | ||||||
|  |  | ||||||
| ## v2.3.0-beta.0 - _October 3, 2019_ |  | ||||||
|  |  | ||||||
|     * Disallow the zero address from being made an authorized address in MixinAuthorizable, and created an archive directory that includes an old version of Ownable (#2019) |  | ||||||
|     * Remove `LibAssetProxyIds` contract (#2055) |  | ||||||
|     * Compile and export all contracts, artifacts, and wrappers by default (#2055) |  | ||||||
|     * Remove unused dependency on IAuthorizable in IAssetProxy (#1910) |  | ||||||
|     * Add `ERC20BridgeProxy` (#2220) |  | ||||||
|     * Add `Eth2DaiBridge` (#2221) |  | ||||||
|     * Add `UniswapBridge` (#2233) |  | ||||||
|     * Replaced `SafeMath` with `LibSafeMath` (#2254) |  | ||||||
|  |  | ||||||
| ## v2.2.8 - _September 17, 2019_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v2.2.7 - _September 3, 2019_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v2.2.6 - _August 22, 2019_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v2.2.5 - _August 8, 2019_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v2.2.4 - _July 31, 2019_ |  | ||||||
|  |  | ||||||
|     * Updated calls to <contract wrapper>.deployFrom0xArtifactAsync to include artifact dependencies. (#1995) |  | ||||||
|  |  | ||||||
| ## v2.2.3 - _July 24, 2019_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v2.2.2 - _July 15, 2019_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v2.2.1 - _July 13, 2019_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v2.2.0 - _July 13, 2019_ |  | ||||||
|  |  | ||||||
|     * Add `LibAssetProxyIds` contract (#1835) |  | ||||||
|     * Updated ERC1155 Asset Proxy. Less optimization. More explicit handling of edge cases. (#1852) |  | ||||||
|     * Implement StaticCallProxy (#1863) |  | ||||||
|  |  | ||||||
| ## v2.1.5 - _May 24, 2019_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v2.1.4 - _May 15, 2019_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v2.1.3 - _May 14, 2019_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v2.1.2 - _May 10, 2019_ |  | ||||||
|  |  | ||||||
|     * Update tests to use contract-built-in `awaitTransactionSuccessAsync` (#1797) |  | ||||||
|     * Make `ERC721Wrapper.setApprovalForAll()` take an owner address instead of a token ID (#1819) |  | ||||||
|     * Automatically set unlimited proxy allowances in `ERC721.setBalancesAndAllowancesAsync()` (#1819) |  | ||||||
|     * Add `setProxyAllowanceForAllAsync()` to `ERC1155ProxyWrapper`. (#1819) |  | ||||||
|  |  | ||||||
| ## v2.1.1 - _April 11, 2019_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v2.1.0 - _March 21, 2019_ |  | ||||||
|  |  | ||||||
|     * Run Web3ProviderEngine without excess block polling (#1695) |  | ||||||
|  |  | ||||||
| ## v2.0.0 - _March 20, 2019_ |  | ||||||
|  |  | ||||||
|     * Do not reexport external dependencies (#1682) |  | ||||||
|     * Add ERC1155Proxy (#1661) |  | ||||||
|     * Bumped solidity version to ^0.5.5 (#1701) |  | ||||||
|     * Integration testing for ERC1155Proxy (#1673) |  | ||||||
|  |  | ||||||
| ## v1.0.9 - _March 1, 2019_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v1.0.8 - _February 27, 2019_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v1.0.7 - _February 26, 2019_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v1.0.6 - _February 25, 2019_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v1.0.5 - _February 9, 2019_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v1.0.4 - _February 7, 2019_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v1.0.3 - _February 7, 2019_ |  | ||||||
|  |  | ||||||
|     * Fake publish to enable pinning |  | ||||||
|  |  | ||||||
| ## v1.0.2 - _February 6, 2019_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v1.0.1 - _February 5, 2019_ |  | ||||||
|  |  | ||||||
|     * Dependencies updated |  | ||||||
|  |  | ||||||
| ## v1.0.0 - _Invalid date_ |  | ||||||
|  |  | ||||||
|     * Move all AssetProxy contracts out of contracts-protocol to new package (#1539) |  | ||||||
| @@ -1,47 +0,0 @@ | |||||||
| [ |  | ||||||
|     { |  | ||||||
|         "name": "MultiAssetProxy", |  | ||||||
|         "version": "1.0.0", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "Add MultiAssetProxy implementation", |  | ||||||
|                 "pr": 1224, |  | ||||||
|                 "networks": { |  | ||||||
|                     "3": "0xab8fbd189c569ccdee3a4d929bb7f557be4028f6", |  | ||||||
|                     "4": "0xb34cde0ad3a83d04abebc0b66e75196f22216621", |  | ||||||
|                     "42": "0xf6313a772c222f51c28f2304c0703b8cf5428fd8" |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "name": "ERC20Proxy", |  | ||||||
|         "version": "1.0.0", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "protocol v2 deploy", |  | ||||||
|                 "networks": { |  | ||||||
|                     "1": "0x2240dab907db71e64d3e0dba4800c83b5c502d4e", |  | ||||||
|                     "3": "0xb1408f4c245a23c31b98d2c626777d4c0d766caa", |  | ||||||
|                     "4": "0x3e809c563c15a295e832e37053798ddc8d6c8dab", |  | ||||||
|                     "42": "0xf1ec01d6236d3cd881a0bf0130ea25fe4234003e" |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "name": "ERC721Proxy", |  | ||||||
|         "version": "1.0.0", |  | ||||||
|         "changes": [ |  | ||||||
|             { |  | ||||||
|                 "note": "protocol v2 deploy", |  | ||||||
|                 "networks": { |  | ||||||
|                     "1": "0x208e41fb445f1bb1b6780d58356e81405f3e6127", |  | ||||||
|                     "3": "0xe654aac058bfbf9f83fcaee7793311dd82f6ddb4", |  | ||||||
|                     "4": "0x8e1ff02637cb5e39f2fa36c14706aa348b065b09", |  | ||||||
|                     "42": "0x2a9127c745688a165106c11cd4d647d2220af821" |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     } |  | ||||||
| ] |  | ||||||
| @@ -1,73 +0,0 @@ | |||||||
| ## AssetProxy |  | ||||||
|  |  | ||||||
| This package contains the implementations of all of the [`AssetProxy`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#assetproxy) contracts available within the 0x protocol. These contracts are responsible for decoding the `assetData` sent to them and performing the actual transfer of assets. Addresses of the deployed contracts can be found in this 0x [guide](https://0x.org/docs/guides/0x-cheat-sheet) or the [DEPLOYS](./DEPLOYS.json) file within this package. |  | ||||||
|  |  | ||||||
| ## Installation |  | ||||||
|  |  | ||||||
| **Install** |  | ||||||
|  |  | ||||||
| ```bash |  | ||||||
| npm install @0x/contracts-asset-proxy --save |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| ## Bug bounty |  | ||||||
|  |  | ||||||
| A bug bounty for the 2.0.0 contracts is ongoing! Instructions can be found [here](https://0x.org/docs/guides/bug-bounty-program). |  | ||||||
|  |  | ||||||
| ## Contributing |  | ||||||
|  |  | ||||||
| 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-asset-proxy yarn build |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| Or continuously rebuild on change: |  | ||||||
|  |  | ||||||
| ```bash |  | ||||||
| PKG=@0x/contracts-asset-proxy 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). |  | ||||||
| @@ -1,27 +0,0 @@ | |||||||
| { |  | ||||||
|     "artifactsDir": "./test/generated-artifacts", |  | ||||||
|     "contractsDir": "./contracts", |  | ||||||
|     "useDockerisedSolc": false, |  | ||||||
|     "isOfflineMode": false, |  | ||||||
|     "shouldSaveStandardInput": true, |  | ||||||
|     "compilerSettings": { |  | ||||||
|         "evmVersion": "istanbul", |  | ||||||
|         "optimizer": { |  | ||||||
|             "enabled": true, |  | ||||||
|             "runs": 1000000, |  | ||||||
|             "details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true } |  | ||||||
|         }, |  | ||||||
|         "outputSelection": { |  | ||||||
|             "*": { |  | ||||||
|                 "*": [ |  | ||||||
|                     "abi", |  | ||||||
|                     "devdoc", |  | ||||||
|                     "evm.bytecode.object", |  | ||||||
|                     "evm.bytecode.sourceMap", |  | ||||||
|                     "evm.deployedBytecode.object", |  | ||||||
|                     "evm.deployedBytecode.sourceMap" |  | ||||||
|                 ] |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,172 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2019 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
|  |  | ||||||
| import "../archive/Ownable.sol"; |  | ||||||
| import "../src/interfaces/IAssetProxy.sol"; |  | ||||||
| import "../src/interfaces/IAssetProxyDispatcher.sol"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| contract MixinAssetProxyDispatcher is |  | ||||||
|     Ownable, |  | ||||||
|     IAssetProxyDispatcher |  | ||||||
| { |  | ||||||
|     // Mapping from Asset Proxy Id's to their respective Asset Proxy |  | ||||||
|     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. |  | ||||||
|     /// @param assetProxy Address of new asset proxy to register. |  | ||||||
|     function registerAssetProxy(address assetProxy) |  | ||||||
|         external |  | ||||||
|         onlyOwner |  | ||||||
|     { |  | ||||||
|         // Ensure that no asset proxy exists with current id. |  | ||||||
|         bytes4 assetProxyId = IAssetProxy(assetProxy).getProxyId(); |  | ||||||
|         address currentAssetProxy = assetProxies[assetProxyId]; |  | ||||||
|         require( |  | ||||||
|             currentAssetProxy == address(0), |  | ||||||
|             "ASSET_PROXY_ALREADY_EXISTS" |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         // Add asset proxy and log registration. |  | ||||||
|         assetProxies[assetProxyId] = assetProxy; |  | ||||||
|         emit AssetProxyRegistered( |  | ||||||
|             assetProxyId, |  | ||||||
|             assetProxy |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Gets an asset proxy. |  | ||||||
|     /// @param assetProxyId Id of the asset proxy. |  | ||||||
|     /// @return The asset proxy registered to assetProxyId. Returns 0x0 if no proxy is registered. |  | ||||||
|     function getAssetProxy(bytes4 assetProxyId) |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (address) |  | ||||||
|     { |  | ||||||
|         return assetProxies[assetProxyId]; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Forwards arguments to assetProxy and calls `transferFrom`. Either succeeds or throws. |  | ||||||
|     /// @param assetData Byte array encoded for the asset. |  | ||||||
|     /// @param from Address to transfer token from. |  | ||||||
|     /// @param to Address to transfer token to. |  | ||||||
|     /// @param amount Amount of token to transfer. |  | ||||||
|     function _dispatchTransferFrom( |  | ||||||
|         bytes memory assetData, |  | ||||||
|         address from, |  | ||||||
|         address to, |  | ||||||
|         uint256 amount |  | ||||||
|     ) |  | ||||||
|         internal |  | ||||||
|     { |  | ||||||
|         // Do nothing if no amount should be transferred. |  | ||||||
|         if (amount > 0 && from != to) { |  | ||||||
|             // Ensure assetData length is valid |  | ||||||
|             require( |  | ||||||
|                 assetData.length > 3, |  | ||||||
|                 "LENGTH_GREATER_THAN_3_REQUIRED" |  | ||||||
|             ); |  | ||||||
|  |  | ||||||
|             // Lookup assetProxy. We do not use `LibBytes.readBytes4` for gas efficiency reasons. |  | ||||||
|             bytes4 assetProxyId; |  | ||||||
|             assembly { |  | ||||||
|                 assetProxyId := and(mload( |  | ||||||
|                     add(assetData, 32)), |  | ||||||
|                     0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 |  | ||||||
|                 ) |  | ||||||
|             } |  | ||||||
|             address assetProxy = assetProxies[assetProxyId]; |  | ||||||
|  |  | ||||||
|             // Ensure that assetProxy exists |  | ||||||
|             require( |  | ||||||
|                 assetProxy != address(0), |  | ||||||
|                 "ASSET_PROXY_DOES_NOT_EXIST" |  | ||||||
|             ); |  | ||||||
|  |  | ||||||
|             // We construct calldata for the `assetProxy.transferFrom` ABI. |  | ||||||
|             // The layout of this calldata is in the table below. |  | ||||||
|             // |  | ||||||
|             // | Area     | Offset | Length  | Contents                                    | |  | ||||||
|             // | -------- |--------|---------|-------------------------------------------- | |  | ||||||
|             // | Header   | 0      | 4       | function selector                           | |  | ||||||
|             // | Params   |        | 4 * 32  | function parameters:                        | |  | ||||||
|             // |          | 4      |         |   1. offset to assetData (*)                | |  | ||||||
|             // |          | 36     |         |   2. from                                   | |  | ||||||
|             // |          | 68     |         |   3. to                                     | |  | ||||||
|             // |          | 100    |         |   4. amount                                 | |  | ||||||
|             // | Data     |        |         | assetData:                                  | |  | ||||||
|             // |          | 132    | 32      | assetData Length                            | |  | ||||||
|             // |          | 164    | **      | assetData Contents                          | |  | ||||||
|  |  | ||||||
|             assembly { |  | ||||||
|                 /////// Setup State /////// |  | ||||||
|                 // `cdStart` is the start of the calldata for `assetProxy.transferFrom` (equal to free memory ptr). |  | ||||||
|                 let cdStart := mload(64) |  | ||||||
|                 // `dataAreaLength` is the total number of words needed to store `assetData` |  | ||||||
|                 //  As-per the ABI spec, this value is padded up to the nearest multiple of 32, |  | ||||||
|                 //  and includes 32-bytes for length. |  | ||||||
|                 let dataAreaLength := and(add(mload(assetData), 63), 0xFFFFFFFFFFFE0) |  | ||||||
|                 // `cdEnd` is the end of the calldata for `assetProxy.transferFrom`. |  | ||||||
|                 let cdEnd := add(cdStart, add(132, dataAreaLength)) |  | ||||||
|  |  | ||||||
|  |  | ||||||
|                 /////// Setup Header Area /////// |  | ||||||
|                 // This area holds the 4-byte `transferFromSelector`. |  | ||||||
|                 // bytes4(keccak256("transferFrom(bytes,address,address,uint256)")) = 0xa85e59e4 |  | ||||||
|                 mstore(cdStart, 0xa85e59e400000000000000000000000000000000000000000000000000000000) |  | ||||||
|  |  | ||||||
|                 /////// Setup Params Area /////// |  | ||||||
|                 // Each parameter is padded to 32-bytes. The entire Params Area is 128 bytes. |  | ||||||
|                 // Notes: |  | ||||||
|                 //   1. The offset to `assetData` is the length of the Params Area (128 bytes). |  | ||||||
|                 //   2. A 20-byte mask is applied to addresses to zero-out the unused bytes. |  | ||||||
|                 mstore(add(cdStart, 4), 128) |  | ||||||
|                 mstore(add(cdStart, 36), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) |  | ||||||
|                 mstore(add(cdStart, 68), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) |  | ||||||
|                 mstore(add(cdStart, 100), amount) |  | ||||||
|  |  | ||||||
|                 /////// Setup Data Area /////// |  | ||||||
|                 // This area holds `assetData`. |  | ||||||
|                 let dataArea := add(cdStart, 132) |  | ||||||
|                 // solhint-disable-next-line no-empty-blocks |  | ||||||
|                 for {} lt(dataArea, cdEnd) {} { |  | ||||||
|                     mstore(dataArea, mload(assetData)) |  | ||||||
|                     dataArea := add(dataArea, 32) |  | ||||||
|                     assetData := add(assetData, 32) |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 /////// Call `assetProxy.transferFrom` using the constructed calldata /////// |  | ||||||
|                 let success := call( |  | ||||||
|                     gas,                    // forward all gas |  | ||||||
|                     assetProxy,             // call address of asset proxy |  | ||||||
|                     0,                      // don't send any ETH |  | ||||||
|                     cdStart,                // pointer to start of input |  | ||||||
|                     sub(cdEnd, cdStart),    // length of input |  | ||||||
|                     cdStart,                // write output over input |  | ||||||
|                     512                     // reserve 512 bytes for output |  | ||||||
|                 ) |  | ||||||
|                 if iszero(success) { |  | ||||||
|                     revert(cdStart, returndatasize()) |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,117 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2019 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
|  |  | ||||||
| import "../archive/Ownable.sol"; |  | ||||||
| import "../src/interfaces/IAuthorizable.sol"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| contract MixinAuthorizable is |  | ||||||
|     Ownable, |  | ||||||
|     IAuthorizable |  | ||||||
| { |  | ||||||
|     /// @dev Only authorized addresses can invoke functions with this modifier. |  | ||||||
|     modifier onlyAuthorized { |  | ||||||
|         require( |  | ||||||
|             authorized[msg.sender], |  | ||||||
|             "SENDER_NOT_AUTHORIZED" |  | ||||||
|         ); |  | ||||||
|         _; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     mapping (address => bool) public authorized; |  | ||||||
|     address[] public authorities; |  | ||||||
|  |  | ||||||
|     /// @dev Authorizes an address. |  | ||||||
|     /// @param target Address to authorize. |  | ||||||
|     function addAuthorizedAddress(address target) |  | ||||||
|         external |  | ||||||
|         onlyOwner |  | ||||||
|     { |  | ||||||
|         require( |  | ||||||
|             !authorized[target], |  | ||||||
|             "TARGET_ALREADY_AUTHORIZED" |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         authorized[target] = true; |  | ||||||
|         authorities.push(target); |  | ||||||
|         emit AuthorizedAddressAdded(target, msg.sender); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Removes authorizion of an address. |  | ||||||
|     /// @param target Address to remove authorization from. |  | ||||||
|     function removeAuthorizedAddress(address target) |  | ||||||
|         external |  | ||||||
|         onlyOwner |  | ||||||
|     { |  | ||||||
|         require( |  | ||||||
|             authorized[target], |  | ||||||
|             "TARGET_NOT_AUTHORIZED" |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         delete authorized[target]; |  | ||||||
|         for (uint256 i = 0; i < authorities.length; i++) { |  | ||||||
|             if (authorities[i] == target) { |  | ||||||
|                 authorities[i] = authorities[authorities.length - 1]; |  | ||||||
|                 authorities.length -= 1; |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         emit AuthorizedAddressRemoved(target, msg.sender); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Removes authorizion of an address. |  | ||||||
|     /// @param target Address to remove authorization from. |  | ||||||
|     /// @param index Index of target in authorities array. |  | ||||||
|     function removeAuthorizedAddressAtIndex( |  | ||||||
|         address target, |  | ||||||
|         uint256 index |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         onlyOwner |  | ||||||
|     { |  | ||||||
|         require( |  | ||||||
|             authorized[target], |  | ||||||
|             "TARGET_NOT_AUTHORIZED" |  | ||||||
|         ); |  | ||||||
|         require( |  | ||||||
|             index < authorities.length, |  | ||||||
|             "INDEX_OUT_OF_BOUNDS" |  | ||||||
|         ); |  | ||||||
|         require( |  | ||||||
|             authorities[index] == target, |  | ||||||
|             "AUTHORIZED_ADDRESS_MISMATCH" |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         delete authorized[target]; |  | ||||||
|         authorities[index] = authorities[authorities.length - 1]; |  | ||||||
|         authorities.length -= 1; |  | ||||||
|         emit AuthorizedAddressRemoved(target, msg.sender); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Gets all authorized addresses. |  | ||||||
|     /// @return Array of authorized addresses. |  | ||||||
|     function getAuthorizedAddresses() |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (address[] memory) |  | ||||||
|     { |  | ||||||
|         return authorities; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,33 +0,0 @@ | |||||||
| pragma solidity ^0.5.9; |  | ||||||
|  |  | ||||||
| import "@0x/contracts-utils/contracts/src/interfaces/IOwnable.sol"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| contract Ownable is |  | ||||||
|     IOwnable |  | ||||||
| { |  | ||||||
|     address public owner; |  | ||||||
|  |  | ||||||
|     constructor () |  | ||||||
|         public |  | ||||||
|     { |  | ||||||
|         owner = msg.sender; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     modifier onlyOwner() { |  | ||||||
|         require( |  | ||||||
|             msg.sender == owner, |  | ||||||
|             "ONLY_CONTRACT_OWNER" |  | ||||||
|         ); |  | ||||||
|         _; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function transferOwnership(address newOwner) |  | ||||||
|         public |  | ||||||
|         onlyOwner |  | ||||||
|     { |  | ||||||
|         if (newOwner != address(0)) { |  | ||||||
|             owner = newOwner; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,97 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2019 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
|  |  | ||||||
| import "@0x/contracts-utils/contracts/src/LibBytes.sol"; |  | ||||||
| import "@0x/contracts-utils/contracts/src/LibSafeMath.sol"; |  | ||||||
| import "@0x/contracts-erc1155/contracts/src/interfaces/IERC1155.sol"; |  | ||||||
| import "../archive/MixinAuthorizable.sol"; |  | ||||||
| import "./interfaces/IAssetProxy.sol"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| contract ERC1155Proxy is |  | ||||||
|     MixinAuthorizable, |  | ||||||
|     IAssetProxy |  | ||||||
| { |  | ||||||
|     using LibBytes for bytes; |  | ||||||
|     using LibSafeMath for uint256; |  | ||||||
|  |  | ||||||
|     // Id of this proxy. |  | ||||||
|     bytes4 constant internal PROXY_ID = bytes4(keccak256("ERC1155Assets(address,uint256[],uint256[],bytes)")); |  | ||||||
|  |  | ||||||
|     /// @dev Transfers batch of ERC1155 assets. Either succeeds or throws. |  | ||||||
|     /// @param assetData Byte array encoded with ERC1155 token address, array of ids, array of values, and callback data. |  | ||||||
|     /// @param from Address to transfer assets from. |  | ||||||
|     /// @param to Address to transfer assets to. |  | ||||||
|     /// @param amount Amount that will be multiplied with each element of `assetData.values` to scale the |  | ||||||
|     ///        values that will be transferred. |  | ||||||
|     function transferFrom( |  | ||||||
|         bytes calldata assetData, |  | ||||||
|         address from, |  | ||||||
|         address to, |  | ||||||
|         uint256 amount |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         onlyAuthorized |  | ||||||
|     { |  | ||||||
|         // Decode params from `assetData` |  | ||||||
|         // solhint-disable indent |  | ||||||
|         ( |  | ||||||
|             address erc1155TokenAddress, |  | ||||||
|             uint256[] memory ids, |  | ||||||
|             uint256[] memory values, |  | ||||||
|             bytes memory data |  | ||||||
|         ) = abi.decode( |  | ||||||
|             assetData.sliceDestructive(4, assetData.length), |  | ||||||
|             (address, uint256[], uint256[], bytes) |  | ||||||
|         ); |  | ||||||
|         // solhint-enable indent |  | ||||||
|  |  | ||||||
|         // Scale values up by `amount` |  | ||||||
|         uint256 length = values.length; |  | ||||||
|         uint256[] memory scaledValues = new uint256[](length); |  | ||||||
|         for (uint256 i = 0; i != length; i++) { |  | ||||||
|             // We write the scaled values to an unused location in memory in order |  | ||||||
|             // to avoid copying over `ids` or `data`. This is possible if they are |  | ||||||
|             // identical to `values` and the offsets for each are pointing to the |  | ||||||
|             // same location in the ABI encoded calldata. |  | ||||||
|             scaledValues[i] = values[i].safeMul(amount); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // Execute `safeBatchTransferFrom` call |  | ||||||
|         // Either succeeds or throws |  | ||||||
|         IERC1155(erc1155TokenAddress).safeBatchTransferFrom( |  | ||||||
|             from, |  | ||||||
|             to, |  | ||||||
|             ids, |  | ||||||
|             scaledValues, |  | ||||||
|             data |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Gets the proxy id associated with the proxy address. |  | ||||||
|     /// @return Proxy id. |  | ||||||
|     function getProxyId() |  | ||||||
|         external |  | ||||||
|         pure |  | ||||||
|         returns (bytes4) |  | ||||||
|     { |  | ||||||
|         return PROXY_ID; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,126 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2019 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
| pragma experimental ABIEncoderV2; |  | ||||||
|  |  | ||||||
| import "@0x/contracts-utils/contracts/src/LibBytes.sol"; |  | ||||||
| import "@0x/contracts-utils/contracts/src/LibSafeMath.sol"; |  | ||||||
| import "@0x/contracts-utils/contracts/src/Authorizable.sol"; |  | ||||||
| import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; |  | ||||||
| import "./interfaces/IAssetProxy.sol"; |  | ||||||
| import "./interfaces/IERC20Bridge.sol"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| contract ERC20BridgeProxy is |  | ||||||
|     IAssetProxy, |  | ||||||
|     Authorizable |  | ||||||
| { |  | ||||||
|     using LibBytes for bytes; |  | ||||||
|     using LibSafeMath for uint256; |  | ||||||
|  |  | ||||||
|     // @dev Id of this proxy. Also the result of a successful bridge call. |  | ||||||
|     //      bytes4(keccak256("ERC20Bridge(address,address,bytes)")) |  | ||||||
|     bytes4 constant private PROXY_ID = 0xdc1600f3; |  | ||||||
|  |  | ||||||
|     /// @dev Calls a bridge contract to transfer `amount` of ERC20 from `from` |  | ||||||
|     ///      to `to`. Asserts that the balance of `to` has increased by `amount`. |  | ||||||
|     /// @param assetData Abi-encoded data for this asset proxy encoded as: |  | ||||||
|     ///          abi.encodeWithSelector( |  | ||||||
|     ///             bytes4 PROXY_ID, |  | ||||||
|     ///             address tokenAddress, |  | ||||||
|     ///             address bridgeAddress, |  | ||||||
|     ///             bytes bridgeData |  | ||||||
|     ///          ) |  | ||||||
|     /// @param from Address to transfer asset from. |  | ||||||
|     /// @param to Address to transfer asset to. |  | ||||||
|     /// @param amount Amount of asset to transfer. |  | ||||||
|     function transferFrom( |  | ||||||
|         bytes calldata assetData, |  | ||||||
|         address from, |  | ||||||
|         address to, |  | ||||||
|         uint256 amount |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         onlyAuthorized |  | ||||||
|     { |  | ||||||
|         // Extract asset data fields. |  | ||||||
|         ( |  | ||||||
|             address tokenAddress, |  | ||||||
|             address bridgeAddress, |  | ||||||
|             bytes memory bridgeData |  | ||||||
|         ) = abi.decode( |  | ||||||
|             assetData.sliceDestructive(4, assetData.length), |  | ||||||
|             (address, address, bytes) |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         // Remember the balance of `to` before calling the bridge. |  | ||||||
|         uint256 balanceBefore = balanceOf(tokenAddress, to); |  | ||||||
|         // Call the bridge, who should transfer `amount` of `tokenAddress` to |  | ||||||
|         // `to`. |  | ||||||
|         bytes4 success = IERC20Bridge(bridgeAddress).bridgeTransferFrom( |  | ||||||
|             tokenAddress, |  | ||||||
|             from, |  | ||||||
|             to, |  | ||||||
|             amount, |  | ||||||
|             bridgeData |  | ||||||
|         ); |  | ||||||
|         // Bridge must return the proxy ID to indicate success. |  | ||||||
|         require(success == PROXY_ID, "BRIDGE_FAILED"); |  | ||||||
|         // Ensure that the balance of `to` has increased by at least `amount`. |  | ||||||
|         require( |  | ||||||
|             balanceBefore.safeAdd(amount) <= balanceOf(tokenAddress, to), |  | ||||||
|             "BRIDGE_UNDERPAY" |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Gets the proxy id associated with this asset proxy. |  | ||||||
|     /// @return proxyId The proxy id. |  | ||||||
|     function getProxyId() |  | ||||||
|         external |  | ||||||
|         pure |  | ||||||
|         returns (bytes4 proxyId) |  | ||||||
|     { |  | ||||||
|         return PROXY_ID; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Retrieves the balance of `owner` for this asset. |  | ||||||
|     /// @return balance The balance of the ERC20 token being transferred by this |  | ||||||
|     ///         asset proxy. |  | ||||||
|     function balanceOf(bytes calldata assetData, address owner) |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (uint256 balance) |  | ||||||
|     { |  | ||||||
|         (address tokenAddress) = abi.decode( |  | ||||||
|             assetData.sliceDestructive(4, assetData.length), |  | ||||||
|             (address) |  | ||||||
|         ); |  | ||||||
|         return balanceOf(tokenAddress, owner); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Retrieves the balance of `owner` given an ERC20 address. |  | ||||||
|     /// @return balance The balance of the ERC20 token for `owner`. |  | ||||||
|     function balanceOf(address tokenAddress, address owner) |  | ||||||
|         private |  | ||||||
|         view |  | ||||||
|         returns (uint256 balance) |  | ||||||
|     { |  | ||||||
|         return IERC20Token(tokenAddress).balanceOf(owner); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,184 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2019 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
|  |  | ||||||
| import "../archive/MixinAuthorizable.sol"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| contract ERC20Proxy is |  | ||||||
|     MixinAuthorizable |  | ||||||
| { |  | ||||||
|     // Id of this proxy. |  | ||||||
|     bytes4 constant internal PROXY_ID = bytes4(keccak256("ERC20Token(address)")); |  | ||||||
|  |  | ||||||
|     // solhint-disable-next-line payable-fallback |  | ||||||
|     function () |  | ||||||
|         external |  | ||||||
|     { |  | ||||||
|         assembly { |  | ||||||
|             // The first 4 bytes of calldata holds the function selector |  | ||||||
|             let selector := and(calldataload(0), 0xffffffff00000000000000000000000000000000000000000000000000000000) |  | ||||||
|  |  | ||||||
|             // `transferFrom` will be called with the following parameters: |  | ||||||
|             // assetData Encoded byte array. |  | ||||||
|             // from Address to transfer asset from. |  | ||||||
|             // to Address to transfer asset to. |  | ||||||
|             // amount Amount of asset to transfer. |  | ||||||
|             // bytes4(keccak256("transferFrom(bytes,address,address,uint256)")) = 0xa85e59e4 |  | ||||||
|             if eq(selector, 0xa85e59e400000000000000000000000000000000000000000000000000000000) { |  | ||||||
|  |  | ||||||
|                 // 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 |  | ||||||
|                 let start := mload(64) |  | ||||||
|                 mstore(start, and(caller, 0xffffffffffffffffffffffffffffffffffffffff)) |  | ||||||
|                 mstore(add(start, 32), authorized_slot) |  | ||||||
|  |  | ||||||
|                 // Revert if authorized[msg.sender] == false |  | ||||||
|                 if iszero(sload(keccak256(start, 64))) { |  | ||||||
|                     // Revert with `Error("SENDER_NOT_AUTHORIZED")` |  | ||||||
|                     mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) |  | ||||||
|                     mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) |  | ||||||
|                     mstore(64, 0x0000001553454e4445525f4e4f545f415554484f52495a454400000000000000) |  | ||||||
|                     mstore(96, 0) |  | ||||||
|                     revert(0, 100) |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 // `transferFrom`. |  | ||||||
|                 // The function is marked `external`, so no abi decodeding is done for |  | ||||||
|                 // us. Instead, we expect the `calldata` memory to contain the |  | ||||||
|                 // following: |  | ||||||
|                 // |  | ||||||
|                 // | Area     | Offset | Length  | Contents                            | |  | ||||||
|                 // |----------|--------|---------|-------------------------------------| |  | ||||||
|                 // | Header   | 0      | 4       | function selector                   | |  | ||||||
|                 // | Params   |        | 4 * 32  | function parameters:                | |  | ||||||
|                 // |          | 4      |         |   1. offset to assetData (*)        | |  | ||||||
|                 // |          | 36     |         |   2. from                           | |  | ||||||
|                 // |          | 68     |         |   3. to                             | |  | ||||||
|                 // |          | 100    |         |   4. amount                         | |  | ||||||
|                 // | Data     |        |         | assetData:                          | |  | ||||||
|                 // |          | 132    | 32      | assetData Length                    | |  | ||||||
|                 // |          | 164    | **      | assetData Contents                  | |  | ||||||
|                 // |  | ||||||
|                 // (*): offset is computed from start of function parameters, so offset |  | ||||||
|                 //      by an additional 4 bytes in the calldata. |  | ||||||
|                 // |  | ||||||
|                 // (**): see table below to compute length of assetData Contents |  | ||||||
|                 // |  | ||||||
|                 // WARNING: The ABIv2 specification allows additional padding between |  | ||||||
|                 //          the Params and Data section. This will result in a larger |  | ||||||
|                 //          offset to assetData. |  | ||||||
|  |  | ||||||
|                 // Asset data itself is encoded as follows: |  | ||||||
|                 // |  | ||||||
|                 // | Area     | Offset | Length  | Contents                            | |  | ||||||
|                 // |----------|--------|---------|-------------------------------------| |  | ||||||
|                 // | Header   | 0      | 4       | function selector                   | |  | ||||||
|                 // | Params   |        | 1 * 32  | function parameters:                | |  | ||||||
|                 // |          | 4      | 12 + 20 |   1. token address                  | |  | ||||||
|  |  | ||||||
|                 // We construct calldata for the `token.transferFrom` ABI. |  | ||||||
|                 // The layout of this calldata is in the table below. |  | ||||||
|                 // |  | ||||||
|                 // | Area     | Offset | Length  | Contents                            | |  | ||||||
|                 // |----------|--------|---------|-------------------------------------| |  | ||||||
|                 // | Header   | 0      | 4       | function selector                   | |  | ||||||
|                 // | Params   |        | 3 * 32  | function parameters:                | |  | ||||||
|                 // |          | 4      |         |   1. from                           | |  | ||||||
|                 // |          | 36     |         |   2. to                             | |  | ||||||
|                 // |          | 68     |         |   3. amount                         | |  | ||||||
|  |  | ||||||
|                 /////// Read token address from calldata /////// |  | ||||||
|                 // * The token address is stored in `assetData`. |  | ||||||
|                 // |  | ||||||
|                 // * The "offset to assetData" is stored at offset 4 in the calldata (table 1). |  | ||||||
|                 //   [assetDataOffsetFromParams = calldataload(4)] |  | ||||||
|                 // |  | ||||||
|                 // * Notes that the "offset to assetData" is relative to the "Params" area of calldata; |  | ||||||
|                 //   add 4 bytes to account for the length of the "Header" area (table 1). |  | ||||||
|                 //   [assetDataOffsetFromHeader = assetDataOffsetFromParams + 4] |  | ||||||
|                 // |  | ||||||
|                 // * The "token address" is offset 32+4=36 bytes into "assetData" (tables 1 & 2). |  | ||||||
|                 //   [tokenOffset = assetDataOffsetFromHeader + 36 = calldataload(4) + 4 + 36] |  | ||||||
|                 let token := calldataload(add(calldataload(4), 40)) |  | ||||||
|  |  | ||||||
|                 /////// Setup Header Area /////// |  | ||||||
|                 // This area holds the 4-byte `transferFrom` selector. |  | ||||||
|                 // Any trailing data in transferFromSelector will be |  | ||||||
|                 // overwritten in the next `mstore` call. |  | ||||||
|                 mstore(0, 0x23b872dd00000000000000000000000000000000000000000000000000000000) |  | ||||||
|  |  | ||||||
|                 /////// Setup Params Area /////// |  | ||||||
|                 // We copy the fields `from`, `to` and `amount` in bulk |  | ||||||
|                 // from our own calldata to the new calldata. |  | ||||||
|                 calldatacopy(4, 36, 96) |  | ||||||
|  |  | ||||||
|                 /////// Call `token.transferFrom` using the calldata /////// |  | ||||||
|                 let success := call( |  | ||||||
|                     gas,            // forward all gas |  | ||||||
|                     token,          // call address of token contract |  | ||||||
|                     0,              // don't send any ETH |  | ||||||
|                     0,              // pointer to start of input |  | ||||||
|                     100,            // length of input |  | ||||||
|                     0,              // write output over input |  | ||||||
|                     32              // output size should be 32 bytes |  | ||||||
|                 ) |  | ||||||
|  |  | ||||||
|                 /////// Check return data. /////// |  | ||||||
|                 // If there is no return data, we assume the token incorrectly |  | ||||||
|                 // does not return a bool. In this case we expect it to revert |  | ||||||
|                 // on failure, which was handled above. |  | ||||||
|                 // If the token does return data, we require that it is a single |  | ||||||
|                 // nonzero 32 bytes value. |  | ||||||
|                 // So the transfer succeeded if the call succeeded and either |  | ||||||
|                 // returned nothing, or returned a non-zero 32 byte value. |  | ||||||
|                 success := and(success, or( |  | ||||||
|                     iszero(returndatasize), |  | ||||||
|                     and( |  | ||||||
|                         eq(returndatasize, 32), |  | ||||||
|                         gt(mload(0), 0) |  | ||||||
|                     ) |  | ||||||
|                 )) |  | ||||||
|                 if success { |  | ||||||
|                     return(0, 0) |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 // Revert with `Error("TRANSFER_FAILED")` |  | ||||||
|                 mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) |  | ||||||
|                 mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) |  | ||||||
|                 mstore(64, 0x0000000f5452414e534645525f4641494c454400000000000000000000000000) |  | ||||||
|                 mstore(96, 0) |  | ||||||
|                 revert(0, 100) |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             // 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; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,171 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2019 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
|  |  | ||||||
| import "../archive/MixinAuthorizable.sol"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| contract ERC721Proxy is |  | ||||||
|     MixinAuthorizable |  | ||||||
| { |  | ||||||
|     // Id of this proxy. |  | ||||||
|     bytes4 constant internal PROXY_ID = bytes4(keccak256("ERC721Token(address,uint256)")); |  | ||||||
|  |  | ||||||
|     // solhint-disable-next-line payable-fallback |  | ||||||
|     function () |  | ||||||
|         external |  | ||||||
|     { |  | ||||||
|         assembly { |  | ||||||
|             // The first 4 bytes of calldata holds the function selector |  | ||||||
|             let selector := and(calldataload(0), 0xffffffff00000000000000000000000000000000000000000000000000000000) |  | ||||||
|  |  | ||||||
|             // `transferFrom` will be called with the following parameters: |  | ||||||
|             // assetData Encoded byte array. |  | ||||||
|             // from Address to transfer asset from. |  | ||||||
|             // to Address to transfer asset to. |  | ||||||
|             // amount Amount of asset to transfer. |  | ||||||
|             // bytes4(keccak256("transferFrom(bytes,address,address,uint256)")) = 0xa85e59e4 |  | ||||||
|             if eq(selector, 0xa85e59e400000000000000000000000000000000000000000000000000000000) { |  | ||||||
|  |  | ||||||
|                 // 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 |  | ||||||
|                 let start := mload(64) |  | ||||||
|                 mstore(start, and(caller, 0xffffffffffffffffffffffffffffffffffffffff)) |  | ||||||
|                 mstore(add(start, 32), authorized_slot) |  | ||||||
|  |  | ||||||
|                 // Revert if authorized[msg.sender] == false |  | ||||||
|                 if iszero(sload(keccak256(start, 64))) { |  | ||||||
|                     // Revert with `Error("SENDER_NOT_AUTHORIZED")` |  | ||||||
|                     mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) |  | ||||||
|                     mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) |  | ||||||
|                     mstore(64, 0x0000001553454e4445525f4e4f545f415554484f52495a454400000000000000) |  | ||||||
|                     mstore(96, 0) |  | ||||||
|                     revert(0, 100) |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 // `transferFrom`. |  | ||||||
|                 // The function is marked `external`, so no abi decodeding is done for |  | ||||||
|                 // us. Instead, we expect the `calldata` memory to contain the |  | ||||||
|                 // following: |  | ||||||
|                 // |  | ||||||
|                 // | Area     | Offset | Length  | Contents                            | |  | ||||||
|                 // |----------|--------|---------|-------------------------------------| |  | ||||||
|                 // | Header   | 0      | 4       | function selector                   | |  | ||||||
|                 // | Params   |        | 4 * 32  | function parameters:                | |  | ||||||
|                 // |          | 4      |         |   1. offset to assetData (*)        | |  | ||||||
|                 // |          | 36     |         |   2. from                           | |  | ||||||
|                 // |          | 68     |         |   3. to                             | |  | ||||||
|                 // |          | 100    |         |   4. amount                         | |  | ||||||
|                 // | Data     |        |         | assetData:                          | |  | ||||||
|                 // |          | 132    | 32      | assetData Length                    | |  | ||||||
|                 // |          | 164    | **      | assetData Contents                  | |  | ||||||
|                 // |  | ||||||
|                 // (*): offset is computed from start of function parameters, so offset |  | ||||||
|                 //      by an additional 4 bytes in the calldata. |  | ||||||
|                 // |  | ||||||
|                 // (**): see table below to compute length of assetData Contents |  | ||||||
|                 // |  | ||||||
|                 // WARNING: The ABIv2 specification allows additional padding between |  | ||||||
|                 //          the Params and Data section. This will result in a larger |  | ||||||
|                 //          offset to assetData. |  | ||||||
|  |  | ||||||
|                 // Asset data itself is encoded as follows: |  | ||||||
|                 // |  | ||||||
|                 // | Area     | Offset | Length  | Contents                            | |  | ||||||
|                 // |----------|--------|---------|-------------------------------------| |  | ||||||
|                 // | Header   | 0      | 4       | function selector                   | |  | ||||||
|                 // | Params   |        | 2 * 32  | function parameters:                | |  | ||||||
|                 // |          | 4      | 12 + 20 |   1. token address                  | |  | ||||||
|                 // |          | 36     |         |   2. tokenId                        | |  | ||||||
|  |  | ||||||
|                 // We construct calldata for the `token.transferFrom` ABI. |  | ||||||
|                 // The layout of this calldata is in the table below. |  | ||||||
|                 // |  | ||||||
|                 // | Area     | Offset | Length  | Contents                            | |  | ||||||
|                 // |----------|--------|---------|-------------------------------------| |  | ||||||
|                 // | Header   | 0      | 4       | function selector                   | |  | ||||||
|                 // | Params   |        | 3 * 32  | function parameters:                | |  | ||||||
|                 // |          | 4      |         |   1. from                           | |  | ||||||
|                 // |          | 36     |         |   2. to                             | |  | ||||||
|                 // |          | 68     |         |   3. tokenId                        | |  | ||||||
|  |  | ||||||
|                 // There exists only 1 of each token. |  | ||||||
|                 // require(amount == 1, "INVALID_AMOUNT") |  | ||||||
|                 if sub(calldataload(100), 1) { |  | ||||||
|                     // Revert with `Error("INVALID_AMOUNT")` |  | ||||||
|                     mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) |  | ||||||
|                     mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) |  | ||||||
|                     mstore(64, 0x0000000e494e56414c49445f414d4f554e540000000000000000000000000000) |  | ||||||
|                     mstore(96, 0) |  | ||||||
|                     revert(0, 100) |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 /////// Setup Header Area /////// |  | ||||||
|                 // This area holds the 4-byte `transferFrom` selector. |  | ||||||
|                 // Any trailing data in transferFromSelector will be |  | ||||||
|                 // overwritten in the next `mstore` call. |  | ||||||
|                 mstore(0, 0x23b872dd00000000000000000000000000000000000000000000000000000000) |  | ||||||
|  |  | ||||||
|                 /////// Setup Params Area /////// |  | ||||||
|                 // We copy the fields `from` and `to` in bulk |  | ||||||
|                 // from our own calldata to the new calldata. |  | ||||||
|                 calldatacopy(4, 36, 64) |  | ||||||
|  |  | ||||||
|                 // Copy `tokenId` field from our own calldata to the new calldata. |  | ||||||
|                 let assetDataOffset := calldataload(4) |  | ||||||
|                 calldatacopy(68, add(assetDataOffset, 72), 32) |  | ||||||
|  |  | ||||||
|                 /////// Call `token.transferFrom` using the calldata /////// |  | ||||||
|                 let token := calldataload(add(assetDataOffset, 40)) |  | ||||||
|                 let success := call( |  | ||||||
|                     gas,            // forward all gas |  | ||||||
|                     token,          // call address of token contract |  | ||||||
|                     0,              // don't send any ETH |  | ||||||
|                     0,              // pointer to start of input |  | ||||||
|                     100,            // length of input |  | ||||||
|                     0,              // write output to null |  | ||||||
|                     0               // output size is 0 bytes |  | ||||||
|                 ) |  | ||||||
|                 if success { |  | ||||||
|                     return(0, 0) |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 // Revert with `Error("TRANSFER_FAILED")` |  | ||||||
|                 mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) |  | ||||||
|                 mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) |  | ||||||
|                 mstore(64, 0x0000000f5452414e534645525f4641494c454400000000000000000000000000) |  | ||||||
|                 mstore(96, 0) |  | ||||||
|                 revert(0, 100) |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             // 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; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,335 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2019 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
|  |  | ||||||
| import "../archive/MixinAssetProxyDispatcher.sol"; |  | ||||||
| import "../archive/MixinAuthorizable.sol"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| contract MultiAssetProxy is |  | ||||||
|     MixinAssetProxyDispatcher, |  | ||||||
|     MixinAuthorizable |  | ||||||
| { |  | ||||||
|     // Id of this proxy. |  | ||||||
|     bytes4 constant internal PROXY_ID = bytes4(keccak256("MultiAsset(uint256[],bytes[])")); |  | ||||||
|  |  | ||||||
|     // solhint-disable-next-line payable-fallback |  | ||||||
|     function () |  | ||||||
|         external |  | ||||||
|     { |  | ||||||
|         // NOTE: The below assembly assumes that clients do some input validation and that the input is properly encoded according to the AbiV2 specification. |  | ||||||
|         // It is technically possible for inputs with very large lengths and offsets to cause overflows. However, this would make the calldata prohibitively |  | ||||||
|         // expensive and we therefore do not check for overflows in these scenarios. |  | ||||||
|         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) |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 // `transferFrom`. |  | ||||||
|                 // The function is marked `external`, so no abi decoding is done for |  | ||||||
|                 // us. Instead, we expect the `calldata` memory to contain the |  | ||||||
|                 // following: |  | ||||||
|                 // |  | ||||||
|                 // | Area     | Offset | Length  | Contents                            | |  | ||||||
|                 // |----------|--------|---------|-------------------------------------| |  | ||||||
|                 // | Header   | 0      | 4       | function selector                   | |  | ||||||
|                 // | Params   |        | 4 * 32  | function parameters:                | |  | ||||||
|                 // |          | 4      |         |   1. offset to assetData (*)        | |  | ||||||
|                 // |          | 36     |         |   2. from                           | |  | ||||||
|                 // |          | 68     |         |   3. to                             | |  | ||||||
|                 // |          | 100    |         |   4. amount                         | |  | ||||||
|                 // | Data     |        |         | assetData:                          | |  | ||||||
|                 // |          | 132    | 32      | assetData Length                    | |  | ||||||
|                 // |          | 164    | **      | assetData Contents                  | |  | ||||||
|                 // |  | ||||||
|                 // (*): offset is computed from start of function parameters, so offset |  | ||||||
|                 //      by an additional 4 bytes in the calldata. |  | ||||||
|                 // |  | ||||||
|                 // (**): see table below to compute length of assetData Contents |  | ||||||
|                 // |  | ||||||
|                 // WARNING: The ABIv2 specification allows additional padding between |  | ||||||
|                 //          the Params and Data section. This will result in a larger |  | ||||||
|                 //          offset to assetData. |  | ||||||
|  |  | ||||||
|                 // Load offset to `assetData` |  | ||||||
|                 let assetDataOffset := add(calldataload(4), 4) |  | ||||||
|  |  | ||||||
|                 // Load length in bytes of `assetData` |  | ||||||
|                 let assetDataLength := calldataload(assetDataOffset) |  | ||||||
|  |  | ||||||
|                 // Asset data itself is encoded as follows: |  | ||||||
|                 // |  | ||||||
|                 // | Area     | Offset      | Length  | Contents                            | |  | ||||||
|                 // |----------|-------------|---------|-------------------------------------| |  | ||||||
|                 // | Header   | 0           | 4       | assetProxyId                        | |  | ||||||
|                 // | Params   |             | 2 * 32  | function parameters:                | |  | ||||||
|                 // |          | 4           |         |   1. offset to amounts (*)          | |  | ||||||
|                 // |          | 36          |         |   2. offset to nestedAssetData (*)  | |  | ||||||
|                 // | Data     |             |         | amounts:                            | |  | ||||||
|                 // |          | 68          | 32      | amounts Length                      | |  | ||||||
|                 // |          | 100         | a       | amounts Contents                    | |  | ||||||
|                 // |          |             |         | nestedAssetData:                    | |  | ||||||
|                 // |          | 100 + a     | 32      | nestedAssetData Length              | |  | ||||||
|                 // |          | 132 + a     | b       | nestedAssetData Contents (offsets)  | |  | ||||||
|                 // |          | 132 + a + b |         | nestedAssetData[0, ..., len]        | |  | ||||||
|  |  | ||||||
|                 // Assert that the length of asset data: |  | ||||||
|                 // 1. Must be at least 68 bytes (see table above) |  | ||||||
|                 // 2. Must be a multiple of 32 (excluding the 4-byte selector) |  | ||||||
|                 if or(lt(assetDataLength, 68), mod(sub(assetDataLength, 4), 32)) { |  | ||||||
|                     // Revert with `Error("INVALID_ASSET_DATA_LENGTH")` |  | ||||||
|                     mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) |  | ||||||
|                     mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) |  | ||||||
|                     mstore(64, 0x00000019494e56414c49445f41535345545f444154415f4c454e475448000000) |  | ||||||
|                     mstore(96, 0) |  | ||||||
|                     revert(0, 100) |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 // End of asset data in calldata |  | ||||||
|                 // assetDataOffset |  | ||||||
|                 // + 32 (assetData len) |  | ||||||
|                 let assetDataEnd := add(assetDataOffset, add(assetDataLength, 32)) |  | ||||||
|                 if gt(assetDataEnd, calldatasize()) { |  | ||||||
|                     // Revert with `Error("INVALID_ASSET_DATA_END")` |  | ||||||
|                     mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) |  | ||||||
|                     mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) |  | ||||||
|                     mstore(64, 0x00000016494e56414c49445f41535345545f444154415f454e44000000000000) |  | ||||||
|                     mstore(96, 0) |  | ||||||
|                     revert(0, 100) |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 // In order to find the offset to `amounts`, we must add: |  | ||||||
|                 // assetDataOffset |  | ||||||
|                 // + 32 (assetData len) |  | ||||||
|                 // + 4 (assetProxyId) |  | ||||||
|                 let amountsOffset := calldataload(add(assetDataOffset, 36)) |  | ||||||
|  |  | ||||||
|                 // In order to find the offset to `nestedAssetData`, we must add: |  | ||||||
|                 // assetDataOffset |  | ||||||
|                 // + 32 (assetData len) |  | ||||||
|                 // + 4 (assetProxyId) |  | ||||||
|                 // + 32 (amounts offset) |  | ||||||
|                 let nestedAssetDataOffset := calldataload(add(assetDataOffset, 68)) |  | ||||||
|  |  | ||||||
|                 // In order to find the start of the `amounts` contents, we must add: |  | ||||||
|                 // assetDataOffset |  | ||||||
|                 // + 32 (assetData len) |  | ||||||
|                 // + 4 (assetProxyId) |  | ||||||
|                 // + amountsOffset |  | ||||||
|                 // + 32 (amounts len) |  | ||||||
|                 let amountsContentsStart := add(assetDataOffset, add(amountsOffset, 68)) |  | ||||||
|  |  | ||||||
|                 // Load number of elements in `amounts` |  | ||||||
|                 let amountsLen := calldataload(sub(amountsContentsStart, 32)) |  | ||||||
|  |  | ||||||
|                 // In order to find the start of the `nestedAssetData` contents, we must add: |  | ||||||
|                 // assetDataOffset |  | ||||||
|                 // + 32 (assetData len) |  | ||||||
|                 // + 4 (assetProxyId) |  | ||||||
|                 // + nestedAssetDataOffset |  | ||||||
|                 // + 32 (nestedAssetData len) |  | ||||||
|                 let nestedAssetDataContentsStart := add(assetDataOffset, add(nestedAssetDataOffset, 68)) |  | ||||||
|  |  | ||||||
|                 // Load number of elements in `nestedAssetData` |  | ||||||
|                 let nestedAssetDataLen := calldataload(sub(nestedAssetDataContentsStart, 32)) |  | ||||||
|  |  | ||||||
|                 // Revert if number of elements in `amounts` differs from number of elements in `nestedAssetData` |  | ||||||
|                 if sub(amountsLen, nestedAssetDataLen) { |  | ||||||
|                     // Revert with `Error("LENGTH_MISMATCH")` |  | ||||||
|                     mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) |  | ||||||
|                     mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) |  | ||||||
|                     mstore(64, 0x0000000f4c454e4754485f4d49534d4154434800000000000000000000000000) |  | ||||||
|                     mstore(96, 0) |  | ||||||
|                     revert(0, 100) |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 // Copy `transferFrom` selector, offset to `assetData`, `from`, and `to` from calldata to memory |  | ||||||
|                 calldatacopy( |  | ||||||
|                     0,   // memory can safely be overwritten from beginning |  | ||||||
|                     0,   // start of calldata |  | ||||||
|                     100  // length of selector (4) and 3 params (32 * 3) |  | ||||||
|                 ) |  | ||||||
|  |  | ||||||
|                 // Overwrite existing offset to `assetData` with our own |  | ||||||
|                 mstore(4, 128) |  | ||||||
|  |  | ||||||
|                 // Load `amount` |  | ||||||
|                 let amount := calldataload(100) |  | ||||||
|  |  | ||||||
|                 // Calculate number of bytes in `amounts` contents |  | ||||||
|                 let amountsByteLen := mul(amountsLen, 32) |  | ||||||
|  |  | ||||||
|                 // Initialize `assetProxyId` and `assetProxy` to 0 |  | ||||||
|                 let assetProxyId := 0 |  | ||||||
|                 let assetProxy := 0 |  | ||||||
|  |  | ||||||
|                 // Loop through `amounts` and `nestedAssetData`, calling `transferFrom` for each respective element |  | ||||||
|                 for {let i := 0} lt(i, amountsByteLen) {i := add(i, 32)} { |  | ||||||
|  |  | ||||||
|                     // Calculate the total amount |  | ||||||
|                     let amountsElement := calldataload(add(amountsContentsStart, i)) |  | ||||||
|                     let totalAmount := mul(amountsElement, amount) |  | ||||||
|  |  | ||||||
|                     // Revert if `amount` != 0 and multiplication resulted in an overflow |  | ||||||
|                     if iszero(or( |  | ||||||
|                         iszero(amount), |  | ||||||
|                         eq(div(totalAmount, amount), amountsElement) |  | ||||||
|                     )) { |  | ||||||
|                         // Revert with `Error("UINT256_OVERFLOW")` |  | ||||||
|                         mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) |  | ||||||
|                         mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) |  | ||||||
|                         mstore(64, 0x0000001055494e543235365f4f564552464c4f57000000000000000000000000) |  | ||||||
|                         mstore(96, 0) |  | ||||||
|                         revert(0, 100) |  | ||||||
|                     } |  | ||||||
|  |  | ||||||
|                     // Write `totalAmount` to memory |  | ||||||
|                     mstore(100, totalAmount) |  | ||||||
|  |  | ||||||
|                     // Load offset to `nestedAssetData[i]` |  | ||||||
|                     let nestedAssetDataElementOffset := calldataload(add(nestedAssetDataContentsStart, i)) |  | ||||||
|  |  | ||||||
|                     // In order to find the start of the `nestedAssetData[i]` contents, we must add: |  | ||||||
|                     // assetDataOffset |  | ||||||
|                     // + 32 (assetData len) |  | ||||||
|                     // + 4 (assetProxyId) |  | ||||||
|                     // + nestedAssetDataOffset |  | ||||||
|                     // + 32 (nestedAssetData len) |  | ||||||
|                     // + nestedAssetDataElementOffset |  | ||||||
|                     // + 32 (nestedAssetDataElement len) |  | ||||||
|                     let nestedAssetDataElementContentsStart := add( |  | ||||||
|                         assetDataOffset, |  | ||||||
|                         add( |  | ||||||
|                             nestedAssetDataOffset, |  | ||||||
|                             add(nestedAssetDataElementOffset, 100) |  | ||||||
|                         ) |  | ||||||
|                     ) |  | ||||||
|  |  | ||||||
|                     // Load length of `nestedAssetData[i]` |  | ||||||
|                     let nestedAssetDataElementLenStart := sub(nestedAssetDataElementContentsStart, 32) |  | ||||||
|                     let nestedAssetDataElementLen := calldataload(nestedAssetDataElementLenStart) |  | ||||||
|  |  | ||||||
|                     // Revert if the `nestedAssetData` does not contain a 4 byte `assetProxyId` |  | ||||||
|                     if lt(nestedAssetDataElementLen, 4) { |  | ||||||
|                         // Revert with `Error("LENGTH_GREATER_THAN_3_REQUIRED")` |  | ||||||
|                         mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) |  | ||||||
|                         mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) |  | ||||||
|                         mstore(64, 0x0000001e4c454e4754485f475245415445525f5448414e5f335f524551554952) |  | ||||||
|                         mstore(96, 0x4544000000000000000000000000000000000000000000000000000000000000) |  | ||||||
|                         revert(0, 100) |  | ||||||
|                     } |  | ||||||
|  |  | ||||||
|                     // Load AssetProxy id |  | ||||||
|                     let currentAssetProxyId := and( |  | ||||||
|                         calldataload(nestedAssetDataElementContentsStart), |  | ||||||
|                         0xffffffff00000000000000000000000000000000000000000000000000000000 |  | ||||||
|                     ) |  | ||||||
|  |  | ||||||
|                     // Only load `assetProxy` if `currentAssetProxyId` does not equal `assetProxyId` |  | ||||||
|                     // We do not need to check if `currentAssetProxyId` is 0 since `assetProxy` is also initialized to 0 |  | ||||||
|                     if sub(currentAssetProxyId, assetProxyId) { |  | ||||||
|                         // Update `assetProxyId` |  | ||||||
|                         assetProxyId := currentAssetProxyId |  | ||||||
|                         // 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(132, assetProxyId) |  | ||||||
|                         mstore(164, assetProxies_slot) |  | ||||||
|                         assetProxy := sload(keccak256(132, 64)) |  | ||||||
|                     } |  | ||||||
|  |  | ||||||
|                     // Revert if AssetProxy with given id does not exist |  | ||||||
|                     if iszero(assetProxy) { |  | ||||||
|                         // Revert with `Error("ASSET_PROXY_DOES_NOT_EXIST")` |  | ||||||
|                         mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) |  | ||||||
|                         mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) |  | ||||||
|                         mstore(64, 0x0000001a41535345545f50524f58595f444f45535f4e4f545f45584953540000) |  | ||||||
|                         mstore(96, 0) |  | ||||||
|                         revert(0, 100) |  | ||||||
|                     } |  | ||||||
|  |  | ||||||
|                     // Copy `nestedAssetData[i]` from calldata to memory |  | ||||||
|                     calldatacopy( |  | ||||||
|                         132,                                // memory slot after `amounts[i]` |  | ||||||
|                         nestedAssetDataElementLenStart,     // location of `nestedAssetData[i]` in calldata |  | ||||||
|                         add(nestedAssetDataElementLen, 32)  // `nestedAssetData[i].length` plus 32 byte length |  | ||||||
|                     ) |  | ||||||
|  |  | ||||||
|                     // call `assetProxy.transferFrom` |  | ||||||
|                     let success := call( |  | ||||||
|                         gas,                                    // forward all gas |  | ||||||
|                         assetProxy,                             // call address of asset proxy |  | ||||||
|                         0,                                      // don't send any ETH |  | ||||||
|                         0,                                      // pointer to start of input |  | ||||||
|                         add(164, nestedAssetDataElementLen),    // length of input |  | ||||||
|                         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 no `transferFrom` calls reverted |  | ||||||
|                 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; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,83 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2019 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
|  |  | ||||||
| import "@0x/contracts-utils/contracts/src/LibBytes.sol"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| // solhint-disable no-unused-vars |  | ||||||
| contract StaticCallProxy { |  | ||||||
|  |  | ||||||
|     using LibBytes for bytes; |  | ||||||
|  |  | ||||||
|     // Id of this proxy. |  | ||||||
|     bytes4 constant internal PROXY_ID = bytes4(keccak256("StaticCall(address,bytes,bytes32)")); |  | ||||||
|  |  | ||||||
|     /// @dev Makes a staticcall to a target address and verifies that the data returned matches the expected return data. |  | ||||||
|     /// @param assetData Byte array encoded with staticCallTarget, staticCallData, and expectedCallResultHash |  | ||||||
|     /// @param from This value is ignored. |  | ||||||
|     /// @param to This value is ignored. |  | ||||||
|     /// @param amount This value is ignored. |  | ||||||
|     function transferFrom( |  | ||||||
|         bytes calldata assetData, |  | ||||||
|         address from, |  | ||||||
|         address to, |  | ||||||
|         uint256 amount |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|     { |  | ||||||
|         // Decode params from `assetData` |  | ||||||
|         ( |  | ||||||
|             address staticCallTarget, |  | ||||||
|             bytes memory staticCallData, |  | ||||||
|             bytes32 expectedReturnDataHash |  | ||||||
|         ) = abi.decode( |  | ||||||
|             assetData.sliceDestructive(4, assetData.length), |  | ||||||
|             (address, bytes, bytes32) |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         // Execute staticcall |  | ||||||
|         (bool success, bytes memory returnData) = staticCallTarget.staticcall(staticCallData); |  | ||||||
|  |  | ||||||
|         // Revert with returned data if staticcall is unsuccessful |  | ||||||
|         if (!success) { |  | ||||||
|             assembly { |  | ||||||
|                 revert(add(returnData, 32), mload(returnData)) |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // Revert if hash of return data is not as expected |  | ||||||
|         bytes32 returnDataHash = keccak256(returnData); |  | ||||||
|         require( |  | ||||||
|             expectedReturnDataHash == returnDataHash, |  | ||||||
|             "UNEXPECTED_STATIC_CALL_RESULT" |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Gets the proxy id associated with the proxy address. |  | ||||||
|     /// @return Proxy id. |  | ||||||
|     function getProxyId() |  | ||||||
|         external |  | ||||||
|         pure |  | ||||||
|         returns (bytes4) |  | ||||||
|     { |  | ||||||
|         return PROXY_ID; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,103 +0,0 @@ | |||||||
|  |  | ||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2020 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
| pragma experimental ABIEncoderV2; |  | ||||||
|  |  | ||||||
| import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; |  | ||||||
| import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol"; |  | ||||||
| import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol"; |  | ||||||
| import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol"; |  | ||||||
| import "../interfaces/IERC20Bridge.sol"; |  | ||||||
| import "../interfaces/IBalancerPool.sol"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| contract BalancerBridge is |  | ||||||
|     IERC20Bridge, |  | ||||||
|     IWallet, |  | ||||||
|     DeploymentConstants |  | ||||||
| { |  | ||||||
|     /// @dev Callback for `IERC20Bridge`. Tries to buy `amount` of |  | ||||||
|     ///      `toTokenAddress` tokens by selling the entirety of the `fromTokenAddress` |  | ||||||
|     ///      token encoded in the bridge data, then transfers the bought |  | ||||||
|     ///      tokens to `to`. |  | ||||||
|     /// @param toTokenAddress The token to buy and transfer to `to`. |  | ||||||
|     /// @param from The maker (this contract). |  | ||||||
|     /// @param to The recipient of the bought tokens. |  | ||||||
|     /// @param amount Minimum amount of `toTokenAddress` tokens to buy. |  | ||||||
|     /// @param bridgeData The abi-encoded addresses of the "from" token and Balancer pool. |  | ||||||
|     /// @return success The magic bytes if successful. |  | ||||||
|     function bridgeTransferFrom( |  | ||||||
|         address toTokenAddress, |  | ||||||
|         address from, |  | ||||||
|         address to, |  | ||||||
|         uint256 amount, |  | ||||||
|         bytes calldata bridgeData |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         returns (bytes4 success) |  | ||||||
|     { |  | ||||||
|         // Decode the bridge data. |  | ||||||
|         (address fromTokenAddress, address poolAddress) = abi.decode( |  | ||||||
|             bridgeData, |  | ||||||
|             (address, address) |  | ||||||
|         ); |  | ||||||
|         require(toTokenAddress != fromTokenAddress, "BalancerBridge/INVALID_PAIR"); |  | ||||||
|  |  | ||||||
|         uint256 fromTokenBalance = IERC20Token(fromTokenAddress).balanceOf(address(this)); |  | ||||||
|         // Grant an allowance to the exchange to spend `fromTokenAddress` token. |  | ||||||
|         LibERC20Token.approveIfBelow(fromTokenAddress, poolAddress, fromTokenBalance); |  | ||||||
|  |  | ||||||
|         // Sell all of this contract's `fromTokenAddress` token balance. |  | ||||||
|         (uint256 boughtAmount,) = IBalancerPool(poolAddress).swapExactAmountIn( |  | ||||||
|             fromTokenAddress, // tokenIn |  | ||||||
|             fromTokenBalance, // tokenAmountIn |  | ||||||
|             toTokenAddress,   // tokenOut |  | ||||||
|             amount,           // minAmountOut |  | ||||||
|             uint256(-1)       // maxPrice |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         // Transfer the converted `toToken`s to `to`. |  | ||||||
|         LibERC20Token.transfer(toTokenAddress, to, boughtAmount); |  | ||||||
|  |  | ||||||
|         emit ERC20BridgeTransfer( |  | ||||||
|             fromTokenAddress, |  | ||||||
|             toTokenAddress, |  | ||||||
|             fromTokenBalance, |  | ||||||
|             boughtAmount, |  | ||||||
|             from, |  | ||||||
|             to |  | ||||||
|         ); |  | ||||||
|         return BRIDGE_SUCCESS; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev `SignatureType.Wallet` callback, so that this bridge can be the maker |  | ||||||
|     ///      and sign for itself in orders. Always succeeds. |  | ||||||
|     /// @return magicValue Magic success bytes, always. |  | ||||||
|     function isValidSignature( |  | ||||||
|         bytes32, |  | ||||||
|         bytes calldata |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (bytes4 magicValue) |  | ||||||
|     { |  | ||||||
|         return LEGACY_WALLET_MAGIC_VALUE; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,144 +0,0 @@ | |||||||
|  |  | ||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2020 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
| pragma experimental ABIEncoderV2; |  | ||||||
|  |  | ||||||
| import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; |  | ||||||
| import "@0x/contracts-erc20/contracts/src/interfaces/IEtherToken.sol"; |  | ||||||
| import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol"; |  | ||||||
| import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol"; |  | ||||||
| import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol"; |  | ||||||
| import "../interfaces/IERC20Bridge.sol"; |  | ||||||
| import "../interfaces/IBancorNetwork.sol"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| contract BancorBridge is |  | ||||||
|     IERC20Bridge, |  | ||||||
|     IWallet, |  | ||||||
|     DeploymentConstants |  | ||||||
| { |  | ||||||
|     struct TransferState { |  | ||||||
|         address bancorNetworkAddress; |  | ||||||
|         address[] path; |  | ||||||
|         IEtherToken weth; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Bancor ETH pseudo-address. |  | ||||||
|     address constant public BANCOR_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; |  | ||||||
|  |  | ||||||
|     // solhint-disable no-empty-blocks |  | ||||||
|     /// @dev Payable fallback to receive ETH from Bancor/WETH. |  | ||||||
|     function () |  | ||||||
|         external |  | ||||||
|         payable |  | ||||||
|     { |  | ||||||
|         // Poor man's receive in 0.5.9 |  | ||||||
|         require(msg.data.length == 0); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Callback for `IERC20Bridge`. Tries to buy `amount` of |  | ||||||
|     ///      `toTokenAddress` tokens by selling the entirety of the `fromTokenAddress` |  | ||||||
|     ///      token encoded in the bridge data, then transfers the bought |  | ||||||
|     ///      tokens to `to`. |  | ||||||
|     /// @param toTokenAddress The token to buy and transfer to `to`. |  | ||||||
|     /// @param from The maker (this contract). |  | ||||||
|     /// @param to The recipient of the bought tokens. |  | ||||||
|     /// @param amount Minimum amount of `toTokenAddress` tokens to buy. |  | ||||||
|     /// @param bridgeData The abi-encoded conversion path addresses and Bancor network address |  | ||||||
|     /// @return success The magic bytes if successful. |  | ||||||
|     function bridgeTransferFrom( |  | ||||||
|         address toTokenAddress, |  | ||||||
|         address from, |  | ||||||
|         address to, |  | ||||||
|         uint256 amount, |  | ||||||
|         bytes calldata bridgeData |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         returns (bytes4 success) |  | ||||||
|     { |  | ||||||
|         // hold variables to get around stack depth limitations |  | ||||||
|         TransferState memory state; |  | ||||||
|         // Decode the bridge data. |  | ||||||
|         ( |  | ||||||
|             state.path, |  | ||||||
|             state.bancorNetworkAddress |  | ||||||
|         // solhint-disable indent |  | ||||||
|         ) = abi.decode(bridgeData, (address[], address)); |  | ||||||
|         // solhint-enable indent |  | ||||||
|         state.weth = IEtherToken(_getWethAddress()); |  | ||||||
|  |  | ||||||
|         require(state.path.length >= 2, "BancorBridge/PATH_LENGTH_MUST_BE_GREATER_THAN_TWO"); |  | ||||||
|  |  | ||||||
|         // Grant an allowance to the Bancor Network to spend `fromTokenAddress` token. |  | ||||||
|         uint256 fromTokenBalance; |  | ||||||
|         uint256 payableAmount = 0; |  | ||||||
|         // If it's ETH in the path then withdraw from WETH |  | ||||||
|         // The Bancor path will have ETH as the 0xeee address |  | ||||||
|         // Bancor expects to be paid in ETH not WETH |  | ||||||
|         if (state.path[0] == BANCOR_ETH_ADDRESS) { |  | ||||||
|             fromTokenBalance = state.weth.balanceOf(address(this)); |  | ||||||
|             state.weth.withdraw(fromTokenBalance); |  | ||||||
|             payableAmount = fromTokenBalance; |  | ||||||
|         } else { |  | ||||||
|             fromTokenBalance = IERC20Token(state.path[0]).balanceOf(address(this)); |  | ||||||
|             LibERC20Token.approveIfBelow(state.path[0], state.bancorNetworkAddress, fromTokenBalance); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // Convert the tokens |  | ||||||
|         uint256 boughtAmount = IBancorNetwork(state.bancorNetworkAddress).convertByPath.value(payableAmount)( |  | ||||||
|             state.path, // path originating with source token and terminating in destination token |  | ||||||
|             fromTokenBalance, // amount of source token to trade |  | ||||||
|             amount, // minimum amount of destination token expected to receive |  | ||||||
|             state.path[state.path.length-1] == BANCOR_ETH_ADDRESS ? address(this) : to, // beneficiary |  | ||||||
|             address(0), // affiliateAccount; no fee paid |  | ||||||
|             0 // affiliateFee; no fee paid |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         if (state.path[state.path.length-1] == BANCOR_ETH_ADDRESS) { |  | ||||||
|             state.weth.deposit.value(boughtAmount)(); |  | ||||||
|             state.weth.transfer(to, boughtAmount); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         emit ERC20BridgeTransfer( |  | ||||||
|             state.path[0] == BANCOR_ETH_ADDRESS ? address(state.weth) : state.path[0], |  | ||||||
|             toTokenAddress, |  | ||||||
|             fromTokenBalance, |  | ||||||
|             boughtAmount, |  | ||||||
|             from, |  | ||||||
|             to |  | ||||||
|         ); |  | ||||||
|         return BRIDGE_SUCCESS; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev `SignatureType.Wallet` callback, so that this bridge can be the maker |  | ||||||
|     ///      and sign for itself in orders. Always succeeds. |  | ||||||
|     /// @return magicValue Magic success bytes, always. |  | ||||||
|     function isValidSignature( |  | ||||||
|         bytes32, |  | ||||||
|         bytes calldata |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (bytes4 magicValue) |  | ||||||
|     { |  | ||||||
|         return LEGACY_WALLET_MAGIC_VALUE; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } |  | ||||||
| @@ -1,75 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2019 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
| pragma experimental ABIEncoderV2; |  | ||||||
|  |  | ||||||
| import "../interfaces/IERC20Bridge.sol"; |  | ||||||
| import "../interfaces/IChai.sol"; |  | ||||||
| import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol"; |  | ||||||
| import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| // solhint-disable space-after-comma |  | ||||||
| contract ChaiBridge is |  | ||||||
|     IERC20Bridge, |  | ||||||
|     DeploymentConstants |  | ||||||
| { |  | ||||||
|     /// @dev Withdraws `amount` of `from` address's Dai from the Chai contract. |  | ||||||
|     ///      Transfers `amount` of Dai to `to` address. |  | ||||||
|     /// @param from Address to transfer asset from. |  | ||||||
|     /// @param to Address to transfer asset to. |  | ||||||
|     /// @param amount Amount of asset to transfer. |  | ||||||
|     /// @return success The magic bytes `0xdc1600f3` if successful. |  | ||||||
|     function bridgeTransferFrom( |  | ||||||
|         address /* tokenAddress */, |  | ||||||
|         address from, |  | ||||||
|         address to, |  | ||||||
|         uint256 amount, |  | ||||||
|         bytes calldata /* bridgeData */ |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         returns (bytes4 success) |  | ||||||
|     { |  | ||||||
|         // Ensure that only the `ERC20BridgeProxy` can call this function. |  | ||||||
|         require( |  | ||||||
|             msg.sender == _getERC20BridgeProxyAddress(), |  | ||||||
|             "ChaiBridge/ONLY_CALLABLE_BY_ERC20_BRIDGE_PROXY" |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         // Withdraw `from` address's Dai. |  | ||||||
|         // NOTE: This contract must be approved to spend Chai on behalf of `from`. |  | ||||||
|         bytes memory drawCalldata = abi.encodeWithSelector( |  | ||||||
|             IChai(address(0)).draw.selector, |  | ||||||
|             from, |  | ||||||
|             amount |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         (bool success,) = _getChaiAddress().call(drawCalldata); |  | ||||||
|         require( |  | ||||||
|             success, |  | ||||||
|             "ChaiBridge/DRAW_DAI_FAILED" |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         // Transfer Dai to `to` |  | ||||||
|         // This will never fail if the `draw` call was successful |  | ||||||
|         IERC20Token(_getDaiAddress()).transfer(to, amount); |  | ||||||
|  |  | ||||||
|         return BRIDGE_SUCCESS; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,103 +0,0 @@ | |||||||
|  |  | ||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2020 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
| pragma experimental ABIEncoderV2; |  | ||||||
|  |  | ||||||
| import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; |  | ||||||
| import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol"; |  | ||||||
| import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol"; |  | ||||||
| import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol"; |  | ||||||
| import "../interfaces/IERC20Bridge.sol"; |  | ||||||
| import "../interfaces/IBalancerPool.sol"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| contract CreamBridge is |  | ||||||
|     IERC20Bridge, |  | ||||||
|     IWallet, |  | ||||||
|     DeploymentConstants |  | ||||||
| { |  | ||||||
|     /// @dev Callback for `IERC20Bridge`. Tries to buy `amount` of |  | ||||||
|     ///      `toTokenAddress` tokens by selling the entirety of the `fromTokenAddress` |  | ||||||
|     ///      token encoded in the bridge data, then transfers the bought |  | ||||||
|     ///      tokens to `to`. |  | ||||||
|     /// @param toTokenAddress The token to buy and transfer to `to`. |  | ||||||
|     /// @param from The maker (this contract). |  | ||||||
|     /// @param to The recipient of the bought tokens. |  | ||||||
|     /// @param amount Minimum amount of `toTokenAddress` tokens to buy. |  | ||||||
|     /// @param bridgeData The abi-encoded addresses of the "from" token and Balancer pool. |  | ||||||
|     /// @return success The magic bytes if successful. |  | ||||||
|     function bridgeTransferFrom( |  | ||||||
|         address toTokenAddress, |  | ||||||
|         address from, |  | ||||||
|         address to, |  | ||||||
|         uint256 amount, |  | ||||||
|         bytes calldata bridgeData |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         returns (bytes4 success) |  | ||||||
|     { |  | ||||||
|         // Decode the bridge data. |  | ||||||
|         (address fromTokenAddress, address poolAddress) = abi.decode( |  | ||||||
|             bridgeData, |  | ||||||
|             (address, address) |  | ||||||
|         ); |  | ||||||
|         require(toTokenAddress != fromTokenAddress, "CreamBridge/INVALID_PAIR"); |  | ||||||
|  |  | ||||||
|         uint256 fromTokenBalance = IERC20Token(fromTokenAddress).balanceOf(address(this)); |  | ||||||
|         // Grant an allowance to the exchange to spend `fromTokenAddress` token. |  | ||||||
|         LibERC20Token.approveIfBelow(fromTokenAddress, poolAddress, fromTokenBalance); |  | ||||||
|  |  | ||||||
|         // Sell all of this contract's `fromTokenAddress` token balance. |  | ||||||
|         (uint256 boughtAmount,) = IBalancerPool(poolAddress).swapExactAmountIn( |  | ||||||
|             fromTokenAddress, // tokenIn |  | ||||||
|             fromTokenBalance, // tokenAmountIn |  | ||||||
|             toTokenAddress,   // tokenOut |  | ||||||
|             amount,           // minAmountOut |  | ||||||
|             uint256(-1)       // maxPrice |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         // Transfer the converted `toToken`s to `to`. |  | ||||||
|         LibERC20Token.transfer(toTokenAddress, to, boughtAmount); |  | ||||||
|  |  | ||||||
|         emit ERC20BridgeTransfer( |  | ||||||
|             fromTokenAddress, |  | ||||||
|             toTokenAddress, |  | ||||||
|             fromTokenBalance, |  | ||||||
|             boughtAmount, |  | ||||||
|             from, |  | ||||||
|             to |  | ||||||
|         ); |  | ||||||
|         return BRIDGE_SUCCESS; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev `SignatureType.Wallet` callback, so that this bridge can be the maker |  | ||||||
|     ///      and sign for itself in orders. Always succeeds. |  | ||||||
|     /// @return magicValue Magic success bytes, always. |  | ||||||
|     function isValidSignature( |  | ||||||
|         bytes32, |  | ||||||
|         bytes calldata |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (bytes4 magicValue) |  | ||||||
|     { |  | ||||||
|         return LEGACY_WALLET_MAGIC_VALUE; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,136 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2020 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
| pragma experimental ABIEncoderV2; |  | ||||||
|  |  | ||||||
| import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; |  | ||||||
| import "@0x/contracts-erc20/contracts/src/interfaces/IEtherToken.sol"; |  | ||||||
| import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol"; |  | ||||||
| import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol"; |  | ||||||
| import "@0x/contracts-utils/contracts/src/LibAddressArray.sol"; |  | ||||||
| import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol"; |  | ||||||
| import "../interfaces/IUniswapV2Router01.sol"; |  | ||||||
| import "../interfaces/IERC20Bridge.sol"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| // solhint-disable space-after-comma |  | ||||||
| // solhint-disable not-rely-on-time |  | ||||||
| contract CryptoComBridge is |  | ||||||
|     IERC20Bridge, |  | ||||||
|     IWallet, |  | ||||||
|     DeploymentConstants |  | ||||||
| { |  | ||||||
|     struct TransferState { |  | ||||||
|         address[] path; |  | ||||||
|         address router; |  | ||||||
|         uint256 fromTokenBalance; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Callback for `IERC20Bridge`. Tries to buy `amount` of |  | ||||||
|     ///      `toTokenAddress` tokens by selling the entirety of the `fromTokenAddress` |  | ||||||
|     ///      token encoded in the bridge data. |  | ||||||
|     /// @param toTokenAddress The token to buy and transfer to `to`. |  | ||||||
|     /// @param from The maker (this contract). |  | ||||||
|     /// @param to The recipient of the bought tokens. |  | ||||||
|     /// @param amount Minimum amount of `toTokenAddress` tokens to buy. |  | ||||||
|     /// @param bridgeData The abi-encoded path of token addresses. Last element must be toTokenAddress |  | ||||||
|     /// @return success The magic bytes if successful. |  | ||||||
|     function bridgeTransferFrom( |  | ||||||
|         address toTokenAddress, |  | ||||||
|         address from, |  | ||||||
|         address to, |  | ||||||
|         uint256 amount, |  | ||||||
|         bytes calldata bridgeData |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         returns (bytes4 success) |  | ||||||
|     { |  | ||||||
|         // hold variables to get around stack depth limitations |  | ||||||
|         TransferState memory state; |  | ||||||
|  |  | ||||||
|         // Decode the bridge data to get the `fromTokenAddress`. |  | ||||||
|         // solhint-disable indent |  | ||||||
|         (state.path, state.router) = abi.decode(bridgeData, (address[], address)); |  | ||||||
|         // solhint-enable indent |  | ||||||
|  |  | ||||||
|         require(state.path.length >= 2, "CryptoComBridge/PATH_LENGTH_MUST_BE_AT_LEAST_TWO"); |  | ||||||
|         require(state.path[state.path.length - 1] == toTokenAddress, "CryptoComBridge/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN"); |  | ||||||
|  |  | ||||||
|         // Just transfer the tokens if they're the same. |  | ||||||
|         if (state.path[0] == toTokenAddress) { |  | ||||||
|             LibERC20Token.transfer(state.path[0], to, amount); |  | ||||||
|             return BRIDGE_SUCCESS; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // Get our balance of `fromTokenAddress` token. |  | ||||||
|         state.fromTokenBalance = IERC20Token(state.path[0]).balanceOf(address(this)); |  | ||||||
|  |  | ||||||
|         // Grant the SushiSwap router an allowance. |  | ||||||
|         LibERC20Token.approveIfBelow( |  | ||||||
|             state.path[0], |  | ||||||
|             state.router, |  | ||||||
|             state.fromTokenBalance |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         // Buy as much `toTokenAddress` token with `fromTokenAddress` token |  | ||||||
|         // and transfer it to `to`. |  | ||||||
|         IUniswapV2Router01 router = IUniswapV2Router01(state.router); |  | ||||||
|         uint[] memory amounts = router.swapExactTokensForTokens( |  | ||||||
|              // Sell all tokens we hold. |  | ||||||
|             state.fromTokenBalance, |  | ||||||
|              // Minimum buy amount. |  | ||||||
|             amount, |  | ||||||
|             // Convert `fromTokenAddress` to `toTokenAddress`. |  | ||||||
|             state.path, |  | ||||||
|             // Recipient is `to`. |  | ||||||
|             to, |  | ||||||
|             // Expires after this block. |  | ||||||
|             block.timestamp |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         emit ERC20BridgeTransfer( |  | ||||||
|             // input token |  | ||||||
|             state.path[0], |  | ||||||
|             // output token |  | ||||||
|             toTokenAddress, |  | ||||||
|             // input token amount |  | ||||||
|             state.fromTokenBalance, |  | ||||||
|             // output token amount |  | ||||||
|             amounts[amounts.length - 1], |  | ||||||
|             from, |  | ||||||
|             to |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         return BRIDGE_SUCCESS; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev `SignatureType.Wallet` callback, so that this bridge can be the maker |  | ||||||
|     ///      and sign for itself in orders. Always succeeds. |  | ||||||
|     /// @return magicValue Success bytes, always. |  | ||||||
|     function isValidSignature( |  | ||||||
|         bytes32, |  | ||||||
|         bytes calldata |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (bytes4 magicValue) |  | ||||||
|     { |  | ||||||
|         return LEGACY_WALLET_MAGIC_VALUE; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,119 +0,0 @@ | |||||||
|  |  | ||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2019 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
| pragma experimental ABIEncoderV2; |  | ||||||
|  |  | ||||||
| import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; |  | ||||||
| import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol"; |  | ||||||
| import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol"; |  | ||||||
| import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol"; |  | ||||||
| import "../interfaces/IERC20Bridge.sol"; |  | ||||||
| import "../interfaces/ICurve.sol"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| // solhint-disable not-rely-on-time |  | ||||||
| // solhint-disable space-after-comma |  | ||||||
| contract CurveBridge is |  | ||||||
|     IERC20Bridge, |  | ||||||
|     IWallet, |  | ||||||
|     DeploymentConstants |  | ||||||
| { |  | ||||||
|     struct CurveBridgeData { |  | ||||||
|         address curveAddress; |  | ||||||
|         bytes4 exchangeFunctionSelector; |  | ||||||
|         address fromTokenAddress; |  | ||||||
|         int128 fromCoinIdx; |  | ||||||
|         int128 toCoinIdx; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Callback for `ICurve`. Tries to buy `amount` of |  | ||||||
|     ///      `toTokenAddress` tokens by selling the entirety of the opposing asset |  | ||||||
|     ///      (DAI, USDC) to the Curve contract, then transfers the bought |  | ||||||
|     ///      tokens to `to`. |  | ||||||
|     /// @param toTokenAddress The token to give to `to` (i.e DAI, USDC, USDT). |  | ||||||
|     /// @param from The maker (this contract). |  | ||||||
|     /// @param to The recipient of the bought tokens. |  | ||||||
|     /// @param amount Minimum amount of `toTokenAddress` tokens to buy. |  | ||||||
|     /// @param bridgeData The abi-encoeded "from" token address. |  | ||||||
|     /// @return success The magic bytes if successful. |  | ||||||
|     function bridgeTransferFrom( |  | ||||||
|         address toTokenAddress, |  | ||||||
|         address from, |  | ||||||
|         address to, |  | ||||||
|         uint256 amount, |  | ||||||
|         bytes calldata bridgeData |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         returns (bytes4 success) |  | ||||||
|     { |  | ||||||
|         // Decode the bridge data to get the Curve metadata. |  | ||||||
|         CurveBridgeData memory data = abi.decode(bridgeData, (CurveBridgeData)); |  | ||||||
|  |  | ||||||
|         require(toTokenAddress != data.fromTokenAddress, "CurveBridge/INVALID_PAIR"); |  | ||||||
|         uint256 fromTokenBalance = IERC20Token(data.fromTokenAddress).balanceOf(address(this)); |  | ||||||
|         // Grant an allowance to the exchange to spend `fromTokenAddress` token. |  | ||||||
|         LibERC20Token.approveIfBelow(data.fromTokenAddress, data.curveAddress, fromTokenBalance); |  | ||||||
|  |  | ||||||
|         // Try to sell all of this contract's `fromTokenAddress` token balance. |  | ||||||
|         { |  | ||||||
|             (bool didSucceed, bytes memory resultData) = |  | ||||||
|                 data.curveAddress.call(abi.encodeWithSelector( |  | ||||||
|                     data.exchangeFunctionSelector, |  | ||||||
|                     data.fromCoinIdx, |  | ||||||
|                     data.toCoinIdx, |  | ||||||
|                     // dx |  | ||||||
|                     fromTokenBalance, |  | ||||||
|                     // min dy |  | ||||||
|                     amount |  | ||||||
|                 )); |  | ||||||
|             if (!didSucceed) { |  | ||||||
|                 assembly { revert(add(resultData, 32), mload(resultData)) } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         uint256 toTokenBalance = IERC20Token(toTokenAddress).balanceOf(address(this)); |  | ||||||
|         // Transfer the converted `toToken`s to `to`. |  | ||||||
|         LibERC20Token.transfer(toTokenAddress, to, toTokenBalance); |  | ||||||
|  |  | ||||||
|         emit ERC20BridgeTransfer( |  | ||||||
|             data.fromTokenAddress, |  | ||||||
|             toTokenAddress, |  | ||||||
|             fromTokenBalance, |  | ||||||
|             toTokenBalance, |  | ||||||
|             from, |  | ||||||
|             to |  | ||||||
|         ); |  | ||||||
|         return BRIDGE_SUCCESS; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev `SignatureType.Wallet` callback, so that this bridge can be the maker |  | ||||||
|     ///      and sign for itself in orders. Always succeeds. |  | ||||||
|     /// @return magicValue Magic success bytes, always. |  | ||||||
|     function isValidSignature( |  | ||||||
|         bytes32, |  | ||||||
|         bytes calldata |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (bytes4 magicValue) |  | ||||||
|     { |  | ||||||
|         return LEGACY_WALLET_MAGIC_VALUE; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,147 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2020 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
| pragma experimental ABIEncoderV2; |  | ||||||
|  |  | ||||||
| import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; |  | ||||||
| import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol"; |  | ||||||
| import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol"; |  | ||||||
| import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol"; |  | ||||||
| import "../interfaces/IERC20Bridge.sol"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| interface IDODOHelper { |  | ||||||
|  |  | ||||||
|     function querySellQuoteToken(address dodo, uint256 amount) external view returns (uint256); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| interface IDODO { |  | ||||||
|  |  | ||||||
|     function sellBaseToken(uint256 amount, uint256 minReceiveQuote, bytes calldata data) external returns (uint256); |  | ||||||
|  |  | ||||||
|     function buyBaseToken(uint256 amount, uint256 maxPayQuote, bytes calldata data) external returns (uint256); |  | ||||||
|  |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| contract DODOBridge is |  | ||||||
|     IERC20Bridge, |  | ||||||
|     IWallet, |  | ||||||
|     DeploymentConstants |  | ||||||
| { |  | ||||||
|  |  | ||||||
|     struct TransferState { |  | ||||||
|         address fromTokenAddress; |  | ||||||
|         uint256 fromTokenBalance; |  | ||||||
|         address pool; |  | ||||||
|         bool isSellBase; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Callback for `IERC20Bridge`. Tries to buy `amount` of |  | ||||||
|     ///      `toTokenAddress` tokens by selling the entirety of the `fromTokenAddress` |  | ||||||
|     ///      token encoded in the bridge data. |  | ||||||
|     /// @param toTokenAddress The token to buy and transfer to `to`. |  | ||||||
|     /// @param from The maker (this contract). |  | ||||||
|     /// @param to The recipient of the bought tokens. |  | ||||||
|     /// @param amount Minimum amount of `toTokenAddress` tokens to buy. |  | ||||||
|     /// @param bridgeData The abi-encoded path of token addresses. Last element must be toTokenAddress |  | ||||||
|     /// @return success The magic bytes if successful. |  | ||||||
|     function bridgeTransferFrom( |  | ||||||
|         address toTokenAddress, |  | ||||||
|         address from, |  | ||||||
|         address to, |  | ||||||
|         uint256 amount, |  | ||||||
|         bytes calldata bridgeData |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         returns (bytes4 success) |  | ||||||
|     { |  | ||||||
|         TransferState memory state; |  | ||||||
|         // Decode the bridge data to get the `fromTokenAddress`. |  | ||||||
|         (state.fromTokenAddress, state.pool, state.isSellBase) = abi.decode(bridgeData, (address, address, bool)); |  | ||||||
|         require(state.pool != address(0), "DODOBridge/InvalidPool"); |  | ||||||
|         IDODO exchange = IDODO(state.pool); |  | ||||||
|         // Get our balance of `fromTokenAddress` token. |  | ||||||
|         state.fromTokenBalance = IERC20Token(state.fromTokenAddress).balanceOf(address(this)); |  | ||||||
|  |  | ||||||
|         // Grant the pool an allowance. |  | ||||||
|         LibERC20Token.approveIfBelow( |  | ||||||
|             state.fromTokenAddress, |  | ||||||
|             address(exchange), |  | ||||||
|             state.fromTokenBalance |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         uint256 boughtAmount; |  | ||||||
|         if (state.isSellBase) { |  | ||||||
|             boughtAmount = exchange.sellBaseToken( |  | ||||||
|                 // amount to sell |  | ||||||
|                 state.fromTokenBalance, |  | ||||||
|                 // min receive amount |  | ||||||
|                 1, |  | ||||||
|                 new bytes(0) |  | ||||||
|             ); |  | ||||||
|         } else { |  | ||||||
|             // Need to re-calculate the sell quote amount into buyBase |  | ||||||
|             boughtAmount = IDODOHelper(_getDODOHelperAddress()).querySellQuoteToken( |  | ||||||
|                 address(exchange), |  | ||||||
|                 state.fromTokenBalance |  | ||||||
|             ); |  | ||||||
|             exchange.buyBaseToken( |  | ||||||
|                 // amount to buy |  | ||||||
|                 boughtAmount, |  | ||||||
|                 // max pay amount |  | ||||||
|                 state.fromTokenBalance, |  | ||||||
|                 new bytes(0) |  | ||||||
|             ); |  | ||||||
|         } |  | ||||||
|         // Transfer funds to `to` |  | ||||||
|         IERC20Token(toTokenAddress).transfer(to, boughtAmount); |  | ||||||
|  |  | ||||||
|  |  | ||||||
|         emit ERC20BridgeTransfer( |  | ||||||
|             // input token |  | ||||||
|             state.fromTokenAddress, |  | ||||||
|             // output token |  | ||||||
|             toTokenAddress, |  | ||||||
|             // input token amount |  | ||||||
|             state.fromTokenBalance, |  | ||||||
|             // output token amount |  | ||||||
|             boughtAmount, |  | ||||||
|             from, |  | ||||||
|             to |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         return BRIDGE_SUCCESS; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev `SignatureType.Wallet` callback, so that this bridge can be the maker |  | ||||||
|     ///      and sign for itself in orders. Always succeeds. |  | ||||||
|     /// @return magicValue Success bytes, always. |  | ||||||
|     function isValidSignature( |  | ||||||
|         bytes32, |  | ||||||
|         bytes calldata |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (bytes4 magicValue) |  | ||||||
|     { |  | ||||||
|         return LEGACY_WALLET_MAGIC_VALUE; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,200 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2020 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
| pragma experimental ABIEncoderV2; |  | ||||||
|  |  | ||||||
| import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; |  | ||||||
| import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol"; |  | ||||||
| import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol"; |  | ||||||
| import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol"; |  | ||||||
| import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol"; |  | ||||||
| import "@0x/contracts-utils/contracts/src/LibBytes.sol"; |  | ||||||
| import "@0x/contracts-utils/contracts/src/LibSafeMath.sol"; |  | ||||||
| import "../interfaces/IERC20Bridge.sol"; |  | ||||||
| import "./MixinGasToken.sol"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| // solhint-disable space-after-comma, indent |  | ||||||
| contract DexForwarderBridge is |  | ||||||
|     IERC20Bridge, |  | ||||||
|     IWallet, |  | ||||||
|     DeploymentConstants, |  | ||||||
|     MixinGasToken |  | ||||||
| { |  | ||||||
|     using LibSafeMath for uint256; |  | ||||||
|  |  | ||||||
|     /// @dev Data needed to reconstruct a bridge call. |  | ||||||
|     struct BridgeCall { |  | ||||||
|         address target; |  | ||||||
|         uint256 inputTokenAmount; |  | ||||||
|         uint256 outputTokenAmount; |  | ||||||
|         bytes bridgeData; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Intermediate state variables used by `bridgeTransferFrom()`, in |  | ||||||
|     ///      struct form to get around stack limits. |  | ||||||
|     struct TransferFromState { |  | ||||||
|         address inputToken; |  | ||||||
|         uint256 initialInputTokenBalance; |  | ||||||
|         uint256 callInputTokenAmount; |  | ||||||
|         uint256 callOutputTokenAmount; |  | ||||||
|         uint256 totalInputTokenSold; |  | ||||||
|         BridgeCall[] calls; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Spends this contract's entire balance of input tokens by forwarding |  | ||||||
|     /// them to other bridges. Reverts if the entire balance is not spent. |  | ||||||
|     /// @param outputToken The token being bought. |  | ||||||
|     /// @param to The recipient of the bought tokens. |  | ||||||
|     /// @param bridgeData The abi-encoded input token address. |  | ||||||
|     /// @return success The magic bytes if successful. |  | ||||||
|     function bridgeTransferFrom( |  | ||||||
|         address outputToken, |  | ||||||
|         address /* from */, |  | ||||||
|         address to, |  | ||||||
|         uint256 /* amount */, |  | ||||||
|         bytes calldata bridgeData |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         freesGasTokensFromCollector |  | ||||||
|         returns (bytes4 success) |  | ||||||
|     { |  | ||||||
|         require( |  | ||||||
|             msg.sender == _getERC20BridgeProxyAddress(), |  | ||||||
|             "DexForwarderBridge/SENDER_NOT_AUTHORIZED" |  | ||||||
|         ); |  | ||||||
|         TransferFromState memory state; |  | ||||||
|         ( |  | ||||||
|             state.inputToken, |  | ||||||
|             state.calls |  | ||||||
|         ) = abi.decode(bridgeData, (address, BridgeCall[])); |  | ||||||
|  |  | ||||||
|         state.initialInputTokenBalance = |  | ||||||
|             IERC20Token(state.inputToken).balanceOf(address(this)); |  | ||||||
|  |  | ||||||
|         for (uint256 i = 0; i < state.calls.length; ++i) { |  | ||||||
|             // Stop if the we've sold all our input tokens. |  | ||||||
|             if (state.totalInputTokenSold >= state.initialInputTokenBalance) { |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             // Compute token amounts. |  | ||||||
|             state.callInputTokenAmount = LibSafeMath.min256( |  | ||||||
|                 state.calls[i].inputTokenAmount, |  | ||||||
|                 state.initialInputTokenBalance.safeSub(state.totalInputTokenSold) |  | ||||||
|             ); |  | ||||||
|             state.callOutputTokenAmount = LibMath.getPartialAmountFloor( |  | ||||||
|                 state.callInputTokenAmount, |  | ||||||
|                 state.calls[i].inputTokenAmount, |  | ||||||
|                 state.calls[i].outputTokenAmount |  | ||||||
|             ); |  | ||||||
|  |  | ||||||
|             // Execute the call in a new context so we can recoup transferred |  | ||||||
|             // funds by reverting. |  | ||||||
|             (bool didSucceed, ) = address(this) |  | ||||||
|                 .call(abi.encodeWithSelector( |  | ||||||
|                     this.executeBridgeCall.selector, |  | ||||||
|                     state.calls[i].target, |  | ||||||
|                     to, |  | ||||||
|                     state.inputToken, |  | ||||||
|                     outputToken, |  | ||||||
|                     state.callInputTokenAmount, |  | ||||||
|                     state.callOutputTokenAmount, |  | ||||||
|                     state.calls[i].bridgeData |  | ||||||
|                 )); |  | ||||||
|  |  | ||||||
|             if (didSucceed) { |  | ||||||
|                 // Increase the amount of tokens sold. |  | ||||||
|                 state.totalInputTokenSold = state.totalInputTokenSold.safeAdd( |  | ||||||
|                     state.callInputTokenAmount |  | ||||||
|                 ); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         // Always succeed. |  | ||||||
|         return BRIDGE_SUCCESS; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Transfers `inputToken` token to a bridge contract then calls |  | ||||||
|     ///      its `bridgeTransferFrom()`. This is executed in separate context |  | ||||||
|     ///      so we can revert the transfer on error. This can only be called |  | ||||||
|     //       by this contract itself. |  | ||||||
|     /// @param bridge The bridge contract. |  | ||||||
|     /// @param to The recipient of `outputToken` tokens. |  | ||||||
|     /// @param inputToken The input token. |  | ||||||
|     /// @param outputToken The output token. |  | ||||||
|     /// @param inputTokenAmount The amount of input tokens to transfer to `bridge`. |  | ||||||
|     /// @param outputTokenAmount The amount of expected output tokens to be sent |  | ||||||
|     ///        to `to` by `bridge`. |  | ||||||
|     function executeBridgeCall( |  | ||||||
|         address bridge, |  | ||||||
|         address to, |  | ||||||
|         address inputToken, |  | ||||||
|         address outputToken, |  | ||||||
|         uint256 inputTokenAmount, |  | ||||||
|         uint256 outputTokenAmount, |  | ||||||
|         bytes calldata bridgeData |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|     { |  | ||||||
|         // Must be called through `bridgeTransferFrom()`. |  | ||||||
|         require(msg.sender == address(this), "DexForwarderBridge/ONLY_SELF"); |  | ||||||
|         // `bridge` must not be this contract. |  | ||||||
|         require(bridge != address(this)); |  | ||||||
|  |  | ||||||
|         // Get the starting balance of output tokens for `to`. |  | ||||||
|         uint256 initialRecipientBalance = IERC20Token(outputToken).balanceOf(to); |  | ||||||
|  |  | ||||||
|         // Transfer input tokens to the bridge. |  | ||||||
|         LibERC20Token.transfer(inputToken, bridge, inputTokenAmount); |  | ||||||
|  |  | ||||||
|         // Call the bridge. |  | ||||||
|         (bool didSucceed, bytes memory resultData) = |  | ||||||
|             bridge.call(abi.encodeWithSelector( |  | ||||||
|                 IERC20Bridge(0).bridgeTransferFrom.selector, |  | ||||||
|                 outputToken, |  | ||||||
|                 bridge, |  | ||||||
|                 to, |  | ||||||
|                 outputTokenAmount, |  | ||||||
|                 bridgeData |  | ||||||
|             )); |  | ||||||
|  |  | ||||||
|         // Revert if the call failed or not enough tokens were bought. |  | ||||||
|         // This will also undo the token transfer. |  | ||||||
|         require( |  | ||||||
|             didSucceed |  | ||||||
|             && resultData.length == 32 |  | ||||||
|             && LibBytes.readBytes32(resultData, 0) == bytes32(BRIDGE_SUCCESS) |  | ||||||
|             && IERC20Token(outputToken).balanceOf(to).safeSub(initialRecipientBalance) >= outputTokenAmount |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev `SignatureType.Wallet` callback, so that this bridge can be the maker |  | ||||||
|     ///      and sign for itself in orders. Always succeeds. |  | ||||||
|     /// @return magicValue Magic success bytes, always. |  | ||||||
|     function isValidSignature( |  | ||||||
|         bytes32, |  | ||||||
|         bytes calldata |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (bytes4 magicValue) |  | ||||||
|     { |  | ||||||
|         return LEGACY_WALLET_MAGIC_VALUE; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,242 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2019 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
| pragma experimental ABIEncoderV2; |  | ||||||
|  |  | ||||||
| import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol"; |  | ||||||
| import "@0x/contracts-utils/contracts/src/LibSafeMath.sol"; |  | ||||||
| import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol"; |  | ||||||
| import "../interfaces/IERC20Bridge.sol"; |  | ||||||
| import "../interfaces/IDydxBridge.sol"; |  | ||||||
| import "../interfaces/IDydx.sol"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| contract DydxBridge is |  | ||||||
|     IERC20Bridge, |  | ||||||
|     IDydxBridge, |  | ||||||
|     DeploymentConstants |  | ||||||
| { |  | ||||||
|  |  | ||||||
|     using LibSafeMath for uint256; |  | ||||||
|  |  | ||||||
|     /// @dev Callback for `IERC20Bridge`. Deposits or withdraws tokens from a dydx account. |  | ||||||
|     ///      Notes: |  | ||||||
|     ///         1. This bridge must be set as an operator of the input dydx account. |  | ||||||
|     ///         2. This function may only be called in the context of the 0x Exchange. |  | ||||||
|     ///         3. The maker or taker of the 0x order must be the dydx account owner. |  | ||||||
|     ///         4. Deposits into dydx are made from the `from` address. |  | ||||||
|     ///         5. Withdrawals from dydx are made to the `to` address. |  | ||||||
|     ///         6. Calling this function must always withdraw at least `amount`, |  | ||||||
|     ///            otherwise the `ERC20Bridge` will revert. |  | ||||||
|     /// @param from The sender of the tokens and owner of the dydx account. |  | ||||||
|     /// @param to The recipient of the tokens. |  | ||||||
|     /// @param amount Minimum amount of `toTokenAddress` tokens to deposit or withdraw. |  | ||||||
|     /// @param encodedBridgeData An abi-encoded `BridgeData` struct. |  | ||||||
|     /// @return success The magic bytes if successful. |  | ||||||
|     function bridgeTransferFrom( |  | ||||||
|         address, /* toTokenAddress */ |  | ||||||
|         address from, |  | ||||||
|         address to, |  | ||||||
|         uint256 amount, |  | ||||||
|         bytes calldata encodedBridgeData |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         returns (bytes4 success) |  | ||||||
|     { |  | ||||||
|         // Ensure that only the `ERC20BridgeProxy` can call this function. |  | ||||||
|         require( |  | ||||||
|             msg.sender == _getERC20BridgeProxyAddress(), |  | ||||||
|             "DydxBridge/ONLY_CALLABLE_BY_ERC20_BRIDGE_PROXY" |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         // Decode bridge data. |  | ||||||
|         (BridgeData memory bridgeData) = abi.decode(encodedBridgeData, (BridgeData)); |  | ||||||
|  |  | ||||||
|         // The dydx accounts are owned by the `from` address. |  | ||||||
|         IDydx.AccountInfo[] memory accounts = _createAccounts(from, bridgeData); |  | ||||||
|  |  | ||||||
|         // Create dydx actions to run on the dydx accounts. |  | ||||||
|         IDydx.ActionArgs[] memory actions = _createActions( |  | ||||||
|             from, |  | ||||||
|             to, |  | ||||||
|             amount, |  | ||||||
|             bridgeData |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         // Run operation. This will revert on failure. |  | ||||||
|         IDydx(_getDydxAddress()).operate(accounts, actions); |  | ||||||
|  |  | ||||||
|         return BRIDGE_SUCCESS; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Creates an array of accounts for dydx to operate on. |  | ||||||
|     ///      All accounts must belong to the same owner. |  | ||||||
|     /// @param accountOwner Owner of the dydx account. |  | ||||||
|     /// @param bridgeData A `BridgeData` struct. |  | ||||||
|     function _createAccounts( |  | ||||||
|         address accountOwner, |  | ||||||
|         BridgeData memory bridgeData |  | ||||||
|     ) |  | ||||||
|         internal |  | ||||||
|         returns (IDydx.AccountInfo[] memory accounts) |  | ||||||
|     { |  | ||||||
|         uint256[] memory accountNumbers = bridgeData.accountNumbers; |  | ||||||
|         uint256 nAccounts = accountNumbers.length; |  | ||||||
|         accounts = new IDydx.AccountInfo[](nAccounts); |  | ||||||
|         for (uint256 i = 0; i < nAccounts; ++i) { |  | ||||||
|             accounts[i] = IDydx.AccountInfo({ |  | ||||||
|                 owner: accountOwner, |  | ||||||
|                 number: accountNumbers[i] |  | ||||||
|             }); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Creates an array of actions to carry out on dydx. |  | ||||||
|     /// @param depositFrom Deposit value from this address (owner of the dydx account). |  | ||||||
|     /// @param withdrawTo Withdraw value to this address. |  | ||||||
|     /// @param amount The amount of value available to operate on. |  | ||||||
|     /// @param bridgeData A `BridgeData` struct. |  | ||||||
|     function _createActions( |  | ||||||
|         address depositFrom, |  | ||||||
|         address withdrawTo, |  | ||||||
|         uint256 amount, |  | ||||||
|         BridgeData memory bridgeData |  | ||||||
|     ) |  | ||||||
|         internal |  | ||||||
|         returns (IDydx.ActionArgs[] memory actions) |  | ||||||
|     { |  | ||||||
|         BridgeAction[] memory bridgeActions = bridgeData.actions; |  | ||||||
|         uint256 nBridgeActions = bridgeActions.length; |  | ||||||
|         actions = new IDydx.ActionArgs[](nBridgeActions); |  | ||||||
|         for (uint256 i = 0; i < nBridgeActions; ++i) { |  | ||||||
|             // Cache current bridge action. |  | ||||||
|             BridgeAction memory bridgeAction = bridgeActions[i]; |  | ||||||
|  |  | ||||||
|             // Scale amount, if conversion rate is set. |  | ||||||
|             uint256 scaledAmount; |  | ||||||
|             if (bridgeAction.conversionRateDenominator > 0) { |  | ||||||
|                 scaledAmount = LibMath.safeGetPartialAmountFloor( |  | ||||||
|                     bridgeAction.conversionRateNumerator, |  | ||||||
|                     bridgeAction.conversionRateDenominator, |  | ||||||
|                     amount |  | ||||||
|                 ); |  | ||||||
|             } else { |  | ||||||
|                 scaledAmount = amount; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             // Construct dydx action. |  | ||||||
|             if (bridgeAction.actionType == BridgeActionType.Deposit) { |  | ||||||
|                 // Deposit tokens from the account owner into their dydx account. |  | ||||||
|                 actions[i] = _createDepositAction( |  | ||||||
|                     depositFrom, |  | ||||||
|                     scaledAmount, |  | ||||||
|                     bridgeAction |  | ||||||
|                 ); |  | ||||||
|             } else if (bridgeAction.actionType == BridgeActionType.Withdraw) { |  | ||||||
|                 // Withdraw tokens from dydx to the `otherAccount`. |  | ||||||
|                 actions[i] = _createWithdrawAction( |  | ||||||
|                     withdrawTo, |  | ||||||
|                     scaledAmount, |  | ||||||
|                     bridgeAction |  | ||||||
|                 ); |  | ||||||
|             } else { |  | ||||||
|                 // If all values in the `Action` enum are handled then this |  | ||||||
|                 // revert is unreachable: Solidity will revert when casting |  | ||||||
|                 // from `uint8` to `Action`. |  | ||||||
|                 revert("DydxBridge/UNRECOGNIZED_BRIDGE_ACTION"); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Returns a dydx `DepositAction`. |  | ||||||
|     /// @param depositFrom Deposit tokens from this address who is also the account owner. |  | ||||||
|     /// @param amount of tokens to deposit. |  | ||||||
|     /// @param bridgeAction A `BridgeAction` struct. |  | ||||||
|     /// @return depositAction The encoded dydx action. |  | ||||||
|     function _createDepositAction( |  | ||||||
|         address depositFrom, |  | ||||||
|         uint256 amount, |  | ||||||
|         BridgeAction memory bridgeAction |  | ||||||
|     ) |  | ||||||
|         internal |  | ||||||
|         pure |  | ||||||
|         returns ( |  | ||||||
|             IDydx.ActionArgs memory depositAction |  | ||||||
|         ) |  | ||||||
|     { |  | ||||||
|         // Create dydx amount. |  | ||||||
|         IDydx.AssetAmount memory dydxAmount = IDydx.AssetAmount({ |  | ||||||
|             sign: true,                                 // true if positive. |  | ||||||
|             denomination: IDydx.AssetDenomination.Wei,  // Wei => actual token amount held in account. |  | ||||||
|             ref: IDydx.AssetReference.Delta,                // Delta => a relative amount. |  | ||||||
|             value: amount                               // amount to deposit. |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         // Create dydx deposit action. |  | ||||||
|         depositAction = IDydx.ActionArgs({ |  | ||||||
|             actionType: IDydx.ActionType.Deposit,           // deposit tokens. |  | ||||||
|             amount: dydxAmount,                             // amount to deposit. |  | ||||||
|             accountIdx: bridgeAction.accountIdx,             // index in the `accounts` when calling `operate`. |  | ||||||
|             primaryMarketId: bridgeAction.marketId,         // indicates which token to deposit. |  | ||||||
|             otherAddress: depositFrom,                      // deposit from the account owner. |  | ||||||
|             // unused parameters |  | ||||||
|             secondaryMarketId: 0, |  | ||||||
|             otherAccountIdx: 0, |  | ||||||
|             data: hex'' |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Returns a dydx `WithdrawAction`. |  | ||||||
|     /// @param withdrawTo Withdraw tokens to this address. |  | ||||||
|     /// @param amount of tokens to withdraw. |  | ||||||
|     /// @param bridgeAction A `BridgeAction` struct. |  | ||||||
|     /// @return withdrawAction The encoded dydx action. |  | ||||||
|     function _createWithdrawAction( |  | ||||||
|         address withdrawTo, |  | ||||||
|         uint256 amount, |  | ||||||
|         BridgeAction memory bridgeAction |  | ||||||
|     ) |  | ||||||
|         internal |  | ||||||
|         pure |  | ||||||
|         returns ( |  | ||||||
|             IDydx.ActionArgs memory withdrawAction |  | ||||||
|         ) |  | ||||||
|     { |  | ||||||
|         // Create dydx amount. |  | ||||||
|         IDydx.AssetAmount memory amountToWithdraw = IDydx.AssetAmount({ |  | ||||||
|             sign: false,                                    // false if negative. |  | ||||||
|             denomination: IDydx.AssetDenomination.Wei,      // Wei => actual token amount held in account. |  | ||||||
|             ref: IDydx.AssetReference.Delta,                // Delta => a relative amount. |  | ||||||
|             value: amount                                   // amount to withdraw. |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         // Create withdraw action. |  | ||||||
|         withdrawAction = IDydx.ActionArgs({ |  | ||||||
|             actionType: IDydx.ActionType.Withdraw,          // withdraw tokens. |  | ||||||
|             amount: amountToWithdraw,                       // amount to withdraw. |  | ||||||
|             accountIdx: bridgeAction.accountIdx,            // index in the `accounts` when calling `operate`. |  | ||||||
|             primaryMarketId: bridgeAction.marketId,         // indicates which token to withdraw. |  | ||||||
|             otherAddress: withdrawTo,                       // withdraw tokens to this address. |  | ||||||
|             // unused parameters |  | ||||||
|             secondaryMarketId: 0, |  | ||||||
|             otherAccountIdx: 0, |  | ||||||
|             data: hex'' |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,98 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2019 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
| pragma experimental ABIEncoderV2; |  | ||||||
|  |  | ||||||
| import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; |  | ||||||
| import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol"; |  | ||||||
| import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol"; |  | ||||||
| import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol"; |  | ||||||
| import "../interfaces/IERC20Bridge.sol"; |  | ||||||
| import "../interfaces/IEth2Dai.sol"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| // solhint-disable space-after-comma |  | ||||||
| contract Eth2DaiBridge is |  | ||||||
|     IERC20Bridge, |  | ||||||
|     IWallet, |  | ||||||
|     DeploymentConstants |  | ||||||
| { |  | ||||||
|     /// @dev Callback for `IERC20Bridge`. Tries to buy `amount` of |  | ||||||
|     ///      `toTokenAddress` tokens by selling the entirety of the opposing asset |  | ||||||
|     ///      (DAI or WETH) to the Eth2Dai contract, then transfers the bought |  | ||||||
|     ///      tokens to `to`. |  | ||||||
|     /// @param toTokenAddress The token to give to `to` (either DAI or WETH). |  | ||||||
|     /// @param from The maker (this contract). |  | ||||||
|     /// @param to The recipient of the bought tokens. |  | ||||||
|     /// @param amount Minimum amount of `toTokenAddress` tokens to buy. |  | ||||||
|     /// @param bridgeData The abi-encoeded "from" token address. |  | ||||||
|     /// @return success The magic bytes if successful. |  | ||||||
|     function bridgeTransferFrom( |  | ||||||
|         address toTokenAddress, |  | ||||||
|         address from, |  | ||||||
|         address to, |  | ||||||
|         uint256 amount, |  | ||||||
|         bytes calldata bridgeData |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         returns (bytes4 success) |  | ||||||
|     { |  | ||||||
|         // Decode the bridge data to get the `fromTokenAddress`. |  | ||||||
|         (address fromTokenAddress) = abi.decode(bridgeData, (address)); |  | ||||||
|  |  | ||||||
|         IEth2Dai exchange = IEth2Dai(_getEth2DaiAddress()); |  | ||||||
|         uint256 fromTokenBalance = IERC20Token(fromTokenAddress).balanceOf(address(this)); |  | ||||||
|         // Grant an allowance to the exchange to spend `fromTokenAddress` token. |  | ||||||
|         LibERC20Token.approveIfBelow(fromTokenAddress, address(exchange), fromTokenBalance); |  | ||||||
|  |  | ||||||
|         // Try to sell all of this contract's `fromTokenAddress` token balance. |  | ||||||
|         uint256 boughtAmount = exchange.sellAllAmount( |  | ||||||
|             fromTokenAddress, |  | ||||||
|             fromTokenBalance, |  | ||||||
|             toTokenAddress, |  | ||||||
|             amount |  | ||||||
|         ); |  | ||||||
|         // Transfer the converted `toToken`s to `to`. |  | ||||||
|         LibERC20Token.transfer(toTokenAddress, to, boughtAmount); |  | ||||||
|  |  | ||||||
|         emit ERC20BridgeTransfer( |  | ||||||
|             fromTokenAddress, |  | ||||||
|             toTokenAddress, |  | ||||||
|             fromTokenBalance, |  | ||||||
|             boughtAmount, |  | ||||||
|             from, |  | ||||||
|             to |  | ||||||
|         ); |  | ||||||
|         return BRIDGE_SUCCESS; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev `SignatureType.Wallet` callback, so that this bridge can be the maker |  | ||||||
|     ///      and sign for itself in orders. Always succeeds. |  | ||||||
|     /// @return magicValue Magic success bytes, always. |  | ||||||
|     function isValidSignature( |  | ||||||
|         bytes32, |  | ||||||
|         bytes calldata |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (bytes4 magicValue) |  | ||||||
|     { |  | ||||||
|         return LEGACY_WALLET_MAGIC_VALUE; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,169 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2020 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
| pragma experimental ABIEncoderV2; |  | ||||||
|  |  | ||||||
| import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; |  | ||||||
| import "@0x/contracts-erc20/contracts/src/interfaces/IEtherToken.sol"; |  | ||||||
| import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol"; |  | ||||||
| import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol"; |  | ||||||
| import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol"; |  | ||||||
| import "@0x/contracts-utils/contracts/src/LibSafeMath.sol"; |  | ||||||
| import "../interfaces/IERC20Bridge.sol"; |  | ||||||
| import "../interfaces/IKyberNetworkProxy.sol"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| // solhint-disable space-after-comma |  | ||||||
| contract KyberBridge is |  | ||||||
|     IERC20Bridge, |  | ||||||
|     IWallet, |  | ||||||
|     DeploymentConstants |  | ||||||
| { |  | ||||||
|     using LibSafeMath for uint256; |  | ||||||
|  |  | ||||||
|     // @dev Structure used internally to get around stack limits. |  | ||||||
|     struct TradeState { |  | ||||||
|         IKyberNetworkProxy kyber; |  | ||||||
|         IEtherToken weth; |  | ||||||
|         address fromTokenAddress; |  | ||||||
|         uint256 fromTokenBalance; |  | ||||||
|         uint256 payableAmount; |  | ||||||
|         uint256 conversionRate; |  | ||||||
|         bytes hint; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Kyber ETH pseudo-address. |  | ||||||
|     address constant public KYBER_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; |  | ||||||
|     /// @dev `bridgeTransferFrom()` failure result. |  | ||||||
|     bytes4 constant private BRIDGE_FAILED = 0x0; |  | ||||||
|     /// @dev Precision of Kyber rates. |  | ||||||
|     uint256 constant private KYBER_RATE_BASE = 10 ** 18; |  | ||||||
|  |  | ||||||
|     // solhint-disable no-empty-blocks |  | ||||||
|     /// @dev Payable fallback to receive ETH from Kyber/WETH. |  | ||||||
|     function () |  | ||||||
|         external |  | ||||||
|         payable |  | ||||||
|     { |  | ||||||
|         // Poor man's receive in 0.5.9 |  | ||||||
|         require(msg.data.length == 0); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Callback for `IKyberBridge`. Tries to buy `amount` of |  | ||||||
|     ///      `toTokenAddress` tokens by selling the entirety of the opposing asset |  | ||||||
|     ///      to the `KyberNetworkProxy` contract, then transfers the bought |  | ||||||
|     ///      tokens to `to`. |  | ||||||
|     /// @param toTokenAddress The token to give to `to`. |  | ||||||
|     /// @param from The maker (this contract). |  | ||||||
|     /// @param to The recipient of the bought tokens. |  | ||||||
|     /// @param amount Minimum amount of `toTokenAddress` tokens to buy. |  | ||||||
|     /// @param bridgeData The abi-encoeded "from" token address. |  | ||||||
|     /// @return success The magic bytes if successful. |  | ||||||
|     function bridgeTransferFrom( |  | ||||||
|         address toTokenAddress, |  | ||||||
|         address from, |  | ||||||
|         address to, |  | ||||||
|         uint256 amount, |  | ||||||
|         bytes calldata bridgeData |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         returns (bytes4 success) |  | ||||||
|     { |  | ||||||
|         TradeState memory state; |  | ||||||
|         state.kyber = IKyberNetworkProxy(_getKyberNetworkProxyAddress()); |  | ||||||
|         state.weth = IEtherToken(_getWethAddress()); |  | ||||||
|         // Decode the bridge data to get the `fromTokenAddress`. |  | ||||||
|         (state.fromTokenAddress, state.hint) = abi.decode(bridgeData, (address, bytes)); |  | ||||||
|         // Query the balance of "from" tokens. |  | ||||||
|         state.fromTokenBalance = IERC20Token(state.fromTokenAddress).balanceOf(address(this)); |  | ||||||
|         if (state.fromTokenBalance == 0) { |  | ||||||
|             // Return failure if no input tokens. |  | ||||||
|             return BRIDGE_FAILED; |  | ||||||
|         } |  | ||||||
|         if (state.fromTokenAddress == toTokenAddress) { |  | ||||||
|             // Just transfer the tokens if they're the same. |  | ||||||
|             LibERC20Token.transfer(state.fromTokenAddress, to, state.fromTokenBalance); |  | ||||||
|             return BRIDGE_SUCCESS; |  | ||||||
|         } |  | ||||||
|         if (state.fromTokenAddress == address(state.weth)) { |  | ||||||
|             // From WETH |  | ||||||
|             state.fromTokenAddress = KYBER_ETH_ADDRESS; |  | ||||||
|             state.payableAmount = state.fromTokenBalance; |  | ||||||
|             state.weth.withdraw(state.fromTokenBalance); |  | ||||||
|         } else { |  | ||||||
|             LibERC20Token.approveIfBelow( |  | ||||||
|                 state.fromTokenAddress, |  | ||||||
|                 address(state.kyber), |  | ||||||
|                 state.fromTokenBalance |  | ||||||
|             ); |  | ||||||
|         } |  | ||||||
|         bool isToTokenWeth = toTokenAddress == address(state.weth); |  | ||||||
|  |  | ||||||
|         // Try to sell all of this contract's input token balance through |  | ||||||
|         // `KyberNetworkProxy.trade()`. |  | ||||||
|         uint256 boughtAmount = state.kyber.tradeWithHint.value(state.payableAmount)( |  | ||||||
|             // Input token. |  | ||||||
|             state.fromTokenAddress, |  | ||||||
|             // Sell amount. |  | ||||||
|             state.fromTokenBalance, |  | ||||||
|             // Output token. |  | ||||||
|             isToTokenWeth ? KYBER_ETH_ADDRESS : toTokenAddress, |  | ||||||
|             // Transfer to this contract if converting to ETH, otherwise |  | ||||||
|             // transfer directly to the recipient. |  | ||||||
|             isToTokenWeth ? address(uint160(address(this))) : address(uint160(to)), |  | ||||||
|             // Buy as much as possible. |  | ||||||
|             uint256(-1), |  | ||||||
|             // The minimum conversion rate |  | ||||||
|             1, |  | ||||||
|             // No affiliate address. |  | ||||||
|             address(0), |  | ||||||
|             state.hint |  | ||||||
|         ); |  | ||||||
|         // Wrap ETH output and transfer to recipient. |  | ||||||
|         if (isToTokenWeth) { |  | ||||||
|             state.weth.deposit.value(boughtAmount)(); |  | ||||||
|             state.weth.transfer(to, boughtAmount); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         emit ERC20BridgeTransfer( |  | ||||||
|             state.fromTokenAddress == KYBER_ETH_ADDRESS ? address(state.weth) : state.fromTokenAddress, |  | ||||||
|             toTokenAddress, |  | ||||||
|             state.fromTokenBalance, |  | ||||||
|             boughtAmount, |  | ||||||
|             from, |  | ||||||
|             to |  | ||||||
|         ); |  | ||||||
|         return BRIDGE_SUCCESS; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev `SignatureType.Wallet` callback, so that this bridge can be the maker |  | ||||||
|     ///      and sign for itself in orders. Always succeeds. |  | ||||||
|     /// @return magicValue Magic success bytes, always. |  | ||||||
|     function isValidSignature( |  | ||||||
|         bytes32, |  | ||||||
|         bytes calldata |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (bytes4 magicValue) |  | ||||||
|     { |  | ||||||
|         return LEGACY_WALLET_MAGIC_VALUE; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } |  | ||||||
| @@ -1,94 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2019 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
| pragma experimental ABIEncoderV2; |  | ||||||
|  |  | ||||||
| import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; |  | ||||||
| import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol"; |  | ||||||
| import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol"; |  | ||||||
| import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol"; |  | ||||||
| import "../interfaces/IERC20Bridge.sol"; |  | ||||||
| import "../interfaces/IMStable.sol"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| contract MStableBridge is |  | ||||||
|     IERC20Bridge, |  | ||||||
|     IWallet, |  | ||||||
|     DeploymentConstants |  | ||||||
| { |  | ||||||
|  |  | ||||||
|     /// @dev Swaps specified tokens against the mStable mUSD contract |  | ||||||
|     /// @param toTokenAddress The token to give to `to` (i.e DAI, USDC, USDT). |  | ||||||
|     /// @param from The maker (this contract). |  | ||||||
|     /// @param to The recipient of the bought tokens. |  | ||||||
|     /// @param amount Minimum amount of `toTokenAddress` tokens to buy. |  | ||||||
|     /// @param bridgeData The abi-encoded "from" token address. |  | ||||||
|     /// @return success The magic bytes if successful. |  | ||||||
|     // solhint-disable no-unused-vars |  | ||||||
|     function bridgeTransferFrom( |  | ||||||
|         address toTokenAddress, |  | ||||||
|         address from, |  | ||||||
|         address to, |  | ||||||
|         uint256 amount, |  | ||||||
|         bytes calldata bridgeData |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         returns (bytes4 success) |  | ||||||
|     { |  | ||||||
|         // Decode the bridge data to get the `fromTokenAddress`. |  | ||||||
|         (address fromTokenAddress) = abi.decode(bridgeData, (address)); |  | ||||||
|  |  | ||||||
|         IMStable exchange = IMStable(_getMUsdAddress()); |  | ||||||
|         uint256 fromTokenBalance = IERC20Token(fromTokenAddress).balanceOf(address(this)); |  | ||||||
|         // Grant an allowance to the exchange to spend `fromTokenAddress` token. |  | ||||||
|         LibERC20Token.approveIfBelow(fromTokenAddress, address(exchange), fromTokenBalance); |  | ||||||
|  |  | ||||||
|         // Try to sell all of this contract's `fromTokenAddress` token balance. |  | ||||||
|         uint256 boughtAmount = exchange.swap( |  | ||||||
|             fromTokenAddress, |  | ||||||
|             toTokenAddress, |  | ||||||
|             fromTokenBalance, |  | ||||||
|             to |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         emit ERC20BridgeTransfer( |  | ||||||
|             fromTokenAddress, |  | ||||||
|             toTokenAddress, |  | ||||||
|             fromTokenBalance, |  | ||||||
|             boughtAmount, |  | ||||||
|             from, |  | ||||||
|             to |  | ||||||
|         ); |  | ||||||
|         return BRIDGE_SUCCESS; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev `SignatureType.Wallet` callback, so that this bridge can be the maker |  | ||||||
|     ///      and sign for itself in orders. Always succeeds. |  | ||||||
|     /// @return magicValue Magic success bytes, always. |  | ||||||
|     function isValidSignature( |  | ||||||
|         bytes32, |  | ||||||
|         bytes calldata |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (bytes4 magicValue) |  | ||||||
|     { |  | ||||||
|         return LEGACY_WALLET_MAGIC_VALUE; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,55 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2019 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.16; |  | ||||||
|  |  | ||||||
| import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol"; |  | ||||||
| import "../interfaces/IGasToken.sol"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| contract MixinGasToken is |  | ||||||
|     DeploymentConstants |  | ||||||
| { |  | ||||||
|  |  | ||||||
|     /// @dev Frees gas tokens based on the amount of gas consumed in the function |  | ||||||
|     modifier freesGasTokens { |  | ||||||
|         uint256 gasBefore = gasleft(); |  | ||||||
|         _; |  | ||||||
|         IGasToken gst = IGasToken(_getGstAddress()); |  | ||||||
|         if (address(gst) != address(0)) { |  | ||||||
|             // (gasUsed + FREE_BASE) / (2 * REIMBURSE - FREE_TOKEN) |  | ||||||
|             //            14154             24000        6870 |  | ||||||
|             uint256 value = (gasBefore - gasleft() + 14154) / 41130; |  | ||||||
|             gst.freeUpTo(value); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Frees gas tokens using the balance of `from`. Amount freed is based |  | ||||||
|     ///     on the gas consumed in the function |  | ||||||
|     modifier freesGasTokensFromCollector() { |  | ||||||
|         uint256 gasBefore = gasleft(); |  | ||||||
|         _; |  | ||||||
|         IGasToken gst = IGasToken(_getGstAddress()); |  | ||||||
|         if (address(gst) != address(0)) { |  | ||||||
|             // (gasUsed + FREE_BASE) / (2 * REIMBURSE - FREE_TOKEN) |  | ||||||
|             //            14154             24000        6870 |  | ||||||
|             uint256 value = (gasBefore - gasleft() + 14154) / 41130; |  | ||||||
|             gst.freeFromUpTo(_getGstCollectorAddress(), value); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,148 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2020 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
| pragma experimental ABIEncoderV2; |  | ||||||
|  |  | ||||||
| import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; |  | ||||||
| import "@0x/contracts-erc20/contracts/src/interfaces/IEtherToken.sol"; |  | ||||||
| import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol"; |  | ||||||
| import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol"; |  | ||||||
| import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol"; |  | ||||||
| import "../interfaces/IERC20Bridge.sol"; |  | ||||||
| import "../interfaces/IMooniswap.sol"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| // solhint-disable space-after-comma |  | ||||||
| // solhint-disable not-rely-on-time |  | ||||||
| contract MooniswapBridge is |  | ||||||
|     IERC20Bridge, |  | ||||||
|     IWallet, |  | ||||||
|     DeploymentConstants |  | ||||||
| { |  | ||||||
|  |  | ||||||
|     struct TransferState { |  | ||||||
|         IMooniswap pool; |  | ||||||
|         uint256 fromTokenBalance; |  | ||||||
|         IEtherToken weth; |  | ||||||
|         uint256 boughtAmount; |  | ||||||
|         address fromTokenAddress; |  | ||||||
|         address toTokenAddress; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // solhint-disable no-empty-blocks |  | ||||||
|     /// @dev Payable fallback to receive ETH from uniswap. |  | ||||||
|     function () |  | ||||||
|         external |  | ||||||
|         payable |  | ||||||
|     {} |  | ||||||
|  |  | ||||||
|     /// @dev Callback for `IERC20Bridge`. Tries to buy `amount` of |  | ||||||
|     ///      `toTokenAddress` tokens by selling the entirety of the `fromTokenAddress` |  | ||||||
|     ///      token encoded in the bridge data. |  | ||||||
|     /// @param toTokenAddress The token to buy and transfer to `to`. |  | ||||||
|     /// @param from The maker (this contract). |  | ||||||
|     /// @param to The recipient of the bought tokens. |  | ||||||
|     /// @param amount Minimum amount of `toTokenAddress` tokens to buy. |  | ||||||
|     /// @param bridgeData The abi-encoded path of token addresses. Last element must be toTokenAddress |  | ||||||
|     /// @return success The magic bytes if successful. |  | ||||||
|     function bridgeTransferFrom( |  | ||||||
|         address toTokenAddress, |  | ||||||
|         address from, |  | ||||||
|         address to, |  | ||||||
|         uint256 amount, |  | ||||||
|         bytes calldata bridgeData |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         returns (bytes4 success) |  | ||||||
|     { |  | ||||||
|         // State memory object to avoid stack overflows. |  | ||||||
|         TransferState memory state; |  | ||||||
|         // Decode the bridge data to get the `fromTokenAddress`. |  | ||||||
|         address fromTokenAddress = abi.decode(bridgeData, (address)); |  | ||||||
|         // Get the weth contract. |  | ||||||
|         state.weth = IEtherToken(_getWethAddress()); |  | ||||||
|         // Get our balance of `fromTokenAddress` token. |  | ||||||
|         state.fromTokenBalance = IERC20Token(fromTokenAddress).balanceOf(address(this)); |  | ||||||
|  |  | ||||||
|         state.fromTokenAddress = fromTokenAddress == address(state.weth) ? address(0) : fromTokenAddress; |  | ||||||
|         state.toTokenAddress = toTokenAddress == address(state.weth) ? address(0) : toTokenAddress; |  | ||||||
|         state.pool = IMooniswap( |  | ||||||
|             IMooniswapRegistry(_getMooniswapAddress()).pools( |  | ||||||
|                 state.fromTokenAddress, |  | ||||||
|                 state.toTokenAddress |  | ||||||
|             ) |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         // withdraw WETH to ETH |  | ||||||
|         if (state.fromTokenAddress == address(0)) { |  | ||||||
|             state.weth.withdraw(state.fromTokenBalance); |  | ||||||
|         } else { |  | ||||||
|             // Grant the pool an allowance. |  | ||||||
|             LibERC20Token.approveIfBelow( |  | ||||||
|                 state.fromTokenAddress, |  | ||||||
|                 address(state.pool), |  | ||||||
|                 state.fromTokenBalance |  | ||||||
|             ); |  | ||||||
|         } |  | ||||||
|         uint256 ethValue = state.fromTokenAddress == address(0) ? state.fromTokenBalance : 0; |  | ||||||
|         state.boughtAmount = state.pool.swap.value(ethValue)( |  | ||||||
|             state.fromTokenAddress, |  | ||||||
|             state.toTokenAddress, |  | ||||||
|             state.fromTokenBalance, |  | ||||||
|             amount, |  | ||||||
|             address(0) |  | ||||||
|         ); |  | ||||||
|         // Deposit to WETH |  | ||||||
|         if (state.toTokenAddress == address(0)) { |  | ||||||
|             state.weth.deposit.value(state.boughtAmount)(); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // Transfer funds to `to` |  | ||||||
|         LibERC20Token.transfer(toTokenAddress, to, state.boughtAmount); |  | ||||||
|  |  | ||||||
|         emit ERC20BridgeTransfer( |  | ||||||
|             // input token |  | ||||||
|             fromTokenAddress, |  | ||||||
|             // output token |  | ||||||
|             toTokenAddress, |  | ||||||
|             // input token amount |  | ||||||
|             state.fromTokenBalance, |  | ||||||
|             // output token amount |  | ||||||
|             state.boughtAmount, |  | ||||||
|             from, |  | ||||||
|             to |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         return BRIDGE_SUCCESS; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev `SignatureType.Wallet` callback, so that this bridge can be the maker |  | ||||||
|     ///      and sign for itself in orders. Always succeeds. |  | ||||||
|     /// @return magicValue Success bytes, always. |  | ||||||
|     function isValidSignature( |  | ||||||
|         bytes32, |  | ||||||
|         bytes calldata |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (bytes4 magicValue) |  | ||||||
|     { |  | ||||||
|         return LEGACY_WALLET_MAGIC_VALUE; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,95 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2019 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
| pragma experimental ABIEncoderV2; |  | ||||||
|  |  | ||||||
| import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; |  | ||||||
| import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol"; |  | ||||||
| import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol"; |  | ||||||
| import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol"; |  | ||||||
| import "../interfaces/IERC20Bridge.sol"; |  | ||||||
| import "../interfaces/IShell.sol"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| contract ShellBridge is |  | ||||||
|     IERC20Bridge, |  | ||||||
|     IWallet, |  | ||||||
|     DeploymentConstants |  | ||||||
| { |  | ||||||
|  |  | ||||||
|     /// @dev Swaps specified tokens against the Shell contract |  | ||||||
|     /// @param toTokenAddress The token to give to `to`. |  | ||||||
|     /// @param from The maker (this contract). |  | ||||||
|     /// @param to The recipient of the bought tokens. |  | ||||||
|     /// @param amount Minimum amount of `toTokenAddress` tokens to buy. |  | ||||||
|     /// @param bridgeData The abi-encoded "from" token address. |  | ||||||
|     /// @return success The magic bytes if successful. |  | ||||||
|     // solhint-disable no-unused-vars |  | ||||||
|     function bridgeTransferFrom( |  | ||||||
|         address toTokenAddress, |  | ||||||
|         address from, |  | ||||||
|         address to, |  | ||||||
|         uint256 amount, |  | ||||||
|         bytes calldata bridgeData |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         returns (bytes4 success) |  | ||||||
|     { |  | ||||||
|         // Decode the bridge data to get the `fromTokenAddress` and `pool`. |  | ||||||
|         (address fromTokenAddress, address pool) = abi.decode(bridgeData, (address, address)); |  | ||||||
|  |  | ||||||
|         uint256 fromTokenBalance = IERC20Token(fromTokenAddress).balanceOf(address(this)); |  | ||||||
|         // Grant an allowance to the exchange to spend `fromTokenAddress` token. |  | ||||||
|         LibERC20Token.approveIfBelow(fromTokenAddress, pool, fromTokenBalance); |  | ||||||
|  |  | ||||||
|         // Try to sell all of this contract's `fromTokenAddress` token balance. |  | ||||||
|         uint256 boughtAmount = IShell(pool).originSwap( |  | ||||||
|             fromTokenAddress, |  | ||||||
|             toTokenAddress, |  | ||||||
|             fromTokenBalance, |  | ||||||
|             amount, // min amount |  | ||||||
|             block.timestamp + 1 |  | ||||||
|         ); |  | ||||||
|         LibERC20Token.transfer(toTokenAddress, to, boughtAmount); |  | ||||||
|  |  | ||||||
|         emit ERC20BridgeTransfer( |  | ||||||
|             fromTokenAddress, |  | ||||||
|             toTokenAddress, |  | ||||||
|             fromTokenBalance, |  | ||||||
|             boughtAmount, |  | ||||||
|             from, |  | ||||||
|             to |  | ||||||
|         ); |  | ||||||
|         return BRIDGE_SUCCESS; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev `SignatureType.Wallet` callback, so that this bridge can be the maker |  | ||||||
|     ///      and sign for itself in orders. Always succeeds. |  | ||||||
|     /// @return magicValue Magic success bytes, always. |  | ||||||
|     function isValidSignature( |  | ||||||
|         bytes32, |  | ||||||
|         bytes calldata |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (bytes4 magicValue) |  | ||||||
|     { |  | ||||||
|         return LEGACY_WALLET_MAGIC_VALUE; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,119 +0,0 @@ | |||||||
|  |  | ||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2019 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
| pragma experimental ABIEncoderV2; |  | ||||||
|  |  | ||||||
| import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; |  | ||||||
| import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol"; |  | ||||||
| import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol"; |  | ||||||
| import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol"; |  | ||||||
| import "../interfaces/IERC20Bridge.sol"; |  | ||||||
| import "../interfaces/ICurve.sol"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| // solhint-disable not-rely-on-time |  | ||||||
| // solhint-disable space-after-comma |  | ||||||
| contract SnowSwapBridge is |  | ||||||
|     IERC20Bridge, |  | ||||||
|     IWallet, |  | ||||||
|     DeploymentConstants |  | ||||||
| { |  | ||||||
|     struct SnowSwapBridgeData { |  | ||||||
|         address curveAddress; |  | ||||||
|         bytes4 exchangeFunctionSelector; |  | ||||||
|         address fromTokenAddress; |  | ||||||
|         int128 fromCoinIdx; |  | ||||||
|         int128 toCoinIdx; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Callback for `ICurve`. Tries to buy `amount` of |  | ||||||
|     ///      `toTokenAddress` tokens by selling the entirety of the opposing asset |  | ||||||
|     ///      (DAI, USDC) to the Curve contract, then transfers the bought |  | ||||||
|     ///      tokens to `to`. |  | ||||||
|     /// @param toTokenAddress The token to give to `to` (i.e DAI, USDC, USDT). |  | ||||||
|     /// @param from The maker (this contract). |  | ||||||
|     /// @param to The recipient of the bought tokens. |  | ||||||
|     /// @param amount Minimum amount of `toTokenAddress` tokens to buy. |  | ||||||
|     /// @param bridgeData The abi-encoeded "from" token address. |  | ||||||
|     /// @return success The magic bytes if successful. |  | ||||||
|     function bridgeTransferFrom( |  | ||||||
|         address toTokenAddress, |  | ||||||
|         address from, |  | ||||||
|         address to, |  | ||||||
|         uint256 amount, |  | ||||||
|         bytes calldata bridgeData |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         returns (bytes4 success) |  | ||||||
|     { |  | ||||||
|         // Decode the bridge data to get the SnowSwap metadata. |  | ||||||
|         SnowSwapBridgeData memory data = abi.decode(bridgeData, (SnowSwapBridgeData)); |  | ||||||
|  |  | ||||||
|         require(toTokenAddress != data.fromTokenAddress, "SnowSwapBridge/INVALID_PAIR"); |  | ||||||
|         uint256 fromTokenBalance = IERC20Token(data.fromTokenAddress).balanceOf(address(this)); |  | ||||||
|         // Grant an allowance to the exchange to spend `fromTokenAddress` token. |  | ||||||
|         LibERC20Token.approveIfBelow(data.fromTokenAddress, data.curveAddress, fromTokenBalance); |  | ||||||
|  |  | ||||||
|         // Try to sell all of this contract's `fromTokenAddress` token balance. |  | ||||||
|         { |  | ||||||
|             (bool didSucceed, bytes memory resultData) = |  | ||||||
|                 data.curveAddress.call(abi.encodeWithSelector( |  | ||||||
|                     data.exchangeFunctionSelector, |  | ||||||
|                     data.fromCoinIdx, |  | ||||||
|                     data.toCoinIdx, |  | ||||||
|                     // dx |  | ||||||
|                     fromTokenBalance, |  | ||||||
|                     // min dy |  | ||||||
|                     amount |  | ||||||
|                 )); |  | ||||||
|             if (!didSucceed) { |  | ||||||
|                 assembly { revert(add(resultData, 32), mload(resultData)) } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         uint256 toTokenBalance = IERC20Token(toTokenAddress).balanceOf(address(this)); |  | ||||||
|         // Transfer the converted `toToken`s to `to`. |  | ||||||
|         LibERC20Token.transfer(toTokenAddress, to, toTokenBalance); |  | ||||||
|  |  | ||||||
|         emit ERC20BridgeTransfer( |  | ||||||
|             data.fromTokenAddress, |  | ||||||
|             toTokenAddress, |  | ||||||
|             fromTokenBalance, |  | ||||||
|             toTokenBalance, |  | ||||||
|             from, |  | ||||||
|             to |  | ||||||
|         ); |  | ||||||
|         return BRIDGE_SUCCESS; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev `SignatureType.Wallet` callback, so that this bridge can be the maker |  | ||||||
|     ///      and sign for itself in orders. Always succeeds. |  | ||||||
|     /// @return magicValue Magic success bytes, always. |  | ||||||
|     function isValidSignature( |  | ||||||
|         bytes32, |  | ||||||
|         bytes calldata |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (bytes4 magicValue) |  | ||||||
|     { |  | ||||||
|         return LEGACY_WALLET_MAGIC_VALUE; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,136 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2020 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
| pragma experimental ABIEncoderV2; |  | ||||||
|  |  | ||||||
| import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; |  | ||||||
| import "@0x/contracts-erc20/contracts/src/interfaces/IEtherToken.sol"; |  | ||||||
| import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol"; |  | ||||||
| import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol"; |  | ||||||
| import "@0x/contracts-utils/contracts/src/LibAddressArray.sol"; |  | ||||||
| import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol"; |  | ||||||
| import "../interfaces/IUniswapV2Router01.sol"; |  | ||||||
| import "../interfaces/IERC20Bridge.sol"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| // solhint-disable space-after-comma |  | ||||||
| // solhint-disable not-rely-on-time |  | ||||||
| contract SushiSwapBridge is |  | ||||||
|     IERC20Bridge, |  | ||||||
|     IWallet, |  | ||||||
|     DeploymentConstants |  | ||||||
| { |  | ||||||
|     struct TransferState { |  | ||||||
|         address[] path; |  | ||||||
|         address router; |  | ||||||
|         uint256 fromTokenBalance; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Callback for `IERC20Bridge`. Tries to buy `amount` of |  | ||||||
|     ///      `toTokenAddress` tokens by selling the entirety of the `fromTokenAddress` |  | ||||||
|     ///      token encoded in the bridge data. |  | ||||||
|     /// @param toTokenAddress The token to buy and transfer to `to`. |  | ||||||
|     /// @param from The maker (this contract). |  | ||||||
|     /// @param to The recipient of the bought tokens. |  | ||||||
|     /// @param amount Minimum amount of `toTokenAddress` tokens to buy. |  | ||||||
|     /// @param bridgeData The abi-encoded path of token addresses. Last element must be toTokenAddress |  | ||||||
|     /// @return success The magic bytes if successful. |  | ||||||
|     function bridgeTransferFrom( |  | ||||||
|         address toTokenAddress, |  | ||||||
|         address from, |  | ||||||
|         address to, |  | ||||||
|         uint256 amount, |  | ||||||
|         bytes calldata bridgeData |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         returns (bytes4 success) |  | ||||||
|     { |  | ||||||
|         // hold variables to get around stack depth limitations |  | ||||||
|         TransferState memory state; |  | ||||||
|  |  | ||||||
|         // Decode the bridge data to get the `fromTokenAddress`. |  | ||||||
|         // solhint-disable indent |  | ||||||
|         (state.path, state.router) = abi.decode(bridgeData, (address[], address)); |  | ||||||
|         // solhint-enable indent |  | ||||||
|  |  | ||||||
|         require(state.path.length >= 2, "SushiSwapBridge/PATH_LENGTH_MUST_BE_AT_LEAST_TWO"); |  | ||||||
|         require(state.path[state.path.length - 1] == toTokenAddress, "SushiSwapBridge/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN"); |  | ||||||
|  |  | ||||||
|         // Just transfer the tokens if they're the same. |  | ||||||
|         if (state.path[0] == toTokenAddress) { |  | ||||||
|             LibERC20Token.transfer(state.path[0], to, amount); |  | ||||||
|             return BRIDGE_SUCCESS; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // Get our balance of `fromTokenAddress` token. |  | ||||||
|         state.fromTokenBalance = IERC20Token(state.path[0]).balanceOf(address(this)); |  | ||||||
|  |  | ||||||
|         // Grant the SushiSwap router an allowance. |  | ||||||
|         LibERC20Token.approveIfBelow( |  | ||||||
|             state.path[0], |  | ||||||
|             state.router, |  | ||||||
|             state.fromTokenBalance |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         // Buy as much `toTokenAddress` token with `fromTokenAddress` token |  | ||||||
|         // and transfer it to `to`. |  | ||||||
|         IUniswapV2Router01 router = IUniswapV2Router01(state.router); |  | ||||||
|         uint[] memory amounts = router.swapExactTokensForTokens( |  | ||||||
|              // Sell all tokens we hold. |  | ||||||
|             state.fromTokenBalance, |  | ||||||
|              // Minimum buy amount. |  | ||||||
|             amount, |  | ||||||
|             // Convert `fromTokenAddress` to `toTokenAddress`. |  | ||||||
|             state.path, |  | ||||||
|             // Recipient is `to`. |  | ||||||
|             to, |  | ||||||
|             // Expires after this block. |  | ||||||
|             block.timestamp |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         emit ERC20BridgeTransfer( |  | ||||||
|             // input token |  | ||||||
|             state.path[0], |  | ||||||
|             // output token |  | ||||||
|             toTokenAddress, |  | ||||||
|             // input token amount |  | ||||||
|             state.fromTokenBalance, |  | ||||||
|             // output token amount |  | ||||||
|             amounts[amounts.length - 1], |  | ||||||
|             from, |  | ||||||
|             to |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         return BRIDGE_SUCCESS; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev `SignatureType.Wallet` callback, so that this bridge can be the maker |  | ||||||
|     ///      and sign for itself in orders. Always succeeds. |  | ||||||
|     /// @return magicValue Success bytes, always. |  | ||||||
|     function isValidSignature( |  | ||||||
|         bytes32, |  | ||||||
|         bytes calldata |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (bytes4 magicValue) |  | ||||||
|     { |  | ||||||
|         return LEGACY_WALLET_MAGIC_VALUE; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,119 +0,0 @@ | |||||||
|  |  | ||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2019 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
| pragma experimental ABIEncoderV2; |  | ||||||
|  |  | ||||||
| import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; |  | ||||||
| import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol"; |  | ||||||
| import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol"; |  | ||||||
| import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol"; |  | ||||||
| import "../interfaces/IERC20Bridge.sol"; |  | ||||||
| import "../interfaces/ICurve.sol"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| // solhint-disable not-rely-on-time |  | ||||||
| // solhint-disable space-after-comma |  | ||||||
| contract SwerveBridge is |  | ||||||
|     IERC20Bridge, |  | ||||||
|     IWallet, |  | ||||||
|     DeploymentConstants |  | ||||||
| { |  | ||||||
|     struct SwerveBridgeData { |  | ||||||
|         address curveAddress; |  | ||||||
|         bytes4 exchangeFunctionSelector; |  | ||||||
|         address fromTokenAddress; |  | ||||||
|         int128 fromCoinIdx; |  | ||||||
|         int128 toCoinIdx; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Callback for `ICurve`. Tries to buy `amount` of |  | ||||||
|     ///      `toTokenAddress` tokens by selling the entirety of the opposing asset |  | ||||||
|     ///      (DAI, USDC) to the Curve contract, then transfers the bought |  | ||||||
|     ///      tokens to `to`. |  | ||||||
|     /// @param toTokenAddress The token to give to `to` (i.e DAI, USDC, USDT). |  | ||||||
|     /// @param from The maker (this contract). |  | ||||||
|     /// @param to The recipient of the bought tokens. |  | ||||||
|     /// @param amount Minimum amount of `toTokenAddress` tokens to buy. |  | ||||||
|     /// @param bridgeData The abi-encoeded "from" token address. |  | ||||||
|     /// @return success The magic bytes if successful. |  | ||||||
|     function bridgeTransferFrom( |  | ||||||
|         address toTokenAddress, |  | ||||||
|         address from, |  | ||||||
|         address to, |  | ||||||
|         uint256 amount, |  | ||||||
|         bytes calldata bridgeData |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         returns (bytes4 success) |  | ||||||
|     { |  | ||||||
|         // Decode the bridge data to get the SwerveBridgeData metadata. |  | ||||||
|         SwerveBridgeData memory data = abi.decode(bridgeData, (SwerveBridgeData)); |  | ||||||
|  |  | ||||||
|         require(toTokenAddress != data.fromTokenAddress, "SwerveBridge/INVALID_PAIR"); |  | ||||||
|         uint256 fromTokenBalance = IERC20Token(data.fromTokenAddress).balanceOf(address(this)); |  | ||||||
|         // Grant an allowance to the exchange to spend `fromTokenAddress` token. |  | ||||||
|         LibERC20Token.approveIfBelow(data.fromTokenAddress, data.curveAddress, fromTokenBalance); |  | ||||||
|  |  | ||||||
|         // Try to sell all of this contract's `fromTokenAddress` token balance. |  | ||||||
|         { |  | ||||||
|             (bool didSucceed, bytes memory resultData) = |  | ||||||
|                 data.curveAddress.call(abi.encodeWithSelector( |  | ||||||
|                     data.exchangeFunctionSelector, |  | ||||||
|                     data.fromCoinIdx, |  | ||||||
|                     data.toCoinIdx, |  | ||||||
|                     // dx |  | ||||||
|                     fromTokenBalance, |  | ||||||
|                     // min dy |  | ||||||
|                     amount |  | ||||||
|                 )); |  | ||||||
|             if (!didSucceed) { |  | ||||||
|                 assembly { revert(add(resultData, 32), mload(resultData)) } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         uint256 toTokenBalance = IERC20Token(toTokenAddress).balanceOf(address(this)); |  | ||||||
|         // Transfer the converted `toToken`s to `to`. |  | ||||||
|         LibERC20Token.transfer(toTokenAddress, to, toTokenBalance); |  | ||||||
|  |  | ||||||
|         emit ERC20BridgeTransfer( |  | ||||||
|             data.fromTokenAddress, |  | ||||||
|             toTokenAddress, |  | ||||||
|             fromTokenBalance, |  | ||||||
|             toTokenBalance, |  | ||||||
|             from, |  | ||||||
|             to |  | ||||||
|         ); |  | ||||||
|         return BRIDGE_SUCCESS; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev `SignatureType.Wallet` callback, so that this bridge can be the maker |  | ||||||
|     ///      and sign for itself in orders. Always succeeds. |  | ||||||
|     /// @return magicValue Magic success bytes, always. |  | ||||||
|     function isValidSignature( |  | ||||||
|         bytes32, |  | ||||||
|         bytes calldata |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (bytes4 magicValue) |  | ||||||
|     { |  | ||||||
|         return LEGACY_WALLET_MAGIC_VALUE; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,220 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2019 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
| pragma experimental ABIEncoderV2; |  | ||||||
|  |  | ||||||
| import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; |  | ||||||
| import "@0x/contracts-erc20/contracts/src/interfaces/IEtherToken.sol"; |  | ||||||
| import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol"; |  | ||||||
| import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol"; |  | ||||||
| import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol"; |  | ||||||
| import "../interfaces/IUniswapExchangeFactory.sol"; |  | ||||||
| import "../interfaces/IUniswapExchange.sol"; |  | ||||||
| import "../interfaces/IERC20Bridge.sol"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| // solhint-disable space-after-comma |  | ||||||
| // solhint-disable not-rely-on-time |  | ||||||
| contract UniswapBridge is |  | ||||||
|     IERC20Bridge, |  | ||||||
|     IWallet, |  | ||||||
|     DeploymentConstants |  | ||||||
| { |  | ||||||
|     // Struct to hold `bridgeTransferFrom()` local variables in memory and to avoid |  | ||||||
|     // stack overflows. |  | ||||||
|     struct TransferState { |  | ||||||
|         IUniswapExchange exchange; |  | ||||||
|         uint256 fromTokenBalance; |  | ||||||
|         IEtherToken weth; |  | ||||||
|         uint256 boughtAmount; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // solhint-disable no-empty-blocks |  | ||||||
|     /// @dev Payable fallback to receive ETH from uniswap. |  | ||||||
|     function () |  | ||||||
|         external |  | ||||||
|         payable |  | ||||||
|     {} |  | ||||||
|  |  | ||||||
|     /// @dev Callback for `IERC20Bridge`. Tries to buy `amount` of |  | ||||||
|     ///      `toTokenAddress` tokens by selling the entirety of the `fromTokenAddress` |  | ||||||
|     ///      token encoded in the bridge data. |  | ||||||
|     /// @param toTokenAddress The token to buy and transfer to `to`. |  | ||||||
|     /// @param from The maker (this contract). |  | ||||||
|     /// @param to The recipient of the bought tokens. |  | ||||||
|     /// @param amount Minimum amount of `toTokenAddress` tokens to buy. |  | ||||||
|     /// @param bridgeData The abi-encoded "from" token address. |  | ||||||
|     /// @return success The magic bytes if successful. |  | ||||||
|     function bridgeTransferFrom( |  | ||||||
|         address toTokenAddress, |  | ||||||
|         address from, |  | ||||||
|         address to, |  | ||||||
|         uint256 amount, |  | ||||||
|         bytes calldata bridgeData |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         returns (bytes4 success) |  | ||||||
|     { |  | ||||||
|         // State memory object to avoid stack overflows. |  | ||||||
|         TransferState memory state; |  | ||||||
|         // Decode the bridge data to get the `fromTokenAddress`. |  | ||||||
|         (address fromTokenAddress) = abi.decode(bridgeData, (address)); |  | ||||||
|  |  | ||||||
|         // Just transfer the tokens if they're the same. |  | ||||||
|         if (fromTokenAddress == toTokenAddress) { |  | ||||||
|             LibERC20Token.transfer(fromTokenAddress, to, amount); |  | ||||||
|             return BRIDGE_SUCCESS; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // Get the exchange for the token pair. |  | ||||||
|         state.exchange = _getUniswapExchangeForTokenPair( |  | ||||||
|             fromTokenAddress, |  | ||||||
|             toTokenAddress |  | ||||||
|         ); |  | ||||||
|         // Get our balance of `fromTokenAddress` token. |  | ||||||
|         state.fromTokenBalance = IERC20Token(fromTokenAddress).balanceOf(address(this)); |  | ||||||
|         // Get the weth contract. |  | ||||||
|         state.weth = IEtherToken(_getWethAddress()); |  | ||||||
|  |  | ||||||
|         // Convert from WETH to a token. |  | ||||||
|         if (fromTokenAddress == address(state.weth)) { |  | ||||||
|             // Unwrap the WETH. |  | ||||||
|             state.weth.withdraw(state.fromTokenBalance); |  | ||||||
|             // Buy as much of `toTokenAddress` token with ETH as possible and |  | ||||||
|             // transfer it to `to`. |  | ||||||
|             state.boughtAmount = state.exchange.ethToTokenTransferInput.value(state.fromTokenBalance)( |  | ||||||
|                 // Minimum buy amount. |  | ||||||
|                 amount, |  | ||||||
|                 // Expires after this block. |  | ||||||
|                 block.timestamp, |  | ||||||
|                 // Recipient is `to`. |  | ||||||
|                 to |  | ||||||
|             ); |  | ||||||
|  |  | ||||||
|         // Convert from a token to WETH. |  | ||||||
|         } else if (toTokenAddress == address(state.weth)) { |  | ||||||
|             // Grant the exchange an allowance. |  | ||||||
|             _grantExchangeAllowance(state.exchange, fromTokenAddress, state.fromTokenBalance); |  | ||||||
|             // Buy as much ETH with `fromTokenAddress` token as possible. |  | ||||||
|             state.boughtAmount = state.exchange.tokenToEthSwapInput( |  | ||||||
|                 // Sell all tokens we hold. |  | ||||||
|                 state.fromTokenBalance, |  | ||||||
|                 // Minimum buy amount. |  | ||||||
|                 amount, |  | ||||||
|                 // Expires after this block. |  | ||||||
|                 block.timestamp |  | ||||||
|             ); |  | ||||||
|             // Wrap the ETH. |  | ||||||
|             state.weth.deposit.value(state.boughtAmount)(); |  | ||||||
|             // Transfer the WETH to `to`. |  | ||||||
|             IEtherToken(toTokenAddress).transfer(to, state.boughtAmount); |  | ||||||
|  |  | ||||||
|         // Convert from one token to another. |  | ||||||
|         } else { |  | ||||||
|             // Grant the exchange an allowance. |  | ||||||
|             _grantExchangeAllowance(state.exchange, fromTokenAddress, state.fromTokenBalance); |  | ||||||
|             // Buy as much `toTokenAddress` token with `fromTokenAddress` token |  | ||||||
|             // and transfer it to `to`. |  | ||||||
|             state.boughtAmount = state.exchange.tokenToTokenTransferInput( |  | ||||||
|                 // Sell all tokens we hold. |  | ||||||
|                 state.fromTokenBalance, |  | ||||||
|                 // Minimum buy amount. |  | ||||||
|                 amount, |  | ||||||
|                 // Must buy at least 1 intermediate ETH. |  | ||||||
|                 1, |  | ||||||
|                 // Expires after this block. |  | ||||||
|                 block.timestamp, |  | ||||||
|                 // Recipient is `to`. |  | ||||||
|                 to, |  | ||||||
|                 // Convert to `toTokenAddress`. |  | ||||||
|                 toTokenAddress |  | ||||||
|             ); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         emit ERC20BridgeTransfer( |  | ||||||
|             fromTokenAddress, |  | ||||||
|             toTokenAddress, |  | ||||||
|             state.fromTokenBalance, |  | ||||||
|             state.boughtAmount, |  | ||||||
|             from, |  | ||||||
|             to |  | ||||||
|         ); |  | ||||||
|         return BRIDGE_SUCCESS; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev `SignatureType.Wallet` callback, so that this bridge can be the maker |  | ||||||
|     ///      and sign for itself in orders. Always succeeds. |  | ||||||
|     /// @return magicValue Success bytes, always. |  | ||||||
|     function isValidSignature( |  | ||||||
|         bytes32, |  | ||||||
|         bytes calldata |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (bytes4 magicValue) |  | ||||||
|     { |  | ||||||
|         return LEGACY_WALLET_MAGIC_VALUE; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Grants an unlimited allowance to the exchange for its token |  | ||||||
|     ///      on behalf of this contract. |  | ||||||
|     /// @param exchange The Uniswap token exchange. |  | ||||||
|     /// @param tokenAddress The token address for the exchange. |  | ||||||
|     /// @param minimumAllowance The minimum necessary allowance. |  | ||||||
|     function _grantExchangeAllowance( |  | ||||||
|         IUniswapExchange exchange, |  | ||||||
|         address tokenAddress, |  | ||||||
|         uint256 minimumAllowance |  | ||||||
|     ) |  | ||||||
|         private |  | ||||||
|     { |  | ||||||
|         LibERC20Token.approveIfBelow( |  | ||||||
|             tokenAddress, |  | ||||||
|             address(exchange), |  | ||||||
|             minimumAllowance |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Retrieves the uniswap exchange for a given token pair. |  | ||||||
|     ///      In the case of a WETH-token exchange, this will be the non-WETH token. |  | ||||||
|     ///      In th ecase of a token-token exchange, this will be the first token. |  | ||||||
|     /// @param fromTokenAddress The address of the token we are converting from. |  | ||||||
|     /// @param toTokenAddress The address of the token we are converting to. |  | ||||||
|     /// @return exchange The uniswap exchange. |  | ||||||
|     function _getUniswapExchangeForTokenPair( |  | ||||||
|         address fromTokenAddress, |  | ||||||
|         address toTokenAddress |  | ||||||
|     ) |  | ||||||
|         private |  | ||||||
|         view |  | ||||||
|         returns (IUniswapExchange exchange) |  | ||||||
|     { |  | ||||||
|         address exchangeTokenAddress = fromTokenAddress; |  | ||||||
|         // Whichever isn't WETH is the exchange token. |  | ||||||
|         if (fromTokenAddress == _getWethAddress()) { |  | ||||||
|             exchangeTokenAddress = toTokenAddress; |  | ||||||
|         } |  | ||||||
|         exchange = IUniswapExchange( |  | ||||||
|             IUniswapExchangeFactory(_getUniswapExchangeFactoryAddress()) |  | ||||||
|             .getExchange(exchangeTokenAddress) |  | ||||||
|         ); |  | ||||||
|         require(address(exchange) != address(0), "NO_UNISWAP_EXCHANGE_FOR_TOKEN"); |  | ||||||
|         return exchange; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,135 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2020 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
| pragma experimental ABIEncoderV2; |  | ||||||
|  |  | ||||||
| import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; |  | ||||||
| import "@0x/contracts-erc20/contracts/src/interfaces/IEtherToken.sol"; |  | ||||||
| import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol"; |  | ||||||
| import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol"; |  | ||||||
| import "@0x/contracts-utils/contracts/src/LibAddressArray.sol"; |  | ||||||
| import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol"; |  | ||||||
| import "../interfaces/IUniswapV2Router01.sol"; |  | ||||||
| import "../interfaces/IERC20Bridge.sol"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| // solhint-disable space-after-comma |  | ||||||
| // solhint-disable not-rely-on-time |  | ||||||
| contract UniswapV2Bridge is |  | ||||||
|     IERC20Bridge, |  | ||||||
|     IWallet, |  | ||||||
|     DeploymentConstants |  | ||||||
| { |  | ||||||
|     struct TransferState { |  | ||||||
|         address[] path; |  | ||||||
|         uint256 fromTokenBalance; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Callback for `IERC20Bridge`. Tries to buy `amount` of |  | ||||||
|     ///      `toTokenAddress` tokens by selling the entirety of the `fromTokenAddress` |  | ||||||
|     ///      token encoded in the bridge data. |  | ||||||
|     /// @param toTokenAddress The token to buy and transfer to `to`. |  | ||||||
|     /// @param from The maker (this contract). |  | ||||||
|     /// @param to The recipient of the bought tokens. |  | ||||||
|     /// @param amount Minimum amount of `toTokenAddress` tokens to buy. |  | ||||||
|     /// @param bridgeData The abi-encoded path of token addresses. Last element must be toTokenAddress |  | ||||||
|     /// @return success The magic bytes if successful. |  | ||||||
|     function bridgeTransferFrom( |  | ||||||
|         address toTokenAddress, |  | ||||||
|         address from, |  | ||||||
|         address to, |  | ||||||
|         uint256 amount, |  | ||||||
|         bytes calldata bridgeData |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         returns (bytes4 success) |  | ||||||
|     { |  | ||||||
|         // hold variables to get around stack depth limitations |  | ||||||
|         TransferState memory state; |  | ||||||
|  |  | ||||||
|         // Decode the bridge data to get the `fromTokenAddress`. |  | ||||||
|         // solhint-disable indent |  | ||||||
|         state.path = abi.decode(bridgeData, (address[])); |  | ||||||
|         // solhint-enable indent |  | ||||||
|  |  | ||||||
|         require(state.path.length >= 2, "UniswapV2Bridge/PATH_LENGTH_MUST_BE_AT_LEAST_TWO"); |  | ||||||
|         require(state.path[state.path.length - 1] == toTokenAddress, "UniswapV2Bridge/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN"); |  | ||||||
|  |  | ||||||
|         // Just transfer the tokens if they're the same. |  | ||||||
|         if (state.path[0] == toTokenAddress) { |  | ||||||
|             LibERC20Token.transfer(state.path[0], to, amount); |  | ||||||
|             return BRIDGE_SUCCESS; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // Get our balance of `fromTokenAddress` token. |  | ||||||
|         state.fromTokenBalance = IERC20Token(state.path[0]).balanceOf(address(this)); |  | ||||||
|  |  | ||||||
|         // Grant the Uniswap router an allowance. |  | ||||||
|         LibERC20Token.approveIfBelow( |  | ||||||
|             state.path[0], |  | ||||||
|             _getUniswapV2Router01Address(), |  | ||||||
|             state.fromTokenBalance |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         // Buy as much `toTokenAddress` token with `fromTokenAddress` token |  | ||||||
|         // and transfer it to `to`. |  | ||||||
|         IUniswapV2Router01 router = IUniswapV2Router01(_getUniswapV2Router01Address()); |  | ||||||
|         uint[] memory amounts = router.swapExactTokensForTokens( |  | ||||||
|              // Sell all tokens we hold. |  | ||||||
|             state.fromTokenBalance, |  | ||||||
|              // Minimum buy amount. |  | ||||||
|             amount, |  | ||||||
|             // Convert `fromTokenAddress` to `toTokenAddress`. |  | ||||||
|             state.path, |  | ||||||
|             // Recipient is `to`. |  | ||||||
|             to, |  | ||||||
|             // Expires after this block. |  | ||||||
|             block.timestamp |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         emit ERC20BridgeTransfer( |  | ||||||
|             // input token |  | ||||||
|             state.path[0], |  | ||||||
|             // output token |  | ||||||
|             toTokenAddress, |  | ||||||
|             // input token amount |  | ||||||
|             state.fromTokenBalance, |  | ||||||
|             // output token amount |  | ||||||
|             amounts[amounts.length - 1], |  | ||||||
|             from, |  | ||||||
|             to |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         return BRIDGE_SUCCESS; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev `SignatureType.Wallet` callback, so that this bridge can be the maker |  | ||||||
|     ///      and sign for itself in orders. Always succeeds. |  | ||||||
|     /// @return magicValue Success bytes, always. |  | ||||||
|     function isValidSignature( |  | ||||||
|         bytes32, |  | ||||||
|         bytes calldata |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (bytes4 magicValue) |  | ||||||
|     { |  | ||||||
|         return LEGACY_WALLET_MAGIC_VALUE; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,88 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2019 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| // solhint-disable |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
| pragma experimental ABIEncoderV2; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| // @dev Interface of the asset proxy's assetData. |  | ||||||
| // The asset proxies take an ABI encoded `bytes assetData` as argument. |  | ||||||
| // This argument is ABI encoded as one of the methods of this interface. |  | ||||||
| interface IAssetData { |  | ||||||
|  |  | ||||||
|     /// @dev Function signature for encoding ERC20 assetData. |  | ||||||
|     /// @param tokenAddress Address of ERC20Token contract. |  | ||||||
|     function ERC20Token(address tokenAddress) |  | ||||||
|         external; |  | ||||||
|  |  | ||||||
|     /// @dev Function signature for encoding ERC721 assetData. |  | ||||||
|     /// @param tokenAddress Address of ERC721 token contract. |  | ||||||
|     /// @param tokenId Id of ERC721 token to be transferred. |  | ||||||
|     function ERC721Token( |  | ||||||
|         address tokenAddress, |  | ||||||
|         uint256 tokenId |  | ||||||
|     ) |  | ||||||
|         external; |  | ||||||
|  |  | ||||||
|     /// @dev Function signature for encoding ERC1155 assetData. |  | ||||||
|     /// @param tokenAddress Address of ERC1155 token contract. |  | ||||||
|     /// @param tokenIds Array of ids of tokens to be transferred. |  | ||||||
|     /// @param values Array of values that correspond to each token id to be transferred. |  | ||||||
|     ///        Note that each value will be multiplied by the amount being filled in the order before transferring. |  | ||||||
|     /// @param callbackData Extra data to be passed to receiver's `onERC1155Received` callback function. |  | ||||||
|     function ERC1155Assets( |  | ||||||
|         address tokenAddress, |  | ||||||
|         uint256[] calldata tokenIds, |  | ||||||
|         uint256[] calldata values, |  | ||||||
|         bytes calldata callbackData |  | ||||||
|     ) |  | ||||||
|         external; |  | ||||||
|  |  | ||||||
|     /// @dev Function signature for encoding MultiAsset assetData. |  | ||||||
|     /// @param values Array of amounts that correspond to each asset to be transferred. |  | ||||||
|     ///        Note that each value will be multiplied by the amount being filled in the order before transferring. |  | ||||||
|     /// @param nestedAssetData Array of assetData fields that will be be dispatched to their correspnding AssetProxy contract. |  | ||||||
|     function MultiAsset( |  | ||||||
|         uint256[] calldata values, |  | ||||||
|         bytes[] calldata nestedAssetData |  | ||||||
|     ) |  | ||||||
|         external; |  | ||||||
|  |  | ||||||
|     /// @dev Function signature for encoding StaticCall assetData. |  | ||||||
|     /// @param staticCallTargetAddress Address that will execute the staticcall. |  | ||||||
|     /// @param staticCallData Data that will be executed via staticcall on the staticCallTargetAddress. |  | ||||||
|     /// @param expectedReturnDataHash Keccak-256 hash of the expected staticcall return data. |  | ||||||
|     function StaticCall( |  | ||||||
|         address staticCallTargetAddress, |  | ||||||
|         bytes calldata staticCallData, |  | ||||||
|         bytes32 expectedReturnDataHash |  | ||||||
|     ) |  | ||||||
|         external; |  | ||||||
|  |  | ||||||
|     /// @dev Function signature for encoding ERC20Bridge assetData. |  | ||||||
|     /// @param tokenAddress Address of token to transfer. |  | ||||||
|     /// @param bridgeAddress Address of the bridge contract. |  | ||||||
|     /// @param bridgeData Arbitrary data to be passed to the bridge contract. |  | ||||||
|     function ERC20Bridge( |  | ||||||
|         address tokenAddress, |  | ||||||
|         address bridgeAddress, |  | ||||||
|         bytes calldata bridgeData |  | ||||||
|     ) |  | ||||||
|         external; |  | ||||||
| } |  | ||||||
| @@ -1,43 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2019 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| contract IAssetProxy { |  | ||||||
|  |  | ||||||
|     /// @dev Transfers assets. Either succeeds or throws. |  | ||||||
|     /// @param assetData Byte array encoded for the respective asset proxy. |  | ||||||
|     /// @param from Address to transfer asset from. |  | ||||||
|     /// @param to Address to transfer asset to. |  | ||||||
|     /// @param amount Amount of asset to transfer. |  | ||||||
|     function transferFrom( |  | ||||||
|         bytes calldata assetData, |  | ||||||
|         address from, |  | ||||||
|         address to, |  | ||||||
|         uint256 amount |  | ||||||
|     ) |  | ||||||
|         external; |  | ||||||
|      |  | ||||||
|     /// @dev Gets the proxy id associated with the proxy address. |  | ||||||
|     /// @return Proxy id. |  | ||||||
|     function getProxyId() |  | ||||||
|         external |  | ||||||
|         pure |  | ||||||
|         returns (bytes4); |  | ||||||
| } |  | ||||||
| @@ -1,43 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2019 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| contract IAssetProxyDispatcher { |  | ||||||
|  |  | ||||||
|     // Logs registration of new asset proxy |  | ||||||
|     event AssetProxyRegistered( |  | ||||||
|         bytes4 id,              // Id of new registered AssetProxy. |  | ||||||
|         address assetProxy      // Address of new registered AssetProxy. |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     /// @dev Registers an asset proxy to its asset proxy id. |  | ||||||
|     ///      Once an asset proxy is registered, it cannot be unregistered. |  | ||||||
|     /// @param assetProxy Address of new asset proxy to register. |  | ||||||
|     function registerAssetProxy(address assetProxy) |  | ||||||
|         external; |  | ||||||
|  |  | ||||||
|     /// @dev Gets an asset proxy. |  | ||||||
|     /// @param assetProxyId Id of the asset proxy. |  | ||||||
|     /// @return The asset proxy registered to assetProxyId. Returns 0x0 if no proxy is registered. |  | ||||||
|     function getAssetProxy(bytes4 assetProxyId) |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (address); |  | ||||||
| } |  | ||||||
| @@ -1,64 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2019 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
|  |  | ||||||
| import "@0x/contracts-utils/contracts/src/interfaces/IOwnable.sol"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| contract IAuthorizable is |  | ||||||
|     IOwnable |  | ||||||
| { |  | ||||||
|     // Event logged when a new address is authorized. |  | ||||||
|     event AuthorizedAddressAdded( |  | ||||||
|         address indexed target, |  | ||||||
|         address indexed caller |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     // Event logged when a currently authorized address is unauthorized. |  | ||||||
|     event AuthorizedAddressRemoved( |  | ||||||
|         address indexed target, |  | ||||||
|         address indexed caller |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     /// @dev Authorizes an address. |  | ||||||
|     /// @param target Address to authorize. |  | ||||||
|     function addAuthorizedAddress(address target) |  | ||||||
|         external; |  | ||||||
|  |  | ||||||
|     /// @dev Removes authorizion of an address. |  | ||||||
|     /// @param target Address to remove authorization from. |  | ||||||
|     function removeAuthorizedAddress(address target) |  | ||||||
|         external; |  | ||||||
|  |  | ||||||
|     /// @dev Removes authorizion of an address. |  | ||||||
|     /// @param target Address to remove authorization from. |  | ||||||
|     /// @param index Index of target in authorities array. |  | ||||||
|     function removeAuthorizedAddressAtIndex( |  | ||||||
|         address target, |  | ||||||
|         uint256 index |  | ||||||
|     ) |  | ||||||
|         external; |  | ||||||
|  |  | ||||||
|     /// @dev Gets all authorized addresses. |  | ||||||
|     /// @return Array of authorized addresses. |  | ||||||
|     function getAuthorizedAddresses() |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (address[] memory); |  | ||||||
| } |  | ||||||
| @@ -1,39 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2020 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| interface IBalancerPool { |  | ||||||
|     /// @dev Sell `tokenAmountIn` of `tokenIn` and receive `tokenOut`. |  | ||||||
|     /// @param tokenIn The token being sold |  | ||||||
|     /// @param tokenAmountIn The amount of `tokenIn` to sell. |  | ||||||
|     /// @param tokenOut The token being bought. |  | ||||||
|     /// @param minAmountOut The minimum amount of `tokenOut` to buy. |  | ||||||
|     /// @param maxPrice The maximum value for `spotPriceAfter`. |  | ||||||
|     /// @return tokenAmountOut The amount of `tokenOut` bought. |  | ||||||
|     /// @return spotPriceAfter The new marginal spot price of the given |  | ||||||
|     ///         token pair for this pool. |  | ||||||
|     function swapExactAmountIn( |  | ||||||
|         address tokenIn, |  | ||||||
|         uint tokenAmountIn, |  | ||||||
|         address tokenOut, |  | ||||||
|         uint minAmountOut, |  | ||||||
|         uint maxPrice |  | ||||||
|     ) external returns (uint tokenAmountOut, uint spotPriceAfter); |  | ||||||
| } |  | ||||||
| @@ -1,38 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2020 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| contract IContractRegistry { |  | ||||||
|     function addressOf( |  | ||||||
|         bytes32 contractName |  | ||||||
|     ) external returns(address); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| contract IBancorNetwork { |  | ||||||
|     function convertByPath( |  | ||||||
|         address[] calldata _path, |  | ||||||
|         uint256 _amount, |  | ||||||
|         uint256 _minReturn, |  | ||||||
|         address _beneficiary, |  | ||||||
|         address _affiliateAccount, |  | ||||||
|         uint256 _affiliateFee |  | ||||||
|     ) external payable returns (uint256); |  | ||||||
| } |  | ||||||
| @@ -1,66 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2019 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
|  |  | ||||||
| import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| contract PotLike { |  | ||||||
|     function chi() external returns (uint256); |  | ||||||
|     function rho() external returns (uint256); |  | ||||||
|     function drip() external returns (uint256); |  | ||||||
|     function join(uint256) external; |  | ||||||
|     function exit(uint256) external; |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| // The actual Chai contract can be found here: https://github.com/dapphub/chai |  | ||||||
| contract IChai is |  | ||||||
|     IERC20Token |  | ||||||
| { |  | ||||||
|     /// @dev Withdraws Dai owned by `src` |  | ||||||
|     /// @param src Address that owns Dai. |  | ||||||
|     /// @param wad Amount of Dai to withdraw. |  | ||||||
|     function draw( |  | ||||||
|         address src, |  | ||||||
|         uint256 wad |  | ||||||
|     ) |  | ||||||
|         external; |  | ||||||
|  |  | ||||||
|     /// @dev Queries Dai balance of Chai holder. |  | ||||||
|     /// @param usr Address of Chai holder. |  | ||||||
|     /// @return Dai balance. |  | ||||||
|     function dai(address usr) |  | ||||||
|         external |  | ||||||
|         returns (uint256); |  | ||||||
|  |  | ||||||
|     /// @dev Queries the Pot contract used by the Chai contract. |  | ||||||
|     function pot() |  | ||||||
|         external |  | ||||||
|         returns (PotLike); |  | ||||||
|  |  | ||||||
|     /// @dev Deposits Dai in exchange for Chai |  | ||||||
|     /// @param dst Address to receive Chai. |  | ||||||
|     /// @param wad Amount of Dai to deposit. |  | ||||||
|     function join( |  | ||||||
|         address dst, |  | ||||||
|         uint256 wad |  | ||||||
|     ) |  | ||||||
|         external; |  | ||||||
| } |  | ||||||
| @@ -1,70 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2019 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| // solhint-disable func-name-mixedcase |  | ||||||
| interface ICurve { |  | ||||||
|  |  | ||||||
|     /// @dev Sell `sellAmount` of `fromToken` token and receive `toToken` token. |  | ||||||
|     ///      This function exists on later versions of Curve (USDC/DAI/USDT) |  | ||||||
|     /// @param i The token index being sold. |  | ||||||
|     /// @param j The token index being bought. |  | ||||||
|     /// @param sellAmount The amount of token being bought. |  | ||||||
|     /// @param minBuyAmount The minimum buy amount of the token being bought. |  | ||||||
|     function exchange_underlying( |  | ||||||
|         int128 i, |  | ||||||
|         int128 j, |  | ||||||
|         uint256 sellAmount, |  | ||||||
|         uint256 minBuyAmount |  | ||||||
|     ) |  | ||||||
|         external; |  | ||||||
|  |  | ||||||
|     /// @dev Get the amount of `toToken` by selling `sellAmount` of `fromToken` |  | ||||||
|     /// @param i The token index being sold. |  | ||||||
|     /// @param j The token index being bought. |  | ||||||
|     /// @param sellAmount The amount of token being bought. |  | ||||||
|     function get_dy_underlying( |  | ||||||
|         int128 i, |  | ||||||
|         int128 j, |  | ||||||
|         uint256 sellAmount |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         returns (uint256 dy); |  | ||||||
|  |  | ||||||
|     /// @dev Get the amount of `fromToken` by buying `buyAmount` of `toToken` |  | ||||||
|     /// @param i The token index being sold. |  | ||||||
|     /// @param j The token index being bought. |  | ||||||
|     /// @param buyAmount The amount of token being bought. |  | ||||||
|     function get_dx_underlying( |  | ||||||
|         int128 i, |  | ||||||
|         int128 j, |  | ||||||
|         uint256 buyAmount |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         returns (uint256 dx); |  | ||||||
|  |  | ||||||
|     /// @dev Get the underlying token address from the token index |  | ||||||
|     /// @param i The token index. |  | ||||||
|     function underlying_coins( |  | ||||||
|         int128 i |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         returns (address tokenAddress); |  | ||||||
| } |  | ||||||
| @@ -1,192 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2019 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
| pragma experimental ABIEncoderV2; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| interface IDydx { |  | ||||||
|  |  | ||||||
|     /// @dev Represents the unique key that specifies an account |  | ||||||
|     struct AccountInfo { |  | ||||||
|         address owner;  // The address that owns the account |  | ||||||
|         uint256 number; // A nonce that allows a single address to control many accounts |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     enum ActionType { |  | ||||||
|         Deposit,   // supply tokens |  | ||||||
|         Withdraw,  // borrow tokens |  | ||||||
|         Transfer,  // transfer balance between accounts |  | ||||||
|         Buy,       // buy an amount of some token (externally) |  | ||||||
|         Sell,      // sell an amount of some token (externally) |  | ||||||
|         Trade,     // trade tokens against another account |  | ||||||
|         Liquidate, // liquidate an undercollateralized or expiring account |  | ||||||
|         Vaporize,  // use excess tokens to zero-out a completely negative account |  | ||||||
|         Call       // send arbitrary data to an address |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Arguments that are passed to Solo in an ordered list as part of a single operation. |  | ||||||
|     /// Each ActionArgs has an actionType which specifies which action struct that this data will be |  | ||||||
|     /// parsed into before being processed. |  | ||||||
|     struct ActionArgs { |  | ||||||
|         ActionType actionType; |  | ||||||
|         uint256 accountIdx; |  | ||||||
|         AssetAmount amount; |  | ||||||
|         uint256 primaryMarketId; |  | ||||||
|         uint256 secondaryMarketId; |  | ||||||
|         address otherAddress; |  | ||||||
|         uint256 otherAccountIdx; |  | ||||||
|         bytes data; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     enum AssetDenomination { |  | ||||||
|         Wei, // the amount is denominated in wei |  | ||||||
|         Par  // the amount is denominated in par |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     enum AssetReference { |  | ||||||
|         Delta, // the amount is given as a delta from the current value |  | ||||||
|         Target // the amount is given as an exact number to end up at |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     struct AssetAmount { |  | ||||||
|         bool sign; // true if positive |  | ||||||
|         AssetDenomination denomination; |  | ||||||
|         AssetReference ref; |  | ||||||
|         uint256 value; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     struct D256 { |  | ||||||
|         uint256 value; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     struct Value { |  | ||||||
|         uint256 value; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     struct Price { |  | ||||||
|         uint256 value; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     struct OperatorArg { |  | ||||||
|         address operator; |  | ||||||
|         bool trusted; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev The global risk parameters that govern the health and security of the system |  | ||||||
|     struct RiskParams { |  | ||||||
|         // Required ratio of over-collateralization |  | ||||||
|         D256 marginRatio; |  | ||||||
|         // Percentage penalty incurred by liquidated accounts |  | ||||||
|         D256 liquidationSpread; |  | ||||||
|         // Percentage of the borrower's interest fee that gets passed to the suppliers |  | ||||||
|         D256 earningsRate; |  | ||||||
|         // The minimum absolute borrow value of an account |  | ||||||
|         // There must be sufficient incentivize to liquidate undercollateralized accounts |  | ||||||
|         Value minBorrowedValue; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev The main entry-point to Solo that allows users and contracts to manage accounts. |  | ||||||
|     ///      Take one or more actions on one or more accounts. The msg.sender must be the owner or |  | ||||||
|     ///      operator of all accounts except for those being liquidated, vaporized, or traded with. |  | ||||||
|     ///      One call to operate() is considered a singular "operation". Account collateralization is |  | ||||||
|     ///      ensured only after the completion of the entire operation. |  | ||||||
|     /// @param  accounts  A list of all accounts that will be used in this operation. Cannot contain |  | ||||||
|     ///                   duplicates. In each action, the relevant account will be referred-to by its |  | ||||||
|     ///                   index in the list. |  | ||||||
|     /// @param  actions   An ordered list of all actions that will be taken in this operation. The |  | ||||||
|     ///                   actions will be processed in order. |  | ||||||
|     function operate( |  | ||||||
|         AccountInfo[] calldata accounts, |  | ||||||
|         ActionArgs[] calldata actions |  | ||||||
|     ) |  | ||||||
|         external; |  | ||||||
|  |  | ||||||
|     // @dev Approves/disapproves any number of operators. An operator is an external address that has the |  | ||||||
|     //      same permissions to manipulate an account as the owner of the account. Operators are simply |  | ||||||
|     //      addresses and therefore may either be externally-owned Ethereum accounts OR smart contracts. |  | ||||||
|     //      Operators are also able to act as AutoTrader contracts on behalf of the account owner if the |  | ||||||
|     //      operator is a smart contract and implements the IAutoTrader interface. |  | ||||||
|     // @param args A list of OperatorArgs which have an address and a boolean. The boolean value |  | ||||||
|     //        denotes whether to approve (true) or revoke approval (false) for that address. |  | ||||||
|     function setOperators(OperatorArg[] calldata args) external; |  | ||||||
|  |  | ||||||
|     /// @dev Return true if a particular address is approved as an operator for an owner's accounts. |  | ||||||
|     ///      Approved operators can act on the accounts of the owner as if it were the operator's own. |  | ||||||
|     /// @param owner The owner of the accounts |  | ||||||
|     /// @param operator The possible operator |  | ||||||
|     /// @return isLocalOperator True if operator is approved for owner's accounts |  | ||||||
|     function getIsLocalOperator( |  | ||||||
|         address owner, |  | ||||||
|         address operator |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (bool isLocalOperator); |  | ||||||
|  |  | ||||||
|     /// @dev Get the ERC20 token address for a market. |  | ||||||
|     /// @param marketId The market to query |  | ||||||
|     /// @return tokenAddress The token address |  | ||||||
|     function getMarketTokenAddress( |  | ||||||
|         uint256 marketId |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (address tokenAddress); |  | ||||||
|  |  | ||||||
|     /// @dev Get all risk parameters in a single struct. |  | ||||||
|     /// @return riskParams All global risk parameters |  | ||||||
|     function getRiskParams() |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (RiskParams memory riskParams); |  | ||||||
|  |  | ||||||
|     /// @dev Get the price of the token for a market. |  | ||||||
|     /// @param marketId The market to query |  | ||||||
|     /// @return price The price of each atomic unit of the token |  | ||||||
|     function getMarketPrice( |  | ||||||
|         uint256 marketId |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (Price memory price); |  | ||||||
|  |  | ||||||
|     /// @dev Get the margin premium for a market. A margin premium makes it so that any positions that |  | ||||||
|     ///      include the market require a higher collateralization to avoid being liquidated. |  | ||||||
|     /// @param  marketId  The market to query |  | ||||||
|     /// @return premium The market's margin premium |  | ||||||
|     function getMarketMarginPremium(uint256 marketId) |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (D256 memory premium); |  | ||||||
|  |  | ||||||
|     /// @dev Get the total supplied and total borrowed values of an account adjusted by the marginPremium |  | ||||||
|     ///      of each market. Supplied values are divided by (1 + marginPremium) for each market and |  | ||||||
|     ///      borrowed values are multiplied by (1 + marginPremium) for each market. Comparing these |  | ||||||
|     ///      adjusted values gives the margin-ratio of the account which will be compared to the global |  | ||||||
|     ///      margin-ratio when determining if the account can be liquidated. |  | ||||||
|     /// @param account The account to query |  | ||||||
|     /// @return supplyValue The supplied value of the account (adjusted for marginPremium) |  | ||||||
|     /// @return borrowValue The borrowed value of the account (adjusted for marginPremium) |  | ||||||
|     function getAdjustedAccountValues( |  | ||||||
|         AccountInfo calldata account |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (Value memory supplyValue, Value memory borrowValue); |  | ||||||
| } |  | ||||||
| @@ -1,42 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2019 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| interface IDydxBridge { |  | ||||||
|  |  | ||||||
|     /// @dev This is the subset of `IDydx.ActionType` that are supported by the bridge. |  | ||||||
|     enum BridgeActionType { |  | ||||||
|         Deposit,                    // Deposit tokens into dydx account. |  | ||||||
|         Withdraw                    // Withdraw tokens from dydx account. |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     struct BridgeAction { |  | ||||||
|         BridgeActionType actionType;            // Action to run on dydx account. |  | ||||||
|         uint256 accountIdx;                     // Index in `BridgeData.accountNumbers` for this action. |  | ||||||
|         uint256 marketId;                       // Market to operate on. |  | ||||||
|         uint256 conversionRateNumerator;        // Optional. If set, transfer amount is scaled by (conversionRateNumerator/conversionRateDenominator). |  | ||||||
|         uint256 conversionRateDenominator;      // Optional. If set, transfer amount is scaled by (conversionRateNumerator/conversionRateDenominator). |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     struct BridgeData { |  | ||||||
|         uint256[] accountNumbers;               // Account number used to identify the owner's specific account. |  | ||||||
|         BridgeAction[] actions;                 // Actions to carry out on the owner's accounts. |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,59 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2019 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| contract IERC20Bridge { |  | ||||||
|  |  | ||||||
|     /// @dev Result of a successful bridge call. |  | ||||||
|     bytes4 constant internal BRIDGE_SUCCESS = 0xdc1600f3; |  | ||||||
|  |  | ||||||
|     /// @dev Emitted when a trade occurs. |  | ||||||
|     /// @param inputToken The token the bridge is converting from. |  | ||||||
|     /// @param outputToken The token the bridge is converting to. |  | ||||||
|     /// @param inputTokenAmount Amount of input token. |  | ||||||
|     /// @param outputTokenAmount Amount of output token. |  | ||||||
|     /// @param from The `from` address in `bridgeTransferFrom()` |  | ||||||
|     /// @param to The `to` address in `bridgeTransferFrom()` |  | ||||||
|     event ERC20BridgeTransfer( |  | ||||||
|         address inputToken, |  | ||||||
|         address outputToken, |  | ||||||
|         uint256 inputTokenAmount, |  | ||||||
|         uint256 outputTokenAmount, |  | ||||||
|         address from, |  | ||||||
|         address to |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     /// @dev Transfers `amount` of the ERC20 `tokenAddress` from `from` to `to`. |  | ||||||
|     /// @param tokenAddress The address of the ERC20 token to transfer. |  | ||||||
|     /// @param from Address to transfer asset from. |  | ||||||
|     /// @param to Address to transfer asset to. |  | ||||||
|     /// @param amount Amount of asset to transfer. |  | ||||||
|     /// @param bridgeData Arbitrary asset data needed by the bridge contract. |  | ||||||
|     /// @return success The magic bytes `0xdc1600f3` if successful. |  | ||||||
|     function bridgeTransferFrom( |  | ||||||
|         address tokenAddress, |  | ||||||
|         address from, |  | ||||||
|         address to, |  | ||||||
|         uint256 amount, |  | ||||||
|         bytes calldata bridgeData |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         returns (bytes4 success); |  | ||||||
| } |  | ||||||
| @@ -1,38 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2019 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| interface IEth2Dai { |  | ||||||
|  |  | ||||||
|     /// @dev Sell `sellAmount` of `fromToken` token and receive `toToken` token. |  | ||||||
|     /// @param fromToken The token being sold. |  | ||||||
|     /// @param sellAmount The amount of `fromToken` token being sold. |  | ||||||
|     /// @param toToken The token being bought. |  | ||||||
|     /// @param minFillAmount Minimum amount of `toToken` token to buy. |  | ||||||
|     /// @return fillAmount Amount of `toToken` bought. |  | ||||||
|     function sellAllAmount( |  | ||||||
|         address fromToken, |  | ||||||
|         uint256 sellAmount, |  | ||||||
|         address toToken, |  | ||||||
|         uint256 minFillAmount |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         returns (uint256 fillAmount); |  | ||||||
| } |  | ||||||
| @@ -1,40 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2019 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.15; |  | ||||||
|  |  | ||||||
| import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| contract IGasToken is IERC20Token { |  | ||||||
|  |  | ||||||
|     /// @dev Frees up to `value` sub-tokens |  | ||||||
|     /// @param value The amount of tokens to free |  | ||||||
|     /// @return How many tokens were freed |  | ||||||
|     function freeUpTo(uint256 value) external returns (uint256 freed); |  | ||||||
|  |  | ||||||
|     /// @dev Frees up to `value` sub-tokens owned by `from` |  | ||||||
|     /// @param from The owner of tokens to spend |  | ||||||
|     /// @param value The amount of tokens to free |  | ||||||
|     /// @return How many tokens were freed |  | ||||||
|     function freeFromUpTo(address from, uint256 value) external returns (uint256 freed); |  | ||||||
|  |  | ||||||
|     /// @dev Mints `value` amount of tokens |  | ||||||
|     /// @param value The amount of tokens to mint |  | ||||||
|     function mint(uint256 value) external; |  | ||||||
| } |  | ||||||
| @@ -1,72 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2019 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| interface IKyberNetworkProxy { |  | ||||||
|  |  | ||||||
|     /// @dev Sells `sellTokenAddress` tokens for `buyTokenAddress` tokens. |  | ||||||
|     /// @param sellTokenAddress Token to sell. |  | ||||||
|     /// @param sellAmount Amount of tokens to sell. |  | ||||||
|     /// @param buyTokenAddress Token to buy. |  | ||||||
|     /// @param recipientAddress Address to send bought tokens to. |  | ||||||
|     /// @param maxBuyTokenAmount A limit on the amount of tokens to buy. |  | ||||||
|     /// @param minConversionRate The minimal conversion rate. If actual rate |  | ||||||
|     ///        is lower, trade is canceled. |  | ||||||
|     /// @param walletId The wallet ID to send part of the fees |  | ||||||
|     /// @return boughtAmount Amount of tokens bought. |  | ||||||
|     function trade( |  | ||||||
|         address sellTokenAddress, |  | ||||||
|         uint256 sellAmount, |  | ||||||
|         address buyTokenAddress, |  | ||||||
|         address payable recipientAddress, |  | ||||||
|         uint256 maxBuyTokenAmount, |  | ||||||
|         uint256 minConversionRate, |  | ||||||
|         address walletId |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         payable |  | ||||||
|         returns (uint256 boughtAmount); |  | ||||||
|  |  | ||||||
|     /// @dev Sells `sellTokenAddress` tokens for `buyTokenAddress` tokens |  | ||||||
|     /// using a hint for the reserve. |  | ||||||
|     /// @param sellTokenAddress Token to sell. |  | ||||||
|     /// @param sellAmount Amount of tokens to sell. |  | ||||||
|     /// @param buyTokenAddress Token to buy. |  | ||||||
|     /// @param recipientAddress Address to send bought tokens to. |  | ||||||
|     /// @param maxBuyTokenAmount A limit on the amount of tokens to buy. |  | ||||||
|     /// @param minConversionRate The minimal conversion rate. If actual rate |  | ||||||
|     ///        is lower, trade is canceled. |  | ||||||
|     /// @param walletId The wallet ID to send part of the fees |  | ||||||
|     /// @param hint The hint for the selective inclusion (or exclusion) of reserves |  | ||||||
|     /// @return boughtAmount Amount of tokens bought. |  | ||||||
|     function tradeWithHint( |  | ||||||
|         address sellTokenAddress, |  | ||||||
|         uint256 sellAmount, |  | ||||||
|         address buyTokenAddress, |  | ||||||
|         address payable recipientAddress, |  | ||||||
|         uint256 maxBuyTokenAmount, |  | ||||||
|         uint256 minConversionRate, |  | ||||||
|         address payable walletId, |  | ||||||
|         bytes calldata hint |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         payable |  | ||||||
|         returns (uint256 boughtAmount); |  | ||||||
| } |  | ||||||
| @@ -1,32 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2020 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| interface IMStable { |  | ||||||
|  |  | ||||||
|     function swap( |  | ||||||
|         address _input, |  | ||||||
|         address _output, |  | ||||||
|         uint256 _quantity, |  | ||||||
|         address _recipient |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         returns (uint256 output); |  | ||||||
| } |  | ||||||
| @@ -1,40 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2020 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| interface IMooniswapRegistry { |  | ||||||
|  |  | ||||||
|     function pools(address token1, address token2) external view returns(address); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| interface IMooniswap { |  | ||||||
|  |  | ||||||
|     function swap( |  | ||||||
|         address fromToken, |  | ||||||
|         address destToken, |  | ||||||
|         uint256 amount, |  | ||||||
|         uint256 minReturn, |  | ||||||
|         address referral |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         payable |  | ||||||
|         returns(uint256 returnAmount); |  | ||||||
| } |  | ||||||
| @@ -1,34 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2020 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| interface IShell { |  | ||||||
|  |  | ||||||
|     function originSwap( |  | ||||||
|         address from, |  | ||||||
|         address to, |  | ||||||
|         uint256 fromAmount, |  | ||||||
|         uint256 minTargetAmount, |  | ||||||
|         uint256 deadline |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         returns (uint256 toAmount); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| @@ -1,70 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2019 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| interface IUniswapExchange { |  | ||||||
|  |  | ||||||
|     /// @dev Buys at least `minTokensBought` tokens with ETH and transfer them |  | ||||||
|     ///      to `recipient`. |  | ||||||
|     /// @param minTokensBought The minimum number of tokens to buy. |  | ||||||
|     /// @param deadline Time when this order expires. |  | ||||||
|     /// @param recipient Who to transfer the tokens to. |  | ||||||
|     /// @return tokensBought Amount of tokens bought. |  | ||||||
|     function ethToTokenTransferInput( |  | ||||||
|         uint256 minTokensBought, |  | ||||||
|         uint256 deadline, |  | ||||||
|         address recipient |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         payable |  | ||||||
|         returns (uint256 tokensBought); |  | ||||||
|  |  | ||||||
|     /// @dev Buys at least `minEthBought` ETH with tokens. |  | ||||||
|     /// @param tokensSold Amount of tokens to sell. |  | ||||||
|     /// @param minEthBought The minimum amount of ETH to buy. |  | ||||||
|     /// @param deadline Time when this order expires. |  | ||||||
|     /// @return ethBought Amount of tokens bought. |  | ||||||
|     function tokenToEthSwapInput( |  | ||||||
|         uint256 tokensSold, |  | ||||||
|         uint256 minEthBought, |  | ||||||
|         uint256 deadline |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         returns (uint256 ethBought); |  | ||||||
|  |  | ||||||
|     /// @dev Buys at least `minTokensBought` tokens with the exchange token |  | ||||||
|     ///      and transfer them to `recipient`. |  | ||||||
|     /// @param minTokensBought The minimum number of tokens to buy. |  | ||||||
|     /// @param minEthBought The minimum amount of intermediate ETH to buy. |  | ||||||
|     /// @param deadline Time when this order expires. |  | ||||||
|     /// @param recipient Who to transfer the tokens to. |  | ||||||
|     /// @param toTokenAddress The token being bought. |  | ||||||
|     /// @return tokensBought Amount of tokens bought. |  | ||||||
|     function tokenToTokenTransferInput( |  | ||||||
|         uint256 tokensSold, |  | ||||||
|         uint256 minTokensBought, |  | ||||||
|         uint256 minEthBought, |  | ||||||
|         uint256 deadline, |  | ||||||
|         address recipient, |  | ||||||
|         address toTokenAddress |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         returns (uint256 tokensBought); |  | ||||||
| } |  | ||||||
| @@ -1,32 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2019 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
|  |  | ||||||
| import "./IUniswapExchange.sol"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| interface IUniswapExchangeFactory { |  | ||||||
|  |  | ||||||
|     /// @dev Get the exchange for a token. |  | ||||||
|     /// @param tokenAddress The address of the token contract. |  | ||||||
|     function getExchange(address tokenAddress) |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (address); |  | ||||||
| } |  | ||||||
| @@ -1,40 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2020 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| interface IUniswapV2Router01 { |  | ||||||
|  |  | ||||||
|     /// @dev Swaps an exact amount of input tokens for as many output tokens as possible, along the route determined by the path. |  | ||||||
|     ///      The first element of path is the input token, the last is the output token, and any intermediate elements represent |  | ||||||
|     ///      intermediate pairs to trade through (if, for example, a direct pair does not exist). |  | ||||||
|     /// @param amountIn The amount of input tokens to send. |  | ||||||
|     /// @param amountOutMin The minimum amount of output tokens that must be received for the transaction not to revert. |  | ||||||
|     /// @param path An array of token addresses. path.length must be >= 2. Pools for each consecutive pair of addresses must exist and have liquidity. |  | ||||||
|     /// @param to Recipient of the output tokens. |  | ||||||
|     /// @param deadline Unix timestamp after which the transaction will revert. |  | ||||||
|     /// @return amounts The input token amount and all subsequent output token amounts. |  | ||||||
|     function swapExactTokensForTokens( |  | ||||||
|         uint amountIn, |  | ||||||
|         uint amountOutMin, |  | ||||||
|         address[] calldata path, |  | ||||||
|         address to, |  | ||||||
|         uint deadline |  | ||||||
|     ) external returns (uint[] memory amounts); |  | ||||||
| } |  | ||||||
| @@ -1,247 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2019 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
| pragma experimental ABIEncoderV2; |  | ||||||
|  |  | ||||||
| import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; |  | ||||||
| import "@0x/contracts-utils/contracts/src/LibSafeMath.sol"; |  | ||||||
| import "@0x/contracts-utils/contracts/src/LibAddressArray.sol"; |  | ||||||
| import "../src/bridges/BancorBridge.sol"; |  | ||||||
| import "../src/interfaces/IBancorNetwork.sol"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| contract TestEventsRaiser { |  | ||||||
|  |  | ||||||
|     event TokenTransfer( |  | ||||||
|         address token, |  | ||||||
|         address from, |  | ||||||
|         address to, |  | ||||||
|         uint256 amount |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     event TokenApprove( |  | ||||||
|         address spender, |  | ||||||
|         uint256 allowance |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     event ConvertByPathInput( |  | ||||||
|         uint amountIn, |  | ||||||
|         uint amountOutMin, |  | ||||||
|         address toTokenAddress, |  | ||||||
|         address to, |  | ||||||
|         address feeRecipient, |  | ||||||
|         uint256 feeAmount |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     function raiseTokenTransfer( |  | ||||||
|         address from, |  | ||||||
|         address to, |  | ||||||
|         uint256 amount |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|     { |  | ||||||
|         emit TokenTransfer( |  | ||||||
|             msg.sender, |  | ||||||
|             from, |  | ||||||
|             to, |  | ||||||
|             amount |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function raiseTokenApprove(address spender, uint256 allowance) external { |  | ||||||
|         emit TokenApprove(spender, allowance); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function raiseConvertByPathInput( |  | ||||||
|         uint amountIn, |  | ||||||
|         uint amountOutMin, |  | ||||||
|         address toTokenAddress, |  | ||||||
|         address to, |  | ||||||
|         address feeRecipient, |  | ||||||
|         uint256 feeAmount |  | ||||||
|     ) external |  | ||||||
|     { |  | ||||||
|         emit ConvertByPathInput( |  | ||||||
|             amountIn, |  | ||||||
|             amountOutMin, |  | ||||||
|             toTokenAddress, |  | ||||||
|             to, |  | ||||||
|             feeRecipient, |  | ||||||
|             feeAmount |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| /// @dev A minimalist ERC20 token. |  | ||||||
| contract TestToken { |  | ||||||
|  |  | ||||||
|     using LibSafeMath for uint256; |  | ||||||
|  |  | ||||||
|     mapping (address => uint256) public balances; |  | ||||||
|     string private _nextRevertReason; |  | ||||||
|  |  | ||||||
|     /// @dev Set the balance for `owner`. |  | ||||||
|     function setBalance(address owner, uint256 balance) |  | ||||||
|         external |  | ||||||
|         payable |  | ||||||
|     { |  | ||||||
|         balances[owner] = balance; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Just emits a TokenTransfer event on the caller |  | ||||||
|     function transfer(address to, uint256 amount) |  | ||||||
|         external |  | ||||||
|         returns (bool) |  | ||||||
|     { |  | ||||||
|         TestEventsRaiser(msg.sender).raiseTokenTransfer(msg.sender, to, amount); |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Just emits a TokenApprove event on the caller |  | ||||||
|     function approve(address spender, uint256 allowance) |  | ||||||
|         external |  | ||||||
|         returns (bool) |  | ||||||
|     { |  | ||||||
|         TestEventsRaiser(msg.sender).raiseTokenApprove(spender, allowance); |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function allowance(address, address) external view returns (uint256) { |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Retrieve the balance for `owner`. |  | ||||||
|     function balanceOf(address owner) |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (uint256) |  | ||||||
|     { |  | ||||||
|         return balances[owner]; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| /// @dev Mock the BancorNetwork contract |  | ||||||
| contract TestBancorNetwork is |  | ||||||
|     IBancorNetwork |  | ||||||
| { |  | ||||||
|     string private _nextRevertReason; |  | ||||||
|  |  | ||||||
|     /// @dev Set the revert reason for `swapExactTokensForTokens`. |  | ||||||
|     function setRevertReason(string calldata reason) |  | ||||||
|         external |  | ||||||
|     { |  | ||||||
|         _nextRevertReason = reason; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function convertByPath( |  | ||||||
|         address[] calldata _path, |  | ||||||
|         uint256 _amount, |  | ||||||
|         uint256 _minReturn, |  | ||||||
|         address _beneficiary, |  | ||||||
|         address _affiliateAccount, |  | ||||||
|         uint256 _affiliateFee |  | ||||||
|     ) external payable returns (uint256) |  | ||||||
|     { |  | ||||||
|         _revertIfReasonExists(); |  | ||||||
|  |  | ||||||
|         TestEventsRaiser(msg.sender).raiseConvertByPathInput( |  | ||||||
|             // tokens sold |  | ||||||
|             _amount, |  | ||||||
|             // tokens bought |  | ||||||
|             _minReturn, |  | ||||||
|             // output token |  | ||||||
|             _path[_path.length - 1], |  | ||||||
|             // recipient |  | ||||||
|             _beneficiary, |  | ||||||
|             // fee recipient |  | ||||||
|             _affiliateAccount, |  | ||||||
|             // fee amount |  | ||||||
|             _affiliateFee |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function _revertIfReasonExists() |  | ||||||
|         private |  | ||||||
|         view |  | ||||||
|     { |  | ||||||
|         if (bytes(_nextRevertReason).length != 0) { |  | ||||||
|             revert(_nextRevertReason); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| /// @dev BancorBridge overridden to mock tokens and BancorNetwork |  | ||||||
| contract TestBancorBridge is |  | ||||||
|     BancorBridge, |  | ||||||
|     TestEventsRaiser |  | ||||||
| { |  | ||||||
|  |  | ||||||
|     // Token address to TestToken instance. |  | ||||||
|     mapping (address => TestToken) private _testTokens; |  | ||||||
|     // TestRouter instance. |  | ||||||
|     TestBancorNetwork private _testNetwork; |  | ||||||
|  |  | ||||||
|     constructor() public { |  | ||||||
|         _testNetwork = new TestBancorNetwork(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function setNetworkRevertReason(string calldata revertReason) |  | ||||||
|         external |  | ||||||
|     { |  | ||||||
|         _testNetwork.setRevertReason(revertReason); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Sets the balance of this contract for an existing token. |  | ||||||
|     function setTokenBalance(address tokenAddress, uint256 balance) |  | ||||||
|         external |  | ||||||
|     { |  | ||||||
|         TestToken token = _testTokens[tokenAddress]; |  | ||||||
|         token.setBalance(address(this), balance); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Create a new token |  | ||||||
|     /// @param tokenAddress The token address. If zero, one will be created. |  | ||||||
|     function createToken( |  | ||||||
|         address tokenAddress |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         returns (TestToken token) |  | ||||||
|     { |  | ||||||
|         token = TestToken(tokenAddress); |  | ||||||
|         if (tokenAddress == address(0)) { |  | ||||||
|             token = new TestToken(); |  | ||||||
|         } |  | ||||||
|         _testTokens[address(token)] = token; |  | ||||||
|  |  | ||||||
|         return token; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function getNetworkAddress() |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (address) |  | ||||||
|     { |  | ||||||
|         return address(_testNetwork); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } |  | ||||||
| @@ -1,80 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2019 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
| pragma experimental ABIEncoderV2; |  | ||||||
|  |  | ||||||
| import "../src/bridges/ChaiBridge.sol"; |  | ||||||
| import "@0x/contracts-erc20/contracts/src/ERC20Token.sol"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| contract TestChaiDai is |  | ||||||
|     ERC20Token |  | ||||||
| { |  | ||||||
|     address private constant ALWAYS_REVERT_ADDRESS = address(1); |  | ||||||
|  |  | ||||||
|     function draw( |  | ||||||
|         address from, |  | ||||||
|         uint256 amount |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|     { |  | ||||||
|         if (from == ALWAYS_REVERT_ADDRESS) { |  | ||||||
|             revert(); |  | ||||||
|         } |  | ||||||
|         balances[msg.sender] += amount; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| contract TestChaiBridge is |  | ||||||
|     ChaiBridge |  | ||||||
| { |  | ||||||
|     address public testChaiDai; |  | ||||||
|     address private constant ALWAYS_REVERT_ADDRESS = address(1); |  | ||||||
|  |  | ||||||
|     constructor() |  | ||||||
|         public |  | ||||||
|     { |  | ||||||
|         testChaiDai = address(new TestChaiDai()); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function _getDaiAddress() |  | ||||||
|         internal |  | ||||||
|         view |  | ||||||
|         returns (address) |  | ||||||
|     { |  | ||||||
|         return testChaiDai; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function _getChaiAddress() |  | ||||||
|         internal |  | ||||||
|         view |  | ||||||
|         returns (address) |  | ||||||
|     { |  | ||||||
|         return testChaiDai; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function _getERC20BridgeProxyAddress() |  | ||||||
|         internal |  | ||||||
|         view |  | ||||||
|         returns (address) |  | ||||||
|     { |  | ||||||
|         return msg.sender == ALWAYS_REVERT_ADDRESS ? address(0) : msg.sender; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,244 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2020 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
| pragma experimental ABIEncoderV2; |  | ||||||
|  |  | ||||||
| import "../src/bridges/DexForwarderBridge.sol"; |  | ||||||
| import "@0x/contracts-utils/contracts/src/LibSafeMath.sol"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| interface ITestDexForwarderBridge { |  | ||||||
|     event BridgeTransferFromCalled( |  | ||||||
|         address caller, |  | ||||||
|         uint256 inputTokenBalance, |  | ||||||
|         address inputToken, |  | ||||||
|         address outputToken, |  | ||||||
|         address from, |  | ||||||
|         address to, |  | ||||||
|         uint256 amount |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     event TokenTransferCalled( |  | ||||||
|         address from, |  | ||||||
|         address to, |  | ||||||
|         uint256 amount |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     function emitBridgeTransferFromCalled( |  | ||||||
|         address caller, |  | ||||||
|         uint256 inputTokenBalance, |  | ||||||
|         address inputToken, |  | ||||||
|         address outputToken, |  | ||||||
|         address from, |  | ||||||
|         address to, |  | ||||||
|         uint256 amount |  | ||||||
|     ) external; |  | ||||||
|  |  | ||||||
|     function emitTokenTransferCalled( |  | ||||||
|         address from, |  | ||||||
|         address to, |  | ||||||
|         uint256 amount |  | ||||||
|     ) external; |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| interface ITestDexForwarderBridgeTestToken { |  | ||||||
|  |  | ||||||
|     function transfer(address to, uint256 amount) |  | ||||||
|         external |  | ||||||
|         returns (bool); |  | ||||||
|  |  | ||||||
|     function mint(address to, uint256 amount) |  | ||||||
|         external; |  | ||||||
|  |  | ||||||
|     function balanceOf(address owner) external view returns (uint256); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| contract TestDexForwarderBridgeTestBridge { |  | ||||||
|  |  | ||||||
|     bytes4 private _returnCode; |  | ||||||
|     string private _revertError; |  | ||||||
|     uint256 private _transferAmount; |  | ||||||
|     ITestDexForwarderBridge private _testContract; |  | ||||||
|  |  | ||||||
|     constructor(bytes4 returnCode, string memory revertError) public { |  | ||||||
|         _testContract = ITestDexForwarderBridge(msg.sender); |  | ||||||
|         _returnCode = returnCode; |  | ||||||
|         _revertError = revertError; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function setTransferAmount(uint256 amount) external { |  | ||||||
|         _transferAmount = amount; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function bridgeTransferFrom( |  | ||||||
|         address outputToken, |  | ||||||
|         address from, |  | ||||||
|         address to, |  | ||||||
|         uint256 amount, |  | ||||||
|         bytes memory bridgeData |  | ||||||
|     ) |  | ||||||
|         public |  | ||||||
|         returns (bytes4 success) |  | ||||||
|     { |  | ||||||
|         if (bytes(_revertError).length != 0) { |  | ||||||
|             revert(_revertError); |  | ||||||
|         } |  | ||||||
|         address inputToken = abi.decode(bridgeData, (address)); |  | ||||||
|         _testContract.emitBridgeTransferFromCalled( |  | ||||||
|             msg.sender, |  | ||||||
|             ITestDexForwarderBridgeTestToken(inputToken).balanceOf(address(this)), |  | ||||||
|             inputToken, |  | ||||||
|             outputToken, |  | ||||||
|             from, |  | ||||||
|             to, |  | ||||||
|             amount |  | ||||||
|         ); |  | ||||||
|         ITestDexForwarderBridgeTestToken(outputToken).mint(to, _transferAmount); |  | ||||||
|         return _returnCode; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| contract TestDexForwarderBridgeTestToken { |  | ||||||
|  |  | ||||||
|     using LibSafeMath for uint256; |  | ||||||
|  |  | ||||||
|     mapping(address => uint256) public balanceOf; |  | ||||||
|     ITestDexForwarderBridge private _testContract; |  | ||||||
|  |  | ||||||
|     constructor() public { |  | ||||||
|         _testContract = ITestDexForwarderBridge(msg.sender); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function transfer(address to, uint256 amount) |  | ||||||
|         external |  | ||||||
|         returns (bool) |  | ||||||
|     { |  | ||||||
|         balanceOf[msg.sender] = balanceOf[msg.sender].safeSub(amount); |  | ||||||
|         balanceOf[to] = balanceOf[to].safeAdd(amount); |  | ||||||
|         _testContract.emitTokenTransferCalled(msg.sender, to, amount); |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function mint(address owner, uint256 amount) |  | ||||||
|         external |  | ||||||
|     { |  | ||||||
|         balanceOf[owner] = balanceOf[owner].safeAdd(amount); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function setBalance(address owner, uint256 amount) |  | ||||||
|         external |  | ||||||
|     { |  | ||||||
|         balanceOf[owner] = amount; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| contract TestDexForwarderBridge is |  | ||||||
|     ITestDexForwarderBridge, |  | ||||||
|     DexForwarderBridge |  | ||||||
| { |  | ||||||
|     address private AUTHORIZED_ADDRESS; // solhint-disable-line var-name-mixedcase |  | ||||||
|  |  | ||||||
|     function setAuthorized(address authorized) |  | ||||||
|         public |  | ||||||
|     { |  | ||||||
|         AUTHORIZED_ADDRESS = authorized; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function createBridge( |  | ||||||
|         bytes4 returnCode, |  | ||||||
|         string memory revertError |  | ||||||
|     ) |  | ||||||
|         public |  | ||||||
|         returns (address bridge) |  | ||||||
|     { |  | ||||||
|         return address(new TestDexForwarderBridgeTestBridge(returnCode, revertError)); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function createToken() public returns (address token) { |  | ||||||
|         return address(new TestDexForwarderBridgeTestToken()); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function setTokenBalance(address token, address owner, uint256 amount) public { |  | ||||||
|         TestDexForwarderBridgeTestToken(token).setBalance(owner, amount); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function setBridgeTransferAmount(address bridge, uint256 amount) public { |  | ||||||
|         TestDexForwarderBridgeTestBridge(bridge).setTransferAmount(amount); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function emitBridgeTransferFromCalled( |  | ||||||
|         address caller, |  | ||||||
|         uint256 inputTokenBalance, |  | ||||||
|         address inputToken, |  | ||||||
|         address outputToken, |  | ||||||
|         address from, |  | ||||||
|         address to, |  | ||||||
|         uint256 amount |  | ||||||
|     ) |  | ||||||
|         public |  | ||||||
|     { |  | ||||||
|         emit BridgeTransferFromCalled( |  | ||||||
|             caller, |  | ||||||
|             inputTokenBalance, |  | ||||||
|             inputToken, |  | ||||||
|             outputToken, |  | ||||||
|             from, |  | ||||||
|             to, |  | ||||||
|             amount |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function emitTokenTransferCalled( |  | ||||||
|         address from, |  | ||||||
|         address to, |  | ||||||
|         uint256 amount |  | ||||||
|     ) |  | ||||||
|         public |  | ||||||
|     { |  | ||||||
|         emit TokenTransferCalled( |  | ||||||
|             from, |  | ||||||
|             to, |  | ||||||
|             amount |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function balanceOf(address token, address owner) public view returns (uint256) { |  | ||||||
|         return TestDexForwarderBridgeTestToken(token).balanceOf(owner); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function _getGstAddress() |  | ||||||
|         internal |  | ||||||
|         view |  | ||||||
|         returns (address gst) |  | ||||||
|     { |  | ||||||
|         return address(0); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function _getERC20BridgeProxyAddress() |  | ||||||
|         internal |  | ||||||
|         view |  | ||||||
|         returns (address erc20BridgeProxyAddress) |  | ||||||
|     { |  | ||||||
|         return AUTHORIZED_ADDRESS; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,246 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2019 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
| pragma experimental ABIEncoderV2; |  | ||||||
|  |  | ||||||
| import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; |  | ||||||
| import "../src/bridges/DydxBridge.sol"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| // solhint-disable no-empty-blocks |  | ||||||
| contract TestDydxBridgeToken { |  | ||||||
|  |  | ||||||
|     uint256 private constant INIT_HOLDER_BALANCE = 10 * 10**18; // 10 tokens |  | ||||||
|     mapping (address => uint256) private _balances; |  | ||||||
|  |  | ||||||
|     /// @dev Sets initial balance of token holders. |  | ||||||
|     constructor(address[] memory holders) |  | ||||||
|         public |  | ||||||
|     { |  | ||||||
|         for (uint256 i = 0; i != holders.length; ++i) { |  | ||||||
|             _balances[holders[i]] = INIT_HOLDER_BALANCE; |  | ||||||
|         } |  | ||||||
|         _balances[msg.sender] = INIT_HOLDER_BALANCE; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Basic transferFrom implementation. |  | ||||||
|     function transferFrom(address from, address to, uint256 amount) |  | ||||||
|         external |  | ||||||
|         returns (bool) |  | ||||||
|     { |  | ||||||
|         if (_balances[from] < amount || _balances[to] + amount < _balances[to]) { |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
|         _balances[from] -= amount; |  | ||||||
|         _balances[to] += amount; |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Returns balance of `holder`. |  | ||||||
|     function balanceOf(address holder) |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (uint256) |  | ||||||
|     { |  | ||||||
|         return _balances[holder]; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| // solhint-disable space-after-comma |  | ||||||
| contract TestDydxBridge is |  | ||||||
|     IDydx, |  | ||||||
|     DydxBridge |  | ||||||
| { |  | ||||||
|  |  | ||||||
|     address private constant ALWAYS_REVERT_ADDRESS = address(1); |  | ||||||
|     address private _testTokenAddress; |  | ||||||
|     bool private _shouldRevertOnOperate; |  | ||||||
|  |  | ||||||
|     event OperateAccount( |  | ||||||
|         address owner, |  | ||||||
|         uint256 number |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     event OperateAction( |  | ||||||
|         ActionType actionType, |  | ||||||
|         uint256 accountIdx, |  | ||||||
|         bool amountSign, |  | ||||||
|         AssetDenomination amountDenomination, |  | ||||||
|         AssetReference amountRef, |  | ||||||
|         uint256 amountValue, |  | ||||||
|         uint256 primaryMarketId, |  | ||||||
|         uint256 secondaryMarketId, |  | ||||||
|         address otherAddress, |  | ||||||
|         uint256 otherAccountId, |  | ||||||
|         bytes data |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     constructor(address[] memory holders) |  | ||||||
|         public |  | ||||||
|     { |  | ||||||
|         // Deploy a test token. This represents the asset being deposited/withdrawn from dydx. |  | ||||||
|         _testTokenAddress = address(new TestDydxBridgeToken(holders)); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Simulates `operate` in dydx contract. |  | ||||||
|     ///      Emits events so that arguments can be validated client-side. |  | ||||||
|     function operate( |  | ||||||
|         AccountInfo[] calldata accounts, |  | ||||||
|         ActionArgs[] calldata actions |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|     { |  | ||||||
|         if (_shouldRevertOnOperate) { |  | ||||||
|             revert("TestDydxBridge/SHOULD_REVERT_ON_OPERATE"); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         for (uint i = 0; i < accounts.length; ++i) { |  | ||||||
|             emit OperateAccount( |  | ||||||
|                 accounts[i].owner, |  | ||||||
|                 accounts[i].number |  | ||||||
|             ); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         for (uint i = 0; i < actions.length; ++i) { |  | ||||||
|             emit OperateAction( |  | ||||||
|                 actions[i].actionType, |  | ||||||
|                 actions[i].accountIdx, |  | ||||||
|                 actions[i].amount.sign, |  | ||||||
|                 actions[i].amount.denomination, |  | ||||||
|                 actions[i].amount.ref, |  | ||||||
|                 actions[i].amount.value, |  | ||||||
|                 actions[i].primaryMarketId, |  | ||||||
|                 actions[i].secondaryMarketId, |  | ||||||
|                 actions[i].otherAddress, |  | ||||||
|                 actions[i].otherAccountIdx, |  | ||||||
|                 actions[i].data |  | ||||||
|             ); |  | ||||||
|  |  | ||||||
|             if (actions[i].actionType == IDydx.ActionType.Withdraw) { |  | ||||||
|                 require( |  | ||||||
|                     IERC20Token(_testTokenAddress).transferFrom( |  | ||||||
|                         address(this), |  | ||||||
|                         actions[i].otherAddress, |  | ||||||
|                         actions[i].amount.value |  | ||||||
|                     ), |  | ||||||
|                     "TestDydxBridge/WITHDRAW_FAILED" |  | ||||||
|                 ); |  | ||||||
|             } else if (actions[i].actionType == IDydx.ActionType.Deposit) { |  | ||||||
|                 require( |  | ||||||
|                     IERC20Token(_testTokenAddress).transferFrom( |  | ||||||
|                         actions[i].otherAddress, |  | ||||||
|                         address(this), |  | ||||||
|                         actions[i].amount.value |  | ||||||
|                     ), |  | ||||||
|                     "TestDydxBridge/DEPOSIT_FAILED" |  | ||||||
|                 ); |  | ||||||
|             } else { |  | ||||||
|                 revert("TestDydxBridge/UNSUPPORTED_ACTION"); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev If `true` then subsequent calls to `operate` will revert. |  | ||||||
|     function setRevertOnOperate(bool shouldRevert) |  | ||||||
|         external |  | ||||||
|     { |  | ||||||
|         _shouldRevertOnOperate = shouldRevert; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Returns test token. |  | ||||||
|     function getTestToken() |  | ||||||
|         external |  | ||||||
|         returns (address) |  | ||||||
|     { |  | ||||||
|         return _testTokenAddress; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Unused. |  | ||||||
|     function setOperators(OperatorArg[] calldata args) external {} |  | ||||||
|  |  | ||||||
|     /// @dev Unused. |  | ||||||
|     function getIsLocalOperator( |  | ||||||
|         address owner, |  | ||||||
|         address operator |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (bool isLocalOperator) |  | ||||||
|     {} |  | ||||||
|  |  | ||||||
|     /// @dev Unused. |  | ||||||
|     function getMarketTokenAddress( |  | ||||||
|         uint256 marketId |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (address tokenAddress) |  | ||||||
|     {} |  | ||||||
|  |  | ||||||
|     /// @dev Unused. |  | ||||||
|     function getRiskParams() |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (RiskParams memory riskParams) |  | ||||||
|     {} |  | ||||||
|  |  | ||||||
|     /// @dev Unsused. |  | ||||||
|     function getMarketPrice( |  | ||||||
|         uint256 marketId |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (Price memory price) |  | ||||||
|     {} |  | ||||||
|  |  | ||||||
|     /// @dev Unsused |  | ||||||
|     function getMarketMarginPremium(uint256 marketId) |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (IDydx.D256 memory premium) |  | ||||||
|     {} |  | ||||||
|  |  | ||||||
|     /// @dev Unused. |  | ||||||
|     function getAdjustedAccountValues( |  | ||||||
|         AccountInfo calldata account |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (Value memory supplyValue, Value memory borrowValue) |  | ||||||
|     {} |  | ||||||
|  |  | ||||||
|     /// @dev overrides `_getDydxAddress()` from `DeploymentConstants` to return this address. |  | ||||||
|     function _getDydxAddress() |  | ||||||
|         internal |  | ||||||
|         view |  | ||||||
|         returns (address) |  | ||||||
|     { |  | ||||||
|         return address(this); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev overrides `_getERC20BridgeProxyAddress()` from `DeploymentConstants` for testing. |  | ||||||
|     function _getERC20BridgeProxyAddress() |  | ||||||
|         internal |  | ||||||
|         view |  | ||||||
|         returns (address) |  | ||||||
|     { |  | ||||||
|         return msg.sender == ALWAYS_REVERT_ADDRESS ? address(0) : msg.sender; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,108 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2019 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
| pragma experimental ABIEncoderV2; |  | ||||||
|  |  | ||||||
| import "../src/interfaces/IERC20Bridge.sol"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| /// @dev Test bridge token |  | ||||||
| contract TestERC20BridgeToken { |  | ||||||
|     mapping (address => uint256) private _balances; |  | ||||||
|  |  | ||||||
|     function addBalance(address owner, int256 amount) |  | ||||||
|         external |  | ||||||
|     { |  | ||||||
|         setBalance(owner, uint256(int256(balanceOf(owner)) + amount)); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function setBalance(address owner, uint256 balance) |  | ||||||
|         public |  | ||||||
|     { |  | ||||||
|         _balances[owner] = balance; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function balanceOf(address owner) |  | ||||||
|         public |  | ||||||
|         view |  | ||||||
|         returns (uint256) |  | ||||||
|     { |  | ||||||
|         return _balances[owner]; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| /// @dev Test bridge contract. |  | ||||||
| contract TestERC20Bridge is |  | ||||||
|     IERC20Bridge |  | ||||||
| { |  | ||||||
|     TestERC20BridgeToken public testToken; |  | ||||||
|  |  | ||||||
|     event BridgeWithdrawTo( |  | ||||||
|         address tokenAddress, |  | ||||||
|         address from, |  | ||||||
|         address to, |  | ||||||
|         uint256 amount, |  | ||||||
|         bytes bridgeData |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     constructor() public { |  | ||||||
|         testToken = new TestERC20BridgeToken(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function setTestTokenBalance(address owner, uint256 balance) |  | ||||||
|         external |  | ||||||
|     { |  | ||||||
|         testToken.setBalance(owner, balance); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function bridgeTransferFrom( |  | ||||||
|         address tokenAddress, |  | ||||||
|         address from, |  | ||||||
|         address to, |  | ||||||
|         uint256 amount, |  | ||||||
|         bytes calldata bridgeData |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         returns (bytes4) |  | ||||||
|     { |  | ||||||
|         emit BridgeWithdrawTo( |  | ||||||
|             tokenAddress, |  | ||||||
|             from, |  | ||||||
|             to, |  | ||||||
|             amount, |  | ||||||
|             bridgeData |  | ||||||
|         ); |  | ||||||
|         // Unpack the bridgeData. |  | ||||||
|         ( |  | ||||||
|             int256 transferAmount, |  | ||||||
|             bytes memory revertData, |  | ||||||
|             bytes memory returnData |  | ||||||
|         ) = abi.decode(bridgeData, (int256, bytes, bytes)); |  | ||||||
|  |  | ||||||
|         // If `revertData` is set, revert. |  | ||||||
|         if (revertData.length != 0) { |  | ||||||
|             assembly { revert(add(revertData, 0x20), mload(revertData)) } |  | ||||||
|         } |  | ||||||
|         // Increase `to`'s balance by `transferAmount`. |  | ||||||
|         TestERC20BridgeToken(tokenAddress).addBalance(to, transferAmount); |  | ||||||
|         // Return `returnData`. |  | ||||||
|         assembly { return(add(returnData, 0x20), mload(returnData)) } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,206 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2019 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
| pragma experimental ABIEncoderV2; |  | ||||||
|  |  | ||||||
| import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; |  | ||||||
| import "../src/bridges/Eth2DaiBridge.sol"; |  | ||||||
| import "../src/interfaces/IEth2Dai.sol"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| // solhint-disable no-simple-event-func-name |  | ||||||
| contract TestEvents { |  | ||||||
|  |  | ||||||
|     event TokenTransfer( |  | ||||||
|         address token, |  | ||||||
|         address from, |  | ||||||
|         address to, |  | ||||||
|         uint256 amount |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     event TokenApprove( |  | ||||||
|         address token, |  | ||||||
|         address spender, |  | ||||||
|         uint256 allowance |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     function raiseTokenTransfer( |  | ||||||
|         address from, |  | ||||||
|         address to, |  | ||||||
|         uint256 amount |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|     { |  | ||||||
|         emit TokenTransfer( |  | ||||||
|             msg.sender, |  | ||||||
|             from, |  | ||||||
|             to, |  | ||||||
|             amount |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function raiseTokenApprove(address spender, uint256 allowance) |  | ||||||
|         external |  | ||||||
|     { |  | ||||||
|         emit TokenApprove(msg.sender, spender, allowance); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| /// @dev A minimalist ERC20 token. |  | ||||||
| contract TestToken { |  | ||||||
|  |  | ||||||
|     mapping (address => uint256) public balances; |  | ||||||
|     string private _nextTransferRevertReason; |  | ||||||
|     bytes private _nextTransferReturnData; |  | ||||||
|  |  | ||||||
|     /// @dev Just calls `raiseTokenTransfer()` on the caller. |  | ||||||
|     function transfer(address to, uint256 amount) |  | ||||||
|         external |  | ||||||
|         returns (bool) |  | ||||||
|     { |  | ||||||
|         TestEvents(msg.sender).raiseTokenTransfer(msg.sender, to, amount); |  | ||||||
|         if (bytes(_nextTransferRevertReason).length != 0) { |  | ||||||
|             revert(_nextTransferRevertReason); |  | ||||||
|         } |  | ||||||
|         bytes memory returnData = _nextTransferReturnData; |  | ||||||
|         assembly { return(add(returnData, 0x20), mload(returnData)) } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Set the balance for `owner`. |  | ||||||
|     function setBalance(address owner, uint256 balance) |  | ||||||
|         external |  | ||||||
|     { |  | ||||||
|         balances[owner] = balance; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Set the behavior of the `transfer()` call. |  | ||||||
|     function setTransferBehavior( |  | ||||||
|         string calldata revertReason, |  | ||||||
|         bytes calldata returnData |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|     { |  | ||||||
|         _nextTransferRevertReason = revertReason; |  | ||||||
|         _nextTransferReturnData = returnData; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Just calls `raiseTokenApprove()` on the caller. |  | ||||||
|     function approve(address spender, uint256 allowance) |  | ||||||
|         external |  | ||||||
|         returns (bool) |  | ||||||
|     { |  | ||||||
|         TestEvents(msg.sender).raiseTokenApprove(spender, allowance); |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function allowance(address, address) external view returns (uint256) { |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Retrieve the balance for `owner`. |  | ||||||
|     function balanceOf(address owner) |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (uint256) |  | ||||||
|     { |  | ||||||
|         return balances[owner]; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| /// @dev Eth2DaiBridge overridden to mock tokens and |  | ||||||
| ///      implement IEth2Dai. |  | ||||||
| contract TestEth2DaiBridge is |  | ||||||
|     TestEvents, |  | ||||||
|     IEth2Dai, |  | ||||||
|     Eth2DaiBridge |  | ||||||
| { |  | ||||||
|     event SellAllAmount( |  | ||||||
|         address sellToken, |  | ||||||
|         uint256 sellTokenAmount, |  | ||||||
|         address buyToken, |  | ||||||
|         uint256 minimumFillAmount |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     mapping (address => TestToken)  public testTokens; |  | ||||||
|     string private _nextRevertReason; |  | ||||||
|     uint256 private _nextFillAmount; |  | ||||||
|  |  | ||||||
|     /// @dev Create a token and set this contract's balance. |  | ||||||
|     function createToken(uint256 balance) |  | ||||||
|         external |  | ||||||
|         returns (address tokenAddress) |  | ||||||
|     { |  | ||||||
|         TestToken token = new TestToken(); |  | ||||||
|         testTokens[address(token)] = token; |  | ||||||
|         token.setBalance(address(this), balance); |  | ||||||
|         return address(token); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Set the behavior for `IEth2Dai.sellAllAmount()`. |  | ||||||
|     function setFillBehavior(string calldata revertReason, uint256 fillAmount) |  | ||||||
|         external |  | ||||||
|     { |  | ||||||
|         _nextRevertReason = revertReason; |  | ||||||
|         _nextFillAmount = fillAmount; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Set the behavior of a token's `transfer()`. |  | ||||||
|     function setTransferBehavior( |  | ||||||
|         address tokenAddress, |  | ||||||
|         string calldata revertReason, |  | ||||||
|         bytes calldata returnData |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|     { |  | ||||||
|         testTokens[tokenAddress].setTransferBehavior(revertReason, returnData); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Implementation of `IEth2Dai.sellAllAmount()` |  | ||||||
|     function sellAllAmount( |  | ||||||
|         address sellTokenAddress, |  | ||||||
|         uint256 sellTokenAmount, |  | ||||||
|         address buyTokenAddress, |  | ||||||
|         uint256 minimumFillAmount |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         returns (uint256 fillAmount) |  | ||||||
|     { |  | ||||||
|         emit SellAllAmount( |  | ||||||
|             sellTokenAddress, |  | ||||||
|             sellTokenAmount, |  | ||||||
|             buyTokenAddress, |  | ||||||
|             minimumFillAmount |  | ||||||
|         ); |  | ||||||
|         if (bytes(_nextRevertReason).length != 0) { |  | ||||||
|             revert(_nextRevertReason); |  | ||||||
|         } |  | ||||||
|         return _nextFillAmount; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // @dev This contract will double as the Eth2Dai contract. |  | ||||||
|     function _getEth2DaiAddress() |  | ||||||
|         internal |  | ||||||
|         view |  | ||||||
|         returns (address) |  | ||||||
|     { |  | ||||||
|         return address(this); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,355 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2019 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
| pragma experimental ABIEncoderV2; |  | ||||||
|  |  | ||||||
| import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; |  | ||||||
| import "../src/bridges/KyberBridge.sol"; |  | ||||||
| import "../src/interfaces/IKyberNetworkProxy.sol"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| // solhint-disable no-simple-event-func-name |  | ||||||
| interface ITestContract { |  | ||||||
|  |  | ||||||
|     function wethWithdraw( |  | ||||||
|         address payable ownerAddress, |  | ||||||
|         uint256 amount |  | ||||||
|     ) |  | ||||||
|         external; |  | ||||||
|  |  | ||||||
|     function wethDeposit( |  | ||||||
|         address ownerAddress |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         payable; |  | ||||||
|  |  | ||||||
|     function tokenTransfer( |  | ||||||
|         address ownerAddress, |  | ||||||
|         address recipientAddress, |  | ||||||
|         uint256 amount |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         returns (bool success); |  | ||||||
|  |  | ||||||
|     function tokenApprove( |  | ||||||
|         address ownerAddress, |  | ||||||
|         address spenderAddress, |  | ||||||
|         uint256 allowance |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         returns (bool success); |  | ||||||
|  |  | ||||||
|     function tokenBalanceOf( |  | ||||||
|         address ownerAddress |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (uint256 balance); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| /// @dev A minimalist ERC20/WETH token. |  | ||||||
| contract TestToken { |  | ||||||
|  |  | ||||||
|     uint8 public decimals; |  | ||||||
|     ITestContract private _testContract; |  | ||||||
|  |  | ||||||
|     constructor(uint8 decimals_) public { |  | ||||||
|         decimals = decimals_; |  | ||||||
|         _testContract = ITestContract(msg.sender); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function approve(address spender, uint256 allowance) |  | ||||||
|         external |  | ||||||
|         returns (bool) |  | ||||||
|     { |  | ||||||
|         return _testContract.tokenApprove( |  | ||||||
|             msg.sender, |  | ||||||
|             spender, |  | ||||||
|             allowance |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function transfer(address recipient, uint256 amount) |  | ||||||
|         external |  | ||||||
|         returns (bool) |  | ||||||
|     { |  | ||||||
|         return _testContract.tokenTransfer( |  | ||||||
|             msg.sender, |  | ||||||
|             recipient, |  | ||||||
|             amount |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function withdraw(uint256 amount) |  | ||||||
|         external |  | ||||||
|     { |  | ||||||
|         return _testContract.wethWithdraw(msg.sender, amount); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function deposit() |  | ||||||
|         external |  | ||||||
|         payable |  | ||||||
|     { |  | ||||||
|         return _testContract.wethDeposit.value(msg.value)(msg.sender); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function allowance(address, address) external view returns (uint256) { |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function balanceOf(address owner) |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (uint256) |  | ||||||
|     { |  | ||||||
|         return _testContract.tokenBalanceOf(owner); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| /// @dev KyberBridge overridden to mock tokens and implement IKyberBridge. |  | ||||||
| contract TestKyberBridge is |  | ||||||
|     KyberBridge, |  | ||||||
|     ITestContract, |  | ||||||
|     IKyberNetworkProxy |  | ||||||
| { |  | ||||||
|     event KyberBridgeTrade( |  | ||||||
|         uint256 msgValue, |  | ||||||
|         address sellTokenAddress, |  | ||||||
|         uint256 sellAmount, |  | ||||||
|         address buyTokenAddress, |  | ||||||
|         address payable recipientAddress, |  | ||||||
|         uint256 maxBuyTokenAmount, |  | ||||||
|         uint256 minConversionRate, |  | ||||||
|         address walletId |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     event KyberBridgeWethWithdraw( |  | ||||||
|         address ownerAddress, |  | ||||||
|         uint256 amount |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     event KyberBridgeWethDeposit( |  | ||||||
|         uint256 msgValue, |  | ||||||
|         address ownerAddress, |  | ||||||
|         uint256 amount |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     event KyberBridgeTokenApprove( |  | ||||||
|         address tokenAddress, |  | ||||||
|         address ownerAddress, |  | ||||||
|         address spenderAddress, |  | ||||||
|         uint256 allowance |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     event KyberBridgeTokenTransfer( |  | ||||||
|         address tokenAddress, |  | ||||||
|         address ownerAddress, |  | ||||||
|         address recipientAddress, |  | ||||||
|         uint256 amount |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     IEtherToken public weth; |  | ||||||
|     mapping (address => mapping (address => uint256)) private _tokenBalances; |  | ||||||
|     uint256 private _nextFillAmount; |  | ||||||
|  |  | ||||||
|     constructor() public { |  | ||||||
|         weth = IEtherToken(address(new TestToken(18))); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Implementation of `IKyberNetworkProxy.trade()` |  | ||||||
|     function trade( |  | ||||||
|         address sellTokenAddress, |  | ||||||
|         uint256 sellAmount, |  | ||||||
|         address buyTokenAddress, |  | ||||||
|         address payable recipientAddress, |  | ||||||
|         uint256 maxBuyTokenAmount, |  | ||||||
|         uint256 minConversionRate, |  | ||||||
|         address walletId |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         payable |  | ||||||
|         returns(uint256 boughtAmount) |  | ||||||
|     { |  | ||||||
|         emit KyberBridgeTrade( |  | ||||||
|             msg.value, |  | ||||||
|             sellTokenAddress, |  | ||||||
|             sellAmount, |  | ||||||
|             buyTokenAddress, |  | ||||||
|             recipientAddress, |  | ||||||
|             maxBuyTokenAmount, |  | ||||||
|             minConversionRate, |  | ||||||
|             walletId |  | ||||||
|         ); |  | ||||||
|         return _nextFillAmount; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function tradeWithHint( |  | ||||||
|         address sellTokenAddress, |  | ||||||
|         uint256 sellAmount, |  | ||||||
|         address buyTokenAddress, |  | ||||||
|         address payable recipientAddress, |  | ||||||
|         uint256 maxBuyTokenAmount, |  | ||||||
|         uint256 minConversionRate, |  | ||||||
|         address payable walletId, |  | ||||||
|         bytes calldata hint |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         payable |  | ||||||
|         returns (uint256 boughtAmount) |  | ||||||
|     { |  | ||||||
|         emit KyberBridgeTrade( |  | ||||||
|             msg.value, |  | ||||||
|             sellTokenAddress, |  | ||||||
|             sellAmount, |  | ||||||
|             buyTokenAddress, |  | ||||||
|             recipientAddress, |  | ||||||
|             maxBuyTokenAmount, |  | ||||||
|             minConversionRate, |  | ||||||
|             walletId |  | ||||||
|         ); |  | ||||||
|         return _nextFillAmount; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function createToken(uint8 decimals) |  | ||||||
|         external |  | ||||||
|         returns (address tokenAddress) |  | ||||||
|     { |  | ||||||
|         return address(new TestToken(decimals)); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function setNextFillAmount(uint256 amount) |  | ||||||
|         external |  | ||||||
|         payable |  | ||||||
|     { |  | ||||||
|         if (msg.value != 0) { |  | ||||||
|             require(amount == msg.value, "VALUE_AMOUNT_MISMATCH"); |  | ||||||
|             grantTokensTo(address(weth), address(this), msg.value); |  | ||||||
|         } |  | ||||||
|         _nextFillAmount = amount; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function wethDeposit( |  | ||||||
|         address ownerAddress |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         payable |  | ||||||
|     { |  | ||||||
|         require(msg.sender == address(weth), "ONLY_WETH"); |  | ||||||
|         grantTokensTo(address(weth), ownerAddress, msg.value); |  | ||||||
|         emit KyberBridgeWethDeposit( |  | ||||||
|             msg.value, |  | ||||||
|             ownerAddress, |  | ||||||
|             msg.value |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function wethWithdraw( |  | ||||||
|         address payable ownerAddress, |  | ||||||
|         uint256 amount |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|     { |  | ||||||
|         require(msg.sender == address(weth), "ONLY_WETH"); |  | ||||||
|         _tokenBalances[address(weth)][ownerAddress] -= amount; |  | ||||||
|         ownerAddress.transfer(amount); |  | ||||||
|         emit KyberBridgeWethWithdraw( |  | ||||||
|             ownerAddress, |  | ||||||
|             amount |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function tokenApprove( |  | ||||||
|         address ownerAddress, |  | ||||||
|         address spenderAddress, |  | ||||||
|         uint256 allowance |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         returns (bool success) |  | ||||||
|     { |  | ||||||
|         emit KyberBridgeTokenApprove( |  | ||||||
|             msg.sender, |  | ||||||
|             ownerAddress, |  | ||||||
|             spenderAddress, |  | ||||||
|             allowance |  | ||||||
|         ); |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function tokenTransfer( |  | ||||||
|         address ownerAddress, |  | ||||||
|         address recipientAddress, |  | ||||||
|         uint256 amount |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         returns (bool success) |  | ||||||
|     { |  | ||||||
|         _tokenBalances[msg.sender][ownerAddress] -= amount; |  | ||||||
|         _tokenBalances[msg.sender][recipientAddress] += amount; |  | ||||||
|         emit KyberBridgeTokenTransfer( |  | ||||||
|             msg.sender, |  | ||||||
|             ownerAddress, |  | ||||||
|             recipientAddress, |  | ||||||
|             amount |  | ||||||
|         ); |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function tokenBalanceOf( |  | ||||||
|         address ownerAddress |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (uint256 balance) |  | ||||||
|     { |  | ||||||
|         return _tokenBalances[msg.sender][ownerAddress]; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function grantTokensTo(address tokenAddress, address ownerAddress, uint256 amount) |  | ||||||
|         public |  | ||||||
|         payable |  | ||||||
|     { |  | ||||||
|         _tokenBalances[tokenAddress][ownerAddress] += amount; |  | ||||||
|         if (tokenAddress != address(weth)) { |  | ||||||
|             // Send back ether if not WETH. |  | ||||||
|             msg.sender.transfer(msg.value); |  | ||||||
|         } else { |  | ||||||
|             require(msg.value == amount, "VALUE_AMOUNT_MISMATCH"); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // @dev overridden to point to this contract. |  | ||||||
|     function _getKyberNetworkProxyAddress() |  | ||||||
|         internal |  | ||||||
|         view |  | ||||||
|         returns (address) |  | ||||||
|     { |  | ||||||
|         return address(this); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // @dev overridden to point to test WETH. |  | ||||||
|     function _getWethAddress() |  | ||||||
|         internal |  | ||||||
|         view |  | ||||||
|         returns (address) |  | ||||||
|     { |  | ||||||
|         return address(weth); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,82 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2019 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
|  |  | ||||||
| import "@0x/contracts-utils/contracts/src/LibBytes.sol"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| contract TestStaticCallTarget { |  | ||||||
|  |  | ||||||
|     using LibBytes for bytes; |  | ||||||
|  |  | ||||||
|     uint256 internal _state; |  | ||||||
|   |  | ||||||
|     function updateState() |  | ||||||
|         external |  | ||||||
|     { |  | ||||||
|         _state++; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function assertEvenNumber(uint256 target) |  | ||||||
|         external |  | ||||||
|         pure |  | ||||||
|     { |  | ||||||
|         require( |  | ||||||
|             target % 2 == 0, |  | ||||||
|             "TARGET_NOT_EVEN" |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function isOddNumber(uint256 target) |  | ||||||
|         external |  | ||||||
|         pure |  | ||||||
|         returns (bool isOdd) |  | ||||||
|     { |  | ||||||
|         isOdd = target % 2 == 1; |  | ||||||
|         return isOdd; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function noInputFunction() |  | ||||||
|         external |  | ||||||
|         pure |  | ||||||
|     { |  | ||||||
|         assert(msg.data.length == 4 && msg.data.readBytes4(0) == bytes4(keccak256("noInputFunction()"))); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function dynamicInputFunction(bytes calldata a) |  | ||||||
|         external |  | ||||||
|         pure |  | ||||||
|     { |  | ||||||
|         bytes memory abiEncodedData = abi.encodeWithSignature("dynamicInputFunction(bytes)", a); |  | ||||||
|         assert(msg.data.equals(abiEncodedData)); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function returnComplexType(uint256 a, uint256 b) |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (bytes memory result) |  | ||||||
|     { |  | ||||||
|         result = abi.encodePacked( |  | ||||||
|             address(this), |  | ||||||
|             a, |  | ||||||
|             b |  | ||||||
|         ); |  | ||||||
|         return result; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,436 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2019 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
| pragma experimental ABIEncoderV2; |  | ||||||
|  |  | ||||||
| import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; |  | ||||||
| import "@0x/contracts-utils/contracts/src/LibSafeMath.sol"; |  | ||||||
| import "../src/bridges/UniswapBridge.sol"; |  | ||||||
| import "../src/interfaces/IUniswapExchangeFactory.sol"; |  | ||||||
| import "../src/interfaces/IUniswapExchange.sol"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| // solhint-disable no-simple-event-func-name |  | ||||||
| contract TestEventsRaiser { |  | ||||||
|  |  | ||||||
|     event TokenTransfer( |  | ||||||
|         address token, |  | ||||||
|         address from, |  | ||||||
|         address to, |  | ||||||
|         uint256 amount |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     event TokenApprove( |  | ||||||
|         address spender, |  | ||||||
|         uint256 allowance |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     event WethDeposit( |  | ||||||
|         uint256 amount |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     event WethWithdraw( |  | ||||||
|         uint256 amount |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     event EthToTokenTransferInput( |  | ||||||
|         address exchange, |  | ||||||
|         uint256 minTokensBought, |  | ||||||
|         uint256 deadline, |  | ||||||
|         address recipient |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     event TokenToEthSwapInput( |  | ||||||
|         address exchange, |  | ||||||
|         uint256 tokensSold, |  | ||||||
|         uint256 minEthBought, |  | ||||||
|         uint256 deadline |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     event TokenToTokenTransferInput( |  | ||||||
|         address exchange, |  | ||||||
|         uint256 tokensSold, |  | ||||||
|         uint256 minTokensBought, |  | ||||||
|         uint256 minEthBought, |  | ||||||
|         uint256 deadline, |  | ||||||
|         address recipient, |  | ||||||
|         address toTokenAddress |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     function raiseEthToTokenTransferInput( |  | ||||||
|         uint256 minTokensBought, |  | ||||||
|         uint256 deadline, |  | ||||||
|         address recipient |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|     { |  | ||||||
|         emit EthToTokenTransferInput( |  | ||||||
|             msg.sender, |  | ||||||
|             minTokensBought, |  | ||||||
|             deadline, |  | ||||||
|             recipient |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function raiseTokenToEthSwapInput( |  | ||||||
|         uint256 tokensSold, |  | ||||||
|         uint256 minEthBought, |  | ||||||
|         uint256 deadline |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|     { |  | ||||||
|         emit TokenToEthSwapInput( |  | ||||||
|             msg.sender, |  | ||||||
|             tokensSold, |  | ||||||
|             minEthBought, |  | ||||||
|             deadline |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function raiseTokenToTokenTransferInput( |  | ||||||
|         uint256 tokensSold, |  | ||||||
|         uint256 minTokensBought, |  | ||||||
|         uint256 minEthBought, |  | ||||||
|         uint256 deadline, |  | ||||||
|         address recipient, |  | ||||||
|         address toTokenAddress |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|     { |  | ||||||
|         emit TokenToTokenTransferInput( |  | ||||||
|             msg.sender, |  | ||||||
|             tokensSold, |  | ||||||
|             minTokensBought, |  | ||||||
|             minEthBought, |  | ||||||
|             deadline, |  | ||||||
|             recipient, |  | ||||||
|             toTokenAddress |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function raiseTokenTransfer( |  | ||||||
|         address from, |  | ||||||
|         address to, |  | ||||||
|         uint256 amount |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|     { |  | ||||||
|         emit TokenTransfer( |  | ||||||
|             msg.sender, |  | ||||||
|             from, |  | ||||||
|             to, |  | ||||||
|             amount |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function raiseTokenApprove(address spender, uint256 allowance) |  | ||||||
|         external |  | ||||||
|     { |  | ||||||
|         emit TokenApprove(spender, allowance); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function raiseWethDeposit(uint256 amount) |  | ||||||
|         external |  | ||||||
|     { |  | ||||||
|         emit WethDeposit(amount); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function raiseWethWithdraw(uint256 amount) |  | ||||||
|         external |  | ||||||
|     { |  | ||||||
|         emit WethWithdraw(amount); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| /// @dev A minimalist ERC20/WETH token. |  | ||||||
| contract TestToken { |  | ||||||
|  |  | ||||||
|     using LibSafeMath for uint256; |  | ||||||
|  |  | ||||||
|     mapping (address => uint256) public balances; |  | ||||||
|     string private _nextRevertReason; |  | ||||||
|  |  | ||||||
|     /// @dev Set the balance for `owner`. |  | ||||||
|     function setBalance(address owner) |  | ||||||
|         external |  | ||||||
|         payable |  | ||||||
|     { |  | ||||||
|         balances[owner] = msg.value; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Set the revert reason for `transfer()`, |  | ||||||
|     ///      `deposit()`, and `withdraw()`. |  | ||||||
|     function setRevertReason(string calldata reason) |  | ||||||
|         external |  | ||||||
|     { |  | ||||||
|         _nextRevertReason = reason; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Just calls `raiseTokenTransfer()` on the caller. |  | ||||||
|     function transfer(address to, uint256 amount) |  | ||||||
|         external |  | ||||||
|         returns (bool) |  | ||||||
|     { |  | ||||||
|         _revertIfReasonExists(); |  | ||||||
|         TestEventsRaiser(msg.sender).raiseTokenTransfer(msg.sender, to, amount); |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Just calls `raiseTokenApprove()` on the caller. |  | ||||||
|     function approve(address spender, uint256 allowance) |  | ||||||
|         external |  | ||||||
|         returns (bool) |  | ||||||
|     { |  | ||||||
|         TestEventsRaiser(msg.sender).raiseTokenApprove(spender, allowance); |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev `IWETH.deposit()` that increases balances and calls |  | ||||||
|     ///     `raiseWethDeposit()` on the caller. |  | ||||||
|     function deposit() |  | ||||||
|         external |  | ||||||
|         payable |  | ||||||
|     { |  | ||||||
|         _revertIfReasonExists(); |  | ||||||
|         balances[msg.sender] += balances[msg.sender].safeAdd(msg.value); |  | ||||||
|         TestEventsRaiser(msg.sender).raiseWethDeposit(msg.value); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev `IWETH.withdraw()` that just reduces balances and calls |  | ||||||
|     ///       `raiseWethWithdraw()` on the caller. |  | ||||||
|     function withdraw(uint256 amount) |  | ||||||
|         external |  | ||||||
|     { |  | ||||||
|         _revertIfReasonExists(); |  | ||||||
|         balances[msg.sender] = balances[msg.sender].safeSub(amount); |  | ||||||
|         msg.sender.transfer(amount); |  | ||||||
|         TestEventsRaiser(msg.sender).raiseWethWithdraw(amount); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function allowance(address, address) external view returns (uint256) { |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Retrieve the balance for `owner`. |  | ||||||
|     function balanceOf(address owner) |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (uint256) |  | ||||||
|     { |  | ||||||
|         return balances[owner]; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function _revertIfReasonExists() |  | ||||||
|         private |  | ||||||
|         view |  | ||||||
|     { |  | ||||||
|         if (bytes(_nextRevertReason).length != 0) { |  | ||||||
|             revert(_nextRevertReason); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| contract TestExchange is |  | ||||||
|     IUniswapExchange |  | ||||||
| { |  | ||||||
|     address public tokenAddress; |  | ||||||
|     string private _nextRevertReason; |  | ||||||
|  |  | ||||||
|     constructor(address _tokenAddress) public { |  | ||||||
|         tokenAddress = _tokenAddress; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function setFillBehavior( |  | ||||||
|         string calldata revertReason |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         payable |  | ||||||
|     { |  | ||||||
|         _nextRevertReason = revertReason; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function ethToTokenTransferInput( |  | ||||||
|         uint256 minTokensBought, |  | ||||||
|         uint256 deadline, |  | ||||||
|         address recipient |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         payable |  | ||||||
|         returns (uint256 tokensBought) |  | ||||||
|     { |  | ||||||
|         TestEventsRaiser(msg.sender).raiseEthToTokenTransferInput( |  | ||||||
|             minTokensBought, |  | ||||||
|             deadline, |  | ||||||
|             recipient |  | ||||||
|         ); |  | ||||||
|         _revertIfReasonExists(); |  | ||||||
|         return address(this).balance; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function tokenToEthSwapInput( |  | ||||||
|         uint256 tokensSold, |  | ||||||
|         uint256 minEthBought, |  | ||||||
|         uint256 deadline |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         returns (uint256 ethBought) |  | ||||||
|     { |  | ||||||
|         TestEventsRaiser(msg.sender).raiseTokenToEthSwapInput( |  | ||||||
|             tokensSold, |  | ||||||
|             minEthBought, |  | ||||||
|             deadline |  | ||||||
|         ); |  | ||||||
|         _revertIfReasonExists(); |  | ||||||
|         uint256 fillAmount = address(this).balance; |  | ||||||
|         msg.sender.transfer(fillAmount); |  | ||||||
|         return fillAmount; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function tokenToTokenTransferInput( |  | ||||||
|         uint256 tokensSold, |  | ||||||
|         uint256 minTokensBought, |  | ||||||
|         uint256 minEthBought, |  | ||||||
|         uint256 deadline, |  | ||||||
|         address recipient, |  | ||||||
|         address toTokenAddress |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         returns (uint256 tokensBought) |  | ||||||
|     { |  | ||||||
|         TestEventsRaiser(msg.sender).raiseTokenToTokenTransferInput( |  | ||||||
|             tokensSold, |  | ||||||
|             minTokensBought, |  | ||||||
|             minEthBought, |  | ||||||
|             deadline, |  | ||||||
|             recipient, |  | ||||||
|             toTokenAddress |  | ||||||
|         ); |  | ||||||
|         _revertIfReasonExists(); |  | ||||||
|         return address(this).balance; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function toTokenAddress() |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (address _tokenAddress) |  | ||||||
|     { |  | ||||||
|         return tokenAddress; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function _revertIfReasonExists() |  | ||||||
|         private |  | ||||||
|         view |  | ||||||
|     { |  | ||||||
|         if (bytes(_nextRevertReason).length != 0) { |  | ||||||
|             revert(_nextRevertReason); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| /// @dev UniswapBridge overridden to mock tokens and implement IUniswapExchangeFactory. |  | ||||||
| contract TestUniswapBridge is |  | ||||||
|     IUniswapExchangeFactory, |  | ||||||
|     TestEventsRaiser, |  | ||||||
|     UniswapBridge |  | ||||||
| { |  | ||||||
|     TestToken public wethToken; |  | ||||||
|     // Token address to TestToken instance. |  | ||||||
|     mapping (address => TestToken) private _testTokens; |  | ||||||
|     // Token address to TestExchange instance. |  | ||||||
|     mapping (address => TestExchange) private _testExchanges; |  | ||||||
|  |  | ||||||
|     constructor() public { |  | ||||||
|         wethToken = new TestToken(); |  | ||||||
|         _testTokens[address(wethToken)] = wethToken; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Sets the balance of this contract for an existing token. |  | ||||||
|     ///      The wei attached will be the balance. |  | ||||||
|     function setTokenBalance(address tokenAddress) |  | ||||||
|         external |  | ||||||
|         payable |  | ||||||
|     { |  | ||||||
|         TestToken token = _testTokens[tokenAddress]; |  | ||||||
|         token.deposit.value(msg.value)(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Sets the revert reason for an existing token. |  | ||||||
|     function setTokenRevertReason(address tokenAddress, string calldata revertReason) |  | ||||||
|         external |  | ||||||
|     { |  | ||||||
|         TestToken token = _testTokens[tokenAddress]; |  | ||||||
|         token.setRevertReason(revertReason); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Create a token and exchange (if they don't exist) for a new token |  | ||||||
|     ///      and sets the exchange revert and fill behavior. The wei attached |  | ||||||
|     ///      will be the fill amount for the exchange. |  | ||||||
|     /// @param tokenAddress The token address. If zero, one will be created. |  | ||||||
|     /// @param revertReason The revert reason for exchange operations. |  | ||||||
|     function createTokenAndExchange( |  | ||||||
|         address tokenAddress, |  | ||||||
|         string calldata revertReason |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         payable |  | ||||||
|         returns (TestToken token, TestExchange exchange) |  | ||||||
|     { |  | ||||||
|         token = TestToken(tokenAddress); |  | ||||||
|         if (tokenAddress == address(0)) { |  | ||||||
|             token = new TestToken(); |  | ||||||
|         } |  | ||||||
|         _testTokens[address(token)] = token; |  | ||||||
|         exchange = _testExchanges[address(token)]; |  | ||||||
|         if (address(exchange) == address(0)) { |  | ||||||
|             _testExchanges[address(token)] = exchange = new TestExchange(address(token)); |  | ||||||
|         } |  | ||||||
|         exchange.setFillBehavior.value(msg.value)(revertReason); |  | ||||||
|         return (token, exchange); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev `IUniswapExchangeFactory.getExchange` |  | ||||||
|     function getExchange(address tokenAddress) |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (address) |  | ||||||
|     { |  | ||||||
|         return address(_testExchanges[tokenAddress]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // @dev Use `wethToken`. |  | ||||||
|     function _getWethAddress() |  | ||||||
|         internal |  | ||||||
|         view |  | ||||||
|         returns (address) |  | ||||||
|     { |  | ||||||
|         return address(wethToken); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // @dev This contract will double as the Uniswap contract. |  | ||||||
|     function _getUniswapExchangeFactoryAddress() |  | ||||||
|         internal |  | ||||||
|         view |  | ||||||
|         returns (address) |  | ||||||
|     { |  | ||||||
|         return address(this); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,253 +0,0 @@ | |||||||
| /* |  | ||||||
|  |  | ||||||
|   Copyright 2019 ZeroEx Intl. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
|  |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| pragma solidity ^0.5.9; |  | ||||||
| pragma experimental ABIEncoderV2; |  | ||||||
|  |  | ||||||
| import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; |  | ||||||
| import "@0x/contracts-utils/contracts/src/LibSafeMath.sol"; |  | ||||||
| import "@0x/contracts-utils/contracts/src/LibAddressArray.sol"; |  | ||||||
| import "../src/bridges/UniswapV2Bridge.sol"; |  | ||||||
| import "../src/interfaces/IUniswapV2Router01.sol"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| contract TestEventsRaiser { |  | ||||||
|  |  | ||||||
|     event TokenTransfer( |  | ||||||
|         address token, |  | ||||||
|         address from, |  | ||||||
|         address to, |  | ||||||
|         uint256 amount |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     event TokenApprove( |  | ||||||
|         address spender, |  | ||||||
|         uint256 allowance |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     event SwapExactTokensForTokensInput( |  | ||||||
|         uint amountIn, |  | ||||||
|         uint amountOutMin, |  | ||||||
|         address toTokenAddress, |  | ||||||
|         address to, |  | ||||||
|         uint deadline |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     function raiseTokenTransfer( |  | ||||||
|         address from, |  | ||||||
|         address to, |  | ||||||
|         uint256 amount |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|     { |  | ||||||
|         emit TokenTransfer( |  | ||||||
|             msg.sender, |  | ||||||
|             from, |  | ||||||
|             to, |  | ||||||
|             amount |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function raiseTokenApprove(address spender, uint256 allowance) external { |  | ||||||
|         emit TokenApprove(spender, allowance); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function raiseSwapExactTokensForTokensInput( |  | ||||||
|         uint amountIn, |  | ||||||
|         uint amountOutMin, |  | ||||||
|         address toTokenAddress, |  | ||||||
|         address to, |  | ||||||
|         uint deadline |  | ||||||
|     ) external |  | ||||||
|     { |  | ||||||
|         emit SwapExactTokensForTokensInput( |  | ||||||
|             amountIn, |  | ||||||
|             amountOutMin, |  | ||||||
|             toTokenAddress, |  | ||||||
|             to, |  | ||||||
|             deadline |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| /// @dev A minimalist ERC20 token. |  | ||||||
| contract TestToken { |  | ||||||
|  |  | ||||||
|     using LibSafeMath for uint256; |  | ||||||
|  |  | ||||||
|     mapping (address => uint256) public balances; |  | ||||||
|     string private _nextRevertReason; |  | ||||||
|  |  | ||||||
|     /// @dev Set the balance for `owner`. |  | ||||||
|     function setBalance(address owner, uint256 balance) |  | ||||||
|         external |  | ||||||
|         payable |  | ||||||
|     { |  | ||||||
|         balances[owner] = balance; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Just emits a TokenTransfer event on the caller |  | ||||||
|     function transfer(address to, uint256 amount) |  | ||||||
|         external |  | ||||||
|         returns (bool) |  | ||||||
|     { |  | ||||||
|         TestEventsRaiser(msg.sender).raiseTokenTransfer(msg.sender, to, amount); |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Just emits a TokenApprove event on the caller |  | ||||||
|     function approve(address spender, uint256 allowance) |  | ||||||
|         external |  | ||||||
|         returns (bool) |  | ||||||
|     { |  | ||||||
|         TestEventsRaiser(msg.sender).raiseTokenApprove(spender, allowance); |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function allowance(address, address) external view returns (uint256) { |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Retrieve the balance for `owner`. |  | ||||||
|     function balanceOf(address owner) |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (uint256) |  | ||||||
|     { |  | ||||||
|         return balances[owner]; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| /// @dev Mock the UniswapV2Router01 contract |  | ||||||
| contract TestRouter is |  | ||||||
|     IUniswapV2Router01 |  | ||||||
| { |  | ||||||
|     string private _nextRevertReason; |  | ||||||
|  |  | ||||||
|     /// @dev Set the revert reason for `swapExactTokensForTokens`. |  | ||||||
|     function setRevertReason(string calldata reason) |  | ||||||
|         external |  | ||||||
|     { |  | ||||||
|         _nextRevertReason = reason; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function swapExactTokensForTokens( |  | ||||||
|         uint amountIn, |  | ||||||
|         uint amountOutMin, |  | ||||||
|         address[] calldata path, |  | ||||||
|         address to, |  | ||||||
|         uint deadline |  | ||||||
|     ) external returns (uint[] memory amounts) |  | ||||||
|     { |  | ||||||
|         _revertIfReasonExists(); |  | ||||||
|  |  | ||||||
|         amounts = new uint[](path.length); |  | ||||||
|         amounts[0] = amountIn; |  | ||||||
|         amounts[amounts.length - 1] = amountOutMin; |  | ||||||
|  |  | ||||||
|         TestEventsRaiser(msg.sender).raiseSwapExactTokensForTokensInput( |  | ||||||
|             // tokens sold |  | ||||||
|             amountIn, |  | ||||||
|             // tokens bought |  | ||||||
|             amountOutMin, |  | ||||||
|             // output token (toTokenAddress) |  | ||||||
|             path[path.length - 1], |  | ||||||
|             // recipient |  | ||||||
|             to, |  | ||||||
|             // deadline |  | ||||||
|             deadline |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function _revertIfReasonExists() |  | ||||||
|         private |  | ||||||
|         view |  | ||||||
|     { |  | ||||||
|         if (bytes(_nextRevertReason).length != 0) { |  | ||||||
|             revert(_nextRevertReason); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| /// @dev UniswapV2Bridge overridden to mock tokens and Uniswap router |  | ||||||
| contract TestUniswapV2Bridge is |  | ||||||
|     UniswapV2Bridge, |  | ||||||
|     TestEventsRaiser |  | ||||||
| { |  | ||||||
|  |  | ||||||
|     // Token address to TestToken instance. |  | ||||||
|     mapping (address => TestToken) private _testTokens; |  | ||||||
|     // TestRouter instance. |  | ||||||
|     TestRouter private _testRouter; |  | ||||||
|  |  | ||||||
|     constructor() public { |  | ||||||
|         _testRouter = new TestRouter(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function setRouterRevertReason(string calldata revertReason) |  | ||||||
|         external |  | ||||||
|     { |  | ||||||
|         _testRouter.setRevertReason(revertReason); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Sets the balance of this contract for an existing token. |  | ||||||
|     ///      The wei attached will be the balance. |  | ||||||
|     function setTokenBalance(address tokenAddress, uint256 balance) |  | ||||||
|         external |  | ||||||
|     { |  | ||||||
|         TestToken token = _testTokens[tokenAddress]; |  | ||||||
|         token.setBalance(address(this), balance); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// @dev Create a new token |  | ||||||
|     /// @param tokenAddress The token address. If zero, one will be created. |  | ||||||
|     function createToken( |  | ||||||
|         address tokenAddress |  | ||||||
|     ) |  | ||||||
|         external |  | ||||||
|         returns (TestToken token) |  | ||||||
|     { |  | ||||||
|         token = TestToken(tokenAddress); |  | ||||||
|         if (tokenAddress == address(0)) { |  | ||||||
|             token = new TestToken(); |  | ||||||
|         } |  | ||||||
|         _testTokens[address(token)] = token; |  | ||||||
|  |  | ||||||
|         return token; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function getRouterAddress() |  | ||||||
|         external |  | ||||||
|         view |  | ||||||
|         returns (address) |  | ||||||
|     { |  | ||||||
|         return address(_testRouter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function _getUniswapV2Router01Address() |  | ||||||
|         internal |  | ||||||
|         view |  | ||||||
|         returns (address) |  | ||||||
|     { |  | ||||||
|         return address(_testRouter); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,99 +0,0 @@ | |||||||
| { |  | ||||||
|     "name": "@0x/contracts-asset-proxy", |  | ||||||
|     "version": "3.7.6", |  | ||||||
|     "engines": { |  | ||||||
|         "node": ">=6.12" |  | ||||||
|     }, |  | ||||||
|     "description": "Smart contract components 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 contracts:gen generate_contract_wrappers contracts:copy", |  | ||||||
|         "test": "yarn run_mocha", |  | ||||||
|         "rebuild_and_test": "run-s build test", |  | ||||||
|         "test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov", |  | ||||||
|         "test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html", |  | ||||||
|         "test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha", |  | ||||||
|         "run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit", |  | ||||||
|         "compile": "sol-compiler", |  | ||||||
|         "watch": "sol-compiler -w", |  | ||||||
|         "clean": "shx rm -rf lib test/generated-artifacts test/generated-wrappers generated-artifacts generated-wrappers", |  | ||||||
|         "generate_contract_wrappers": "abi-gen --debug --abis  ${npm_package_config_abis} --output test/generated-wrappers --backend ethers", |  | ||||||
|         "lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./test/generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude ./test/generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts", |  | ||||||
|         "fix": "tslint --fix --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./test/generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude ./test/generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts", |  | ||||||
|         "coverage:report:text": "istanbul report text", |  | ||||||
|         "coverage:report:html": "istanbul report html && open coverage/index.html", |  | ||||||
|         "profiler:report:html": "istanbul report html && open coverage/index.html", |  | ||||||
|         "coverage:report:lcov": "istanbul report lcov", |  | ||||||
|         "test:circleci": "yarn test", |  | ||||||
|         "contracts:gen": "contracts-gen generate", |  | ||||||
|         "contracts:copy": "contracts-gen copy", |  | ||||||
|         "lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol", |  | ||||||
|         "compile:truffle": "truffle compile", |  | ||||||
|         "docs:md": "ts-doc-gen --sourceDir='$PROJECT_FILES' --output=$MD_FILE_DIR --fileExtension=mdx --tsconfig=./typedoc-tsconfig.json", |  | ||||||
|         "docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES" |  | ||||||
|     }, |  | ||||||
|     "config": { |  | ||||||
|         "abis": "./test/generated-artifacts/@(BalancerBridge|BancorBridge|ChaiBridge|CreamBridge|CryptoComBridge|CurveBridge|DODOBridge|DexForwarderBridge|DydxBridge|ERC1155Proxy|ERC20BridgeProxy|ERC20Proxy|ERC721Proxy|Eth2DaiBridge|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|IBalancerPool|IBancorNetwork|IChai|ICurve|IDydx|IDydxBridge|IERC20Bridge|IEth2Dai|IGasToken|IKyberNetworkProxy|IMStable|IMooniswap|IShell|IUniswapExchange|IUniswapExchangeFactory|IUniswapV2Router01|KyberBridge|MStableBridge|MixinAssetProxyDispatcher|MixinAuthorizable|MixinGasToken|MooniswapBridge|MultiAssetProxy|Ownable|ShellBridge|SnowSwapBridge|StaticCallProxy|SushiSwapBridge|SwerveBridge|TestBancorBridge|TestChaiBridge|TestDexForwarderBridge|TestDydxBridge|TestERC20Bridge|TestEth2DaiBridge|TestKyberBridge|TestStaticCallTarget|TestUniswapBridge|TestUniswapV2Bridge|UniswapBridge|UniswapV2Bridge).json", |  | ||||||
|         "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually." |  | ||||||
|     }, |  | ||||||
|     "repository": { |  | ||||||
|         "type": "git", |  | ||||||
|         "url": "https://github.com/0xProject/protocol.git" |  | ||||||
|     }, |  | ||||||
|     "license": "Apache-2.0", |  | ||||||
|     "bugs": { |  | ||||||
|         "url": "https://github.com/0xProject/protocol/issues" |  | ||||||
|     }, |  | ||||||
|     "homepage": "https://github.com/0xProject/protocol/tree/main/contracts/protocol", |  | ||||||
|     "devDependencies": { |  | ||||||
|         "@0x/abi-gen": "^5.4.19", |  | ||||||
|         "@0x/contract-wrappers": "^13.12.3", |  | ||||||
|         "@0x/contracts-gen": "^2.0.30", |  | ||||||
|         "@0x/contracts-test-utils": "^5.3.21", |  | ||||||
|         "@0x/contracts-utils": "^4.7.3", |  | ||||||
|         "@0x/dev-utils": "^4.2.1", |  | ||||||
|         "@0x/sol-compiler": "^4.5.2", |  | ||||||
|         "@0x/ts-doc-gen": "^0.0.28", |  | ||||||
|         "@0x/tslint-config": "^4.1.3", |  | ||||||
|         "@types/lodash": "4.14.104", |  | ||||||
|         "@types/mocha": "^5.2.7", |  | ||||||
|         "@types/node": "12.12.54", |  | ||||||
|         "chai": "^4.0.1", |  | ||||||
|         "chai-as-promised": "^7.1.0", |  | ||||||
|         "chai-bignumber": "^3.0.0", |  | ||||||
|         "dirty-chai": "^2.0.1", |  | ||||||
|         "ethereumjs-util": "^5.1.1", |  | ||||||
|         "make-promises-safe": "^1.1.0", |  | ||||||
|         "mocha": "^6.2.0", |  | ||||||
|         "npm-run-all": "^4.1.2", |  | ||||||
|         "shx": "^0.2.2", |  | ||||||
|         "solhint": "^1.4.1", |  | ||||||
|         "truffle": "^5.0.32", |  | ||||||
|         "tslint": "5.11.0", |  | ||||||
|         "typedoc": "~0.16.11", |  | ||||||
|         "typescript": "3.0.1" |  | ||||||
|     }, |  | ||||||
|     "dependencies": { |  | ||||||
|         "@0x/base-contract": "^6.2.18", |  | ||||||
|         "@0x/contracts-erc1155": "^2.1.24", |  | ||||||
|         "@0x/contracts-erc20": "^3.3.3", |  | ||||||
|         "@0x/contracts-erc721": "^3.1.24", |  | ||||||
|         "@0x/contracts-exchange-libs": "^4.3.24", |  | ||||||
|         "@0x/order-utils": "^10.4.16", |  | ||||||
|         "@0x/types": "^3.3.1", |  | ||||||
|         "@0x/typescript-typings": "^5.1.6", |  | ||||||
|         "@0x/utils": "^6.2.0", |  | ||||||
|         "@0x/web3-wrapper": "^7.4.1", |  | ||||||
|         "ethereum-types": "^3.4.0", |  | ||||||
|         "lodash": "^4.17.11" |  | ||||||
|     }, |  | ||||||
|     "publishConfig": { |  | ||||||
|         "access": "public" |  | ||||||
|     }, |  | ||||||
|     "gitHead": "4f91bfd907996b2f4dd383778b50c479c2602b56" |  | ||||||
| } |  | ||||||
| @@ -1,127 +0,0 @@ | |||||||
| /* |  | ||||||
|  * ----------------------------------------------------------------------------- |  | ||||||
|  * Warning: This file is auto-generated by contracts-gen. Don't edit manually. |  | ||||||
|  * ----------------------------------------------------------------------------- |  | ||||||
|  */ |  | ||||||
| import { ContractArtifact } from 'ethereum-types'; |  | ||||||
|  |  | ||||||
| import * as BalancerBridge from '../generated-artifacts/BalancerBridge.json'; |  | ||||||
| import * as BancorBridge from '../generated-artifacts/BancorBridge.json'; |  | ||||||
| import * as ChaiBridge from '../generated-artifacts/ChaiBridge.json'; |  | ||||||
| import * as CreamBridge from '../generated-artifacts/CreamBridge.json'; |  | ||||||
| import * as CryptoComBridge from '../generated-artifacts/CryptoComBridge.json'; |  | ||||||
| import * as CurveBridge from '../generated-artifacts/CurveBridge.json'; |  | ||||||
| import * as DexForwarderBridge from '../generated-artifacts/DexForwarderBridge.json'; |  | ||||||
| import * as DODOBridge from '../generated-artifacts/DODOBridge.json'; |  | ||||||
| import * as DydxBridge from '../generated-artifacts/DydxBridge.json'; |  | ||||||
| import * as ERC1155Proxy from '../generated-artifacts/ERC1155Proxy.json'; |  | ||||||
| import * as ERC20BridgeProxy from '../generated-artifacts/ERC20BridgeProxy.json'; |  | ||||||
| import * as ERC20Proxy from '../generated-artifacts/ERC20Proxy.json'; |  | ||||||
| import * as ERC721Proxy from '../generated-artifacts/ERC721Proxy.json'; |  | ||||||
| import * as Eth2DaiBridge from '../generated-artifacts/Eth2DaiBridge.json'; |  | ||||||
| import * as IAssetData from '../generated-artifacts/IAssetData.json'; |  | ||||||
| import * as IAssetProxy from '../generated-artifacts/IAssetProxy.json'; |  | ||||||
| import * as IAssetProxyDispatcher from '../generated-artifacts/IAssetProxyDispatcher.json'; |  | ||||||
| import * as IAuthorizable from '../generated-artifacts/IAuthorizable.json'; |  | ||||||
| import * as IBalancerPool from '../generated-artifacts/IBalancerPool.json'; |  | ||||||
| import * as IBancorNetwork from '../generated-artifacts/IBancorNetwork.json'; |  | ||||||
| import * as IChai from '../generated-artifacts/IChai.json'; |  | ||||||
| import * as ICurve from '../generated-artifacts/ICurve.json'; |  | ||||||
| import * as IDydx from '../generated-artifacts/IDydx.json'; |  | ||||||
| import * as IDydxBridge from '../generated-artifacts/IDydxBridge.json'; |  | ||||||
| import * as IERC20Bridge from '../generated-artifacts/IERC20Bridge.json'; |  | ||||||
| import * as IEth2Dai from '../generated-artifacts/IEth2Dai.json'; |  | ||||||
| import * as IGasToken from '../generated-artifacts/IGasToken.json'; |  | ||||||
| import * as IKyberNetworkProxy from '../generated-artifacts/IKyberNetworkProxy.json'; |  | ||||||
| import * as IMooniswap from '../generated-artifacts/IMooniswap.json'; |  | ||||||
| import * as IMStable from '../generated-artifacts/IMStable.json'; |  | ||||||
| import * as IShell from '../generated-artifacts/IShell.json'; |  | ||||||
| import * as IUniswapExchange from '../generated-artifacts/IUniswapExchange.json'; |  | ||||||
| import * as IUniswapExchangeFactory from '../generated-artifacts/IUniswapExchangeFactory.json'; |  | ||||||
| import * as IUniswapV2Router01 from '../generated-artifacts/IUniswapV2Router01.json'; |  | ||||||
| import * as KyberBridge from '../generated-artifacts/KyberBridge.json'; |  | ||||||
| import * as MixinAssetProxyDispatcher from '../generated-artifacts/MixinAssetProxyDispatcher.json'; |  | ||||||
| import * as MixinAuthorizable from '../generated-artifacts/MixinAuthorizable.json'; |  | ||||||
| import * as MixinGasToken from '../generated-artifacts/MixinGasToken.json'; |  | ||||||
| import * as MooniswapBridge from '../generated-artifacts/MooniswapBridge.json'; |  | ||||||
| import * as MStableBridge from '../generated-artifacts/MStableBridge.json'; |  | ||||||
| import * as MultiAssetProxy from '../generated-artifacts/MultiAssetProxy.json'; |  | ||||||
| import * as Ownable from '../generated-artifacts/Ownable.json'; |  | ||||||
| import * as ShellBridge from '../generated-artifacts/ShellBridge.json'; |  | ||||||
| import * as SnowSwapBridge from '../generated-artifacts/SnowSwapBridge.json'; |  | ||||||
| import * as StaticCallProxy from '../generated-artifacts/StaticCallProxy.json'; |  | ||||||
| import * as SushiSwapBridge from '../generated-artifacts/SushiSwapBridge.json'; |  | ||||||
| import * as SwerveBridge from '../generated-artifacts/SwerveBridge.json'; |  | ||||||
| import * as TestBancorBridge from '../generated-artifacts/TestBancorBridge.json'; |  | ||||||
| import * as TestChaiBridge from '../generated-artifacts/TestChaiBridge.json'; |  | ||||||
| import * as TestDexForwarderBridge from '../generated-artifacts/TestDexForwarderBridge.json'; |  | ||||||
| import * as TestDydxBridge from '../generated-artifacts/TestDydxBridge.json'; |  | ||||||
| import * as TestERC20Bridge from '../generated-artifacts/TestERC20Bridge.json'; |  | ||||||
| import * as TestEth2DaiBridge from '../generated-artifacts/TestEth2DaiBridge.json'; |  | ||||||
| import * as TestKyberBridge from '../generated-artifacts/TestKyberBridge.json'; |  | ||||||
| import * as TestStaticCallTarget from '../generated-artifacts/TestStaticCallTarget.json'; |  | ||||||
| import * as TestUniswapBridge from '../generated-artifacts/TestUniswapBridge.json'; |  | ||||||
| import * as TestUniswapV2Bridge from '../generated-artifacts/TestUniswapV2Bridge.json'; |  | ||||||
| import * as UniswapBridge from '../generated-artifacts/UniswapBridge.json'; |  | ||||||
| import * as UniswapV2Bridge from '../generated-artifacts/UniswapV2Bridge.json'; |  | ||||||
| export const artifacts = { |  | ||||||
|     MixinAssetProxyDispatcher: MixinAssetProxyDispatcher as ContractArtifact, |  | ||||||
|     MixinAuthorizable: MixinAuthorizable as ContractArtifact, |  | ||||||
|     Ownable: Ownable as ContractArtifact, |  | ||||||
|     ERC1155Proxy: ERC1155Proxy as ContractArtifact, |  | ||||||
|     ERC20BridgeProxy: ERC20BridgeProxy as ContractArtifact, |  | ||||||
|     ERC20Proxy: ERC20Proxy as ContractArtifact, |  | ||||||
|     ERC721Proxy: ERC721Proxy as ContractArtifact, |  | ||||||
|     MultiAssetProxy: MultiAssetProxy as ContractArtifact, |  | ||||||
|     StaticCallProxy: StaticCallProxy as ContractArtifact, |  | ||||||
|     BalancerBridge: BalancerBridge as ContractArtifact, |  | ||||||
|     BancorBridge: BancorBridge as ContractArtifact, |  | ||||||
|     ChaiBridge: ChaiBridge as ContractArtifact, |  | ||||||
|     CreamBridge: CreamBridge as ContractArtifact, |  | ||||||
|     CryptoComBridge: CryptoComBridge as ContractArtifact, |  | ||||||
|     CurveBridge: CurveBridge as ContractArtifact, |  | ||||||
|     DODOBridge: DODOBridge as ContractArtifact, |  | ||||||
|     DexForwarderBridge: DexForwarderBridge as ContractArtifact, |  | ||||||
|     DydxBridge: DydxBridge as ContractArtifact, |  | ||||||
|     Eth2DaiBridge: Eth2DaiBridge as ContractArtifact, |  | ||||||
|     KyberBridge: KyberBridge as ContractArtifact, |  | ||||||
|     MStableBridge: MStableBridge as ContractArtifact, |  | ||||||
|     MixinGasToken: MixinGasToken as ContractArtifact, |  | ||||||
|     MooniswapBridge: MooniswapBridge as ContractArtifact, |  | ||||||
|     ShellBridge: ShellBridge as ContractArtifact, |  | ||||||
|     SnowSwapBridge: SnowSwapBridge as ContractArtifact, |  | ||||||
|     SushiSwapBridge: SushiSwapBridge as ContractArtifact, |  | ||||||
|     SwerveBridge: SwerveBridge as ContractArtifact, |  | ||||||
|     UniswapBridge: UniswapBridge as ContractArtifact, |  | ||||||
|     UniswapV2Bridge: UniswapV2Bridge as ContractArtifact, |  | ||||||
|     IAssetData: IAssetData as ContractArtifact, |  | ||||||
|     IAssetProxy: IAssetProxy as ContractArtifact, |  | ||||||
|     IAssetProxyDispatcher: IAssetProxyDispatcher as ContractArtifact, |  | ||||||
|     IAuthorizable: IAuthorizable as ContractArtifact, |  | ||||||
|     IBalancerPool: IBalancerPool as ContractArtifact, |  | ||||||
|     IBancorNetwork: IBancorNetwork as ContractArtifact, |  | ||||||
|     IChai: IChai as ContractArtifact, |  | ||||||
|     ICurve: ICurve as ContractArtifact, |  | ||||||
|     IDydx: IDydx as ContractArtifact, |  | ||||||
|     IDydxBridge: IDydxBridge as ContractArtifact, |  | ||||||
|     IERC20Bridge: IERC20Bridge as ContractArtifact, |  | ||||||
|     IEth2Dai: IEth2Dai as ContractArtifact, |  | ||||||
|     IGasToken: IGasToken as ContractArtifact, |  | ||||||
|     IKyberNetworkProxy: IKyberNetworkProxy as ContractArtifact, |  | ||||||
|     IMStable: IMStable as ContractArtifact, |  | ||||||
|     IMooniswap: IMooniswap as ContractArtifact, |  | ||||||
|     IShell: IShell as ContractArtifact, |  | ||||||
|     IUniswapExchange: IUniswapExchange as ContractArtifact, |  | ||||||
|     IUniswapExchangeFactory: IUniswapExchangeFactory as ContractArtifact, |  | ||||||
|     IUniswapV2Router01: IUniswapV2Router01 as ContractArtifact, |  | ||||||
|     TestBancorBridge: TestBancorBridge as ContractArtifact, |  | ||||||
|     TestChaiBridge: TestChaiBridge as ContractArtifact, |  | ||||||
|     TestDexForwarderBridge: TestDexForwarderBridge as ContractArtifact, |  | ||||||
|     TestDydxBridge: TestDydxBridge as ContractArtifact, |  | ||||||
|     TestERC20Bridge: TestERC20Bridge as ContractArtifact, |  | ||||||
|     TestEth2DaiBridge: TestEth2DaiBridge as ContractArtifact, |  | ||||||
|     TestKyberBridge: TestKyberBridge as ContractArtifact, |  | ||||||
|     TestStaticCallTarget: TestStaticCallTarget as ContractArtifact, |  | ||||||
|     TestUniswapBridge: TestUniswapBridge as ContractArtifact, |  | ||||||
|     TestUniswapV2Bridge: TestUniswapV2Bridge as ContractArtifact, |  | ||||||
| }; |  | ||||||
| @@ -1,112 +0,0 @@ | |||||||
| import { AssetProxyId } from '@0x/types'; |  | ||||||
| import { BigNumber, hexUtils } from '@0x/utils'; |  | ||||||
|  |  | ||||||
| import { IAssetDataContract } from './wrappers'; |  | ||||||
|  |  | ||||||
| const assetDataIface = new IAssetDataContract('0x0000000000000000000000000000000000000000', { isEIP1193: true } as any); |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Get the proxy ID from encoded asset data. |  | ||||||
|  */ |  | ||||||
| export function getAssetDataProxyId(encoded: string): AssetProxyId { |  | ||||||
|     // tslint:disable-next-line: no-unnecessary-type-assertion |  | ||||||
|     return hexUtils.slice(encoded, 0, 4) as AssetProxyId; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Decode ERC20 asset data. |  | ||||||
|  */ |  | ||||||
| export function decodeERC20AssetData(encoded: string): string { |  | ||||||
|     return assetDataIface.getABIDecodedTransactionData<string>('ERC20Token', encoded); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Decode ERC721 asset data. |  | ||||||
|  */ |  | ||||||
| export function decodeERC721AssetData(encoded: string): [string, BigNumber] { |  | ||||||
|     return assetDataIface.getABIDecodedTransactionData<[string, BigNumber]>('ERC721Token', encoded); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Decode ERC1155 asset data. |  | ||||||
|  */ |  | ||||||
| export function decodeERC1155AssetData(encoded: string): [string, BigNumber[], BigNumber[], string] { |  | ||||||
|     return assetDataIface.getABIDecodedTransactionData<[string, BigNumber[], BigNumber[], string]>( |  | ||||||
|         'ERC1155Assets', |  | ||||||
|         encoded, |  | ||||||
|     ); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Decode MultiAsset asset data. |  | ||||||
|  */ |  | ||||||
| export function decodeMultiAssetData(encoded: string): [BigNumber[], string[]] { |  | ||||||
|     return assetDataIface.getABIDecodedTransactionData<[BigNumber[], string[]]>('MultiAsset', encoded); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Decode StaticCall asset data. |  | ||||||
|  */ |  | ||||||
| export function decodeStaticCallAssetData(encoded: string): [string, string, string] { |  | ||||||
|     return assetDataIface.getABIDecodedTransactionData<[string, string, string]>('StaticCall', encoded); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Decode ERC20Bridge asset data. |  | ||||||
|  */ |  | ||||||
| export function decodeERC20BridgeAssetData(encoded: string): [string, string, string] { |  | ||||||
|     return assetDataIface.getABIDecodedTransactionData<[string, string, string]>('ERC20Bridge', encoded); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Encode ERC20 asset data. |  | ||||||
|  */ |  | ||||||
| export function encodeERC20AssetData(tokenAddress: string): string { |  | ||||||
|     return assetDataIface.ERC20Token(tokenAddress).getABIEncodedTransactionData(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Encode ERC721 asset data. |  | ||||||
|  */ |  | ||||||
| export function encodeERC721AssetData(tokenAddress: string, tokenId: BigNumber): string { |  | ||||||
|     return assetDataIface.ERC721Token(tokenAddress, tokenId).getABIEncodedTransactionData(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Encode ERC1155 asset data. |  | ||||||
|  */ |  | ||||||
| export function encodeERC1155AssetData( |  | ||||||
|     tokenAddress: string, |  | ||||||
|     tokenIds: BigNumber[], |  | ||||||
|     values: BigNumber[], |  | ||||||
|     callbackData: string, |  | ||||||
| ): string { |  | ||||||
|     return assetDataIface.ERC1155Assets(tokenAddress, tokenIds, values, callbackData).getABIEncodedTransactionData(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Encode MultiAsset asset data. |  | ||||||
|  */ |  | ||||||
| export function encodeMultiAssetData(values: BigNumber[], nestedAssetData: string[]): string { |  | ||||||
|     return assetDataIface.MultiAsset(values, nestedAssetData).getABIEncodedTransactionData(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Encode StaticCall asset data. |  | ||||||
|  */ |  | ||||||
| export function encodeStaticCallAssetData( |  | ||||||
|     staticCallTargetAddress: string, |  | ||||||
|     staticCallData: string, |  | ||||||
|     expectedReturnDataHash: string, |  | ||||||
| ): string { |  | ||||||
|     return assetDataIface |  | ||||||
|         .StaticCall(staticCallTargetAddress, staticCallData, expectedReturnDataHash) |  | ||||||
|         .getABIEncodedTransactionData(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Encode ERC20Bridge asset data. |  | ||||||
|  */ |  | ||||||
| export function encodeERC20BridgeAssetData(tokenAddress: string, bridgeAddress: string, bridgeData: string): string { |  | ||||||
|     return assetDataIface.ERC20Bridge(tokenAddress, bridgeAddress, bridgeData).getABIEncodedTransactionData(); |  | ||||||
| } |  | ||||||
| @@ -1,27 +0,0 @@ | |||||||
| import { AbiEncoder, BigNumber } from '@0x/utils'; |  | ||||||
|  |  | ||||||
| export interface DexForwarderBridgeCall { |  | ||||||
|     target: string; |  | ||||||
|     inputTokenAmount: BigNumber; |  | ||||||
|     outputTokenAmount: BigNumber; |  | ||||||
|     bridgeData: string; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export interface DexForwaderBridgeData { |  | ||||||
|     inputToken: string; |  | ||||||
|     calls: DexForwarderBridgeCall[]; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export const dexForwarderBridgeDataEncoder = AbiEncoder.create([ |  | ||||||
|     { name: 'inputToken', type: 'address' }, |  | ||||||
|     { |  | ||||||
|         name: 'calls', |  | ||||||
|         type: 'tuple[]', |  | ||||||
|         components: [ |  | ||||||
|             { name: 'target', type: 'address' }, |  | ||||||
|             { name: 'inputTokenAmount', type: 'uint256' }, |  | ||||||
|             { name: 'outputTokenAmount', type: 'uint256' }, |  | ||||||
|             { name: 'bridgeData', type: 'bytes' }, |  | ||||||
|         ], |  | ||||||
|     }, |  | ||||||
| ]); |  | ||||||
| @@ -1,40 +0,0 @@ | |||||||
| import { AbiEncoder, BigNumber } from '@0x/utils'; |  | ||||||
|  |  | ||||||
| export enum DydxBridgeActionType { |  | ||||||
|     Deposit, |  | ||||||
|     Withdraw, |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export interface DydxBridgeAction { |  | ||||||
|     actionType: DydxBridgeActionType; |  | ||||||
|     accountIdx: BigNumber; |  | ||||||
|     marketId: BigNumber; |  | ||||||
|     conversionRateNumerator: BigNumber; |  | ||||||
|     conversionRateDenominator: BigNumber; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export interface DydxBridgeData { |  | ||||||
|     accountNumbers: BigNumber[]; |  | ||||||
|     actions: DydxBridgeAction[]; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export const dydxBridgeDataEncoder = AbiEncoder.create([ |  | ||||||
|     { |  | ||||||
|         name: 'bridgeData', |  | ||||||
|         type: 'tuple', |  | ||||||
|         components: [ |  | ||||||
|             { name: 'accountNumbers', type: 'uint256[]' }, |  | ||||||
|             { |  | ||||||
|                 name: 'actions', |  | ||||||
|                 type: 'tuple[]', |  | ||||||
|                 components: [ |  | ||||||
|                     { name: 'actionType', type: 'uint8' }, |  | ||||||
|                     { name: 'accountIdx', type: 'uint256' }, |  | ||||||
|                     { name: 'marketId', type: 'uint256' }, |  | ||||||
|                     { name: 'conversionRateNumerator', type: 'uint256' }, |  | ||||||
|                     { name: 'conversionRateDenominator', type: 'uint256' }, |  | ||||||
|                 ], |  | ||||||
|             }, |  | ||||||
|         ], |  | ||||||
|     }, |  | ||||||
| ]); |  | ||||||
| @@ -1,410 +0,0 @@ | |||||||
| import { artifacts as erc1155Artifacts, ERC1155MintableContract, Erc1155Wrapper } from '@0x/contracts-erc1155'; |  | ||||||
| import { |  | ||||||
|     constants, |  | ||||||
|     ERC1155FungibleHoldingsByOwner, |  | ||||||
|     ERC1155HoldingsByOwner, |  | ||||||
|     ERC1155NonFungibleHoldingsByOwner, |  | ||||||
|     LogDecoder, |  | ||||||
|     txDefaults, |  | ||||||
| } from '@0x/contracts-test-utils'; |  | ||||||
| import { BigNumber } from '@0x/utils'; |  | ||||||
| import { Web3Wrapper } from '@0x/web3-wrapper'; |  | ||||||
| import { Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types'; |  | ||||||
| import * as _ from 'lodash'; |  | ||||||
|  |  | ||||||
| import { artifacts } from './artifacts'; |  | ||||||
|  |  | ||||||
| import { ERC1155ProxyContract, IAssetDataContract, IAssetProxyContract } from './wrappers'; |  | ||||||
|  |  | ||||||
| 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 readonly _assetDataInterface: IAssetDataContract; |  | ||||||
|     private _proxyContract?: ERC1155ProxyContract; |  | ||||||
|     private _proxyIdIfExists?: string; |  | ||||||
|     private _initialTokenIdsByOwner: ERC1155HoldingsByOwner = { fungible: {}, nonFungible: {} }; |  | ||||||
|  |  | ||||||
|     constructor(provider: Provider, tokenOwnerAddresses: string[], contractOwnerAddress: string) { |  | ||||||
|         this._web3Wrapper = new Web3Wrapper(provider); |  | ||||||
|         this._provider = provider; |  | ||||||
|         const allArtifacts = _.merge(artifacts, erc1155Artifacts); |  | ||||||
|         this._logDecoder = new LogDecoder(this._web3Wrapper, allArtifacts); |  | ||||||
|         this._dummyTokenWrappers = []; |  | ||||||
|         this._assetProxyInterface = new IAssetProxyContract(constants.NULL_ADDRESS, provider); |  | ||||||
|         this._assetDataInterface = new IAssetDataContract(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, |  | ||||||
|                 artifacts, |  | ||||||
|             ); |  | ||||||
|             const erc1155Wrapper = new Erc1155Wrapper(erc1155Contract, 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, |  | ||||||
|             artifacts, |  | ||||||
|         ); |  | ||||||
|         this._proxyIdIfExists = await this._proxyContract.getProxyId().callAsync(); |  | ||||||
|         return this._proxyContract; |  | ||||||
|     } |  | ||||||
|     /** |  | ||||||
|      * @dev Gets the ERC1155 proxy id |  | ||||||
|      */ |  | ||||||
|     public getProxyId(): string { |  | ||||||
|         this._validateProxyContractExistsOrThrow(); |  | ||||||
|         return this._proxyIdIfExists as string; |  | ||||||
|     } |  | ||||||
|     /** |  | ||||||
|      * @dev generates abi-encoded tx data for transferring erc1155 fungible/non-fungible tokens. |  | ||||||
|      * @param from source address |  | ||||||
|      * @param to destination address |  | ||||||
|      * @param contractAddress address of erc155 contract |  | ||||||
|      * @param tokensToTransfer array of erc1155 tokens to transfer |  | ||||||
|      * @param valuesToTransfer array of corresponding values for each erc1155 token to transfer |  | ||||||
|      * @param valueMultiplier each value in `valuesToTransfer` is multiplied by this |  | ||||||
|      * @param receiverCallbackData callback data if `to` is a contract |  | ||||||
|      * @param authorizedSender sender of `transferFrom` transaction |  | ||||||
|      * @param extraData extra data to append to `transferFrom` transaction. Optional. |  | ||||||
|      * @return abi encoded tx data. |  | ||||||
|      */ |  | ||||||
|     public async getTransferFromAbiEncodedTxDataAsync( |  | ||||||
|         from: string, |  | ||||||
|         to: string, |  | ||||||
|         contractAddress: string, |  | ||||||
|         tokensToTransfer: BigNumber[], |  | ||||||
|         valuesToTransfer: BigNumber[], |  | ||||||
|         valueMultiplier: BigNumber, |  | ||||||
|         receiverCallbackData: string, |  | ||||||
|         authorizedSender: string, |  | ||||||
|         assetData_?: string, |  | ||||||
|     ): Promise<string> { |  | ||||||
|         this._validateProxyContractExistsOrThrow(); |  | ||||||
|         const assetData = |  | ||||||
|             assetData_ === undefined |  | ||||||
|                 ? this._assetDataInterface |  | ||||||
|                       .ERC1155Assets(contractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData) |  | ||||||
|                       .getABIEncodedTransactionData() |  | ||||||
|                 : assetData_; |  | ||||||
|         const data = this._assetProxyInterface |  | ||||||
|             .transferFrom(assetData, from, to, valueMultiplier) |  | ||||||
|             .getABIEncodedTransactionData(); |  | ||||||
|         return data; |  | ||||||
|     } |  | ||||||
|     /** |  | ||||||
|      * @dev transfers erc1155 fungible/non-fungible tokens. |  | ||||||
|      * @param txData: abi-encoded tx data |  | ||||||
|      * @param authorizedSender sender of `transferFrom` transaction |  | ||||||
|      */ |  | ||||||
|     public async transferFromRawAsync( |  | ||||||
|         txData: string, |  | ||||||
|         authorizedSender: string, |  | ||||||
|     ): Promise<TransactionReceiptWithDecodedLogs> { |  | ||||||
|         const txHash = await this._web3Wrapper.sendTransactionAsync({ |  | ||||||
|             to: (this._proxyContract as ERC1155ProxyContract).address, |  | ||||||
|             data: txData, |  | ||||||
|             from: authorizedSender, |  | ||||||
|             gas: 300000, |  | ||||||
|         }); |  | ||||||
|         const txReceipt = await this._logDecoder.getTxWithDecodedLogsAsync(txHash); |  | ||||||
|         return txReceipt; |  | ||||||
|     } |  | ||||||
|     /** |  | ||||||
|      * @dev transfers erc1155 fungible/non-fungible tokens. |  | ||||||
|      * @param from source address |  | ||||||
|      * @param to destination address |  | ||||||
|      * @param contractAddress address of erc155 contract |  | ||||||
|      * @param tokensToTransfer array of erc1155 tokens to transfer |  | ||||||
|      * @param valuesToTransfer array of corresponding values for each erc1155 token to transfer |  | ||||||
|      * @param valueMultiplier each value in `valuesToTransfer` is multiplied by this |  | ||||||
|      * @param receiverCallbackData callback data if `to` is a contract |  | ||||||
|      * @param authorizedSender sender of `transferFrom` transaction |  | ||||||
|      * @param extraData extra data to append to `transferFrom` transaction. Optional. |  | ||||||
|      * @return tranasction hash. |  | ||||||
|      */ |  | ||||||
|     public async transferFromAsync( |  | ||||||
|         from: string, |  | ||||||
|         to: string, |  | ||||||
|         contractAddress: string, |  | ||||||
|         tokensToTransfer: BigNumber[], |  | ||||||
|         valuesToTransfer: BigNumber[], |  | ||||||
|         valueMultiplier: BigNumber, |  | ||||||
|         receiverCallbackData: string, |  | ||||||
|         authorizedSender: string, |  | ||||||
|         assetData_?: string, |  | ||||||
|     ): Promise<TransactionReceiptWithDecodedLogs> { |  | ||||||
|         this._validateProxyContractExistsOrThrow(); |  | ||||||
|         const assetData = |  | ||||||
|             assetData_ === undefined |  | ||||||
|                 ? this._assetDataInterface |  | ||||||
|                       .ERC1155Assets(contractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData) |  | ||||||
|                       .getABIEncodedTransactionData() |  | ||||||
|                 : assetData_; |  | ||||||
|         const data = this._assetProxyInterface |  | ||||||
|             .transferFrom(assetData, from, to, valueMultiplier) |  | ||||||
|             .getABIEncodedTransactionData(); |  | ||||||
|         const txHash = await this._web3Wrapper.sendTransactionAsync({ |  | ||||||
|             to: (this._proxyContract as ERC1155ProxyContract).address, |  | ||||||
|             data, |  | ||||||
|             from: authorizedSender, |  | ||||||
|             gas: 300000, |  | ||||||
|         }); |  | ||||||
|         const txReceipt = await this._logDecoder.getTxWithDecodedLogsAsync(txHash); |  | ||||||
|         return txReceipt; |  | ||||||
|     } |  | ||||||
|     /** |  | ||||||
|      * @dev For each deployed ERC1155 contract, this function mints a set of fungible/non-fungible |  | ||||||
|      *      tokens for each token owner address (`_tokenOwnerAddresses`). |  | ||||||
|      * @return Balances of each token owner, across all ERC1155 contracts and tokens. |  | ||||||
|      */ |  | ||||||
|     public async setBalancesAndAllowancesAsync(): Promise<ERC1155HoldingsByOwner> { |  | ||||||
|         this._validateDummyTokenContractsExistOrThrow(); |  | ||||||
|         this._validateProxyContractExistsOrThrow(); |  | ||||||
|         this._initialTokenIdsByOwner = { |  | ||||||
|             fungible: {}, |  | ||||||
|             nonFungible: {}, |  | ||||||
|         }; |  | ||||||
|         const fungibleHoldingsByOwner: ERC1155FungibleHoldingsByOwner = {}; |  | ||||||
|         const nonFungibleHoldingsByOwner: ERC1155NonFungibleHoldingsByOwner = {}; |  | ||||||
|         // Set balances accordingly |  | ||||||
|         for (const dummyWrapper of this._dummyTokenWrappers) { |  | ||||||
|             const dummyAddress = dummyWrapper.getContract().address; |  | ||||||
|             // tslint:disable-next-line:no-unused-variable |  | ||||||
|             for (const i of _.times(constants.NUM_ERC1155_FUNGIBLE_TOKENS_MINT)) { |  | ||||||
|                 // Create a fungible token |  | ||||||
|                 const tokenId = await dummyWrapper.mintFungibleTokensAsync( |  | ||||||
|                     this._tokenOwnerAddresses, |  | ||||||
|                     constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, |  | ||||||
|                 ); |  | ||||||
|                 const tokenIdAsString = tokenId.toString(); |  | ||||||
|                 this._fungibleTokenIds.push(tokenIdAsString); |  | ||||||
|                 // Mint tokens for each owner for this token |  | ||||||
|                 for (const tokenOwnerAddress of this._tokenOwnerAddresses) { |  | ||||||
|                     // tslint:disable-next-line:no-unused-variable |  | ||||||
|                     if (fungibleHoldingsByOwner[tokenOwnerAddress] === undefined) { |  | ||||||
|                         fungibleHoldingsByOwner[tokenOwnerAddress] = {}; |  | ||||||
|                     } |  | ||||||
|                     if (fungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress] === undefined) { |  | ||||||
|                         fungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress] = {}; |  | ||||||
|                     } |  | ||||||
|                     fungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress][tokenIdAsString] = |  | ||||||
|                         constants.INITIAL_ERC1155_FUNGIBLE_BALANCE; |  | ||||||
|                     await dummyWrapper.setApprovalForAllAsync( |  | ||||||
|                         tokenOwnerAddress, |  | ||||||
|                         (this._proxyContract as ERC1155ProxyContract).address, |  | ||||||
|                         true, |  | ||||||
|                     ); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             // Non-fungible tokens |  | ||||||
|             // tslint:disable-next-line:no-unused-variable |  | ||||||
|             for (const j of _.times(constants.NUM_ERC1155_NONFUNGIBLE_TOKENS_MINT)) { |  | ||||||
|                 const [tokenId, nftIds] = await dummyWrapper.mintNonFungibleTokensAsync(this._tokenOwnerAddresses); |  | ||||||
|                 const tokenIdAsString = tokenId.toString(); |  | ||||||
|                 this._nonFungibleTokenIds.push(tokenIdAsString); |  | ||||||
|                 _.each(this._tokenOwnerAddresses, async (tokenOwnerAddress: string, i: number) => { |  | ||||||
|                     if (nonFungibleHoldingsByOwner[tokenOwnerAddress] === undefined) { |  | ||||||
|                         nonFungibleHoldingsByOwner[tokenOwnerAddress] = {}; |  | ||||||
|                     } |  | ||||||
|                     if (nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress] === undefined) { |  | ||||||
|                         nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress] = {}; |  | ||||||
|                     } |  | ||||||
|                     if (nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress][tokenIdAsString] === undefined) { |  | ||||||
|                         nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress][tokenIdAsString] = []; |  | ||||||
|                     } |  | ||||||
|                     this._nfts.push({ id: nftIds[i], tokenId }); |  | ||||||
|                     nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress][tokenIdAsString].push(nftIds[i]); |  | ||||||
|                     await dummyWrapper.setApprovalForAllAsync( |  | ||||||
|                         tokenOwnerAddress, |  | ||||||
|                         (this._proxyContract as ERC1155ProxyContract).address, |  | ||||||
|                         true, |  | ||||||
|                     ); |  | ||||||
|                 }); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         this._initialTokenIdsByOwner = { |  | ||||||
|             fungible: fungibleHoldingsByOwner, |  | ||||||
|             nonFungible: nonFungibleHoldingsByOwner, |  | ||||||
|         }; |  | ||||||
|         return this._initialTokenIdsByOwner; |  | ||||||
|     } |  | ||||||
|     /** |  | ||||||
|      * @dev For each deployed ERC1155 contract, this function quieries the set of fungible/non-fungible |  | ||||||
|      *      tokens for each token owner address (`_tokenOwnerAddresses`). |  | ||||||
|      * @return Balances of each token owner, across all ERC1155 contracts and tokens. |  | ||||||
|      */ |  | ||||||
|     public async getBalancesAsync(): Promise<ERC1155HoldingsByOwner> { |  | ||||||
|         this._validateDummyTokenContractsExistOrThrow(); |  | ||||||
|         this._validateBalancesAndAllowancesSetOrThrow(); |  | ||||||
|         const tokenHoldingsByOwner: ERC1155FungibleHoldingsByOwner = {}; |  | ||||||
|         const nonFungibleHoldingsByOwner: ERC1155NonFungibleHoldingsByOwner = {}; |  | ||||||
|         for (const dummyTokenWrapper of this._dummyTokenWrappers) { |  | ||||||
|             const tokenContract = dummyTokenWrapper.getContract(); |  | ||||||
|             const tokenAddress = tokenContract.address; |  | ||||||
|             // Construct batch balance call |  | ||||||
|             const tokenOwners: string[] = []; |  | ||||||
|             const tokenIds: BigNumber[] = []; |  | ||||||
|             for (const tokenOwnerAddress of this._tokenOwnerAddresses) { |  | ||||||
|                 for (const tokenId of this._fungibleTokenIds) { |  | ||||||
|                     tokenOwners.push(tokenOwnerAddress); |  | ||||||
|                     tokenIds.push(new BigNumber(tokenId)); |  | ||||||
|                 } |  | ||||||
|                 for (const nft of this._nfts) { |  | ||||||
|                     tokenOwners.push(tokenOwnerAddress); |  | ||||||
|                     tokenIds.push(nft.id); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             const balances = await dummyTokenWrapper.getBalancesAsync(tokenOwners, tokenIds); |  | ||||||
|             // Parse out balances into fungible / non-fungible token holdings |  | ||||||
|             let i = 0; |  | ||||||
|             for (const tokenOwnerAddress of this._tokenOwnerAddresses) { |  | ||||||
|                 // Fungible tokens |  | ||||||
|                 for (const tokenId of this._fungibleTokenIds) { |  | ||||||
|                     if (tokenHoldingsByOwner[tokenOwnerAddress] === undefined) { |  | ||||||
|                         tokenHoldingsByOwner[tokenOwnerAddress] = {}; |  | ||||||
|                     } |  | ||||||
|                     if (tokenHoldingsByOwner[tokenOwnerAddress][tokenAddress] === undefined) { |  | ||||||
|                         tokenHoldingsByOwner[tokenOwnerAddress][tokenAddress] = {}; |  | ||||||
|                     } |  | ||||||
|                     tokenHoldingsByOwner[tokenOwnerAddress][tokenAddress][tokenId] = balances[i++]; |  | ||||||
|                 } |  | ||||||
|                 // Non-fungible tokens |  | ||||||
|                 for (const nft of this._nfts) { |  | ||||||
|                     if (nonFungibleHoldingsByOwner[tokenOwnerAddress] === undefined) { |  | ||||||
|                         nonFungibleHoldingsByOwner[tokenOwnerAddress] = {}; |  | ||||||
|                     } |  | ||||||
|                     if (nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress] === undefined) { |  | ||||||
|                         nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress] = {}; |  | ||||||
|                     } |  | ||||||
|                     if ( |  | ||||||
|                         nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress][nft.tokenId.toString()] === |  | ||||||
|                         undefined |  | ||||||
|                     ) { |  | ||||||
|                         nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress][nft.tokenId.toString()] = []; |  | ||||||
|                     } |  | ||||||
|                     const isOwner = balances[i++]; |  | ||||||
|                     if (isOwner.isEqualTo(1)) { |  | ||||||
|                         nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress][nft.tokenId.toString()].push( |  | ||||||
|                             nft.id, |  | ||||||
|                         ); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         const holdingsByOwner = { |  | ||||||
|             fungible: tokenHoldingsByOwner, |  | ||||||
|             nonFungible: nonFungibleHoldingsByOwner, |  | ||||||
|         }; |  | ||||||
|         return holdingsByOwner; |  | ||||||
|     } |  | ||||||
|     /** |  | ||||||
|      * @dev Set the approval for the proxy on behalf of `userAddress` . |  | ||||||
|      * @param userAddress owner of ERC1155 tokens. |  | ||||||
|      * @param contractAddress address of ERC1155 contract. |  | ||||||
|      * @param isApproved Whether to approve the proxy for all or not. |  | ||||||
|      */ |  | ||||||
|     public async setProxyAllowanceForAllAsync( |  | ||||||
|         userAddress: string, |  | ||||||
|         contractAddress: string, |  | ||||||
|         isApproved: boolean, |  | ||||||
|     ): Promise<void> { |  | ||||||
|         this._validateProxyContractExistsOrThrow(); |  | ||||||
|         const tokenWrapper = this.getContractWrapper(contractAddress); |  | ||||||
|         const operator = (this._proxyContract as ERC1155ProxyContract).address; |  | ||||||
|         await tokenWrapper.setApprovalForAllAsync(userAddress, operator, isApproved); |  | ||||||
|     } |  | ||||||
|     /** |  | ||||||
|      * @dev Checks if proxy is approved to transfer tokens on behalf of `userAddress`. |  | ||||||
|      * @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(userAddress, operator).callAsync(); |  | ||||||
|         return didApproveAll; |  | ||||||
|     } |  | ||||||
|     public getFungibleTokenIds(): BigNumber[] { |  | ||||||
|         const fungibleTokenIds = _.map(this._fungibleTokenIds, (tokenIdAsString: string) => { |  | ||||||
|             return new BigNumber(tokenIdAsString); |  | ||||||
|         }); |  | ||||||
|         return fungibleTokenIds; |  | ||||||
|     } |  | ||||||
|     public getNonFungibleTokenIds(): BigNumber[] { |  | ||||||
|         const nonFungibleTokenIds = _.map(this._nonFungibleTokenIds, (tokenIdAsString: string) => { |  | ||||||
|             return new BigNumber(tokenIdAsString); |  | ||||||
|         }); |  | ||||||
|         return nonFungibleTokenIds; |  | ||||||
|     } |  | ||||||
|     public getTokenOwnerAddresses(): string[] { |  | ||||||
|         return this._tokenOwnerAddresses; |  | ||||||
|     } |  | ||||||
|     public getContractWrapper(contractAddress: string): Erc1155Wrapper { |  | ||||||
|         const tokenWrapper = _.find(this._dummyTokenWrappers, (wrapper: Erc1155Wrapper) => { |  | ||||||
|             return wrapper.getContract().address === contractAddress; |  | ||||||
|         }); |  | ||||||
|         if (tokenWrapper === undefined) { |  | ||||||
|             throw new Error(`Contract: ${contractAddress} was not deployed through ERC1155ProxyWrapper`); |  | ||||||
|         } |  | ||||||
|         return tokenWrapper; |  | ||||||
|     } |  | ||||||
|     private _getContractFromAddress(tokenAddress: string): ERC1155MintableContract { |  | ||||||
|         const tokenContractIfExists = _.find(this._dummyTokenWrappers, c => c.getContract().address === tokenAddress); |  | ||||||
|         if (tokenContractIfExists === undefined) { |  | ||||||
|             throw new Error(`Token: ${tokenAddress} was not deployed through ERC1155ProxyWrapper`); |  | ||||||
|         } |  | ||||||
|         return tokenContractIfExists.getContract(); |  | ||||||
|     } |  | ||||||
|     private _validateDummyTokenContractsExistOrThrow(): void { |  | ||||||
|         if (this._dummyTokenWrappers === undefined) { |  | ||||||
|             throw new Error('Dummy ERC1155 tokens not yet deployed, please call "deployDummyTokensAsync"'); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     private _validateProxyContractExistsOrThrow(): void { |  | ||||||
|         if (this._proxyContract === undefined) { |  | ||||||
|             throw new Error('ERC1155 proxy contract not yet deployed, please call "deployProxyAsync"'); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     private _validateBalancesAndAllowancesSetOrThrow(): void { |  | ||||||
|         if ( |  | ||||||
|             _.keys(this._initialTokenIdsByOwner.fungible).length === 0 || |  | ||||||
|             _.keys(this._initialTokenIdsByOwner.nonFungible).length === 0 |  | ||||||
|         ) { |  | ||||||
|             throw new Error( |  | ||||||
|                 'Dummy ERC1155 balances and allowances not yet set, please call "setBalancesAndAllowancesAsync"', |  | ||||||
|             ); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,164 +0,0 @@ | |||||||
| import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contracts-erc20'; |  | ||||||
| import { constants, ERC20BalancesByOwner, txDefaults } from '@0x/contracts-test-utils'; |  | ||||||
| import { BigNumber } from '@0x/utils'; |  | ||||||
| import { ZeroExProvider } from 'ethereum-types'; |  | ||||||
| import * as _ from 'lodash'; |  | ||||||
|  |  | ||||||
| import { artifacts } from './artifacts'; |  | ||||||
|  |  | ||||||
| import { ERC20ProxyContract, IAssetDataContract } from './wrappers'; |  | ||||||
|  |  | ||||||
| export class ERC20Wrapper { |  | ||||||
|     private readonly _tokenOwnerAddresses: string[]; |  | ||||||
|     private readonly _contractOwnerAddress: string; |  | ||||||
|     private readonly _provider: ZeroExProvider; |  | ||||||
|     private readonly _dummyTokenContracts: DummyERC20TokenContract[]; |  | ||||||
|     private readonly _assetDataInterface: IAssetDataContract; |  | ||||||
|     private _proxyContract?: ERC20ProxyContract; |  | ||||||
|     private _proxyIdIfExists?: string; |  | ||||||
|     /** |  | ||||||
|      * Instanitates an ERC20Wrapper |  | ||||||
|      * @param provider Web3 provider to use for all JSON RPC requests |  | ||||||
|      * @param tokenOwnerAddresses Addresses that we want to endow as owners for dummy ERC20 tokens |  | ||||||
|      * @param contractOwnerAddress Desired owner of the contract |  | ||||||
|      * Instance of ERC20Wrapper |  | ||||||
|      */ |  | ||||||
|     constructor(provider: ZeroExProvider, tokenOwnerAddresses: string[], contractOwnerAddress: string) { |  | ||||||
|         this._dummyTokenContracts = []; |  | ||||||
|         this._provider = provider; |  | ||||||
|         this._tokenOwnerAddresses = tokenOwnerAddresses; |  | ||||||
|         this._contractOwnerAddress = contractOwnerAddress; |  | ||||||
|         this._assetDataInterface = new IAssetDataContract(constants.NULL_ADDRESS, provider); |  | ||||||
|     } |  | ||||||
|     public async deployDummyTokensAsync( |  | ||||||
|         numberToDeploy: number, |  | ||||||
|         decimals: BigNumber, |  | ||||||
|     ): Promise<DummyERC20TokenContract[]> { |  | ||||||
|         for (let i = 0; i < numberToDeploy; i++) { |  | ||||||
|             this._dummyTokenContracts.push( |  | ||||||
|                 await DummyERC20TokenContract.deployFrom0xArtifactAsync( |  | ||||||
|                     erc20Artifacts.DummyERC20Token, |  | ||||||
|                     this._provider, |  | ||||||
|                     txDefaults, |  | ||||||
|                     artifacts, |  | ||||||
|                     constants.DUMMY_TOKEN_NAME, |  | ||||||
|                     constants.DUMMY_TOKEN_SYMBOL, |  | ||||||
|                     decimals, |  | ||||||
|                     constants.DUMMY_TOKEN_TOTAL_SUPPLY, |  | ||||||
|                 ), |  | ||||||
|             ); |  | ||||||
|         } |  | ||||||
|         return this._dummyTokenContracts; |  | ||||||
|     } |  | ||||||
|     public async deployProxyAsync(): Promise<ERC20ProxyContract> { |  | ||||||
|         this._proxyContract = await ERC20ProxyContract.deployFrom0xArtifactAsync( |  | ||||||
|             artifacts.ERC20Proxy, |  | ||||||
|             this._provider, |  | ||||||
|             txDefaults, |  | ||||||
|             artifacts, |  | ||||||
|         ); |  | ||||||
|         this._proxyIdIfExists = await this._proxyContract.getProxyId().callAsync(); |  | ||||||
|         return this._proxyContract; |  | ||||||
|     } |  | ||||||
|     public getProxyId(): string { |  | ||||||
|         this._validateProxyContractExistsOrThrow(); |  | ||||||
|         return this._proxyIdIfExists as string; |  | ||||||
|     } |  | ||||||
|     public async setBalancesAndAllowancesAsync(): Promise<void> { |  | ||||||
|         this._validateDummyTokenContractsExistOrThrow(); |  | ||||||
|         this._validateProxyContractExistsOrThrow(); |  | ||||||
|         for (const dummyTokenContract of this._dummyTokenContracts) { |  | ||||||
|             for (const tokenOwnerAddress of this._tokenOwnerAddresses) { |  | ||||||
|                 await dummyTokenContract |  | ||||||
|                     .setBalance(tokenOwnerAddress, constants.INITIAL_ERC20_BALANCE) |  | ||||||
|                     .awaitTransactionSuccessAsync({ from: this._contractOwnerAddress }); |  | ||||||
|                 await dummyTokenContract |  | ||||||
|                     .approve((this._proxyContract as ERC20ProxyContract).address, constants.INITIAL_ERC20_ALLOWANCE) |  | ||||||
|                     .awaitTransactionSuccessAsync({ from: tokenOwnerAddress }); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     public async getBalanceAsync(userAddress: string, assetData: string): Promise<BigNumber> { |  | ||||||
|         const tokenContract = await this._getTokenContractFromAssetDataAsync(assetData); |  | ||||||
|         const balance = new BigNumber(await tokenContract.balanceOf(userAddress).callAsync()); |  | ||||||
|         return balance; |  | ||||||
|     } |  | ||||||
|     public async setBalanceAsync(userAddress: string, assetData: string, amount: BigNumber): Promise<void> { |  | ||||||
|         const tokenContract = await this._getTokenContractFromAssetDataAsync(assetData); |  | ||||||
|         await tokenContract |  | ||||||
|             .setBalance(userAddress, amount) |  | ||||||
|             .awaitTransactionSuccessAsync( |  | ||||||
|                 { from: this._contractOwnerAddress }, |  | ||||||
|                 { pollingIntervalMs: constants.AWAIT_TRANSACTION_MINED_MS }, |  | ||||||
|             ); |  | ||||||
|     } |  | ||||||
|     public async getProxyAllowanceAsync(userAddress: string, assetData: string): Promise<BigNumber> { |  | ||||||
|         const tokenContract = await this._getTokenContractFromAssetDataAsync(assetData); |  | ||||||
|         const proxyAddress = (this._proxyContract as ERC20ProxyContract).address; |  | ||||||
|         const allowance = new BigNumber(await tokenContract.allowance(userAddress, proxyAddress).callAsync()); |  | ||||||
|         return allowance; |  | ||||||
|     } |  | ||||||
|     public async setAllowanceAsync(userAddress: string, assetData: string, amount: BigNumber): Promise<void> { |  | ||||||
|         const tokenContract = await this._getTokenContractFromAssetDataAsync(assetData); |  | ||||||
|         const proxyAddress = (this._proxyContract as ERC20ProxyContract).address; |  | ||||||
|         await tokenContract.approve(proxyAddress, amount).awaitTransactionSuccessAsync({ from: userAddress }); |  | ||||||
|     } |  | ||||||
|     public async getBalancesAsync(): Promise<ERC20BalancesByOwner> { |  | ||||||
|         this._validateDummyTokenContractsExistOrThrow(); |  | ||||||
|         const balancesByOwner: ERC20BalancesByOwner = {}; |  | ||||||
|         const balances: BigNumber[] = []; |  | ||||||
|         const balanceInfo: Array<{ tokenOwnerAddress: string; tokenAddress: string }> = []; |  | ||||||
|         for (const dummyTokenContract of this._dummyTokenContracts) { |  | ||||||
|             for (const tokenOwnerAddress of this._tokenOwnerAddresses) { |  | ||||||
|                 balances.push(await dummyTokenContract.balanceOf(tokenOwnerAddress).callAsync()); |  | ||||||
|                 balanceInfo.push({ |  | ||||||
|                     tokenOwnerAddress, |  | ||||||
|                     tokenAddress: dummyTokenContract.address, |  | ||||||
|                 }); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         _.forEach(balances, (balance, balanceIndex) => { |  | ||||||
|             const tokenAddress = balanceInfo[balanceIndex].tokenAddress; |  | ||||||
|             const tokenOwnerAddress = balanceInfo[balanceIndex].tokenOwnerAddress; |  | ||||||
|             if (balancesByOwner[tokenOwnerAddress] === undefined) { |  | ||||||
|                 balancesByOwner[tokenOwnerAddress] = {}; |  | ||||||
|             } |  | ||||||
|             const wrappedBalance = new BigNumber(balance); |  | ||||||
|             balancesByOwner[tokenOwnerAddress][tokenAddress] = wrappedBalance; |  | ||||||
|         }); |  | ||||||
|         return balancesByOwner; |  | ||||||
|     } |  | ||||||
|     public addDummyTokenContract(dummy: DummyERC20TokenContract): void { |  | ||||||
|         if (this._dummyTokenContracts !== undefined) { |  | ||||||
|             this._dummyTokenContracts.push(dummy); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     public addTokenOwnerAddress(address: string): void { |  | ||||||
|         this._tokenOwnerAddresses.push(address); |  | ||||||
|     } |  | ||||||
|     public getTokenOwnerAddresses(): string[] { |  | ||||||
|         return this._tokenOwnerAddresses; |  | ||||||
|     } |  | ||||||
|     public getTokenAddresses(): string[] { |  | ||||||
|         const tokenAddresses = _.map(this._dummyTokenContracts, dummyTokenContract => dummyTokenContract.address); |  | ||||||
|         return tokenAddresses; |  | ||||||
|     } |  | ||||||
|     private async _getTokenContractFromAssetDataAsync(assetData: string): Promise<DummyERC20TokenContract> { |  | ||||||
|         const tokenAddress = this._assetDataInterface.getABIDecodedTransactionData<string>('ERC20Token', assetData); // tslint:disable-line:no-unused-variable |  | ||||||
|         const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === tokenAddress); |  | ||||||
|         if (tokenContractIfExists === undefined) { |  | ||||||
|             throw new Error(`Token: ${tokenAddress} was not deployed through ERC20Wrapper`); |  | ||||||
|         } |  | ||||||
|         return tokenContractIfExists; |  | ||||||
|     } |  | ||||||
|     private _validateDummyTokenContractsExistOrThrow(): void { |  | ||||||
|         if (this._dummyTokenContracts === undefined) { |  | ||||||
|             throw new Error('Dummy ERC20 tokens not yet deployed, please call "deployDummyTokensAsync"'); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     private _validateProxyContractExistsOrThrow(): void { |  | ||||||
|         if (this._proxyContract === undefined) { |  | ||||||
|             throw new Error('ERC20 proxy contract not yet deployed, please call "deployProxyAsync"'); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,220 +0,0 @@ | |||||||
| 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 { ZeroExProvider } from 'ethereum-types'; |  | ||||||
| import * as _ from 'lodash'; |  | ||||||
|  |  | ||||||
| import { artifacts } from './artifacts'; |  | ||||||
|  |  | ||||||
| import { ERC721ProxyContract } from './wrappers'; |  | ||||||
|  |  | ||||||
| export class ERC721Wrapper { |  | ||||||
|     private readonly _tokenOwnerAddresses: string[]; |  | ||||||
|     private readonly _contractOwnerAddress: string; |  | ||||||
|     private readonly _provider: ZeroExProvider; |  | ||||||
|     private readonly _dummyTokenContracts: DummyERC721TokenContract[]; |  | ||||||
|     private _proxyContract?: ERC721ProxyContract; |  | ||||||
|     private _proxyIdIfExists?: string; |  | ||||||
|     private _initialTokenIdsByOwner: ERC721TokenIdsByOwner = {}; |  | ||||||
|     constructor(provider: ZeroExProvider, tokenOwnerAddresses: string[], contractOwnerAddress: string) { |  | ||||||
|         this._provider = provider; |  | ||||||
|         this._dummyTokenContracts = []; |  | ||||||
|         this._tokenOwnerAddresses = tokenOwnerAddresses; |  | ||||||
|         this._contractOwnerAddress = contractOwnerAddress; |  | ||||||
|     } |  | ||||||
|     public async deployDummyTokensAsync(): Promise<DummyERC721TokenContract[]> { |  | ||||||
|         // tslint:disable-next-line:no-unused-variable |  | ||||||
|         for (const i of _.times(constants.NUM_DUMMY_ERC721_TO_DEPLOY)) { |  | ||||||
|             this._dummyTokenContracts.push( |  | ||||||
|                 await DummyERC721TokenContract.deployFrom0xArtifactAsync( |  | ||||||
|                     erc721Artifacts.DummyERC721Token, |  | ||||||
|                     this._provider, |  | ||||||
|                     txDefaults, |  | ||||||
|                     artifacts, |  | ||||||
|                     constants.DUMMY_TOKEN_NAME, |  | ||||||
|                     constants.DUMMY_TOKEN_SYMBOL, |  | ||||||
|                 ), |  | ||||||
|             ); |  | ||||||
|         } |  | ||||||
|         return this._dummyTokenContracts; |  | ||||||
|     } |  | ||||||
|     public async deployProxyAsync(): Promise<ERC721ProxyContract> { |  | ||||||
|         this._proxyContract = await ERC721ProxyContract.deployFrom0xArtifactAsync( |  | ||||||
|             artifacts.ERC721Proxy, |  | ||||||
|             this._provider, |  | ||||||
|             txDefaults, |  | ||||||
|             artifacts, |  | ||||||
|         ); |  | ||||||
|         this._proxyIdIfExists = await this._proxyContract.getProxyId().callAsync(); |  | ||||||
|         return this._proxyContract; |  | ||||||
|     } |  | ||||||
|     public getProxyId(): string { |  | ||||||
|         this._validateProxyContractExistsOrThrow(); |  | ||||||
|         return this._proxyIdIfExists as string; |  | ||||||
|     } |  | ||||||
|     public async setBalancesAndAllowancesAsync(): Promise<void> { |  | ||||||
|         this._validateDummyTokenContractsExistOrThrow(); |  | ||||||
|         this._validateProxyContractExistsOrThrow(); |  | ||||||
|         this._initialTokenIdsByOwner = {}; |  | ||||||
|         for (const dummyTokenContract of this._dummyTokenContracts) { |  | ||||||
|             for (const tokenOwnerAddress of this._tokenOwnerAddresses) { |  | ||||||
|                 // tslint:disable-next-line:no-unused-variable |  | ||||||
|                 for (const i of _.times(constants.NUM_ERC721_TOKENS_TO_MINT)) { |  | ||||||
|                     const tokenId = generatePseudoRandomSalt(); |  | ||||||
|                     await this.mintAsync(dummyTokenContract.address, tokenId, tokenOwnerAddress); |  | ||||||
|                     if (this._initialTokenIdsByOwner[tokenOwnerAddress] === undefined) { |  | ||||||
|                         this._initialTokenIdsByOwner[tokenOwnerAddress] = { |  | ||||||
|                             [dummyTokenContract.address]: [], |  | ||||||
|                         }; |  | ||||||
|                     } |  | ||||||
|                     if (this._initialTokenIdsByOwner[tokenOwnerAddress][dummyTokenContract.address] === undefined) { |  | ||||||
|                         this._initialTokenIdsByOwner[tokenOwnerAddress][dummyTokenContract.address] = []; |  | ||||||
|                     } |  | ||||||
|                     this._initialTokenIdsByOwner[tokenOwnerAddress][dummyTokenContract.address].push(tokenId); |  | ||||||
|  |  | ||||||
|                     await this.approveProxyForAllAsync(dummyTokenContract.address, tokenOwnerAddress, true); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     public async doesTokenExistAsync(tokenAddress: string, tokenId: BigNumber): Promise<boolean> { |  | ||||||
|         const tokenContract = this._getTokenContractFromAssetData(tokenAddress); |  | ||||||
|         const owner = await tokenContract.ownerOf(tokenId).callAsync(); |  | ||||||
|         const doesExist = owner !== constants.NULL_ADDRESS; |  | ||||||
|         return doesExist; |  | ||||||
|     } |  | ||||||
|     public async approveProxyAsync(tokenAddress: string, tokenId: BigNumber): Promise<void> { |  | ||||||
|         const proxyAddress = (this._proxyContract as ERC721ProxyContract).address; |  | ||||||
|         await this.approveAsync(proxyAddress, tokenAddress, tokenId); |  | ||||||
|     } |  | ||||||
|     public async approveProxyForAllAsync( |  | ||||||
|         tokenAddress: string, |  | ||||||
|         ownerAddress: string, |  | ||||||
|         isApproved: boolean, |  | ||||||
|     ): Promise<void> { |  | ||||||
|         const tokenContract = this._getTokenContractFromAssetData(tokenAddress); |  | ||||||
|         const proxyAddress = (this._proxyContract as ERC721ProxyContract).address; |  | ||||||
|         await tokenContract.setApprovalForAll(proxyAddress, isApproved).awaitTransactionSuccessAsync({ |  | ||||||
|             from: ownerAddress, |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|     public async approveAsync(to: string, tokenAddress: string, tokenId: BigNumber): Promise<void> { |  | ||||||
|         const tokenContract = this._getTokenContractFromAssetData(tokenAddress); |  | ||||||
|         const tokenOwner = await this.ownerOfAsync(tokenAddress, tokenId); |  | ||||||
|         await tokenContract.approve(to, tokenId).awaitTransactionSuccessAsync({ from: tokenOwner }); |  | ||||||
|     } |  | ||||||
|     public async transferFromAsync( |  | ||||||
|         tokenAddress: string, |  | ||||||
|         tokenId: BigNumber, |  | ||||||
|         currentOwner: string, |  | ||||||
|         userAddress: string, |  | ||||||
|     ): Promise<void> { |  | ||||||
|         const tokenContract = this._getTokenContractFromAssetData(tokenAddress); |  | ||||||
|         await tokenContract.transferFrom(currentOwner, userAddress, tokenId).awaitTransactionSuccessAsync({ |  | ||||||
|             from: currentOwner, |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|     public async mintAsync(tokenAddress: string, tokenId: BigNumber, userAddress: string): Promise<void> { |  | ||||||
|         const tokenContract = this._getTokenContractFromAssetData(tokenAddress); |  | ||||||
|         await tokenContract.mint(userAddress, tokenId).awaitTransactionSuccessAsync({ |  | ||||||
|             from: this._contractOwnerAddress, |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|     public async burnAsync(tokenAddress: string, tokenId: BigNumber, owner: string): Promise<void> { |  | ||||||
|         const tokenContract = this._getTokenContractFromAssetData(tokenAddress); |  | ||||||
|         await tokenContract.burn(owner, tokenId).awaitTransactionSuccessAsync({ from: this._contractOwnerAddress }); |  | ||||||
|     } |  | ||||||
|     public async ownerOfAsync(tokenAddress: string, tokenId: BigNumber): Promise<string> { |  | ||||||
|         const tokenContract = this._getTokenContractFromAssetData(tokenAddress); |  | ||||||
|         const owner = await tokenContract.ownerOf(tokenId).callAsync(); |  | ||||||
|         return owner; |  | ||||||
|     } |  | ||||||
|     public async isOwnerAsync(userAddress: string, tokenAddress: string, tokenId: BigNumber): Promise<boolean> { |  | ||||||
|         const tokenContract = this._getTokenContractFromAssetData(tokenAddress); |  | ||||||
|         const tokenOwner = await tokenContract.ownerOf(tokenId).callAsync(); |  | ||||||
|         const isOwner = tokenOwner === userAddress; |  | ||||||
|         return isOwner; |  | ||||||
|     } |  | ||||||
|     public async isProxyApprovedForAllAsync(userAddress: string, tokenAddress: string): Promise<boolean> { |  | ||||||
|         this._validateProxyContractExistsOrThrow(); |  | ||||||
|         const tokenContract = this._getTokenContractFromAssetData(tokenAddress); |  | ||||||
|         const operator = (this._proxyContract as ERC721ProxyContract).address; |  | ||||||
|         const didApproveAll = await tokenContract.isApprovedForAll(userAddress, operator).callAsync(); |  | ||||||
|         return didApproveAll; |  | ||||||
|     } |  | ||||||
|     public async isProxyApprovedAsync(tokenAddress: string, tokenId: BigNumber): Promise<boolean> { |  | ||||||
|         this._validateProxyContractExistsOrThrow(); |  | ||||||
|         const tokenContract = this._getTokenContractFromAssetData(tokenAddress); |  | ||||||
|         const approvedAddress = await tokenContract.getApproved(tokenId).callAsync(); |  | ||||||
|         const proxyAddress = (this._proxyContract as ERC721ProxyContract).address; |  | ||||||
|         const isProxyAnApprovedOperator = approvedAddress === proxyAddress; |  | ||||||
|         return isProxyAnApprovedOperator; |  | ||||||
|     } |  | ||||||
|     public async getBalancesAsync(): Promise<ERC721TokenIdsByOwner> { |  | ||||||
|         this._validateDummyTokenContractsExistOrThrow(); |  | ||||||
|         this._validateBalancesAndAllowancesSetOrThrow(); |  | ||||||
|         const tokenIdsByOwner: ERC721TokenIdsByOwner = {}; |  | ||||||
|         const tokenOwnerAddresses: string[] = []; |  | ||||||
|         const tokenInfo: Array<{ tokenId: BigNumber; tokenAddress: string }> = []; |  | ||||||
|         for (const dummyTokenContract of this._dummyTokenContracts) { |  | ||||||
|             for (const tokenOwnerAddress of this._tokenOwnerAddresses) { |  | ||||||
|                 const initialTokenOwnerIds = this._initialTokenIdsByOwner[tokenOwnerAddress][ |  | ||||||
|                     dummyTokenContract.address |  | ||||||
|                 ]; |  | ||||||
|                 for (const tokenId of initialTokenOwnerIds) { |  | ||||||
|                     tokenOwnerAddresses.push(await dummyTokenContract.ownerOf(tokenId).callAsync()); |  | ||||||
|                     tokenInfo.push({ |  | ||||||
|                         tokenId, |  | ||||||
|                         tokenAddress: dummyTokenContract.address, |  | ||||||
|                     }); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         _.forEach(tokenOwnerAddresses, (tokenOwnerAddress, ownerIndex) => { |  | ||||||
|             const tokenAddress = tokenInfo[ownerIndex].tokenAddress; |  | ||||||
|             const tokenId = tokenInfo[ownerIndex].tokenId; |  | ||||||
|             if (tokenIdsByOwner[tokenOwnerAddress] === undefined) { |  | ||||||
|                 tokenIdsByOwner[tokenOwnerAddress] = { |  | ||||||
|                     [tokenAddress]: [], |  | ||||||
|                 }; |  | ||||||
|             } |  | ||||||
|             if (tokenIdsByOwner[tokenOwnerAddress][tokenAddress] === undefined) { |  | ||||||
|                 tokenIdsByOwner[tokenOwnerAddress][tokenAddress] = []; |  | ||||||
|             } |  | ||||||
|             tokenIdsByOwner[tokenOwnerAddress][tokenAddress].push(tokenId); |  | ||||||
|         }); |  | ||||||
|         return tokenIdsByOwner; |  | ||||||
|     } |  | ||||||
|     public getTokenOwnerAddresses(): string[] { |  | ||||||
|         return this._tokenOwnerAddresses; |  | ||||||
|     } |  | ||||||
|     public getTokenAddresses(): string[] { |  | ||||||
|         const tokenAddresses = _.map(this._dummyTokenContracts, dummyTokenContract => dummyTokenContract.address); |  | ||||||
|         return tokenAddresses; |  | ||||||
|     } |  | ||||||
|     private _getTokenContractFromAssetData(tokenAddress: string): DummyERC721TokenContract { |  | ||||||
|         const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === tokenAddress); |  | ||||||
|         if (tokenContractIfExists === undefined) { |  | ||||||
|             throw new Error(`Token: ${tokenAddress} was not deployed through ERC20Wrapper`); |  | ||||||
|         } |  | ||||||
|         return tokenContractIfExists; |  | ||||||
|     } |  | ||||||
|     private _validateDummyTokenContractsExistOrThrow(): void { |  | ||||||
|         if (this._dummyTokenContracts === undefined) { |  | ||||||
|             throw new Error('Dummy ERC721 tokens not yet deployed, please call "deployDummyTokensAsync"'); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     private _validateProxyContractExistsOrThrow(): void { |  | ||||||
|         if (this._proxyContract === undefined) { |  | ||||||
|             throw new Error('ERC721 proxy contract not yet deployed, please call "deployProxyAsync"'); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     private _validateBalancesAndAllowancesSetOrThrow(): void { |  | ||||||
|         if (_.keys(this._initialTokenIdsByOwner).length === 0) { |  | ||||||
|             throw new Error( |  | ||||||
|                 'Dummy ERC721 balances and allowances not yet set, please call "setBalancesAndAllowancesAsync"', |  | ||||||
|             ); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,93 +0,0 @@ | |||||||
| export { artifacts } from './artifacts'; |  | ||||||
| export { |  | ||||||
|     BalancerBridgeContract, |  | ||||||
|     ChaiBridgeContract, |  | ||||||
|     ERC1155ProxyContract, |  | ||||||
|     ERC20BridgeProxyContract, |  | ||||||
|     ERC20ProxyContract, |  | ||||||
|     ERC721ProxyContract, |  | ||||||
|     Eth2DaiBridgeContract, |  | ||||||
|     DydxBridgeContract, |  | ||||||
|     IAssetDataContract, |  | ||||||
|     IAssetProxyContract, |  | ||||||
|     IChaiContract, |  | ||||||
|     IDydxContract, |  | ||||||
|     KyberBridgeContract, |  | ||||||
|     MultiAssetProxyContract, |  | ||||||
|     StaticCallProxyContract, |  | ||||||
|     TestDydxBridgeContract, |  | ||||||
|     TestStaticCallTargetContract, |  | ||||||
|     UniswapBridgeContract, |  | ||||||
|     DexForwarderBridgeContract, |  | ||||||
| } from './wrappers'; |  | ||||||
|  |  | ||||||
| export { ERC20Wrapper } from './erc20_wrapper'; |  | ||||||
| export { ERC721Wrapper } from './erc721_wrapper'; |  | ||||||
| export { ERC1155ProxyWrapper } from './erc1155_proxy_wrapper'; |  | ||||||
| export { ERC1155MintableContract, Erc1155Wrapper } from '@0x/contracts-erc1155'; |  | ||||||
| export { DummyERC20TokenContract } from '@0x/contracts-erc20'; |  | ||||||
| export { DummyERC721TokenContract } from '@0x/contracts-erc721'; |  | ||||||
| export { AssetProxyId } from '@0x/types'; |  | ||||||
| export { |  | ||||||
|     ERC1155HoldingsByOwner, |  | ||||||
|     ERC20BalancesByOwner, |  | ||||||
|     ERC721TokenIdsByOwner, |  | ||||||
|     ERC1155FungibleHoldingsByOwner, |  | ||||||
|     ERC1155NonFungibleHoldingsByOwner, |  | ||||||
| } from '@0x/contracts-test-utils'; |  | ||||||
| export { |  | ||||||
|     TransactionReceiptWithDecodedLogs, |  | ||||||
|     Provider, |  | ||||||
|     ZeroExProvider, |  | ||||||
|     JSONRPCRequestPayload, |  | ||||||
|     JSONRPCErrorCallback, |  | ||||||
|     TransactionReceiptStatus, |  | ||||||
|     JSONRPCResponsePayload, |  | ||||||
|     JSONRPCResponseError, |  | ||||||
|     ContractArtifact, |  | ||||||
|     ContractChains, |  | ||||||
|     CompilerOpts, |  | ||||||
|     StandardContractOutput, |  | ||||||
|     CompilerSettings, |  | ||||||
|     ContractChainData, |  | ||||||
|     ContractAbi, |  | ||||||
|     DevdocOutput, |  | ||||||
|     EvmOutput, |  | ||||||
|     CompilerSettingsMetadata, |  | ||||||
|     OptimizerSettings, |  | ||||||
|     OutputField, |  | ||||||
|     ParamDescription, |  | ||||||
|     EvmBytecodeOutput, |  | ||||||
|     EvmBytecodeOutputLinkReferences, |  | ||||||
|     AbiDefinition, |  | ||||||
|     FunctionAbi, |  | ||||||
|     EventAbi, |  | ||||||
|     RevertErrorAbi, |  | ||||||
|     EventParameter, |  | ||||||
|     DataItem, |  | ||||||
|     MethodAbi, |  | ||||||
|     ConstructorAbi, |  | ||||||
|     FallbackAbi, |  | ||||||
|     ConstructorStateMutability, |  | ||||||
|     TupleDataItem, |  | ||||||
|     StateMutability, |  | ||||||
| } from 'ethereum-types'; |  | ||||||
|  |  | ||||||
| export { |  | ||||||
|     decodeERC1155AssetData, |  | ||||||
|     decodeERC20AssetData, |  | ||||||
|     decodeERC20BridgeAssetData, |  | ||||||
|     decodeERC721AssetData, |  | ||||||
|     decodeMultiAssetData, |  | ||||||
|     decodeStaticCallAssetData, |  | ||||||
|     encodeERC1155AssetData, |  | ||||||
|     encodeERC20AssetData, |  | ||||||
|     encodeERC20BridgeAssetData, |  | ||||||
|     encodeERC721AssetData, |  | ||||||
|     encodeMultiAssetData, |  | ||||||
|     encodeStaticCallAssetData, |  | ||||||
|     getAssetDataProxyId, |  | ||||||
| } from './asset_data'; |  | ||||||
|  |  | ||||||
| export * from './dydx_bridge_encoder'; |  | ||||||
| export * from './dex_forwarder_bridge'; |  | ||||||
| @@ -1,64 +0,0 @@ | |||||||
| /* |  | ||||||
|  * ----------------------------------------------------------------------------- |  | ||||||
|  * Warning: This file is auto-generated by contracts-gen. Don't edit manually. |  | ||||||
|  * ----------------------------------------------------------------------------- |  | ||||||
|  */ |  | ||||||
| export * from '../generated-wrappers/balancer_bridge'; |  | ||||||
| export * from '../generated-wrappers/bancor_bridge'; |  | ||||||
| export * from '../generated-wrappers/chai_bridge'; |  | ||||||
| export * from '../generated-wrappers/cream_bridge'; |  | ||||||
| export * from '../generated-wrappers/crypto_com_bridge'; |  | ||||||
| export * from '../generated-wrappers/curve_bridge'; |  | ||||||
| export * from '../generated-wrappers/d_o_d_o_bridge'; |  | ||||||
| export * from '../generated-wrappers/dex_forwarder_bridge'; |  | ||||||
| export * from '../generated-wrappers/dydx_bridge'; |  | ||||||
| export * from '../generated-wrappers/erc1155_proxy'; |  | ||||||
| export * from '../generated-wrappers/erc20_bridge_proxy'; |  | ||||||
| export * from '../generated-wrappers/erc20_proxy'; |  | ||||||
| export * from '../generated-wrappers/erc721_proxy'; |  | ||||||
| export * from '../generated-wrappers/eth2_dai_bridge'; |  | ||||||
| export * from '../generated-wrappers/i_asset_data'; |  | ||||||
| export * from '../generated-wrappers/i_asset_proxy'; |  | ||||||
| export * from '../generated-wrappers/i_asset_proxy_dispatcher'; |  | ||||||
| export * from '../generated-wrappers/i_authorizable'; |  | ||||||
| export * from '../generated-wrappers/i_balancer_pool'; |  | ||||||
| export * from '../generated-wrappers/i_bancor_network'; |  | ||||||
| export * from '../generated-wrappers/i_chai'; |  | ||||||
| export * from '../generated-wrappers/i_curve'; |  | ||||||
| export * from '../generated-wrappers/i_dydx'; |  | ||||||
| export * from '../generated-wrappers/i_dydx_bridge'; |  | ||||||
| export * from '../generated-wrappers/i_erc20_bridge'; |  | ||||||
| export * from '../generated-wrappers/i_eth2_dai'; |  | ||||||
| export * from '../generated-wrappers/i_gas_token'; |  | ||||||
| export * from '../generated-wrappers/i_kyber_network_proxy'; |  | ||||||
| export * from '../generated-wrappers/i_m_stable'; |  | ||||||
| export * from '../generated-wrappers/i_mooniswap'; |  | ||||||
| export * from '../generated-wrappers/i_shell'; |  | ||||||
| export * from '../generated-wrappers/i_uniswap_exchange'; |  | ||||||
| export * from '../generated-wrappers/i_uniswap_exchange_factory'; |  | ||||||
| export * from '../generated-wrappers/i_uniswap_v2_router01'; |  | ||||||
| export * from '../generated-wrappers/kyber_bridge'; |  | ||||||
| export * from '../generated-wrappers/m_stable_bridge'; |  | ||||||
| export * from '../generated-wrappers/mixin_asset_proxy_dispatcher'; |  | ||||||
| export * from '../generated-wrappers/mixin_authorizable'; |  | ||||||
| export * from '../generated-wrappers/mixin_gas_token'; |  | ||||||
| export * from '../generated-wrappers/mooniswap_bridge'; |  | ||||||
| export * from '../generated-wrappers/multi_asset_proxy'; |  | ||||||
| export * from '../generated-wrappers/ownable'; |  | ||||||
| export * from '../generated-wrappers/shell_bridge'; |  | ||||||
| export * from '../generated-wrappers/snow_swap_bridge'; |  | ||||||
| export * from '../generated-wrappers/static_call_proxy'; |  | ||||||
| export * from '../generated-wrappers/sushi_swap_bridge'; |  | ||||||
| export * from '../generated-wrappers/swerve_bridge'; |  | ||||||
| export * from '../generated-wrappers/test_bancor_bridge'; |  | ||||||
| export * from '../generated-wrappers/test_chai_bridge'; |  | ||||||
| export * from '../generated-wrappers/test_dex_forwarder_bridge'; |  | ||||||
| export * from '../generated-wrappers/test_dydx_bridge'; |  | ||||||
| export * from '../generated-wrappers/test_erc20_bridge'; |  | ||||||
| export * from '../generated-wrappers/test_eth2_dai_bridge'; |  | ||||||
| export * from '../generated-wrappers/test_kyber_bridge'; |  | ||||||
| export * from '../generated-wrappers/test_static_call_target'; |  | ||||||
| export * from '../generated-wrappers/test_uniswap_bridge'; |  | ||||||
| export * from '../generated-wrappers/test_uniswap_v2_bridge'; |  | ||||||
| export * from '../generated-wrappers/uniswap_bridge'; |  | ||||||
| export * from '../generated-wrappers/uniswap_v2_bridge'; |  | ||||||
| @@ -1,127 +0,0 @@ | |||||||
| /* |  | ||||||
|  * ----------------------------------------------------------------------------- |  | ||||||
|  * Warning: This file is auto-generated by contracts-gen. Don't edit manually. |  | ||||||
|  * ----------------------------------------------------------------------------- |  | ||||||
|  */ |  | ||||||
| import { ContractArtifact } from 'ethereum-types'; |  | ||||||
|  |  | ||||||
| import * as BalancerBridge from '../test/generated-artifacts/BalancerBridge.json'; |  | ||||||
| import * as BancorBridge from '../test/generated-artifacts/BancorBridge.json'; |  | ||||||
| import * as ChaiBridge from '../test/generated-artifacts/ChaiBridge.json'; |  | ||||||
| import * as CreamBridge from '../test/generated-artifacts/CreamBridge.json'; |  | ||||||
| import * as CryptoComBridge from '../test/generated-artifacts/CryptoComBridge.json'; |  | ||||||
| import * as CurveBridge from '../test/generated-artifacts/CurveBridge.json'; |  | ||||||
| import * as DexForwarderBridge from '../test/generated-artifacts/DexForwarderBridge.json'; |  | ||||||
| import * as DODOBridge from '../test/generated-artifacts/DODOBridge.json'; |  | ||||||
| import * as DydxBridge from '../test/generated-artifacts/DydxBridge.json'; |  | ||||||
| import * as ERC1155Proxy from '../test/generated-artifacts/ERC1155Proxy.json'; |  | ||||||
| import * as ERC20BridgeProxy from '../test/generated-artifacts/ERC20BridgeProxy.json'; |  | ||||||
| import * as ERC20Proxy from '../test/generated-artifacts/ERC20Proxy.json'; |  | ||||||
| import * as ERC721Proxy from '../test/generated-artifacts/ERC721Proxy.json'; |  | ||||||
| import * as Eth2DaiBridge from '../test/generated-artifacts/Eth2DaiBridge.json'; |  | ||||||
| import * as IAssetData from '../test/generated-artifacts/IAssetData.json'; |  | ||||||
| import * as IAssetProxy from '../test/generated-artifacts/IAssetProxy.json'; |  | ||||||
| import * as IAssetProxyDispatcher from '../test/generated-artifacts/IAssetProxyDispatcher.json'; |  | ||||||
| import * as IAuthorizable from '../test/generated-artifacts/IAuthorizable.json'; |  | ||||||
| import * as IBalancerPool from '../test/generated-artifacts/IBalancerPool.json'; |  | ||||||
| import * as IBancorNetwork from '../test/generated-artifacts/IBancorNetwork.json'; |  | ||||||
| import * as IChai from '../test/generated-artifacts/IChai.json'; |  | ||||||
| import * as ICurve from '../test/generated-artifacts/ICurve.json'; |  | ||||||
| import * as IDydx from '../test/generated-artifacts/IDydx.json'; |  | ||||||
| import * as IDydxBridge from '../test/generated-artifacts/IDydxBridge.json'; |  | ||||||
| import * as IERC20Bridge from '../test/generated-artifacts/IERC20Bridge.json'; |  | ||||||
| import * as IEth2Dai from '../test/generated-artifacts/IEth2Dai.json'; |  | ||||||
| import * as IGasToken from '../test/generated-artifacts/IGasToken.json'; |  | ||||||
| import * as IKyberNetworkProxy from '../test/generated-artifacts/IKyberNetworkProxy.json'; |  | ||||||
| import * as IMooniswap from '../test/generated-artifacts/IMooniswap.json'; |  | ||||||
| import * as IMStable from '../test/generated-artifacts/IMStable.json'; |  | ||||||
| import * as IShell from '../test/generated-artifacts/IShell.json'; |  | ||||||
| import * as IUniswapExchange from '../test/generated-artifacts/IUniswapExchange.json'; |  | ||||||
| import * as IUniswapExchangeFactory from '../test/generated-artifacts/IUniswapExchangeFactory.json'; |  | ||||||
| import * as IUniswapV2Router01 from '../test/generated-artifacts/IUniswapV2Router01.json'; |  | ||||||
| import * as KyberBridge from '../test/generated-artifacts/KyberBridge.json'; |  | ||||||
| import * as MixinAssetProxyDispatcher from '../test/generated-artifacts/MixinAssetProxyDispatcher.json'; |  | ||||||
| import * as MixinAuthorizable from '../test/generated-artifacts/MixinAuthorizable.json'; |  | ||||||
| import * as MixinGasToken from '../test/generated-artifacts/MixinGasToken.json'; |  | ||||||
| import * as MooniswapBridge from '../test/generated-artifacts/MooniswapBridge.json'; |  | ||||||
| import * as MStableBridge from '../test/generated-artifacts/MStableBridge.json'; |  | ||||||
| import * as MultiAssetProxy from '../test/generated-artifacts/MultiAssetProxy.json'; |  | ||||||
| import * as Ownable from '../test/generated-artifacts/Ownable.json'; |  | ||||||
| import * as ShellBridge from '../test/generated-artifacts/ShellBridge.json'; |  | ||||||
| import * as SnowSwapBridge from '../test/generated-artifacts/SnowSwapBridge.json'; |  | ||||||
| import * as StaticCallProxy from '../test/generated-artifacts/StaticCallProxy.json'; |  | ||||||
| import * as SushiSwapBridge from '../test/generated-artifacts/SushiSwapBridge.json'; |  | ||||||
| import * as SwerveBridge from '../test/generated-artifacts/SwerveBridge.json'; |  | ||||||
| import * as TestBancorBridge from '../test/generated-artifacts/TestBancorBridge.json'; |  | ||||||
| import * as TestChaiBridge from '../test/generated-artifacts/TestChaiBridge.json'; |  | ||||||
| import * as TestDexForwarderBridge from '../test/generated-artifacts/TestDexForwarderBridge.json'; |  | ||||||
| import * as TestDydxBridge from '../test/generated-artifacts/TestDydxBridge.json'; |  | ||||||
| import * as TestERC20Bridge from '../test/generated-artifacts/TestERC20Bridge.json'; |  | ||||||
| import * as TestEth2DaiBridge from '../test/generated-artifacts/TestEth2DaiBridge.json'; |  | ||||||
| import * as TestKyberBridge from '../test/generated-artifacts/TestKyberBridge.json'; |  | ||||||
| import * as TestStaticCallTarget from '../test/generated-artifacts/TestStaticCallTarget.json'; |  | ||||||
| import * as TestUniswapBridge from '../test/generated-artifacts/TestUniswapBridge.json'; |  | ||||||
| import * as TestUniswapV2Bridge from '../test/generated-artifacts/TestUniswapV2Bridge.json'; |  | ||||||
| import * as UniswapBridge from '../test/generated-artifacts/UniswapBridge.json'; |  | ||||||
| import * as UniswapV2Bridge from '../test/generated-artifacts/UniswapV2Bridge.json'; |  | ||||||
| export const artifacts = { |  | ||||||
|     MixinAssetProxyDispatcher: MixinAssetProxyDispatcher as ContractArtifact, |  | ||||||
|     MixinAuthorizable: MixinAuthorizable as ContractArtifact, |  | ||||||
|     Ownable: Ownable as ContractArtifact, |  | ||||||
|     ERC1155Proxy: ERC1155Proxy as ContractArtifact, |  | ||||||
|     ERC20BridgeProxy: ERC20BridgeProxy as ContractArtifact, |  | ||||||
|     ERC20Proxy: ERC20Proxy as ContractArtifact, |  | ||||||
|     ERC721Proxy: ERC721Proxy as ContractArtifact, |  | ||||||
|     MultiAssetProxy: MultiAssetProxy as ContractArtifact, |  | ||||||
|     StaticCallProxy: StaticCallProxy as ContractArtifact, |  | ||||||
|     BalancerBridge: BalancerBridge as ContractArtifact, |  | ||||||
|     BancorBridge: BancorBridge as ContractArtifact, |  | ||||||
|     ChaiBridge: ChaiBridge as ContractArtifact, |  | ||||||
|     CreamBridge: CreamBridge as ContractArtifact, |  | ||||||
|     CryptoComBridge: CryptoComBridge as ContractArtifact, |  | ||||||
|     CurveBridge: CurveBridge as ContractArtifact, |  | ||||||
|     DODOBridge: DODOBridge as ContractArtifact, |  | ||||||
|     DexForwarderBridge: DexForwarderBridge as ContractArtifact, |  | ||||||
|     DydxBridge: DydxBridge as ContractArtifact, |  | ||||||
|     Eth2DaiBridge: Eth2DaiBridge as ContractArtifact, |  | ||||||
|     KyberBridge: KyberBridge as ContractArtifact, |  | ||||||
|     MStableBridge: MStableBridge as ContractArtifact, |  | ||||||
|     MixinGasToken: MixinGasToken as ContractArtifact, |  | ||||||
|     MooniswapBridge: MooniswapBridge as ContractArtifact, |  | ||||||
|     ShellBridge: ShellBridge as ContractArtifact, |  | ||||||
|     SnowSwapBridge: SnowSwapBridge as ContractArtifact, |  | ||||||
|     SushiSwapBridge: SushiSwapBridge as ContractArtifact, |  | ||||||
|     SwerveBridge: SwerveBridge as ContractArtifact, |  | ||||||
|     UniswapBridge: UniswapBridge as ContractArtifact, |  | ||||||
|     UniswapV2Bridge: UniswapV2Bridge as ContractArtifact, |  | ||||||
|     IAssetData: IAssetData as ContractArtifact, |  | ||||||
|     IAssetProxy: IAssetProxy as ContractArtifact, |  | ||||||
|     IAssetProxyDispatcher: IAssetProxyDispatcher as ContractArtifact, |  | ||||||
|     IAuthorizable: IAuthorizable as ContractArtifact, |  | ||||||
|     IBalancerPool: IBalancerPool as ContractArtifact, |  | ||||||
|     IBancorNetwork: IBancorNetwork as ContractArtifact, |  | ||||||
|     IChai: IChai as ContractArtifact, |  | ||||||
|     ICurve: ICurve as ContractArtifact, |  | ||||||
|     IDydx: IDydx as ContractArtifact, |  | ||||||
|     IDydxBridge: IDydxBridge as ContractArtifact, |  | ||||||
|     IERC20Bridge: IERC20Bridge as ContractArtifact, |  | ||||||
|     IEth2Dai: IEth2Dai as ContractArtifact, |  | ||||||
|     IGasToken: IGasToken as ContractArtifact, |  | ||||||
|     IKyberNetworkProxy: IKyberNetworkProxy as ContractArtifact, |  | ||||||
|     IMStable: IMStable as ContractArtifact, |  | ||||||
|     IMooniswap: IMooniswap as ContractArtifact, |  | ||||||
|     IShell: IShell as ContractArtifact, |  | ||||||
|     IUniswapExchange: IUniswapExchange as ContractArtifact, |  | ||||||
|     IUniswapExchangeFactory: IUniswapExchangeFactory as ContractArtifact, |  | ||||||
|     IUniswapV2Router01: IUniswapV2Router01 as ContractArtifact, |  | ||||||
|     TestBancorBridge: TestBancorBridge as ContractArtifact, |  | ||||||
|     TestChaiBridge: TestChaiBridge as ContractArtifact, |  | ||||||
|     TestDexForwarderBridge: TestDexForwarderBridge as ContractArtifact, |  | ||||||
|     TestDydxBridge: TestDydxBridge as ContractArtifact, |  | ||||||
|     TestERC20Bridge: TestERC20Bridge as ContractArtifact, |  | ||||||
|     TestEth2DaiBridge: TestEth2DaiBridge as ContractArtifact, |  | ||||||
|     TestKyberBridge: TestKyberBridge as ContractArtifact, |  | ||||||
|     TestStaticCallTarget: TestStaticCallTarget as ContractArtifact, |  | ||||||
|     TestUniswapBridge: TestUniswapBridge as ContractArtifact, |  | ||||||
|     TestUniswapV2Bridge: TestUniswapV2Bridge as ContractArtifact, |  | ||||||
| }; |  | ||||||
| @@ -1,128 +0,0 @@ | |||||||
| import { blockchainTests, expect, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils'; |  | ||||||
| import { RevertReason } from '@0x/types'; |  | ||||||
| import { BigNumber } from '@0x/utils'; |  | ||||||
|  |  | ||||||
| import { artifacts } from './artifacts'; |  | ||||||
|  |  | ||||||
| import { MixinAuthorizableContract } from './wrappers'; |  | ||||||
|  |  | ||||||
| blockchainTests.resets('Authorizable', () => { |  | ||||||
|     let owner: string; |  | ||||||
|     let notOwner: string; |  | ||||||
|     let address: string; |  | ||||||
|     let authorizable: MixinAuthorizableContract; |  | ||||||
|  |  | ||||||
|     before(async () => { |  | ||||||
|         const accounts = await web3Wrapper.getAvailableAddressesAsync(); |  | ||||||
|         [owner, address, notOwner] = accounts.slice(0, 3); |  | ||||||
|         authorizable = await MixinAuthorizableContract.deployFrom0xArtifactAsync( |  | ||||||
|             artifacts.MixinAuthorizable, |  | ||||||
|             provider, |  | ||||||
|             txDefaults, |  | ||||||
|             artifacts, |  | ||||||
|         ); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     describe('addAuthorizedAddress', () => { |  | ||||||
|         it('should revert if not called by owner', async () => { |  | ||||||
|             const tx = authorizable.addAuthorizedAddress(notOwner).sendTransactionAsync({ from: notOwner }); |  | ||||||
|             return expect(tx).to.revertWith(RevertReason.OnlyContractOwner); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('should allow owner to add an authorized address', async () => { |  | ||||||
|             await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner }); |  | ||||||
|             const isAuthorized = await authorizable.authorized(address).callAsync(); |  | ||||||
|             expect(isAuthorized).to.be.true(); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('should revert if owner attempts to authorize a duplicate address', async () => { |  | ||||||
|             await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner }); |  | ||||||
|             const tx = authorizable.addAuthorizedAddress(address).sendTransactionAsync({ from: owner }); |  | ||||||
|             return expect(tx).to.revertWith(RevertReason.TargetAlreadyAuthorized); |  | ||||||
|         }); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     describe('removeAuthorizedAddress', () => { |  | ||||||
|         it('should revert if not called by owner', async () => { |  | ||||||
|             await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner }); |  | ||||||
|             const tx = authorizable.removeAuthorizedAddress(address).sendTransactionAsync({ from: notOwner }); |  | ||||||
|             return expect(tx).to.revertWith(RevertReason.OnlyContractOwner); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('should allow owner to remove an authorized address', async () => { |  | ||||||
|             await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner }); |  | ||||||
|             await authorizable.removeAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner }); |  | ||||||
|             const isAuthorized = await authorizable.authorized(address).callAsync(); |  | ||||||
|             expect(isAuthorized).to.be.false(); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('should revert if owner attempts to remove an address that is not authorized', async () => { |  | ||||||
|             const tx = authorizable.removeAuthorizedAddress(address).sendTransactionAsync({ from: owner }); |  | ||||||
|             return expect(tx).to.revertWith(RevertReason.TargetNotAuthorized); |  | ||||||
|         }); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     describe('removeAuthorizedAddressAtIndex', () => { |  | ||||||
|         it('should revert if not called by owner', async () => { |  | ||||||
|             await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner }); |  | ||||||
|             const index = new BigNumber(0); |  | ||||||
|             const tx = authorizable |  | ||||||
|                 .removeAuthorizedAddressAtIndex(address, index) |  | ||||||
|                 .sendTransactionAsync({ from: notOwner }); |  | ||||||
|             return expect(tx).to.revertWith(RevertReason.OnlyContractOwner); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('should revert if index is >= authorities.length', async () => { |  | ||||||
|             await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner }); |  | ||||||
|             const index = new BigNumber(1); |  | ||||||
|             const tx = authorizable |  | ||||||
|                 .removeAuthorizedAddressAtIndex(address, index) |  | ||||||
|                 .sendTransactionAsync({ from: owner }); |  | ||||||
|             return expect(tx).to.revertWith(RevertReason.IndexOutOfBounds); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('should revert if owner attempts to remove an address that is not authorized', async () => { |  | ||||||
|             const index = new BigNumber(0); |  | ||||||
|             const tx = authorizable |  | ||||||
|                 .removeAuthorizedAddressAtIndex(address, index) |  | ||||||
|                 .sendTransactionAsync({ from: owner }); |  | ||||||
|             return expect(tx).to.revertWith(RevertReason.TargetNotAuthorized); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('should revert if address at index does not match target', async () => { |  | ||||||
|             const address1 = address; |  | ||||||
|             const address2 = notOwner; |  | ||||||
|             await authorizable.addAuthorizedAddress(address1).awaitTransactionSuccessAsync({ from: owner }); |  | ||||||
|             await authorizable.addAuthorizedAddress(address2).awaitTransactionSuccessAsync({ from: owner }); |  | ||||||
|             const address1Index = new BigNumber(0); |  | ||||||
|             const tx = authorizable |  | ||||||
|                 .removeAuthorizedAddressAtIndex(address2, address1Index) |  | ||||||
|                 .sendTransactionAsync({ from: owner }); |  | ||||||
|             return expect(tx).to.revertWith(RevertReason.AuthorizedAddressMismatch); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('should allow owner to remove an authorized address', async () => { |  | ||||||
|             await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner }); |  | ||||||
|             const index = new BigNumber(0); |  | ||||||
|             await authorizable.removeAuthorizedAddressAtIndex(address, index).awaitTransactionSuccessAsync({ |  | ||||||
|                 from: owner, |  | ||||||
|             }); |  | ||||||
|             const isAuthorized = await authorizable.authorized(address).callAsync(); |  | ||||||
|             expect(isAuthorized).to.be.false(); |  | ||||||
|         }); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     describe('getAuthorizedAddresses', () => { |  | ||||||
|         it('should return all authorized addresses', async () => { |  | ||||||
|             const initial = await authorizable.getAuthorizedAddresses().callAsync(); |  | ||||||
|             expect(initial).to.have.length(0); |  | ||||||
|             await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner }); |  | ||||||
|             const afterAdd = await authorizable.getAuthorizedAddresses().callAsync(); |  | ||||||
|             expect(afterAdd).to.have.length(1); |  | ||||||
|             expect(afterAdd).to.include(address); |  | ||||||
|             await authorizable.removeAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner }); |  | ||||||
|             const afterRemove = await authorizable.getAuthorizedAddresses().callAsync(); |  | ||||||
|             expect(afterRemove).to.have.length(0); |  | ||||||
|         }); |  | ||||||
|     }); |  | ||||||
| }); |  | ||||||
| @@ -1,185 +0,0 @@ | |||||||
| import { |  | ||||||
|     blockchainTests, |  | ||||||
|     constants, |  | ||||||
|     expect, |  | ||||||
|     filterLogsToArguments, |  | ||||||
|     getRandomInteger, |  | ||||||
|     randomAddress, |  | ||||||
| } from '@0x/contracts-test-utils'; |  | ||||||
| import { AssetProxyId } from '@0x/types'; |  | ||||||
| import { AbiEncoder, BigNumber, hexUtils } from '@0x/utils'; |  | ||||||
| import { DecodedLogs } from 'ethereum-types'; |  | ||||||
| import * as _ from 'lodash'; |  | ||||||
|  |  | ||||||
| import { artifacts } from './artifacts'; |  | ||||||
| import { TestBancorBridgeContract } from './generated-wrappers/test_bancor_bridge'; |  | ||||||
| import { |  | ||||||
|     TestBancorBridgeConvertByPathInputEventArgs as ConvertByPathArgs, |  | ||||||
|     TestBancorBridgeEvents as ContractEvents, |  | ||||||
|     TestBancorBridgeTokenApproveEventArgs as TokenApproveArgs, |  | ||||||
| } from './wrappers'; |  | ||||||
|  |  | ||||||
| blockchainTests.resets('Bancor unit tests', env => { |  | ||||||
|     const FROM_TOKEN_DECIMALS = 6; |  | ||||||
|     const TO_TOKEN_DECIMALS = 18; |  | ||||||
|     const FROM_TOKEN_BASE = new BigNumber(10).pow(FROM_TOKEN_DECIMALS); |  | ||||||
|     const TO_TOKEN_BASE = new BigNumber(10).pow(TO_TOKEN_DECIMALS); |  | ||||||
|     let testContract: TestBancorBridgeContract; |  | ||||||
|  |  | ||||||
|     before(async () => { |  | ||||||
|         testContract = await TestBancorBridgeContract.deployFrom0xArtifactAsync( |  | ||||||
|             artifacts.TestBancorBridge, |  | ||||||
|             env.provider, |  | ||||||
|             env.txDefaults, |  | ||||||
|             artifacts, |  | ||||||
|         ); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     describe('isValidSignature()', () => { |  | ||||||
|         it('returns success bytes', async () => { |  | ||||||
|             const LEGACY_WALLET_MAGIC_VALUE = '0xb0671381'; |  | ||||||
|             const result = await testContract |  | ||||||
|                 .isValidSignature(hexUtils.random(), hexUtils.random(_.random(0, 32))) |  | ||||||
|                 .callAsync(); |  | ||||||
|             expect(result).to.eq(LEGACY_WALLET_MAGIC_VALUE); |  | ||||||
|         }); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     describe('bridgeTransferFrom()', () => { |  | ||||||
|         interface TransferFromOpts { |  | ||||||
|             tokenAddressesPath: string[]; |  | ||||||
|             toAddress: string; |  | ||||||
|             // Amount to pass into `bridgeTransferFrom()` |  | ||||||
|             amount: BigNumber; |  | ||||||
|             // Token balance of the bridge. |  | ||||||
|             fromTokenBalance: BigNumber; |  | ||||||
|             // Router reverts with this reason |  | ||||||
|             routerRevertReason: string; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         interface TransferFromResult { |  | ||||||
|             opts: TransferFromOpts; |  | ||||||
|             result: string; |  | ||||||
|             logs: DecodedLogs; |  | ||||||
|             blocktime: number; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         function createTransferFromOpts(opts?: Partial<TransferFromOpts>): TransferFromOpts { |  | ||||||
|             const amount = getRandomInteger(1, TO_TOKEN_BASE.times(100)); |  | ||||||
|             return { |  | ||||||
|                 tokenAddressesPath: Array(3).fill(constants.NULL_ADDRESS), |  | ||||||
|                 amount, |  | ||||||
|                 toAddress: randomAddress(), |  | ||||||
|                 fromTokenBalance: getRandomInteger(1, FROM_TOKEN_BASE.times(100)), |  | ||||||
|                 routerRevertReason: '', |  | ||||||
|                 ...opts, |  | ||||||
|             }; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         const bridgeDataEncoder = AbiEncoder.create('(address[], address)'); |  | ||||||
|  |  | ||||||
|         async function transferFromAsync(opts?: Partial<TransferFromOpts>): Promise<TransferFromResult> { |  | ||||||
|             const _opts = createTransferFromOpts(opts); |  | ||||||
|  |  | ||||||
|             for (let i = 0; i < _opts.tokenAddressesPath.length; i++) { |  | ||||||
|                 const createFromTokenFn = testContract.createToken(_opts.tokenAddressesPath[i]); |  | ||||||
|                 _opts.tokenAddressesPath[i] = await createFromTokenFn.callAsync(); |  | ||||||
|                 await createFromTokenFn.awaitTransactionSuccessAsync(); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             // Set the token balance for the token we're converting from. |  | ||||||
|             await testContract |  | ||||||
|                 .setTokenBalance(_opts.tokenAddressesPath[0], _opts.fromTokenBalance) |  | ||||||
|                 .awaitTransactionSuccessAsync(); |  | ||||||
|  |  | ||||||
|             // Set revert reason for the router. |  | ||||||
|             await testContract.setNetworkRevertReason(_opts.routerRevertReason).awaitTransactionSuccessAsync(); |  | ||||||
|  |  | ||||||
|             // Call bridgeTransferFrom(). |  | ||||||
|             const bridgeTransferFromFn = testContract.bridgeTransferFrom( |  | ||||||
|                 // Output token |  | ||||||
|                 _opts.tokenAddressesPath[_opts.tokenAddressesPath.length - 1], |  | ||||||
|                 // Random maker address. |  | ||||||
|                 randomAddress(), |  | ||||||
|                 // Recipient address. |  | ||||||
|                 _opts.toAddress, |  | ||||||
|                 // Transfer amount. |  | ||||||
|                 _opts.amount, |  | ||||||
|                 // ABI-encode the input token address as the bridge data. |  | ||||||
|                 bridgeDataEncoder.encode([ |  | ||||||
|                     _opts.tokenAddressesPath, |  | ||||||
|                     await testContract.getNetworkAddress().callAsync(), |  | ||||||
|                 ]), |  | ||||||
|             ); |  | ||||||
|             const result = await bridgeTransferFromFn.callAsync(); |  | ||||||
|             const receipt = await bridgeTransferFromFn.awaitTransactionSuccessAsync(); |  | ||||||
|             return { |  | ||||||
|                 opts: _opts, |  | ||||||
|                 result, |  | ||||||
|                 logs: (receipt.logs as any) as DecodedLogs, |  | ||||||
|                 blocktime: await env.web3Wrapper.getBlockTimestampAsync(receipt.blockNumber), |  | ||||||
|             }; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         it('returns magic bytes on success', async () => { |  | ||||||
|             const { result } = await transferFromAsync(); |  | ||||||
|             expect(result).to.eq(AssetProxyId.ERC20Bridge); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         describe('token -> token', async () => { |  | ||||||
|             it('calls BancorNetwork.convertByPath()', async () => { |  | ||||||
|                 const { opts, result, logs } = await transferFromAsync(); |  | ||||||
|                 expect(result).to.eq(AssetProxyId.ERC20Bridge, 'asset proxy id'); |  | ||||||
|                 const transfers = filterLogsToArguments<ConvertByPathArgs>(logs, ContractEvents.ConvertByPathInput); |  | ||||||
|  |  | ||||||
|                 expect(transfers.length).to.eq(1); |  | ||||||
|                 expect(transfers[0].toTokenAddress).to.eq( |  | ||||||
|                     opts.tokenAddressesPath[opts.tokenAddressesPath.length - 1], |  | ||||||
|                     'output token address', |  | ||||||
|                 ); |  | ||||||
|                 expect(transfers[0].to).to.eq(opts.toAddress, 'recipient address'); |  | ||||||
|                 expect(transfers[0].amountIn).to.bignumber.eq(opts.fromTokenBalance, 'input token amount'); |  | ||||||
|                 expect(transfers[0].amountOutMin).to.bignumber.eq(opts.amount, 'output token amount'); |  | ||||||
|                 expect(transfers[0].feeRecipient).to.eq(constants.NULL_ADDRESS); |  | ||||||
|                 expect(transfers[0].feeAmount).to.bignumber.eq(new BigNumber(0)); |  | ||||||
|             }); |  | ||||||
|  |  | ||||||
|             it('sets allowance for "from" token', async () => { |  | ||||||
|                 const { logs } = await transferFromAsync(); |  | ||||||
|                 const approvals = filterLogsToArguments<TokenApproveArgs>(logs, ContractEvents.TokenApprove); |  | ||||||
|                 const networkAddress = await testContract.getNetworkAddress().callAsync(); |  | ||||||
|                 expect(approvals.length).to.eq(1); |  | ||||||
|                 expect(approvals[0].spender).to.eq(networkAddress); |  | ||||||
|                 expect(approvals[0].allowance).to.bignumber.eq(constants.MAX_UINT256); |  | ||||||
|             }); |  | ||||||
|  |  | ||||||
|             it('fails if the router fails', async () => { |  | ||||||
|                 const revertReason = 'FOOBAR'; |  | ||||||
|                 const tx = transferFromAsync({ |  | ||||||
|                     routerRevertReason: revertReason, |  | ||||||
|                 }); |  | ||||||
|                 return expect(tx).to.eventually.be.rejectedWith(revertReason); |  | ||||||
|             }); |  | ||||||
|         }); |  | ||||||
|         describe('token -> token -> token', async () => { |  | ||||||
|             it('calls BancorNetwork.convertByPath()', async () => { |  | ||||||
|                 const { opts, result, logs } = await transferFromAsync({ |  | ||||||
|                     tokenAddressesPath: Array(5).fill(constants.NULL_ADDRESS), |  | ||||||
|                 }); |  | ||||||
|                 expect(result).to.eq(AssetProxyId.ERC20Bridge, 'asset proxy id'); |  | ||||||
|                 const transfers = filterLogsToArguments<ConvertByPathArgs>(logs, ContractEvents.ConvertByPathInput); |  | ||||||
|  |  | ||||||
|                 expect(transfers.length).to.eq(1); |  | ||||||
|                 expect(transfers[0].toTokenAddress).to.eq( |  | ||||||
|                     opts.tokenAddressesPath[opts.tokenAddressesPath.length - 1], |  | ||||||
|                     'output token address', |  | ||||||
|                 ); |  | ||||||
|                 expect(transfers[0].to).to.eq(opts.toAddress, 'recipient address'); |  | ||||||
|                 expect(transfers[0].amountIn).to.bignumber.eq(opts.fromTokenBalance, 'input token amount'); |  | ||||||
|                 expect(transfers[0].amountOutMin).to.bignumber.eq(opts.amount, 'output token amount'); |  | ||||||
|                 expect(transfers[0].feeRecipient).to.eq(constants.NULL_ADDRESS); |  | ||||||
|                 expect(transfers[0].feeAmount).to.bignumber.eq(new BigNumber(0)); |  | ||||||
|             }); |  | ||||||
|         }); |  | ||||||
|     }); |  | ||||||
| }); |  | ||||||
| @@ -1,60 +0,0 @@ | |||||||
| import { ERC20TokenContract } from '@0x/contracts-erc20'; |  | ||||||
| import { blockchainTests, constants, expect, randomAddress } from '@0x/contracts-test-utils'; |  | ||||||
| import { AssetProxyId, RevertReason } from '@0x/types'; |  | ||||||
| import { BigNumber } from '@0x/utils'; |  | ||||||
|  |  | ||||||
| import { artifacts } from './artifacts'; |  | ||||||
| import { TestChaiBridgeContract } from './wrappers'; |  | ||||||
|  |  | ||||||
| blockchainTests.resets('ChaiBridge unit tests', env => { |  | ||||||
|     let chaiBridgeContract: TestChaiBridgeContract; |  | ||||||
|     let testDaiContract: ERC20TokenContract; |  | ||||||
|     let fromAddress: string; |  | ||||||
|     let toAddress: string; |  | ||||||
|  |  | ||||||
|     const alwaysRevertAddress = '0x0000000000000000000000000000000000000001'; |  | ||||||
|     const amount = new BigNumber(1); |  | ||||||
|  |  | ||||||
|     before(async () => { |  | ||||||
|         [fromAddress, toAddress] = await env.getAccountAddressesAsync(); |  | ||||||
|         chaiBridgeContract = await TestChaiBridgeContract.deployFrom0xArtifactAsync( |  | ||||||
|             artifacts.TestChaiBridge, |  | ||||||
|             env.provider, |  | ||||||
|             env.txDefaults, |  | ||||||
|             artifacts, |  | ||||||
|         ); |  | ||||||
|         const testChaiDaiAddress = await chaiBridgeContract.testChaiDai().callAsync(); |  | ||||||
|         testDaiContract = new ERC20TokenContract(testChaiDaiAddress, env.provider, env.txDefaults); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     describe('bridgeTransferFrom()', () => { |  | ||||||
|         it('fails if not called by ERC20BridgeProxy', async () => { |  | ||||||
|             return expect( |  | ||||||
|                 chaiBridgeContract |  | ||||||
|                     .bridgeTransferFrom(randomAddress(), fromAddress, toAddress, amount, constants.NULL_BYTES) |  | ||||||
|                     .awaitTransactionSuccessAsync({ from: alwaysRevertAddress }), |  | ||||||
|             ).to.revertWith(RevertReason.ChaiBridgeOnlyCallableByErc20BridgeProxy); |  | ||||||
|         }); |  | ||||||
|         it('returns magic bytes upon success', async () => { |  | ||||||
|             const magicBytes = await chaiBridgeContract |  | ||||||
|                 .bridgeTransferFrom(randomAddress(), fromAddress, toAddress, amount, constants.NULL_BYTES) |  | ||||||
|                 .callAsync(); |  | ||||||
|             expect(magicBytes).to.eq(AssetProxyId.ERC20Bridge); |  | ||||||
|         }); |  | ||||||
|         it('should increase the Dai balance of `toAddress` by `amount` if successful', async () => { |  | ||||||
|             const initialBalance = await testDaiContract.balanceOf(toAddress).callAsync(); |  | ||||||
|             await chaiBridgeContract |  | ||||||
|                 .bridgeTransferFrom(randomAddress(), fromAddress, toAddress, amount, constants.NULL_BYTES) |  | ||||||
|                 .awaitTransactionSuccessAsync(); |  | ||||||
|             const endBalance = await testDaiContract.balanceOf(toAddress).callAsync(); |  | ||||||
|             expect(endBalance).to.bignumber.eq(initialBalance.plus(amount)); |  | ||||||
|         }); |  | ||||||
|         it('fails if the `chai.draw` call fails', async () => { |  | ||||||
|             return expect( |  | ||||||
|                 chaiBridgeContract |  | ||||||
|                     .bridgeTransferFrom(randomAddress(), alwaysRevertAddress, toAddress, amount, constants.NULL_BYTES) |  | ||||||
|                     .awaitTransactionSuccessAsync(), |  | ||||||
|             ).to.revertWith(RevertReason.ChaiBridgeDrawDaiFailed); |  | ||||||
|         }); |  | ||||||
|     }); |  | ||||||
| }); |  | ||||||
| @@ -1,354 +0,0 @@ | |||||||
| import { ContractTxFunctionObj } from '@0x/contract-wrappers'; |  | ||||||
| import { |  | ||||||
|     blockchainTests, |  | ||||||
|     constants, |  | ||||||
|     expect, |  | ||||||
|     filterLogsToArguments, |  | ||||||
|     getRandomInteger, |  | ||||||
|     randomAddress, |  | ||||||
|     shortZip, |  | ||||||
| } from '@0x/contracts-test-utils'; |  | ||||||
| import { BigNumber, hexUtils, NULL_ADDRESS } from '@0x/utils'; |  | ||||||
| import { DecodedLogs } from 'ethereum-types'; |  | ||||||
| import * as _ from 'lodash'; |  | ||||||
|  |  | ||||||
| import { DexForwarderBridgeCall, dexForwarderBridgeDataEncoder } from '../src/dex_forwarder_bridge'; |  | ||||||
|  |  | ||||||
| import { artifacts } from './artifacts'; |  | ||||||
| import { |  | ||||||
|     TestDexForwarderBridgeBridgeTransferFromCalledEventArgs as BtfCalledEventArgs, |  | ||||||
|     TestDexForwarderBridgeContract, |  | ||||||
|     TestDexForwarderBridgeEvents as TestEvents, |  | ||||||
| } from './wrappers'; |  | ||||||
|  |  | ||||||
| const { ZERO_AMOUNT } = constants; |  | ||||||
|  |  | ||||||
| blockchainTests.resets('DexForwarderBridge unit tests', env => { |  | ||||||
|     let testContract: TestDexForwarderBridgeContract; |  | ||||||
|     let inputToken: string; |  | ||||||
|     let outputToken: string; |  | ||||||
|     const BRIDGE_SUCCESS = '0xdc1600f3'; |  | ||||||
|     const BRIDGE_FAILURE = '0xffffffff'; |  | ||||||
|     const BRIDGE_REVERT_ERROR = 'oopsie'; |  | ||||||
|     const NOT_AUTHORIZED_REVERT = 'DexForwarderBridge/SENDER_NOT_AUTHORIZED'; |  | ||||||
|     const DEFAULTS = { |  | ||||||
|         toAddress: randomAddress(), |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     before(async () => { |  | ||||||
|         testContract = await TestDexForwarderBridgeContract.deployFrom0xArtifactAsync( |  | ||||||
|             artifacts.TestDexForwarderBridge, |  | ||||||
|             env.provider, |  | ||||||
|             env.txDefaults, |  | ||||||
|             artifacts, |  | ||||||
|         ); |  | ||||||
|         // Create test tokens. |  | ||||||
|         [inputToken, outputToken] = [ |  | ||||||
|             await callAndTransactAsync(testContract.createToken()), |  | ||||||
|             await callAndTransactAsync(testContract.createToken()), |  | ||||||
|         ]; |  | ||||||
|         await callAndTransactAsync(testContract.setAuthorized(env.txDefaults.from as string)); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     async function callAndTransactAsync<TResult>(fnCall: ContractTxFunctionObj<TResult>): Promise<TResult> { |  | ||||||
|         const result = await fnCall.callAsync(); |  | ||||||
|         await fnCall.awaitTransactionSuccessAsync({}, { shouldValidate: false }); |  | ||||||
|         return result; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function getRandomBridgeCall( |  | ||||||
|         bridgeAddress: string, |  | ||||||
|         fields: Partial<DexForwarderBridgeCall> = {}, |  | ||||||
|     ): DexForwarderBridgeCall { |  | ||||||
|         return { |  | ||||||
|             target: bridgeAddress, |  | ||||||
|             inputTokenAmount: getRandomInteger(1, '100e18'), |  | ||||||
|             outputTokenAmount: getRandomInteger(1, '100e18'), |  | ||||||
|             bridgeData: hexUtils.leftPad(inputToken), |  | ||||||
|             ...fields, |  | ||||||
|         }; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     describe('bridgeTransferFrom()', () => { |  | ||||||
|         let goodBridgeCalls: DexForwarderBridgeCall[]; |  | ||||||
|         let revertingBridgeCall: DexForwarderBridgeCall; |  | ||||||
|         let failingBridgeCall: DexForwarderBridgeCall; |  | ||||||
|         let allBridgeCalls: DexForwarderBridgeCall[]; |  | ||||||
|         let totalFillableOutputAmount: BigNumber; |  | ||||||
|         let totalFillableInputAmount: BigNumber; |  | ||||||
|         let recipientOutputBalance: BigNumber; |  | ||||||
|  |  | ||||||
|         beforeEach(async () => { |  | ||||||
|             goodBridgeCalls = []; |  | ||||||
|             for (let i = 0; i < 4; ++i) { |  | ||||||
|                 goodBridgeCalls.push(await createBridgeCallAsync({ returnCode: BRIDGE_SUCCESS })); |  | ||||||
|             } |  | ||||||
|             revertingBridgeCall = await createBridgeCallAsync({ revertError: BRIDGE_REVERT_ERROR }); |  | ||||||
|             failingBridgeCall = await createBridgeCallAsync({ returnCode: BRIDGE_FAILURE }); |  | ||||||
|             allBridgeCalls = _.shuffle([failingBridgeCall, revertingBridgeCall, ...goodBridgeCalls]); |  | ||||||
|  |  | ||||||
|             totalFillableInputAmount = BigNumber.sum(...goodBridgeCalls.map(c => c.inputTokenAmount)); |  | ||||||
|             totalFillableOutputAmount = BigNumber.sum(...goodBridgeCalls.map(c => c.outputTokenAmount)); |  | ||||||
|  |  | ||||||
|             // Grant the taker some output tokens. |  | ||||||
|             await testContract.setTokenBalance( |  | ||||||
|                 outputToken, |  | ||||||
|                 DEFAULTS.toAddress, |  | ||||||
|                 (recipientOutputBalance = getRandomInteger(1, '100e18')), |  | ||||||
|             ); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         async function setForwarderInputBalanceAsync(amount: BigNumber): Promise<void> { |  | ||||||
|             await testContract |  | ||||||
|                 .setTokenBalance(inputToken, testContract.address, amount) |  | ||||||
|                 .awaitTransactionSuccessAsync({}, { shouldValidate: false }); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         async function createBridgeCallAsync( |  | ||||||
|             opts: Partial<{ |  | ||||||
|                 returnCode: string; |  | ||||||
|                 revertError: string; |  | ||||||
|                 callFields: Partial<DexForwarderBridgeCall>; |  | ||||||
|                 outputFillAmount: BigNumber; |  | ||||||
|             }>, |  | ||||||
|         ): Promise<DexForwarderBridgeCall> { |  | ||||||
|             const { returnCode, revertError, callFields, outputFillAmount } = { |  | ||||||
|                 returnCode: BRIDGE_SUCCESS, |  | ||||||
|                 revertError: '', |  | ||||||
|                 ...opts, |  | ||||||
|             }; |  | ||||||
|             const bridge = await callAndTransactAsync(testContract.createBridge(returnCode, revertError)); |  | ||||||
|             const call = getRandomBridgeCall(bridge, callFields); |  | ||||||
|             await testContract |  | ||||||
|                 .setBridgeTransferAmount(call.target, outputFillAmount || call.outputTokenAmount) |  | ||||||
|                 .awaitTransactionSuccessAsync({}, { shouldValidate: false }); |  | ||||||
|             return call; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         async function callBridgeTransferFromAsync(opts: { |  | ||||||
|             bridgeData: string; |  | ||||||
|             sellAmount?: BigNumber; |  | ||||||
|             buyAmount?: BigNumber; |  | ||||||
|         }): Promise<DecodedLogs> { |  | ||||||
|             // Fund the forwarder with input tokens to sell. |  | ||||||
|             await setForwarderInputBalanceAsync(opts.sellAmount || totalFillableInputAmount); |  | ||||||
|             const call = testContract.bridgeTransferFrom( |  | ||||||
|                 outputToken, |  | ||||||
|                 testContract.address, |  | ||||||
|                 DEFAULTS.toAddress, |  | ||||||
|                 opts.buyAmount || totalFillableOutputAmount, |  | ||||||
|                 opts.bridgeData, |  | ||||||
|             ); |  | ||||||
|             const returnCode = await call.callAsync(); |  | ||||||
|             if (returnCode !== BRIDGE_SUCCESS) { |  | ||||||
|                 throw new Error('Expected BRIDGE_SUCCESS'); |  | ||||||
|             } |  | ||||||
|             const receipt = await call.awaitTransactionSuccessAsync({}, { shouldValidate: false }); |  | ||||||
|             // tslint:disable-next-line: no-unnecessary-type-assertion |  | ||||||
|             return receipt.logs as DecodedLogs; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         it('succeeds with no bridge calls and no input balance', async () => { |  | ||||||
|             const bridgeData = dexForwarderBridgeDataEncoder.encode({ |  | ||||||
|                 inputToken, |  | ||||||
|                 calls: [], |  | ||||||
|             }); |  | ||||||
|             await callBridgeTransferFromAsync({ bridgeData, sellAmount: ZERO_AMOUNT }); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('succeeds with bridge calls and no input balance', async () => { |  | ||||||
|             const bridgeData = dexForwarderBridgeDataEncoder.encode({ |  | ||||||
|                 inputToken, |  | ||||||
|                 calls: allBridgeCalls, |  | ||||||
|             }); |  | ||||||
|             await callBridgeTransferFromAsync({ bridgeData, sellAmount: ZERO_AMOUNT }); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('succeeds with no bridge calls and an input balance', async () => { |  | ||||||
|             const bridgeData = dexForwarderBridgeDataEncoder.encode({ |  | ||||||
|                 inputToken, |  | ||||||
|                 calls: [], |  | ||||||
|             }); |  | ||||||
|             await callBridgeTransferFromAsync({ |  | ||||||
|                 bridgeData, |  | ||||||
|                 sellAmount: new BigNumber(1), |  | ||||||
|             }); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('succeeds if entire input token balance is not consumed', async () => { |  | ||||||
|             const bridgeData = dexForwarderBridgeDataEncoder.encode({ |  | ||||||
|                 inputToken, |  | ||||||
|                 calls: allBridgeCalls, |  | ||||||
|             }); |  | ||||||
|             await callBridgeTransferFromAsync({ |  | ||||||
|                 bridgeData, |  | ||||||
|                 sellAmount: totalFillableInputAmount.plus(1), |  | ||||||
|             }); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('fails if not authorized', async () => { |  | ||||||
|             const calls = goodBridgeCalls.slice(0, 1); |  | ||||||
|             const bridgeData = dexForwarderBridgeDataEncoder.encode({ |  | ||||||
|                 inputToken, |  | ||||||
|                 calls, |  | ||||||
|             }); |  | ||||||
|             await callAndTransactAsync(testContract.setAuthorized(NULL_ADDRESS)); |  | ||||||
|             return expect(callBridgeTransferFromAsync({ bridgeData, sellAmount: new BigNumber(1) })).to.revertWith( |  | ||||||
|                 NOT_AUTHORIZED_REVERT, |  | ||||||
|             ); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('succeeds with one bridge call', async () => { |  | ||||||
|             const calls = goodBridgeCalls.slice(0, 1); |  | ||||||
|             const bridgeData = dexForwarderBridgeDataEncoder.encode({ |  | ||||||
|                 inputToken, |  | ||||||
|                 calls, |  | ||||||
|             }); |  | ||||||
|             await callBridgeTransferFromAsync({ bridgeData, sellAmount: calls[0].inputTokenAmount }); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('succeeds with many bridge calls', async () => { |  | ||||||
|             const calls = goodBridgeCalls; |  | ||||||
|             const bridgeData = dexForwarderBridgeDataEncoder.encode({ |  | ||||||
|                 inputToken, |  | ||||||
|                 calls, |  | ||||||
|             }); |  | ||||||
|             await callBridgeTransferFromAsync({ bridgeData }); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('swallows a failing bridge call', async () => { |  | ||||||
|             const calls = _.shuffle([...goodBridgeCalls, failingBridgeCall]); |  | ||||||
|             const bridgeData = dexForwarderBridgeDataEncoder.encode({ |  | ||||||
|                 inputToken, |  | ||||||
|                 calls, |  | ||||||
|             }); |  | ||||||
|             await callBridgeTransferFromAsync({ bridgeData }); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('consumes input tokens for output tokens', async () => { |  | ||||||
|             const calls = allBridgeCalls; |  | ||||||
|             const bridgeData = dexForwarderBridgeDataEncoder.encode({ |  | ||||||
|                 inputToken, |  | ||||||
|                 calls, |  | ||||||
|             }); |  | ||||||
|             await callBridgeTransferFromAsync({ bridgeData }); |  | ||||||
|             const currentBridgeInputBalance = await testContract |  | ||||||
|                 .balanceOf(inputToken, testContract.address) |  | ||||||
|                 .callAsync(); |  | ||||||
|             expect(currentBridgeInputBalance).to.bignumber.eq(0); |  | ||||||
|             const currentRecipientOutputBalance = await testContract |  | ||||||
|                 .balanceOf(outputToken, DEFAULTS.toAddress) |  | ||||||
|                 .callAsync(); |  | ||||||
|             expect(currentRecipientOutputBalance).to.bignumber.eq(totalFillableOutputAmount); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it("transfers only up to each call's input amount to each bridge", async () => { |  | ||||||
|             const calls = goodBridgeCalls; |  | ||||||
|             const bridgeData = dexForwarderBridgeDataEncoder.encode({ |  | ||||||
|                 inputToken, |  | ||||||
|                 calls, |  | ||||||
|             }); |  | ||||||
|             const logs = await callBridgeTransferFromAsync({ bridgeData }); |  | ||||||
|             const btfs = filterLogsToArguments<BtfCalledEventArgs>(logs, TestEvents.BridgeTransferFromCalled); |  | ||||||
|             for (const [call, btf] of shortZip(goodBridgeCalls, btfs)) { |  | ||||||
|                 expect(btf.inputTokenBalance).to.bignumber.eq(call.inputTokenAmount); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('transfers only up to outstanding sell amount to each bridge', async () => { |  | ||||||
|             // Prepend an extra bridge call. |  | ||||||
|             const calls = [ |  | ||||||
|                 await createBridgeCallAsync({ |  | ||||||
|                     callFields: { |  | ||||||
|                         inputTokenAmount: new BigNumber(1), |  | ||||||
|                         outputTokenAmount: new BigNumber(1), |  | ||||||
|                     }, |  | ||||||
|                 }), |  | ||||||
|                 ...goodBridgeCalls, |  | ||||||
|             ]; |  | ||||||
|             const bridgeData = dexForwarderBridgeDataEncoder.encode({ |  | ||||||
|                 inputToken, |  | ||||||
|                 calls, |  | ||||||
|             }); |  | ||||||
|             const logs = await callBridgeTransferFromAsync({ bridgeData }); |  | ||||||
|             const btfs = filterLogsToArguments<BtfCalledEventArgs>(logs, TestEvents.BridgeTransferFromCalled); |  | ||||||
|             expect(btfs).to.be.length(goodBridgeCalls.length + 1); |  | ||||||
|             // The last call will receive 1 less token. |  | ||||||
|             const lastCall = calls.slice(-1)[0]; |  | ||||||
|             const lastBtf = btfs.slice(-1)[0]; |  | ||||||
|             expect(lastBtf.inputTokenBalance).to.bignumber.eq(lastCall.inputTokenAmount.minus(1)); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('recoups funds from a bridge that fails', async () => { |  | ||||||
|             // Prepend a call that will take the whole input amount but will |  | ||||||
|             // fail. |  | ||||||
|             const badCall = await createBridgeCallAsync({ |  | ||||||
|                 callFields: { inputTokenAmount: totalFillableInputAmount }, |  | ||||||
|                 returnCode: BRIDGE_FAILURE, |  | ||||||
|             }); |  | ||||||
|             const calls = [badCall, ...goodBridgeCalls]; |  | ||||||
|             const bridgeData = dexForwarderBridgeDataEncoder.encode({ |  | ||||||
|                 inputToken, |  | ||||||
|                 calls, |  | ||||||
|             }); |  | ||||||
|             const logs = await callBridgeTransferFromAsync({ bridgeData }); |  | ||||||
|             const btfs = filterLogsToArguments<BtfCalledEventArgs>(logs, TestEvents.BridgeTransferFromCalled); |  | ||||||
|             expect(btfs).to.be.length(goodBridgeCalls.length); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('recoups funds from a bridge that reverts', async () => { |  | ||||||
|             // Prepend a call that will take the whole input amount but will |  | ||||||
|             // revert. |  | ||||||
|             const badCall = await createBridgeCallAsync({ |  | ||||||
|                 callFields: { inputTokenAmount: totalFillableInputAmount }, |  | ||||||
|                 revertError: BRIDGE_REVERT_ERROR, |  | ||||||
|             }); |  | ||||||
|             const calls = [badCall, ...goodBridgeCalls]; |  | ||||||
|             const bridgeData = dexForwarderBridgeDataEncoder.encode({ |  | ||||||
|                 inputToken, |  | ||||||
|                 calls, |  | ||||||
|             }); |  | ||||||
|             const logs = await callBridgeTransferFromAsync({ bridgeData }); |  | ||||||
|             const btfs = filterLogsToArguments<BtfCalledEventArgs>(logs, TestEvents.BridgeTransferFromCalled); |  | ||||||
|             expect(btfs).to.be.length(goodBridgeCalls.length); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('recoups funds from a bridge that under-pays', async () => { |  | ||||||
|             // Prepend a call that will take the whole input amount but will |  | ||||||
|             // underpay the output amount.. |  | ||||||
|             const badCall = await createBridgeCallAsync({ |  | ||||||
|                 callFields: { |  | ||||||
|                     inputTokenAmount: totalFillableInputAmount, |  | ||||||
|                     outputTokenAmount: new BigNumber(2), |  | ||||||
|                 }, |  | ||||||
|                 outputFillAmount: new BigNumber(1), |  | ||||||
|             }); |  | ||||||
|             const calls = [badCall, ...goodBridgeCalls]; |  | ||||||
|             const bridgeData = dexForwarderBridgeDataEncoder.encode({ |  | ||||||
|                 inputToken, |  | ||||||
|                 calls, |  | ||||||
|             }); |  | ||||||
|             const logs = await callBridgeTransferFromAsync({ bridgeData }); |  | ||||||
|             const btfs = filterLogsToArguments<BtfCalledEventArgs>(logs, TestEvents.BridgeTransferFromCalled); |  | ||||||
|             expect(btfs).to.be.length(goodBridgeCalls.length); |  | ||||||
|         }); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     describe('executeBridgeCall()', () => { |  | ||||||
|         it('cannot be called externally', async () => { |  | ||||||
|             return expect( |  | ||||||
|                 testContract |  | ||||||
|                     .executeBridgeCall( |  | ||||||
|                         randomAddress(), |  | ||||||
|                         randomAddress(), |  | ||||||
|                         randomAddress(), |  | ||||||
|                         randomAddress(), |  | ||||||
|                         new BigNumber(1), |  | ||||||
|                         new BigNumber(1), |  | ||||||
|                         constants.NULL_BYTES, |  | ||||||
|                     ) |  | ||||||
|                     .callAsync(), |  | ||||||
|             ).to.revertWith('DexForwarderBridge/ONLY_SELF'); |  | ||||||
|         }); |  | ||||||
|     }); |  | ||||||
| }); |  | ||||||
| @@ -1,399 +0,0 @@ | |||||||
| import { LibMathRevertErrors } from '@0x/contracts-exchange-libs'; |  | ||||||
| import { blockchainTests, constants, expect, verifyEventsFromLogs } from '@0x/contracts-test-utils'; |  | ||||||
| import { AssetProxyId, RevertReason } from '@0x/types'; |  | ||||||
| import { BigNumber } from '@0x/utils'; |  | ||||||
| import * as _ from 'lodash'; |  | ||||||
|  |  | ||||||
| import { DydxBridgeActionType, DydxBridgeData, dydxBridgeDataEncoder } from '../src/dydx_bridge_encoder'; |  | ||||||
| import { ERC20BridgeProxyContract, IAssetDataContract } from '../src/wrappers'; |  | ||||||
|  |  | ||||||
| import { artifacts } from './artifacts'; |  | ||||||
| import { TestDydxBridgeContract, TestDydxBridgeEvents } from './wrappers'; |  | ||||||
|  |  | ||||||
| blockchainTests.resets('DydxBridge unit tests', env => { |  | ||||||
|     const defaultAccountNumber = new BigNumber(1); |  | ||||||
|     const marketId = new BigNumber(2); |  | ||||||
|     const defaultAmount = new BigNumber(4); |  | ||||||
|     const notAuthorized = '0x0000000000000000000000000000000000000001'; |  | ||||||
|     const defaultDepositAction = { |  | ||||||
|         actionType: DydxBridgeActionType.Deposit, |  | ||||||
|         accountIdx: constants.ZERO_AMOUNT, |  | ||||||
|         marketId, |  | ||||||
|         conversionRateNumerator: constants.ZERO_AMOUNT, |  | ||||||
|         conversionRateDenominator: constants.ZERO_AMOUNT, |  | ||||||
|     }; |  | ||||||
|     const defaultWithdrawAction = { |  | ||||||
|         actionType: DydxBridgeActionType.Withdraw, |  | ||||||
|         accountIdx: constants.ZERO_AMOUNT, |  | ||||||
|         marketId, |  | ||||||
|         conversionRateNumerator: constants.ZERO_AMOUNT, |  | ||||||
|         conversionRateDenominator: constants.ZERO_AMOUNT, |  | ||||||
|     }; |  | ||||||
|     let testContract: TestDydxBridgeContract; |  | ||||||
|     let testProxyContract: ERC20BridgeProxyContract; |  | ||||||
|     let assetDataEncoder: IAssetDataContract; |  | ||||||
|     let owner: string; |  | ||||||
|     let authorized: string; |  | ||||||
|     let accountOwner: string; |  | ||||||
|     let receiver: string; |  | ||||||
|  |  | ||||||
|     before(async () => { |  | ||||||
|         // Get accounts |  | ||||||
|         const accounts = await env.web3Wrapper.getAvailableAddressesAsync(); |  | ||||||
|         [owner, authorized, accountOwner, receiver] = accounts; |  | ||||||
|  |  | ||||||
|         // Deploy dydx bridge |  | ||||||
|         testContract = await TestDydxBridgeContract.deployFrom0xArtifactAsync( |  | ||||||
|             artifacts.TestDydxBridge, |  | ||||||
|             env.provider, |  | ||||||
|             env.txDefaults, |  | ||||||
|             artifacts, |  | ||||||
|             [accountOwner, receiver], |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         // Deploy test erc20 bridge proxy |  | ||||||
|         testProxyContract = await ERC20BridgeProxyContract.deployFrom0xArtifactAsync( |  | ||||||
|             artifacts.ERC20BridgeProxy, |  | ||||||
|             env.provider, |  | ||||||
|             env.txDefaults, |  | ||||||
|             artifacts, |  | ||||||
|         ); |  | ||||||
|         await testProxyContract.addAuthorizedAddress(authorized).awaitTransactionSuccessAsync({ from: owner }); |  | ||||||
|  |  | ||||||
|         // Setup asset data encoder |  | ||||||
|         assetDataEncoder = new IAssetDataContract(constants.NULL_ADDRESS, env.provider); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     describe('bridgeTransferFrom()', () => { |  | ||||||
|         const callBridgeTransferFrom = async ( |  | ||||||
|             from: string, |  | ||||||
|             to: string, |  | ||||||
|             amount: BigNumber, |  | ||||||
|             bridgeData: DydxBridgeData, |  | ||||||
|             sender: string, |  | ||||||
|         ): Promise<string> => { |  | ||||||
|             const returnValue = await testContract |  | ||||||
|                 .bridgeTransferFrom( |  | ||||||
|                     constants.NULL_ADDRESS, |  | ||||||
|                     from, |  | ||||||
|                     to, |  | ||||||
|                     amount, |  | ||||||
|                     dydxBridgeDataEncoder.encode({ bridgeData }), |  | ||||||
|                 ) |  | ||||||
|                 .callAsync({ from: sender }); |  | ||||||
|             return returnValue; |  | ||||||
|         }; |  | ||||||
|         const executeBridgeTransferFromAndVerifyEvents = async ( |  | ||||||
|             from: string, |  | ||||||
|             to: string, |  | ||||||
|             amount: BigNumber, |  | ||||||
|             bridgeData: DydxBridgeData, |  | ||||||
|             sender: string, |  | ||||||
|         ): Promise<void> => { |  | ||||||
|             // Execute transaction. |  | ||||||
|             const txReceipt = await testContract |  | ||||||
|                 .bridgeTransferFrom( |  | ||||||
|                     constants.NULL_ADDRESS, |  | ||||||
|                     from, |  | ||||||
|                     to, |  | ||||||
|                     amount, |  | ||||||
|                     dydxBridgeDataEncoder.encode({ bridgeData }), |  | ||||||
|                 ) |  | ||||||
|                 .awaitTransactionSuccessAsync({ from: sender }); |  | ||||||
|  |  | ||||||
|             // Verify `OperateAccount` event. |  | ||||||
|             const expectedOperateAccountEvents = []; |  | ||||||
|             for (const accountNumber of bridgeData.accountNumbers) { |  | ||||||
|                 expectedOperateAccountEvents.push({ |  | ||||||
|                     owner: accountOwner, |  | ||||||
|                     number: accountNumber, |  | ||||||
|                 }); |  | ||||||
|             } |  | ||||||
|             verifyEventsFromLogs(txReceipt.logs, expectedOperateAccountEvents, TestDydxBridgeEvents.OperateAccount); |  | ||||||
|  |  | ||||||
|             // Verify `OperateAction` event. |  | ||||||
|             const weiDenomination = 0; |  | ||||||
|             const deltaAmountRef = 0; |  | ||||||
|             const expectedOperateActionEvents = []; |  | ||||||
|             for (const action of bridgeData.actions) { |  | ||||||
|                 expectedOperateActionEvents.push({ |  | ||||||
|                     actionType: action.actionType as number, |  | ||||||
|                     accountIdx: action.accountIdx, |  | ||||||
|                     amountSign: action.actionType === DydxBridgeActionType.Deposit ? true : false, |  | ||||||
|                     amountDenomination: weiDenomination, |  | ||||||
|                     amountRef: deltaAmountRef, |  | ||||||
|                     amountValue: action.conversionRateDenominator.gt(0) |  | ||||||
|                         ? amount |  | ||||||
|                               .times(action.conversionRateNumerator) |  | ||||||
|                               .dividedToIntegerBy(action.conversionRateDenominator) |  | ||||||
|                         : amount, |  | ||||||
|                     primaryMarketId: marketId, |  | ||||||
|                     secondaryMarketId: constants.ZERO_AMOUNT, |  | ||||||
|                     otherAddress: action.actionType === DydxBridgeActionType.Deposit ? from : to, |  | ||||||
|                     otherAccountId: constants.ZERO_AMOUNT, |  | ||||||
|                     data: '0x', |  | ||||||
|                 }); |  | ||||||
|             } |  | ||||||
|             verifyEventsFromLogs(txReceipt.logs, expectedOperateActionEvents, TestDydxBridgeEvents.OperateAction); |  | ||||||
|         }; |  | ||||||
|         it('succeeds when calling with zero amount', async () => { |  | ||||||
|             const bridgeData = { |  | ||||||
|                 accountNumbers: [defaultAccountNumber], |  | ||||||
|                 actions: [defaultDepositAction], |  | ||||||
|             }; |  | ||||||
|             await executeBridgeTransferFromAndVerifyEvents( |  | ||||||
|                 accountOwner, |  | ||||||
|                 receiver, |  | ||||||
|                 constants.ZERO_AMOUNT, |  | ||||||
|                 bridgeData, |  | ||||||
|                 authorized, |  | ||||||
|             ); |  | ||||||
|         }); |  | ||||||
|         it('succeeds when calling with no accounts', async () => { |  | ||||||
|             const bridgeData = { |  | ||||||
|                 accountNumbers: [], |  | ||||||
|                 actions: [defaultDepositAction], |  | ||||||
|             }; |  | ||||||
|             await executeBridgeTransferFromAndVerifyEvents( |  | ||||||
|                 accountOwner, |  | ||||||
|                 receiver, |  | ||||||
|                 defaultAmount, |  | ||||||
|                 bridgeData, |  | ||||||
|                 authorized, |  | ||||||
|             ); |  | ||||||
|         }); |  | ||||||
|         it('succeeds when calling with no actions', async () => { |  | ||||||
|             const bridgeData = { |  | ||||||
|                 accountNumbers: [defaultAccountNumber], |  | ||||||
|                 actions: [], |  | ||||||
|             }; |  | ||||||
|             await executeBridgeTransferFromAndVerifyEvents( |  | ||||||
|                 accountOwner, |  | ||||||
|                 receiver, |  | ||||||
|                 defaultAmount, |  | ||||||
|                 bridgeData, |  | ||||||
|                 authorized, |  | ||||||
|             ); |  | ||||||
|         }); |  | ||||||
|         it('succeeds when calling `operate` with the `deposit` action and a single account', async () => { |  | ||||||
|             const bridgeData = { |  | ||||||
|                 accountNumbers: [defaultAccountNumber], |  | ||||||
|                 actions: [defaultDepositAction], |  | ||||||
|             }; |  | ||||||
|             await executeBridgeTransferFromAndVerifyEvents( |  | ||||||
|                 accountOwner, |  | ||||||
|                 receiver, |  | ||||||
|                 defaultAmount, |  | ||||||
|                 bridgeData, |  | ||||||
|                 authorized, |  | ||||||
|             ); |  | ||||||
|         }); |  | ||||||
|         it('succeeds when calling `operate` with the `deposit` action and multiple accounts', async () => { |  | ||||||
|             const bridgeData = { |  | ||||||
|                 accountNumbers: [defaultAccountNumber, defaultAccountNumber.plus(1)], |  | ||||||
|                 actions: [defaultDepositAction], |  | ||||||
|             }; |  | ||||||
|             await executeBridgeTransferFromAndVerifyEvents( |  | ||||||
|                 accountOwner, |  | ||||||
|                 receiver, |  | ||||||
|                 defaultAmount, |  | ||||||
|                 bridgeData, |  | ||||||
|                 authorized, |  | ||||||
|             ); |  | ||||||
|         }); |  | ||||||
|         it('succeeds when calling `operate` with the `withdraw` action and a single account', async () => { |  | ||||||
|             const bridgeData = { |  | ||||||
|                 accountNumbers: [defaultAccountNumber], |  | ||||||
|                 actions: [defaultWithdrawAction], |  | ||||||
|             }; |  | ||||||
|             await executeBridgeTransferFromAndVerifyEvents( |  | ||||||
|                 accountOwner, |  | ||||||
|                 receiver, |  | ||||||
|                 defaultAmount, |  | ||||||
|                 bridgeData, |  | ||||||
|                 authorized, |  | ||||||
|             ); |  | ||||||
|         }); |  | ||||||
|         it('succeeds when calling `operate` with the `withdraw` action and multiple accounts', async () => { |  | ||||||
|             const bridgeData = { |  | ||||||
|                 accountNumbers: [defaultAccountNumber, defaultAccountNumber.plus(1)], |  | ||||||
|                 actions: [defaultWithdrawAction], |  | ||||||
|             }; |  | ||||||
|             await executeBridgeTransferFromAndVerifyEvents( |  | ||||||
|                 accountOwner, |  | ||||||
|                 receiver, |  | ||||||
|                 defaultAmount, |  | ||||||
|                 bridgeData, |  | ||||||
|                 authorized, |  | ||||||
|             ); |  | ||||||
|         }); |  | ||||||
|         it('succeeds when calling `operate` with the `deposit` action and multiple accounts', async () => { |  | ||||||
|             const bridgeData = { |  | ||||||
|                 accountNumbers: [defaultAccountNumber, defaultAccountNumber.plus(1)], |  | ||||||
|                 actions: [defaultWithdrawAction, defaultDepositAction], |  | ||||||
|             }; |  | ||||||
|             await executeBridgeTransferFromAndVerifyEvents( |  | ||||||
|                 accountOwner, |  | ||||||
|                 receiver, |  | ||||||
|                 defaultAmount, |  | ||||||
|                 bridgeData, |  | ||||||
|                 authorized, |  | ||||||
|             ); |  | ||||||
|         }); |  | ||||||
|         it('succeeds when calling `operate` with multiple actions under a single account', async () => { |  | ||||||
|             const bridgeData = { |  | ||||||
|                 accountNumbers: [defaultAccountNumber], |  | ||||||
|                 actions: [defaultWithdrawAction, defaultDepositAction], |  | ||||||
|             }; |  | ||||||
|             await executeBridgeTransferFromAndVerifyEvents( |  | ||||||
|                 accountOwner, |  | ||||||
|                 receiver, |  | ||||||
|                 defaultAmount, |  | ||||||
|                 bridgeData, |  | ||||||
|                 authorized, |  | ||||||
|             ); |  | ||||||
|         }); |  | ||||||
|         it('succeeds when scaling the `amount` to deposit', async () => { |  | ||||||
|             const conversionRateNumerator = new BigNumber(1); |  | ||||||
|             const conversionRateDenominator = new BigNumber(2); |  | ||||||
|             const bridgeData = { |  | ||||||
|                 accountNumbers: [defaultAccountNumber], |  | ||||||
|                 actions: [ |  | ||||||
|                     defaultWithdrawAction, |  | ||||||
|                     { |  | ||||||
|                         ...defaultDepositAction, |  | ||||||
|                         conversionRateNumerator, |  | ||||||
|                         conversionRateDenominator, |  | ||||||
|                     }, |  | ||||||
|                 ], |  | ||||||
|             }; |  | ||||||
|             await executeBridgeTransferFromAndVerifyEvents( |  | ||||||
|                 accountOwner, |  | ||||||
|                 receiver, |  | ||||||
|                 defaultAmount, |  | ||||||
|                 bridgeData, |  | ||||||
|                 authorized, |  | ||||||
|             ); |  | ||||||
|         }); |  | ||||||
|         it('succeeds when scaling the `amount` to withdraw', async () => { |  | ||||||
|             const conversionRateNumerator = new BigNumber(1); |  | ||||||
|             const conversionRateDenominator = new BigNumber(2); |  | ||||||
|             const bridgeData = { |  | ||||||
|                 accountNumbers: [defaultAccountNumber], |  | ||||||
|                 actions: [ |  | ||||||
|                     defaultDepositAction, |  | ||||||
|                     { |  | ||||||
|                         ...defaultWithdrawAction, |  | ||||||
|                         conversionRateNumerator, |  | ||||||
|                         conversionRateDenominator, |  | ||||||
|                     }, |  | ||||||
|                 ], |  | ||||||
|             }; |  | ||||||
|             await executeBridgeTransferFromAndVerifyEvents( |  | ||||||
|                 accountOwner, |  | ||||||
|                 receiver, |  | ||||||
|                 defaultAmount, |  | ||||||
|                 bridgeData, |  | ||||||
|                 authorized, |  | ||||||
|             ); |  | ||||||
|         }); |  | ||||||
|         it('reverts if not called by the ERC20 Bridge Proxy', async () => { |  | ||||||
|             const bridgeData = { |  | ||||||
|                 accountNumbers: [defaultAccountNumber], |  | ||||||
|                 actions: [defaultDepositAction], |  | ||||||
|             }; |  | ||||||
|             const callBridgeTransferFromPromise = callBridgeTransferFrom( |  | ||||||
|                 accountOwner, |  | ||||||
|                 receiver, |  | ||||||
|                 defaultAmount, |  | ||||||
|                 bridgeData, |  | ||||||
|                 notAuthorized, |  | ||||||
|             ); |  | ||||||
|             const expectedError = RevertReason.DydxBridgeOnlyCallableByErc20BridgeProxy; |  | ||||||
|             return expect(callBridgeTransferFromPromise).to.revertWith(expectedError); |  | ||||||
|         }); |  | ||||||
|         it('should return magic bytes if call succeeds', async () => { |  | ||||||
|             const bridgeData = { |  | ||||||
|                 accountNumbers: [defaultAccountNumber], |  | ||||||
|                 actions: [defaultDepositAction], |  | ||||||
|             }; |  | ||||||
|             const returnValue = await callBridgeTransferFrom( |  | ||||||
|                 accountOwner, |  | ||||||
|                 receiver, |  | ||||||
|                 defaultAmount, |  | ||||||
|                 bridgeData, |  | ||||||
|                 authorized, |  | ||||||
|             ); |  | ||||||
|             expect(returnValue).to.equal(AssetProxyId.ERC20Bridge); |  | ||||||
|         }); |  | ||||||
|         it('should revert when `Operate` reverts', async () => { |  | ||||||
|             // Set revert flag. |  | ||||||
|             await testContract.setRevertOnOperate(true).awaitTransactionSuccessAsync(); |  | ||||||
|  |  | ||||||
|             // Execute transfer. |  | ||||||
|             const bridgeData = { |  | ||||||
|                 accountNumbers: [defaultAccountNumber], |  | ||||||
|                 actions: [defaultDepositAction], |  | ||||||
|             }; |  | ||||||
|             const tx = callBridgeTransferFrom(accountOwner, receiver, defaultAmount, bridgeData, authorized); |  | ||||||
|             const expectedError = 'TestDydxBridge/SHOULD_REVERT_ON_OPERATE'; |  | ||||||
|             return expect(tx).to.revertWith(expectedError); |  | ||||||
|         }); |  | ||||||
|         it('should revert when there is a rounding error', async () => { |  | ||||||
|             // Setup a rounding error |  | ||||||
|             const conversionRateNumerator = new BigNumber(5318); |  | ||||||
|             const conversionRateDenominator = new BigNumber(47958); |  | ||||||
|             const amount = new BigNumber(9000); |  | ||||||
|             const bridgeData = { |  | ||||||
|                 accountNumbers: [defaultAccountNumber], |  | ||||||
|                 actions: [ |  | ||||||
|                     defaultDepositAction, |  | ||||||
|                     { |  | ||||||
|                         ...defaultWithdrawAction, |  | ||||||
|                         conversionRateNumerator, |  | ||||||
|                         conversionRateDenominator, |  | ||||||
|                     }, |  | ||||||
|                 ], |  | ||||||
|             }; |  | ||||||
|  |  | ||||||
|             // Execute transfer and assert error. |  | ||||||
|             const tx = callBridgeTransferFrom(accountOwner, receiver, amount, bridgeData, authorized); |  | ||||||
|             const expectedError = new LibMathRevertErrors.RoundingError( |  | ||||||
|                 conversionRateNumerator, |  | ||||||
|                 conversionRateDenominator, |  | ||||||
|                 amount, |  | ||||||
|             ); |  | ||||||
|             return expect(tx).to.revertWith(expectedError); |  | ||||||
|         }); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     describe('ERC20BridgeProxy.transferFrom()', () => { |  | ||||||
|         const bridgeData = { |  | ||||||
|             accountNumbers: [defaultAccountNumber], |  | ||||||
|             actions: [defaultWithdrawAction], |  | ||||||
|         }; |  | ||||||
|         let assetData: string; |  | ||||||
|  |  | ||||||
|         before(async () => { |  | ||||||
|             const testTokenAddress = await testContract.getTestToken().callAsync(); |  | ||||||
|             assetData = assetDataEncoder |  | ||||||
|                 .ERC20Bridge(testTokenAddress, testContract.address, dydxBridgeDataEncoder.encode({ bridgeData })) |  | ||||||
|                 .getABIEncodedTransactionData(); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('should succeed if `bridgeTransferFrom` succeeds', async () => { |  | ||||||
|             await testProxyContract |  | ||||||
|                 .transferFrom(assetData, accountOwner, receiver, defaultAmount) |  | ||||||
|                 .awaitTransactionSuccessAsync({ from: authorized }); |  | ||||||
|         }); |  | ||||||
|         it('should revert if `bridgeTransferFrom` reverts', async () => { |  | ||||||
|             // Set revert flag. |  | ||||||
|             await testContract.setRevertOnOperate(true).awaitTransactionSuccessAsync(); |  | ||||||
|             const tx = testProxyContract |  | ||||||
|                 .transferFrom(assetData, accountOwner, receiver, defaultAmount) |  | ||||||
|                 .awaitTransactionSuccessAsync({ from: authorized }); |  | ||||||
|             const expectedError = 'TestDydxBridge/SHOULD_REVERT_ON_OPERATE'; |  | ||||||
|             return expect(tx).to.revertWith(expectedError); |  | ||||||
|         }); |  | ||||||
|     }); |  | ||||||
| }); |  | ||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,287 +0,0 @@ | |||||||
| import { |  | ||||||
|     blockchainTests, |  | ||||||
|     constants, |  | ||||||
|     expect, |  | ||||||
|     getRandomInteger, |  | ||||||
|     Numberish, |  | ||||||
|     randomAddress, |  | ||||||
| } from '@0x/contracts-test-utils'; |  | ||||||
| import { AuthorizableRevertErrors } from '@0x/contracts-utils'; |  | ||||||
| import { AssetProxyId } from '@0x/types'; |  | ||||||
| import { AbiEncoder, BigNumber, hexUtils, StringRevertError } from '@0x/utils'; |  | ||||||
| import { DecodedLogs } from 'ethereum-types'; |  | ||||||
| import * as _ from 'lodash'; |  | ||||||
|  |  | ||||||
| import { artifacts } from './artifacts'; |  | ||||||
|  |  | ||||||
| import { ERC20BridgeProxyContract, TestERC20BridgeContract } from './wrappers'; |  | ||||||
|  |  | ||||||
| blockchainTests.resets('ERC20BridgeProxy unit tests', env => { |  | ||||||
|     const PROXY_ID = AssetProxyId.ERC20Bridge; |  | ||||||
|     const BRIDGE_SUCCESS_RETURN_DATA = hexUtils.rightPad(PROXY_ID); |  | ||||||
|     let owner: string; |  | ||||||
|     let badCaller: string; |  | ||||||
|     let assetProxy: ERC20BridgeProxyContract; |  | ||||||
|     let bridgeContract: TestERC20BridgeContract; |  | ||||||
|     let testTokenAddress: string; |  | ||||||
|  |  | ||||||
|     before(async () => { |  | ||||||
|         [owner, badCaller] = await env.getAccountAddressesAsync(); |  | ||||||
|         assetProxy = await ERC20BridgeProxyContract.deployFrom0xArtifactAsync( |  | ||||||
|             artifacts.ERC20BridgeProxy, |  | ||||||
|             env.provider, |  | ||||||
|             env.txDefaults, |  | ||||||
|             artifacts, |  | ||||||
|         ); |  | ||||||
|         bridgeContract = await TestERC20BridgeContract.deployFrom0xArtifactAsync( |  | ||||||
|             artifacts.TestERC20Bridge, |  | ||||||
|             env.provider, |  | ||||||
|             env.txDefaults, |  | ||||||
|             artifacts, |  | ||||||
|         ); |  | ||||||
|         testTokenAddress = await bridgeContract.testToken().callAsync(); |  | ||||||
|         await assetProxy.addAuthorizedAddress(owner).awaitTransactionSuccessAsync(); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     interface AssetDataOpts { |  | ||||||
|         tokenAddress: string; |  | ||||||
|         bridgeAddress: string; |  | ||||||
|         bridgeData: BridgeDataOpts; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     interface BridgeDataOpts { |  | ||||||
|         transferAmount: Numberish; |  | ||||||
|         revertError?: string; |  | ||||||
|         returnData: string; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function createAssetData(opts?: Partial<AssetDataOpts>): AssetDataOpts { |  | ||||||
|         return _.merge( |  | ||||||
|             { |  | ||||||
|                 tokenAddress: testTokenAddress, |  | ||||||
|                 bridgeAddress: bridgeContract.address, |  | ||||||
|                 bridgeData: createBridgeData(), |  | ||||||
|             }, |  | ||||||
|             opts, |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function createBridgeData(opts?: Partial<BridgeDataOpts>): BridgeDataOpts { |  | ||||||
|         return _.merge( |  | ||||||
|             { |  | ||||||
|                 transferAmount: constants.ZERO_AMOUNT, |  | ||||||
|                 returnData: BRIDGE_SUCCESS_RETURN_DATA, |  | ||||||
|             }, |  | ||||||
|             opts, |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function encodeAssetData(opts: AssetDataOpts): string { |  | ||||||
|         const encoder = AbiEncoder.createMethod('ERC20BridgeProxy', [ |  | ||||||
|             { name: 'tokenAddress', type: 'address' }, |  | ||||||
|             { name: 'bridgeAddress', type: 'address' }, |  | ||||||
|             { name: 'bridgeData', type: 'bytes' }, |  | ||||||
|         ]); |  | ||||||
|         return encoder.encode([opts.tokenAddress, opts.bridgeAddress, encodeBridgeData(opts.bridgeData)]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function encodeBridgeData(opts: BridgeDataOpts): string { |  | ||||||
|         const encoder = AbiEncoder.create([ |  | ||||||
|             { name: 'transferAmount', type: 'int256' }, |  | ||||||
|             { name: 'revertData', type: 'bytes' }, |  | ||||||
|             { name: 'returnData', type: 'bytes' }, |  | ||||||
|         ]); |  | ||||||
|         const revertErrorBytes = |  | ||||||
|             opts.revertError !== undefined ? new StringRevertError(opts.revertError).encode() : '0x'; |  | ||||||
|         return encoder.encode([new BigNumber(opts.transferAmount), revertErrorBytes, opts.returnData]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     async function setTestTokenBalanceAsync(_owner: string, balance: Numberish): Promise<void> { |  | ||||||
|         await bridgeContract.setTestTokenBalance(_owner, new BigNumber(balance)).awaitTransactionSuccessAsync(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     describe('transferFrom()', () => { |  | ||||||
|         interface TransferFromOpts { |  | ||||||
|             assetData: AssetDataOpts; |  | ||||||
|             from: string; |  | ||||||
|             to: string; |  | ||||||
|             amount: Numberish; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         function createTransferFromOpts(opts?: Partial<TransferFromOpts>): TransferFromOpts { |  | ||||||
|             const transferAmount = _.get(opts, ['amount'], getRandomInteger(1, 100e18)) as BigNumber; |  | ||||||
|             return _.merge( |  | ||||||
|                 { |  | ||||||
|                     assetData: createAssetData({ |  | ||||||
|                         bridgeData: createBridgeData({ |  | ||||||
|                             transferAmount, |  | ||||||
|                         }), |  | ||||||
|                     }), |  | ||||||
|                     from: randomAddress(), |  | ||||||
|                     to: randomAddress(), |  | ||||||
|                     amount: transferAmount, |  | ||||||
|                 }, |  | ||||||
|                 opts, |  | ||||||
|             ); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         async function transferFromAsync(opts?: Partial<TransferFromOpts>, caller?: string): Promise<DecodedLogs> { |  | ||||||
|             const _opts = createTransferFromOpts(opts); |  | ||||||
|             const { logs } = await assetProxy |  | ||||||
|                 .transferFrom(encodeAssetData(_opts.assetData), _opts.from, _opts.to, new BigNumber(_opts.amount)) |  | ||||||
|                 .awaitTransactionSuccessAsync({ from: caller }); |  | ||||||
|             return (logs as any) as DecodedLogs; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         it('succeeds if the bridge succeeds and balance increases by `amount`', async () => { |  | ||||||
|             const tx = transferFromAsync(); |  | ||||||
|             return expect(tx).to.be.fulfilled(''); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('succeeds if balance increases more than `amount`', async () => { |  | ||||||
|             const amount = getRandomInteger(1, 100e18); |  | ||||||
|             const tx = transferFromAsync({ |  | ||||||
|                 amount, |  | ||||||
|                 assetData: createAssetData({ |  | ||||||
|                     bridgeData: createBridgeData({ |  | ||||||
|                         transferAmount: amount.plus(1), |  | ||||||
|                     }), |  | ||||||
|                 }), |  | ||||||
|             }); |  | ||||||
|             return expect(tx).to.be.fulfilled(''); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('passes the correct arguments to the bridge contract', async () => { |  | ||||||
|             const opts = createTransferFromOpts(); |  | ||||||
|             const logs = await transferFromAsync(opts); |  | ||||||
|             expect(logs.length).to.eq(1); |  | ||||||
|             const args = logs[0].args; |  | ||||||
|             expect(args.tokenAddress).to.eq(opts.assetData.tokenAddress); |  | ||||||
|             expect(args.from).to.eq(opts.from); |  | ||||||
|             expect(args.to).to.eq(opts.to); |  | ||||||
|             expect(args.amount).to.bignumber.eq(opts.amount); |  | ||||||
|             expect(args.bridgeData).to.eq(encodeBridgeData(opts.assetData.bridgeData)); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('fails if not called by an authorized address', async () => { |  | ||||||
|             const tx = transferFromAsync({}, badCaller); |  | ||||||
|             return expect(tx).to.revertWith(new AuthorizableRevertErrors.SenderNotAuthorizedError(badCaller)); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('fails if asset data is truncated', async () => { |  | ||||||
|             const opts = createTransferFromOpts(); |  | ||||||
|             const truncatedAssetData = hexUtils.slice(encodeAssetData(opts.assetData), 0, -1); |  | ||||||
|             const tx = assetProxy |  | ||||||
|                 .transferFrom(truncatedAssetData, opts.from, opts.to, new BigNumber(opts.amount)) |  | ||||||
|                 .awaitTransactionSuccessAsync(); |  | ||||||
|             return expect(tx).to.be.rejected(); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('fails if bridge returns nothing', async () => { |  | ||||||
|             const tx = transferFromAsync({ |  | ||||||
|                 assetData: createAssetData({ |  | ||||||
|                     bridgeData: createBridgeData({ |  | ||||||
|                         returnData: '0x', |  | ||||||
|                     }), |  | ||||||
|                 }), |  | ||||||
|             }); |  | ||||||
|             // This will actually revert when the AP tries to decode the return |  | ||||||
|             // value. |  | ||||||
|             return expect(tx).to.be.rejected(); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('fails if bridge returns true', async () => { |  | ||||||
|             const tx = transferFromAsync({ |  | ||||||
|                 assetData: createAssetData({ |  | ||||||
|                     bridgeData: createBridgeData({ |  | ||||||
|                         returnData: hexUtils.leftPad('0x1'), |  | ||||||
|                     }), |  | ||||||
|                 }), |  | ||||||
|             }); |  | ||||||
|             // This will actually revert when the AP tries to decode the return |  | ||||||
|             // value. |  | ||||||
|             return expect(tx).to.be.rejected(); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('fails if bridge returns 0x1', async () => { |  | ||||||
|             const tx = transferFromAsync({ |  | ||||||
|                 assetData: createAssetData({ |  | ||||||
|                     bridgeData: createBridgeData({ |  | ||||||
|                         returnData: hexUtils.rightPad('0x1'), |  | ||||||
|                     }), |  | ||||||
|                 }), |  | ||||||
|             }); |  | ||||||
|             return expect(tx).to.revertWith('BRIDGE_FAILED'); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('fails if bridge is an EOA', async () => { |  | ||||||
|             const tx = transferFromAsync({ |  | ||||||
|                 assetData: createAssetData({ |  | ||||||
|                     bridgeAddress: randomAddress(), |  | ||||||
|                 }), |  | ||||||
|             }); |  | ||||||
|             // This will actually revert when the AP tries to decode the return |  | ||||||
|             // value. |  | ||||||
|             return expect(tx).to.be.rejected(); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('fails if bridge reverts', async () => { |  | ||||||
|             const revertError = 'FOOBAR'; |  | ||||||
|             const tx = transferFromAsync({ |  | ||||||
|                 assetData: createAssetData({ |  | ||||||
|                     bridgeData: createBridgeData({ |  | ||||||
|                         revertError, |  | ||||||
|                     }), |  | ||||||
|                 }), |  | ||||||
|             }); |  | ||||||
|             return expect(tx).to.revertWith(revertError); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('fails if balance of `to` increases by less than `amount`', async () => { |  | ||||||
|             const amount = getRandomInteger(1, 100e18); |  | ||||||
|             const tx = transferFromAsync({ |  | ||||||
|                 amount, |  | ||||||
|                 assetData: createAssetData({ |  | ||||||
|                     bridgeData: createBridgeData({ |  | ||||||
|                         transferAmount: amount.minus(1), |  | ||||||
|                     }), |  | ||||||
|                 }), |  | ||||||
|             }); |  | ||||||
|             return expect(tx).to.revertWith('BRIDGE_UNDERPAY'); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('fails if balance of `to` decreases', async () => { |  | ||||||
|             const toAddress = randomAddress(); |  | ||||||
|             await setTestTokenBalanceAsync(toAddress, 1e18); |  | ||||||
|             const tx = transferFromAsync({ |  | ||||||
|                 to: toAddress, |  | ||||||
|                 assetData: createAssetData({ |  | ||||||
|                     bridgeData: createBridgeData({ |  | ||||||
|                         transferAmount: -1, |  | ||||||
|                     }), |  | ||||||
|                 }), |  | ||||||
|             }); |  | ||||||
|             return expect(tx).to.revertWith('BRIDGE_UNDERPAY'); |  | ||||||
|         }); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     describe('balanceOf()', () => { |  | ||||||
|         it('retrieves the balance of the encoded token', async () => { |  | ||||||
|             const _owner = randomAddress(); |  | ||||||
|             const balance = getRandomInteger(1, 100e18); |  | ||||||
|             await bridgeContract.setTestTokenBalance(_owner, balance).awaitTransactionSuccessAsync(); |  | ||||||
|             const assetData = createAssetData({ |  | ||||||
|                 tokenAddress: testTokenAddress, |  | ||||||
|             }); |  | ||||||
|             const actualBalance = await assetProxy.balanceOf(encodeAssetData(assetData), _owner).callAsync(); |  | ||||||
|             expect(actualBalance).to.bignumber.eq(balance); |  | ||||||
|         }); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     describe('getProxyId()', () => { |  | ||||||
|         it('returns the correct proxy ID', async () => { |  | ||||||
|             const proxyId = await assetProxy.getProxyId().callAsync(); |  | ||||||
|             expect(proxyId).to.eq(PROXY_ID); |  | ||||||
|         }); |  | ||||||
|     }); |  | ||||||
| }); |  | ||||||
| @@ -1,191 +0,0 @@ | |||||||
| import { |  | ||||||
|     blockchainTests, |  | ||||||
|     constants, |  | ||||||
|     expect, |  | ||||||
|     filterLogsToArguments, |  | ||||||
|     getRandomInteger, |  | ||||||
|     Numberish, |  | ||||||
|     randomAddress, |  | ||||||
| } from '@0x/contracts-test-utils'; |  | ||||||
| import { AssetProxyId } from '@0x/types'; |  | ||||||
| import { BigNumber, hexUtils, RawRevertError } from '@0x/utils'; |  | ||||||
| import { DecodedLogs } from 'ethereum-types'; |  | ||||||
| import * as _ from 'lodash'; |  | ||||||
|  |  | ||||||
| import { artifacts } from './artifacts'; |  | ||||||
|  |  | ||||||
| import { |  | ||||||
|     TestEth2DaiBridgeContract, |  | ||||||
|     TestEth2DaiBridgeEvents, |  | ||||||
|     TestEth2DaiBridgeSellAllAmountEventArgs, |  | ||||||
|     TestEth2DaiBridgeTokenApproveEventArgs, |  | ||||||
|     TestEth2DaiBridgeTokenTransferEventArgs, |  | ||||||
| } from './wrappers'; |  | ||||||
|  |  | ||||||
| blockchainTests.resets('Eth2DaiBridge unit tests', env => { |  | ||||||
|     let testContract: TestEth2DaiBridgeContract; |  | ||||||
|  |  | ||||||
|     before(async () => { |  | ||||||
|         testContract = await TestEth2DaiBridgeContract.deployFrom0xArtifactAsync( |  | ||||||
|             artifacts.TestEth2DaiBridge, |  | ||||||
|             env.provider, |  | ||||||
|             env.txDefaults, |  | ||||||
|             artifacts, |  | ||||||
|         ); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     describe('isValidSignature()', () => { |  | ||||||
|         it('returns success bytes', async () => { |  | ||||||
|             const LEGACY_WALLET_MAGIC_VALUE = '0xb0671381'; |  | ||||||
|             const result = await testContract |  | ||||||
|                 .isValidSignature(hexUtils.random(), hexUtils.random(_.random(0, 32))) |  | ||||||
|                 .callAsync(); |  | ||||||
|             expect(result).to.eq(LEGACY_WALLET_MAGIC_VALUE); |  | ||||||
|         }); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     describe('bridgeTransferFrom()', () => { |  | ||||||
|         interface WithdrawToOpts { |  | ||||||
|             toTokenAddress?: string; |  | ||||||
|             fromTokenAddress?: string; |  | ||||||
|             toAddress: string; |  | ||||||
|             amount: Numberish; |  | ||||||
|             fromTokenBalance: Numberish; |  | ||||||
|             revertReason: string; |  | ||||||
|             fillAmount: Numberish; |  | ||||||
|             toTokentransferRevertReason: string; |  | ||||||
|             toTokenTransferReturnData: string; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         interface WithdrawToResult { |  | ||||||
|             opts: WithdrawToOpts; |  | ||||||
|             result: string; |  | ||||||
|             logs: DecodedLogs; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         function createWithdrawToOpts(opts?: Partial<WithdrawToOpts>): WithdrawToOpts { |  | ||||||
|             return { |  | ||||||
|                 toAddress: randomAddress(), |  | ||||||
|                 amount: getRandomInteger(1, 100e18), |  | ||||||
|                 revertReason: '', |  | ||||||
|                 fillAmount: getRandomInteger(1, 100e18), |  | ||||||
|                 fromTokenBalance: getRandomInteger(1, 100e18), |  | ||||||
|                 toTokentransferRevertReason: '', |  | ||||||
|                 toTokenTransferReturnData: hexUtils.leftPad(1), |  | ||||||
|                 ...opts, |  | ||||||
|             }; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         async function withdrawToAsync(opts?: Partial<WithdrawToOpts>): Promise<WithdrawToResult> { |  | ||||||
|             const _opts = createWithdrawToOpts(opts); |  | ||||||
|             // Set the fill behavior. |  | ||||||
|             await testContract |  | ||||||
|                 .setFillBehavior(_opts.revertReason, new BigNumber(_opts.fillAmount)) |  | ||||||
|                 .awaitTransactionSuccessAsync(); |  | ||||||
|             // Create tokens and balances. |  | ||||||
|             if (_opts.fromTokenAddress === undefined) { |  | ||||||
|                 const createTokenFn = testContract.createToken(new BigNumber(_opts.fromTokenBalance)); |  | ||||||
|                 _opts.fromTokenAddress = await createTokenFn.callAsync(); |  | ||||||
|                 await createTokenFn.awaitTransactionSuccessAsync(); |  | ||||||
|             } |  | ||||||
|             if (_opts.toTokenAddress === undefined) { |  | ||||||
|                 const createTokenFn = testContract.createToken(constants.ZERO_AMOUNT); |  | ||||||
|                 _opts.toTokenAddress = await createTokenFn.callAsync(); |  | ||||||
|                 await createTokenFn.awaitTransactionSuccessAsync(); |  | ||||||
|             } |  | ||||||
|             // Set the transfer behavior of `toTokenAddress`. |  | ||||||
|             await testContract |  | ||||||
|                 .setTransferBehavior( |  | ||||||
|                     _opts.toTokenAddress, |  | ||||||
|                     _opts.toTokentransferRevertReason, |  | ||||||
|                     _opts.toTokenTransferReturnData, |  | ||||||
|                 ) |  | ||||||
|                 .awaitTransactionSuccessAsync(); |  | ||||||
|             // Call bridgeTransferFrom(). |  | ||||||
|             const bridgeTransferFromFn = testContract.bridgeTransferFrom( |  | ||||||
|                 // "to" token address |  | ||||||
|                 _opts.toTokenAddress, |  | ||||||
|                 // Random from address. |  | ||||||
|                 randomAddress(), |  | ||||||
|                 // To address. |  | ||||||
|                 _opts.toAddress, |  | ||||||
|                 new BigNumber(_opts.amount), |  | ||||||
|                 // ABI-encode the "from" token address as the bridge data. |  | ||||||
|                 hexUtils.leftPad(_opts.fromTokenAddress as string), |  | ||||||
|             ); |  | ||||||
|             const result = await bridgeTransferFromFn.callAsync(); |  | ||||||
|             const { logs } = await bridgeTransferFromFn.awaitTransactionSuccessAsync(); |  | ||||||
|             return { |  | ||||||
|                 opts: _opts, |  | ||||||
|                 result, |  | ||||||
|                 logs: (logs as any) as DecodedLogs, |  | ||||||
|             }; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         it('returns magic bytes on success', async () => { |  | ||||||
|             const BRIDGE_SUCCESS_RETURN_DATA = AssetProxyId.ERC20Bridge; |  | ||||||
|             const { result } = await withdrawToAsync(); |  | ||||||
|             expect(result).to.eq(BRIDGE_SUCCESS_RETURN_DATA); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('calls `Eth2Dai.sellAllAmount()`', async () => { |  | ||||||
|             const { opts, logs } = await withdrawToAsync(); |  | ||||||
|             const transfers = filterLogsToArguments<TestEth2DaiBridgeSellAllAmountEventArgs>( |  | ||||||
|                 logs, |  | ||||||
|                 TestEth2DaiBridgeEvents.SellAllAmount, |  | ||||||
|             ); |  | ||||||
|             expect(transfers.length).to.eq(1); |  | ||||||
|             expect(transfers[0].sellToken).to.eq(opts.fromTokenAddress); |  | ||||||
|             expect(transfers[0].buyToken).to.eq(opts.toTokenAddress); |  | ||||||
|             expect(transfers[0].sellTokenAmount).to.bignumber.eq(opts.fromTokenBalance); |  | ||||||
|             expect(transfers[0].minimumFillAmount).to.bignumber.eq(opts.amount); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('sets an unlimited allowance on the `fromTokenAddress` token', async () => { |  | ||||||
|             const { opts, logs } = await withdrawToAsync(); |  | ||||||
|             const approvals = filterLogsToArguments<TestEth2DaiBridgeTokenApproveEventArgs>( |  | ||||||
|                 logs, |  | ||||||
|                 TestEth2DaiBridgeEvents.TokenApprove, |  | ||||||
|             ); |  | ||||||
|             expect(approvals.length).to.eq(1); |  | ||||||
|             expect(approvals[0].token).to.eq(opts.fromTokenAddress); |  | ||||||
|             expect(approvals[0].spender).to.eq(testContract.address); |  | ||||||
|             expect(approvals[0].allowance).to.bignumber.eq(constants.MAX_UINT256); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('transfers filled amount to `to`', async () => { |  | ||||||
|             const { opts, logs } = await withdrawToAsync(); |  | ||||||
|             const transfers = filterLogsToArguments<TestEth2DaiBridgeTokenTransferEventArgs>( |  | ||||||
|                 logs, |  | ||||||
|                 TestEth2DaiBridgeEvents.TokenTransfer, |  | ||||||
|             ); |  | ||||||
|             expect(transfers.length).to.eq(1); |  | ||||||
|             expect(transfers[0].token).to.eq(opts.toTokenAddress); |  | ||||||
|             expect(transfers[0].from).to.eq(testContract.address); |  | ||||||
|             expect(transfers[0].to).to.eq(opts.toAddress); |  | ||||||
|             expect(transfers[0].amount).to.bignumber.eq(opts.fillAmount); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('fails if `Eth2Dai.sellAllAmount()` reverts', async () => { |  | ||||||
|             const opts = createWithdrawToOpts({ revertReason: 'FOOBAR' }); |  | ||||||
|             const tx = withdrawToAsync(opts); |  | ||||||
|             return expect(tx).to.revertWith(opts.revertReason); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('fails if `toTokenAddress.transfer()` reverts', async () => { |  | ||||||
|             const opts = createWithdrawToOpts({ toTokentransferRevertReason: 'FOOBAR' }); |  | ||||||
|             const tx = withdrawToAsync(opts); |  | ||||||
|             return expect(tx).to.revertWith(opts.toTokentransferRevertReason); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('fails if `toTokenAddress.transfer()` returns false', async () => { |  | ||||||
|             const opts = createWithdrawToOpts({ toTokenTransferReturnData: hexUtils.leftPad(0) }); |  | ||||||
|             const tx = withdrawToAsync(opts); |  | ||||||
|             return expect(tx).to.revertWith(new RawRevertError(hexUtils.leftPad(0))); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('succeeds if `toTokenAddress.transfer()` returns true', async () => { |  | ||||||
|             await withdrawToAsync({ toTokenTransferReturnData: hexUtils.leftPad(1) }); |  | ||||||
|         }); |  | ||||||
|     }); |  | ||||||
| }); |  | ||||||
| @@ -1,19 +0,0 @@ | |||||||
| import { env, EnvVars } from '@0x/dev-utils'; |  | ||||||
|  |  | ||||||
| import { coverage, profiler, provider } from '@0x/contracts-test-utils'; |  | ||||||
| import { providerUtils } from '@0x/utils'; |  | ||||||
|  |  | ||||||
| before('start web3 provider', () => { |  | ||||||
|     providerUtils.startProviderEngine(provider); |  | ||||||
| }); |  | ||||||
| after('generate coverage report', async () => { |  | ||||||
|     if (env.parseBoolean(EnvVars.SolidityCoverage)) { |  | ||||||
|         const coverageSubprovider = coverage.getCoverageSubproviderSingleton(); |  | ||||||
|         await coverageSubprovider.writeCoverageAsync(); |  | ||||||
|     } |  | ||||||
|     if (env.parseBoolean(EnvVars.SolidityProfiler)) { |  | ||||||
|         const profilerSubprovider = profiler.getProfilerSubproviderSingleton(); |  | ||||||
|         await profilerSubprovider.writeProfilerOutputAsync(); |  | ||||||
|     } |  | ||||||
|     provider.stop(); |  | ||||||
| }); |  | ||||||
| @@ -1,284 +0,0 @@ | |||||||
| import { |  | ||||||
|     blockchainTests, |  | ||||||
|     constants, |  | ||||||
|     expect, |  | ||||||
|     getRandomInteger, |  | ||||||
|     getRandomPortion, |  | ||||||
|     randomAddress, |  | ||||||
|     verifyEventsFromLogs, |  | ||||||
| } from '@0x/contracts-test-utils'; |  | ||||||
| import { AssetProxyId } from '@0x/types'; |  | ||||||
| import { BigNumber, hexUtils } from '@0x/utils'; |  | ||||||
| import { DecodedLogs } from 'ethereum-types'; |  | ||||||
| import * as _ from 'lodash'; |  | ||||||
|  |  | ||||||
| import { artifacts } from './artifacts'; |  | ||||||
|  |  | ||||||
| import { TestKyberBridgeContract, TestKyberBridgeEvents } from './wrappers'; |  | ||||||
|  |  | ||||||
| // TODO(dorothy-zbornak): Tests need to be updated. |  | ||||||
| blockchainTests.resets.skip('KyberBridge unit tests', env => { |  | ||||||
|     const KYBER_ETH_ADDRESS = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee'; |  | ||||||
|     const FROM_TOKEN_DECIMALS = 6; |  | ||||||
|     const TO_TOKEN_DECIMALS = 18; |  | ||||||
|     const FROM_TOKEN_BASE = new BigNumber(10).pow(FROM_TOKEN_DECIMALS); |  | ||||||
|     const TO_TOKEN_BASE = new BigNumber(10).pow(TO_TOKEN_DECIMALS); |  | ||||||
|     const WETH_BASE = new BigNumber(10).pow(18); |  | ||||||
|     const KYBER_RATE_BASE = WETH_BASE; |  | ||||||
|     let testContract: TestKyberBridgeContract; |  | ||||||
|  |  | ||||||
|     before(async () => { |  | ||||||
|         testContract = await TestKyberBridgeContract.deployFrom0xArtifactAsync( |  | ||||||
|             artifacts.TestKyberBridge, |  | ||||||
|             env.provider, |  | ||||||
|             env.txDefaults, |  | ||||||
|             artifacts, |  | ||||||
|         ); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     describe('isValidSignature()', () => { |  | ||||||
|         it('returns success bytes', async () => { |  | ||||||
|             const LEGACY_WALLET_MAGIC_VALUE = '0xb0671381'; |  | ||||||
|             const result = await testContract |  | ||||||
|                 .isValidSignature(hexUtils.random(), hexUtils.random(_.random(0, 32))) |  | ||||||
|                 .callAsync(); |  | ||||||
|             expect(result).to.eq(LEGACY_WALLET_MAGIC_VALUE); |  | ||||||
|         }); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     describe('bridgeTransferFrom()', () => { |  | ||||||
|         let fromTokenAddress: string; |  | ||||||
|         let toTokenAddress: string; |  | ||||||
|         let wethAddress: string; |  | ||||||
|  |  | ||||||
|         before(async () => { |  | ||||||
|             wethAddress = await testContract.weth().callAsync(); |  | ||||||
|             fromTokenAddress = await testContract.createToken(FROM_TOKEN_DECIMALS).callAsync(); |  | ||||||
|             await testContract.createToken(FROM_TOKEN_DECIMALS).awaitTransactionSuccessAsync(); |  | ||||||
|             toTokenAddress = await testContract.createToken(TO_TOKEN_DECIMALS).callAsync(); |  | ||||||
|             await testContract.createToken(TO_TOKEN_DECIMALS).awaitTransactionSuccessAsync(); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         const STATIC_KYBER_TRADE_ARGS = { |  | ||||||
|             maxBuyTokenAmount: constants.MAX_UINT256, |  | ||||||
|             walletId: constants.NULL_ADDRESS, |  | ||||||
|         }; |  | ||||||
|  |  | ||||||
|         interface TransferFromOpts { |  | ||||||
|             toTokenAddress: string; |  | ||||||
|             fromTokenAddress: string; |  | ||||||
|             toAddress: string; |  | ||||||
|             // Amount to pass into `bridgeTransferFrom()` |  | ||||||
|             amount: BigNumber; |  | ||||||
|             // Amount to convert in `trade()`. |  | ||||||
|             fillAmount: BigNumber; |  | ||||||
|             // Token balance of the bridge. |  | ||||||
|             fromTokenBalance: BigNumber; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         interface TransferFromResult { |  | ||||||
|             opts: TransferFromOpts; |  | ||||||
|             result: string; |  | ||||||
|             logs: DecodedLogs; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         function createTransferFromOpts(opts?: Partial<TransferFromOpts>): TransferFromOpts { |  | ||||||
|             const amount = getRandomInteger(1, TO_TOKEN_BASE.times(100)); |  | ||||||
|             return { |  | ||||||
|                 fromTokenAddress, |  | ||||||
|                 toTokenAddress, |  | ||||||
|                 amount, |  | ||||||
|                 toAddress: randomAddress(), |  | ||||||
|                 fillAmount: getRandomPortion(amount), |  | ||||||
|                 fromTokenBalance: getRandomInteger(1, FROM_TOKEN_BASE.times(100)), |  | ||||||
|                 ...opts, |  | ||||||
|             }; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         async function withdrawToAsync(opts?: Partial<TransferFromOpts>): Promise<TransferFromResult> { |  | ||||||
|             const _opts = createTransferFromOpts(opts); |  | ||||||
|             // Fund the contract with input tokens. |  | ||||||
|             await testContract |  | ||||||
|                 .grantTokensTo(_opts.fromTokenAddress, testContract.address, _opts.fromTokenBalance) |  | ||||||
|                 .awaitTransactionSuccessAsync({ value: _opts.fromTokenBalance }); |  | ||||||
|             // Fund the contract with output tokens. |  | ||||||
|             await testContract.setNextFillAmount(_opts.fillAmount).awaitTransactionSuccessAsync({ |  | ||||||
|                 value: _opts.toTokenAddress === wethAddress ? _opts.fillAmount : constants.ZERO_AMOUNT, |  | ||||||
|             }); |  | ||||||
|             // Call bridgeTransferFrom(). |  | ||||||
|             const bridgeTransferFromFn = testContract.bridgeTransferFrom( |  | ||||||
|                 // Output token |  | ||||||
|                 _opts.toTokenAddress, |  | ||||||
|                 // Random maker address. |  | ||||||
|                 randomAddress(), |  | ||||||
|                 // Recipient address. |  | ||||||
|                 _opts.toAddress, |  | ||||||
|                 // Transfer amount. |  | ||||||
|                 _opts.amount, |  | ||||||
|                 // ABI-encode the input token address as the bridge data. |  | ||||||
|                 hexUtils.concat(hexUtils.leftPad(_opts.fromTokenAddress), hexUtils.leftPad(32), hexUtils.leftPad(0)), |  | ||||||
|             ); |  | ||||||
|             const result = await bridgeTransferFromFn.callAsync(); |  | ||||||
|             const { logs } = await bridgeTransferFromFn.awaitTransactionSuccessAsync(); |  | ||||||
|             return { |  | ||||||
|                 opts: _opts, |  | ||||||
|                 result, |  | ||||||
|                 logs: (logs as any) as DecodedLogs, |  | ||||||
|             }; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         function getMinimumConversionRate(opts: TransferFromOpts): BigNumber { |  | ||||||
|             const fromBase = opts.fromTokenAddress === wethAddress ? WETH_BASE : FROM_TOKEN_BASE; |  | ||||||
|             const toBase = opts.toTokenAddress === wethAddress ? WETH_BASE : TO_TOKEN_BASE; |  | ||||||
|             return opts.amount |  | ||||||
|                 .div(toBase) |  | ||||||
|                 .div(opts.fromTokenBalance.div(fromBase)) |  | ||||||
|                 .times(KYBER_RATE_BASE) |  | ||||||
|                 .integerValue(BigNumber.ROUND_DOWN); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         it('returns magic bytes on success', async () => { |  | ||||||
|             const BRIDGE_SUCCESS_RETURN_DATA = AssetProxyId.ERC20Bridge; |  | ||||||
|             const { result } = await withdrawToAsync(); |  | ||||||
|             expect(result).to.eq(BRIDGE_SUCCESS_RETURN_DATA); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('can trade token -> token', async () => { |  | ||||||
|             const { opts, logs } = await withdrawToAsync(); |  | ||||||
|             verifyEventsFromLogs( |  | ||||||
|                 logs, |  | ||||||
|                 [ |  | ||||||
|                     { |  | ||||||
|                         sellTokenAddress: opts.fromTokenAddress, |  | ||||||
|                         buyTokenAddress: opts.toTokenAddress, |  | ||||||
|                         sellAmount: opts.fromTokenBalance, |  | ||||||
|                         recipientAddress: opts.toAddress, |  | ||||||
|                         minConversionRate: getMinimumConversionRate(opts), |  | ||||||
|                         msgValue: constants.ZERO_AMOUNT, |  | ||||||
|                         ...STATIC_KYBER_TRADE_ARGS, |  | ||||||
|                     }, |  | ||||||
|                 ], |  | ||||||
|                 TestKyberBridgeEvents.KyberBridgeTrade, |  | ||||||
|             ); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('can trade token -> ETH', async () => { |  | ||||||
|             const { opts, logs } = await withdrawToAsync({ |  | ||||||
|                 toTokenAddress: wethAddress, |  | ||||||
|             }); |  | ||||||
|             verifyEventsFromLogs( |  | ||||||
|                 logs, |  | ||||||
|                 [ |  | ||||||
|                     { |  | ||||||
|                         sellTokenAddress: opts.fromTokenAddress, |  | ||||||
|                         buyTokenAddress: KYBER_ETH_ADDRESS, |  | ||||||
|                         sellAmount: opts.fromTokenBalance, |  | ||||||
|                         recipientAddress: testContract.address, |  | ||||||
|                         minConversionRate: getMinimumConversionRate(opts), |  | ||||||
|                         msgValue: constants.ZERO_AMOUNT, |  | ||||||
|                         ...STATIC_KYBER_TRADE_ARGS, |  | ||||||
|                     }, |  | ||||||
|                 ], |  | ||||||
|                 TestKyberBridgeEvents.KyberBridgeTrade, |  | ||||||
|             ); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('can trade ETH -> token', async () => { |  | ||||||
|             const { opts, logs } = await withdrawToAsync({ |  | ||||||
|                 fromTokenAddress: wethAddress, |  | ||||||
|             }); |  | ||||||
|             verifyEventsFromLogs( |  | ||||||
|                 logs, |  | ||||||
|                 [ |  | ||||||
|                     { |  | ||||||
|                         sellTokenAddress: KYBER_ETH_ADDRESS, |  | ||||||
|                         buyTokenAddress: opts.toTokenAddress, |  | ||||||
|                         sellAmount: opts.fromTokenBalance, |  | ||||||
|                         recipientAddress: opts.toAddress, |  | ||||||
|                         minConversionRate: getMinimumConversionRate(opts), |  | ||||||
|                         msgValue: opts.fromTokenBalance, |  | ||||||
|                         ...STATIC_KYBER_TRADE_ARGS, |  | ||||||
|                     }, |  | ||||||
|                 ], |  | ||||||
|                 TestKyberBridgeEvents.KyberBridgeTrade, |  | ||||||
|             ); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('does nothing if bridge has no token balance', async () => { |  | ||||||
|             const { logs } = await withdrawToAsync({ |  | ||||||
|                 fromTokenBalance: constants.ZERO_AMOUNT, |  | ||||||
|             }); |  | ||||||
|             expect(logs).to.be.length(0); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('only transfers the token if trading the same token', async () => { |  | ||||||
|             const { opts, logs } = await withdrawToAsync({ |  | ||||||
|                 toTokenAddress: fromTokenAddress, |  | ||||||
|             }); |  | ||||||
|             verifyEventsFromLogs( |  | ||||||
|                 logs, |  | ||||||
|                 [ |  | ||||||
|                     { |  | ||||||
|                         tokenAddress: fromTokenAddress, |  | ||||||
|                         ownerAddress: testContract.address, |  | ||||||
|                         recipientAddress: opts.toAddress, |  | ||||||
|                         amount: opts.fromTokenBalance, |  | ||||||
|                     }, |  | ||||||
|                 ], |  | ||||||
|                 TestKyberBridgeEvents.KyberBridgeTokenTransfer, |  | ||||||
|             ); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('grants Kyber an allowance when selling non-WETH', async () => { |  | ||||||
|             const { opts, logs } = await withdrawToAsync(); |  | ||||||
|             verifyEventsFromLogs( |  | ||||||
|                 logs, |  | ||||||
|                 [ |  | ||||||
|                     { |  | ||||||
|                         tokenAddress: opts.fromTokenAddress, |  | ||||||
|                         ownerAddress: testContract.address, |  | ||||||
|                         spenderAddress: testContract.address, |  | ||||||
|                         allowance: constants.MAX_UINT256, |  | ||||||
|                     }, |  | ||||||
|                 ], |  | ||||||
|                 TestKyberBridgeEvents.KyberBridgeTokenApprove, |  | ||||||
|             ); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('does not grant Kyber an allowance when selling WETH', async () => { |  | ||||||
|             const { logs } = await withdrawToAsync({ |  | ||||||
|                 fromTokenAddress: wethAddress, |  | ||||||
|             }); |  | ||||||
|             verifyEventsFromLogs(logs, [], TestKyberBridgeEvents.KyberBridgeTokenApprove); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('withdraws WETH and passes it to Kyber when selling WETH', async () => { |  | ||||||
|             const { opts, logs } = await withdrawToAsync({ |  | ||||||
|                 fromTokenAddress: wethAddress, |  | ||||||
|             }); |  | ||||||
|             expect(logs[0].event).to.eq(TestKyberBridgeEvents.KyberBridgeWethWithdraw); |  | ||||||
|             expect(logs[0].args).to.deep.eq({ |  | ||||||
|                 ownerAddress: testContract.address, |  | ||||||
|                 amount: opts.fromTokenBalance, |  | ||||||
|             }); |  | ||||||
|             expect(logs[1].event).to.eq(TestKyberBridgeEvents.KyberBridgeTrade); |  | ||||||
|             expect(logs[1].args.msgValue).to.bignumber.eq(opts.fromTokenBalance); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('wraps WETH and transfers it to the recipient when buyng WETH', async () => { |  | ||||||
|             const { opts, logs } = await withdrawToAsync({ |  | ||||||
|                 toTokenAddress: wethAddress, |  | ||||||
|             }); |  | ||||||
|             expect(logs[0].event).to.eq(TestKyberBridgeEvents.KyberBridgeTokenApprove); |  | ||||||
|             expect(logs[0].args.tokenAddress).to.eq(opts.fromTokenAddress); |  | ||||||
|             expect(logs[1].event).to.eq(TestKyberBridgeEvents.KyberBridgeTrade); |  | ||||||
|             expect(logs[1].args.recipientAddress).to.eq(testContract.address); |  | ||||||
|             expect(logs[2].event).to.eq(TestKyberBridgeEvents.KyberBridgeWethDeposit); |  | ||||||
|             expect(logs[2].args).to.deep.eq({ |  | ||||||
|                 msgValue: opts.fillAmount, |  | ||||||
|                 ownerAddress: testContract.address, |  | ||||||
|                 amount: opts.fillAmount, |  | ||||||
|             }); |  | ||||||
|         }); |  | ||||||
|     }); |  | ||||||
| }); |  | ||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,245 +0,0 @@ | |||||||
| import { |  | ||||||
|     chaiSetup, |  | ||||||
|     constants, |  | ||||||
|     expectTransactionFailedWithoutReasonAsync, |  | ||||||
|     provider, |  | ||||||
|     txDefaults, |  | ||||||
|     web3Wrapper, |  | ||||||
| } from '@0x/contracts-test-utils'; |  | ||||||
| import { BlockchainLifecycle } from '@0x/dev-utils'; |  | ||||||
| import { AssetProxyId, RevertReason } from '@0x/types'; |  | ||||||
| import { AbiEncoder, BigNumber } from '@0x/utils'; |  | ||||||
| import * as chai from 'chai'; |  | ||||||
| import * as ethUtil from 'ethereumjs-util'; |  | ||||||
|  |  | ||||||
| import { artifacts } from './artifacts'; |  | ||||||
|  |  | ||||||
| import { |  | ||||||
|     IAssetDataContract, |  | ||||||
|     IAssetProxyContract, |  | ||||||
|     StaticCallProxyContract, |  | ||||||
|     TestStaticCallTargetContract, |  | ||||||
| } from './wrappers'; |  | ||||||
|  |  | ||||||
| chaiSetup.configure(); |  | ||||||
| const expect = chai.expect; |  | ||||||
| const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); |  | ||||||
|  |  | ||||||
| describe('StaticCallProxy', () => { |  | ||||||
|     const amount = constants.ZERO_AMOUNT; |  | ||||||
|     let fromAddress: string; |  | ||||||
|     let toAddress: string; |  | ||||||
|  |  | ||||||
|     let assetDataInterface: IAssetDataContract; |  | ||||||
|     let staticCallProxy: IAssetProxyContract; |  | ||||||
|     let staticCallTarget: TestStaticCallTargetContract; |  | ||||||
|  |  | ||||||
|     before(async () => { |  | ||||||
|         await blockchainLifecycle.startAsync(); |  | ||||||
|     }); |  | ||||||
|     after(async () => { |  | ||||||
|         await blockchainLifecycle.revertAsync(); |  | ||||||
|     }); |  | ||||||
|     before(async () => { |  | ||||||
|         const accounts = await web3Wrapper.getAvailableAddressesAsync(); |  | ||||||
|         [fromAddress, toAddress] = accounts.slice(0, 2); |  | ||||||
|         const staticCallProxyWithoutTransferFrom = await StaticCallProxyContract.deployFrom0xArtifactAsync( |  | ||||||
|             artifacts.StaticCallProxy, |  | ||||||
|             provider, |  | ||||||
|             txDefaults, |  | ||||||
|             artifacts, |  | ||||||
|         ); |  | ||||||
|         assetDataInterface = new IAssetDataContract(constants.NULL_ADDRESS, provider); |  | ||||||
|         staticCallProxy = new IAssetProxyContract( |  | ||||||
|             staticCallProxyWithoutTransferFrom.address, |  | ||||||
|             provider, |  | ||||||
|             txDefaults, |  | ||||||
|             {}, |  | ||||||
|             StaticCallProxyContract.deployedBytecode, |  | ||||||
|         ); |  | ||||||
|         staticCallTarget = await TestStaticCallTargetContract.deployFrom0xArtifactAsync( |  | ||||||
|             artifacts.TestStaticCallTarget, |  | ||||||
|             provider, |  | ||||||
|             txDefaults, |  | ||||||
|             artifacts, |  | ||||||
|         ); |  | ||||||
|     }); |  | ||||||
|     beforeEach(async () => { |  | ||||||
|         await blockchainLifecycle.startAsync(); |  | ||||||
|     }); |  | ||||||
|     afterEach(async () => { |  | ||||||
|         await blockchainLifecycle.revertAsync(); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     describe('general', () => { |  | ||||||
|         it('should revert if undefined function is called', async () => { |  | ||||||
|             const undefinedSelector = '0x01020304'; |  | ||||||
|             await expectTransactionFailedWithoutReasonAsync( |  | ||||||
|                 web3Wrapper.sendTransactionAsync({ |  | ||||||
|                     from: fromAddress, |  | ||||||
|                     to: staticCallProxy.address, |  | ||||||
|                     value: constants.ZERO_AMOUNT, |  | ||||||
|                     data: undefinedSelector, |  | ||||||
|                 }), |  | ||||||
|             ); |  | ||||||
|         }); |  | ||||||
|         it('should have an id of 0xc339d10a', async () => { |  | ||||||
|             const proxyId = await staticCallProxy.getProxyId().callAsync(); |  | ||||||
|             const expectedProxyId = AssetProxyId.StaticCall; |  | ||||||
|             expect(proxyId).to.equal(expectedProxyId); |  | ||||||
|         }); |  | ||||||
|     }); |  | ||||||
|     describe('transferFrom', () => { |  | ||||||
|         it('should revert if assetData lies outside the bounds of calldata', async () => { |  | ||||||
|             const staticCallData = staticCallTarget.noInputFunction().getABIEncodedTransactionData(); |  | ||||||
|             const expectedResultHash = constants.KECCAK256_NULL; |  | ||||||
|             const assetData = assetDataInterface |  | ||||||
|                 .StaticCall(staticCallTarget.address, staticCallData, expectedResultHash) |  | ||||||
|                 .getABIEncodedTransactionData(); |  | ||||||
|             const txData = staticCallProxy |  | ||||||
|                 .transferFrom(assetData, fromAddress, toAddress, amount) |  | ||||||
|                 .getABIEncodedTransactionData(); |  | ||||||
|             const offsetToAssetData = '0000000000000000000000000000000000000000000000000000000000000080'; |  | ||||||
|             const txDataEndBuffer = ethUtil.toBuffer((txData.length - 2) / 2 - 4); |  | ||||||
|             const paddedTxDataEndBuffer = ethUtil.setLengthLeft(txDataEndBuffer, 32); |  | ||||||
|             const invalidOffsetToAssetData = ethUtil.bufferToHex(paddedTxDataEndBuffer).slice(2); |  | ||||||
|             const newAssetData = '0000000000000000000000000000000000000000000000000000000000000304'; |  | ||||||
|             const badTxData = `${txData.replace(offsetToAssetData, invalidOffsetToAssetData)}${newAssetData}`; |  | ||||||
|             await expectTransactionFailedWithoutReasonAsync( |  | ||||||
|                 web3Wrapper.sendTransactionAsync({ |  | ||||||
|                     to: staticCallProxy.address, |  | ||||||
|                     from: fromAddress, |  | ||||||
|                     data: badTxData, |  | ||||||
|                 }), |  | ||||||
|             ); |  | ||||||
|         }); |  | ||||||
|         it('should revert if the length of assetData is less than 100 bytes', async () => { |  | ||||||
|             const staticCallData = constants.NULL_BYTES; |  | ||||||
|             const expectedResultHash = constants.KECCAK256_NULL; |  | ||||||
|             const assetData = assetDataInterface |  | ||||||
|                 .StaticCall(staticCallTarget.address, staticCallData, expectedResultHash) |  | ||||||
|                 .getABIEncodedTransactionData() |  | ||||||
|                 .slice(0, -128); |  | ||||||
|             const assetDataByteLen = (assetData.length - 2) / 2; |  | ||||||
|             expect((assetDataByteLen - 4) % 32).to.equal(0); |  | ||||||
|             await expectTransactionFailedWithoutReasonAsync( |  | ||||||
|                 staticCallProxy.transferFrom(assetData, fromAddress, toAddress, amount).sendTransactionAsync(), |  | ||||||
|             ); |  | ||||||
|         }); |  | ||||||
|         it('should revert if the offset to `staticCallData` points to outside of assetData', async () => { |  | ||||||
|             const staticCallData = staticCallTarget.noInputFunction().getABIEncodedTransactionData(); |  | ||||||
|             const expectedResultHash = constants.KECCAK256_NULL; |  | ||||||
|             const assetData = assetDataInterface |  | ||||||
|                 .StaticCall(staticCallTarget.address, staticCallData, expectedResultHash) |  | ||||||
|                 .getABIEncodedTransactionData(); |  | ||||||
|             const offsetToStaticCallData = '0000000000000000000000000000000000000000000000000000000000000060'; |  | ||||||
|             const assetDataEndBuffer = ethUtil.toBuffer((assetData.length - 2) / 2 - 4); |  | ||||||
|             const paddedAssetDataEndBuffer = ethUtil.setLengthLeft(assetDataEndBuffer, 32); |  | ||||||
|             const invalidOffsetToStaticCallData = ethUtil.bufferToHex(paddedAssetDataEndBuffer).slice(2); |  | ||||||
|             const newStaticCallData = '0000000000000000000000000000000000000000000000000000000000000304'; |  | ||||||
|             const badAssetData = `${assetData.replace( |  | ||||||
|                 offsetToStaticCallData, |  | ||||||
|                 invalidOffsetToStaticCallData, |  | ||||||
|             )}${newStaticCallData}`; |  | ||||||
|             await expectTransactionFailedWithoutReasonAsync( |  | ||||||
|                 staticCallProxy.transferFrom(badAssetData, fromAddress, toAddress, amount).sendTransactionAsync(), |  | ||||||
|             ); |  | ||||||
|         }); |  | ||||||
|         it('should revert if the callTarget attempts to write to state', async () => { |  | ||||||
|             const staticCallData = staticCallTarget.updateState().getABIEncodedTransactionData(); |  | ||||||
|             const expectedResultHash = constants.KECCAK256_NULL; |  | ||||||
|             const assetData = assetDataInterface |  | ||||||
|                 .StaticCall(staticCallTarget.address, staticCallData, expectedResultHash) |  | ||||||
|                 .getABIEncodedTransactionData(); |  | ||||||
|             await expectTransactionFailedWithoutReasonAsync( |  | ||||||
|                 staticCallProxy.transferFrom(assetData, fromAddress, toAddress, amount).sendTransactionAsync(), |  | ||||||
|             ); |  | ||||||
|         }); |  | ||||||
|         it('should revert with data provided by the callTarget if the staticcall reverts', async () => { |  | ||||||
|             const staticCallData = staticCallTarget.assertEvenNumber(new BigNumber(1)).getABIEncodedTransactionData(); |  | ||||||
|             const expectedResultHash = constants.KECCAK256_NULL; |  | ||||||
|             const assetData = assetDataInterface |  | ||||||
|                 .StaticCall(staticCallTarget.address, staticCallData, expectedResultHash) |  | ||||||
|                 .getABIEncodedTransactionData(); |  | ||||||
|             return expect( |  | ||||||
|                 staticCallProxy.transferFrom(assetData, fromAddress, toAddress, amount).awaitTransactionSuccessAsync(), |  | ||||||
|             ).to.revertWith(RevertReason.TargetNotEven); |  | ||||||
|         }); |  | ||||||
|         it('should revert if the hash of the output is different than expected expected', async () => { |  | ||||||
|             const staticCallData = staticCallTarget.isOddNumber(new BigNumber(0)).getABIEncodedTransactionData(); |  | ||||||
|             const trueAsBuffer = ethUtil.toBuffer('0x0000000000000000000000000000000000000000000000000000000000000001'); |  | ||||||
|             const expectedResultHash = ethUtil.bufferToHex(ethUtil.sha3(trueAsBuffer)); |  | ||||||
|             const assetData = assetDataInterface |  | ||||||
|                 .StaticCall(staticCallTarget.address, staticCallData, expectedResultHash) |  | ||||||
|                 .getABIEncodedTransactionData(); |  | ||||||
|             return expect( |  | ||||||
|                 staticCallProxy.transferFrom(assetData, fromAddress, toAddress, amount).awaitTransactionSuccessAsync(), |  | ||||||
|             ).to.revertWith(RevertReason.UnexpectedStaticCallResult); |  | ||||||
|         }); |  | ||||||
|         it('should be successful if a function call with no inputs and no outputs is successful', async () => { |  | ||||||
|             const staticCallData = staticCallTarget.noInputFunction().getABIEncodedTransactionData(); |  | ||||||
|             const expectedResultHash = constants.KECCAK256_NULL; |  | ||||||
|             const assetData = assetDataInterface |  | ||||||
|                 .StaticCall(staticCallTarget.address, staticCallData, expectedResultHash) |  | ||||||
|                 .getABIEncodedTransactionData(); |  | ||||||
|             await staticCallProxy |  | ||||||
|                 .transferFrom(assetData, fromAddress, toAddress, amount) |  | ||||||
|                 .awaitTransactionSuccessAsync(); |  | ||||||
|         }); |  | ||||||
|         it('should be successful if the staticCallTarget is not a contract and no return value is expected', async () => { |  | ||||||
|             const staticCallData = '0x0102030405060708'; |  | ||||||
|             const expectedResultHash = constants.KECCAK256_NULL; |  | ||||||
|             const assetData = assetDataInterface |  | ||||||
|                 .StaticCall(toAddress, staticCallData, expectedResultHash) |  | ||||||
|                 .getABIEncodedTransactionData(); |  | ||||||
|             await staticCallProxy |  | ||||||
|                 .transferFrom(assetData, fromAddress, toAddress, amount) |  | ||||||
|                 .awaitTransactionSuccessAsync(); |  | ||||||
|         }); |  | ||||||
|         it('should be successful if a function call with one static input returns the correct value', async () => { |  | ||||||
|             const staticCallData = staticCallTarget.isOddNumber(new BigNumber(1)).getABIEncodedTransactionData(); |  | ||||||
|             const trueAsBuffer = ethUtil.toBuffer('0x0000000000000000000000000000000000000000000000000000000000000001'); |  | ||||||
|             const expectedResultHash = ethUtil.bufferToHex(ethUtil.sha3(trueAsBuffer)); |  | ||||||
|             const assetData = assetDataInterface |  | ||||||
|                 .StaticCall(staticCallTarget.address, staticCallData, expectedResultHash) |  | ||||||
|                 .getABIEncodedTransactionData(); |  | ||||||
|             await staticCallProxy |  | ||||||
|                 .transferFrom(assetData, fromAddress, toAddress, amount) |  | ||||||
|                 .awaitTransactionSuccessAsync(); |  | ||||||
|         }); |  | ||||||
|         it('should be successful if a function with one dynamic input is successful', async () => { |  | ||||||
|             const dynamicInput = '0x0102030405060708'; |  | ||||||
|             const staticCallData = staticCallTarget.dynamicInputFunction(dynamicInput).getABIEncodedTransactionData(); |  | ||||||
|             const expectedResultHash = constants.KECCAK256_NULL; |  | ||||||
|             const assetData = assetDataInterface |  | ||||||
|                 .StaticCall(staticCallTarget.address, staticCallData, expectedResultHash) |  | ||||||
|                 .getABIEncodedTransactionData(); |  | ||||||
|             await staticCallProxy |  | ||||||
|                 .transferFrom(assetData, fromAddress, toAddress, amount) |  | ||||||
|                 .awaitTransactionSuccessAsync(); |  | ||||||
|         }); |  | ||||||
|         it('should be successful if a function call returns a complex type', async () => { |  | ||||||
|             const a = new BigNumber(1); |  | ||||||
|             const b = new BigNumber(2); |  | ||||||
|             const staticCallData = staticCallTarget.returnComplexType(a, b).getABIEncodedTransactionData(); |  | ||||||
|             const abiEncoder = new AbiEncoder.DynamicBytes({ |  | ||||||
|                 name: '', |  | ||||||
|                 type: 'bytes', |  | ||||||
|             }); |  | ||||||
|             const aHex = '0000000000000000000000000000000000000000000000000000000000000001'; |  | ||||||
|             const bHex = '0000000000000000000000000000000000000000000000000000000000000002'; |  | ||||||
|             const expectedResults = `${staticCallTarget.address}${aHex}${bHex}`; |  | ||||||
|             const offset = '0000000000000000000000000000000000000000000000000000000000000020'; |  | ||||||
|             const encodedExpectedResultWithOffset = `0x${offset}${abiEncoder.encode(expectedResults).slice(2)}`; |  | ||||||
|             const expectedResultHash = ethUtil.bufferToHex( |  | ||||||
|                 ethUtil.sha3(ethUtil.toBuffer(encodedExpectedResultWithOffset)), |  | ||||||
|             ); |  | ||||||
|             const assetData = assetDataInterface |  | ||||||
|                 .StaticCall(staticCallTarget.address, staticCallData, expectedResultHash) |  | ||||||
|                 .getABIEncodedTransactionData(); |  | ||||||
|             await staticCallProxy |  | ||||||
|                 .transferFrom(assetData, fromAddress, toAddress, amount) |  | ||||||
|                 .awaitTransactionSuccessAsync(); |  | ||||||
|         }); |  | ||||||
|     }); |  | ||||||
| }); |  | ||||||
| @@ -1,370 +0,0 @@ | |||||||
| import { |  | ||||||
|     blockchainTests, |  | ||||||
|     constants, |  | ||||||
|     expect, |  | ||||||
|     filterLogs, |  | ||||||
|     filterLogsToArguments, |  | ||||||
|     getRandomInteger, |  | ||||||
|     Numberish, |  | ||||||
|     randomAddress, |  | ||||||
| } from '@0x/contracts-test-utils'; |  | ||||||
| import { AssetProxyId } from '@0x/types'; |  | ||||||
| import { BigNumber, hexUtils } from '@0x/utils'; |  | ||||||
| import { DecodedLogs } from 'ethereum-types'; |  | ||||||
| import * as _ from 'lodash'; |  | ||||||
|  |  | ||||||
| import { artifacts } from './artifacts'; |  | ||||||
|  |  | ||||||
| import { |  | ||||||
|     TestUniswapBridgeContract, |  | ||||||
|     TestUniswapBridgeEthToTokenTransferInputEventArgs as EthToTokenTransferInputArgs, |  | ||||||
|     TestUniswapBridgeEvents as ContractEvents, |  | ||||||
|     TestUniswapBridgeTokenApproveEventArgs as TokenApproveArgs, |  | ||||||
|     TestUniswapBridgeTokenToEthSwapInputEventArgs as TokenToEthSwapInputArgs, |  | ||||||
|     TestUniswapBridgeTokenToTokenTransferInputEventArgs as TokenToTokenTransferInputArgs, |  | ||||||
|     TestUniswapBridgeTokenTransferEventArgs as TokenTransferArgs, |  | ||||||
|     TestUniswapBridgeWethDepositEventArgs as WethDepositArgs, |  | ||||||
|     TestUniswapBridgeWethWithdrawEventArgs as WethWithdrawArgs, |  | ||||||
| } from './wrappers'; |  | ||||||
|  |  | ||||||
| blockchainTests.resets('UniswapBridge unit tests', env => { |  | ||||||
|     let testContract: TestUniswapBridgeContract; |  | ||||||
|     let wethTokenAddress: string; |  | ||||||
|  |  | ||||||
|     before(async () => { |  | ||||||
|         testContract = await TestUniswapBridgeContract.deployFrom0xArtifactAsync( |  | ||||||
|             artifacts.TestUniswapBridge, |  | ||||||
|             env.provider, |  | ||||||
|             env.txDefaults, |  | ||||||
|             artifacts, |  | ||||||
|         ); |  | ||||||
|         wethTokenAddress = await testContract.wethToken().callAsync(); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     describe('isValidSignature()', () => { |  | ||||||
|         it('returns success bytes', async () => { |  | ||||||
|             const LEGACY_WALLET_MAGIC_VALUE = '0xb0671381'; |  | ||||||
|             const result = await testContract |  | ||||||
|                 .isValidSignature(hexUtils.random(), hexUtils.random(_.random(0, 32))) |  | ||||||
|                 .callAsync(); |  | ||||||
|             expect(result).to.eq(LEGACY_WALLET_MAGIC_VALUE); |  | ||||||
|         }); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     describe('bridgeTransferFrom()', () => { |  | ||||||
|         interface WithdrawToOpts { |  | ||||||
|             fromTokenAddress: string; |  | ||||||
|             toTokenAddress: string; |  | ||||||
|             fromTokenBalance: Numberish; |  | ||||||
|             toAddress: string; |  | ||||||
|             amount: Numberish; |  | ||||||
|             exchangeRevertReason: string; |  | ||||||
|             exchangeFillAmount: Numberish; |  | ||||||
|             toTokenRevertReason: string; |  | ||||||
|             fromTokenRevertReason: string; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         function createWithdrawToOpts(opts?: Partial<WithdrawToOpts>): WithdrawToOpts { |  | ||||||
|             return { |  | ||||||
|                 fromTokenAddress: constants.NULL_ADDRESS, |  | ||||||
|                 toTokenAddress: constants.NULL_ADDRESS, |  | ||||||
|                 fromTokenBalance: getRandomInteger(1, 1e18), |  | ||||||
|                 toAddress: randomAddress(), |  | ||||||
|                 amount: getRandomInteger(1, 1e18), |  | ||||||
|                 exchangeRevertReason: '', |  | ||||||
|                 exchangeFillAmount: getRandomInteger(1, 1e18), |  | ||||||
|                 toTokenRevertReason: '', |  | ||||||
|                 fromTokenRevertReason: '', |  | ||||||
|                 ...opts, |  | ||||||
|             }; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         interface WithdrawToResult { |  | ||||||
|             opts: WithdrawToOpts; |  | ||||||
|             result: string; |  | ||||||
|             logs: DecodedLogs; |  | ||||||
|             blockTime: number; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         async function withdrawToAsync(opts?: Partial<WithdrawToOpts>): Promise<WithdrawToResult> { |  | ||||||
|             const _opts = createWithdrawToOpts(opts); |  | ||||||
|             const callData = { value: new BigNumber(_opts.exchangeFillAmount) }; |  | ||||||
|             // Create the "from" token and exchange. |  | ||||||
|             const createFromTokenFn = testContract.createTokenAndExchange( |  | ||||||
|                 _opts.fromTokenAddress, |  | ||||||
|                 _opts.exchangeRevertReason, |  | ||||||
|             ); |  | ||||||
|             [_opts.fromTokenAddress] = await createFromTokenFn.callAsync(callData); |  | ||||||
|             await createFromTokenFn.awaitTransactionSuccessAsync(callData); |  | ||||||
|  |  | ||||||
|             // Create the "to" token and exchange. |  | ||||||
|             const createToTokenFn = testContract.createTokenAndExchange( |  | ||||||
|                 _opts.toTokenAddress, |  | ||||||
|                 _opts.exchangeRevertReason, |  | ||||||
|             ); |  | ||||||
|             [_opts.toTokenAddress] = await createToTokenFn.callAsync(callData); |  | ||||||
|             await createToTokenFn.awaitTransactionSuccessAsync(callData); |  | ||||||
|  |  | ||||||
|             await testContract |  | ||||||
|                 .setTokenRevertReason(_opts.toTokenAddress, _opts.toTokenRevertReason) |  | ||||||
|                 .awaitTransactionSuccessAsync(); |  | ||||||
|             await testContract |  | ||||||
|                 .setTokenRevertReason(_opts.fromTokenAddress, _opts.fromTokenRevertReason) |  | ||||||
|                 .awaitTransactionSuccessAsync(); |  | ||||||
|             // Set the token balance for the token we're converting from. |  | ||||||
|             await testContract.setTokenBalance(_opts.fromTokenAddress).awaitTransactionSuccessAsync({ |  | ||||||
|                 value: new BigNumber(_opts.fromTokenBalance), |  | ||||||
|             }); |  | ||||||
|             // Call bridgeTransferFrom(). |  | ||||||
|             const bridgeTransferFromFn = testContract.bridgeTransferFrom( |  | ||||||
|                 // The "to" token address. |  | ||||||
|                 _opts.toTokenAddress, |  | ||||||
|                 // The "from" address. |  | ||||||
|                 randomAddress(), |  | ||||||
|                 // The "to" address. |  | ||||||
|                 _opts.toAddress, |  | ||||||
|                 // The amount to transfer to "to" |  | ||||||
|                 new BigNumber(_opts.amount), |  | ||||||
|                 // ABI-encoded "from" token address. |  | ||||||
|                 hexUtils.leftPad(_opts.fromTokenAddress), |  | ||||||
|             ); |  | ||||||
|             const result = await bridgeTransferFromFn.callAsync(); |  | ||||||
|             const receipt = await bridgeTransferFromFn.awaitTransactionSuccessAsync(); |  | ||||||
|             return { |  | ||||||
|                 opts: _opts, |  | ||||||
|                 result, |  | ||||||
|                 logs: (receipt.logs as any) as DecodedLogs, |  | ||||||
|                 blockTime: await env.web3Wrapper.getBlockTimestampAsync(receipt.blockNumber), |  | ||||||
|             }; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         async function getExchangeForTokenAsync(tokenAddress: string): Promise<string> { |  | ||||||
|             return testContract.getExchange(tokenAddress).callAsync(); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         it('returns magic bytes on success', async () => { |  | ||||||
|             const { result } = await withdrawToAsync(); |  | ||||||
|             expect(result).to.eq(AssetProxyId.ERC20Bridge); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('just transfers tokens to `to` if the same tokens are in play', async () => { |  | ||||||
|             const createTokenFn = await testContract.createTokenAndExchange(constants.NULL_ADDRESS, ''); |  | ||||||
|             const [tokenAddress] = await createTokenFn.callAsync(); |  | ||||||
|             await createTokenFn.awaitTransactionSuccessAsync(); |  | ||||||
|             const { opts, result, logs } = await withdrawToAsync({ |  | ||||||
|                 fromTokenAddress: tokenAddress, |  | ||||||
|                 toTokenAddress: tokenAddress, |  | ||||||
|             }); |  | ||||||
|             expect(result).to.eq(AssetProxyId.ERC20Bridge); |  | ||||||
|             const transfers = filterLogsToArguments<TokenTransferArgs>(logs, ContractEvents.TokenTransfer); |  | ||||||
|             expect(transfers.length).to.eq(1); |  | ||||||
|             expect(transfers[0].token).to.eq(tokenAddress); |  | ||||||
|             expect(transfers[0].from).to.eq(testContract.address); |  | ||||||
|             expect(transfers[0].to).to.eq(opts.toAddress); |  | ||||||
|             expect(transfers[0].amount).to.bignumber.eq(opts.amount); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         describe('token -> token', () => { |  | ||||||
|             it('calls `IUniswapExchange.tokenToTokenTransferInput()', async () => { |  | ||||||
|                 const { opts, logs, blockTime } = await withdrawToAsync(); |  | ||||||
|                 const exchangeAddress = await getExchangeForTokenAsync(opts.fromTokenAddress); |  | ||||||
|                 const calls = filterLogsToArguments<TokenToTokenTransferInputArgs>( |  | ||||||
|                     logs, |  | ||||||
|                     ContractEvents.TokenToTokenTransferInput, |  | ||||||
|                 ); |  | ||||||
|                 expect(calls.length).to.eq(1); |  | ||||||
|                 expect(calls[0].exchange).to.eq(exchangeAddress); |  | ||||||
|                 expect(calls[0].tokensSold).to.bignumber.eq(opts.fromTokenBalance); |  | ||||||
|                 expect(calls[0].minTokensBought).to.bignumber.eq(opts.amount); |  | ||||||
|                 expect(calls[0].minEthBought).to.bignumber.eq(1); |  | ||||||
|                 expect(calls[0].deadline).to.bignumber.eq(blockTime); |  | ||||||
|                 expect(calls[0].recipient).to.eq(opts.toAddress); |  | ||||||
|                 expect(calls[0].toTokenAddress).to.eq(opts.toTokenAddress); |  | ||||||
|             }); |  | ||||||
|  |  | ||||||
|             it('sets allowance for "from" token', async () => { |  | ||||||
|                 const { opts, logs } = await withdrawToAsync(); |  | ||||||
|                 const approvals = filterLogsToArguments<TokenApproveArgs>(logs, ContractEvents.TokenApprove); |  | ||||||
|                 const exchangeAddress = await getExchangeForTokenAsync(opts.fromTokenAddress); |  | ||||||
|                 expect(approvals.length).to.eq(1); |  | ||||||
|                 expect(approvals[0].spender).to.eq(exchangeAddress); |  | ||||||
|                 expect(approvals[0].allowance).to.bignumber.eq(constants.MAX_UINT256); |  | ||||||
|             }); |  | ||||||
|  |  | ||||||
|             it('sets allowance for "from" token on subsequent calls', async () => { |  | ||||||
|                 const { opts } = await withdrawToAsync(); |  | ||||||
|                 const { logs } = await withdrawToAsync(opts); |  | ||||||
|                 const approvals = filterLogsToArguments<TokenApproveArgs>(logs, ContractEvents.TokenApprove); |  | ||||||
|                 const exchangeAddress = await getExchangeForTokenAsync(opts.fromTokenAddress); |  | ||||||
|                 expect(approvals.length).to.eq(1); |  | ||||||
|                 expect(approvals[0].spender).to.eq(exchangeAddress); |  | ||||||
|                 expect(approvals[0].allowance).to.bignumber.eq(constants.MAX_UINT256); |  | ||||||
|             }); |  | ||||||
|  |  | ||||||
|             it('fails if "from" token does not exist', async () => { |  | ||||||
|                 const tx = testContract |  | ||||||
|                     .bridgeTransferFrom( |  | ||||||
|                         randomAddress(), |  | ||||||
|                         randomAddress(), |  | ||||||
|                         randomAddress(), |  | ||||||
|                         getRandomInteger(1, 1e18), |  | ||||||
|                         hexUtils.leftPad(randomAddress()), |  | ||||||
|                     ) |  | ||||||
|                     .awaitTransactionSuccessAsync(); |  | ||||||
|                 return expect(tx).to.eventually.be.rejectedWith('NO_UNISWAP_EXCHANGE_FOR_TOKEN'); |  | ||||||
|             }); |  | ||||||
|  |  | ||||||
|             it('fails if the exchange fails', async () => { |  | ||||||
|                 const revertReason = 'FOOBAR'; |  | ||||||
|                 const tx = withdrawToAsync({ |  | ||||||
|                     exchangeRevertReason: revertReason, |  | ||||||
|                 }); |  | ||||||
|                 return expect(tx).to.eventually.be.rejectedWith(revertReason); |  | ||||||
|             }); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         describe('token -> ETH', () => { |  | ||||||
|             it('calls `IUniswapExchange.tokenToEthSwapInput()`, `WETH.deposit()`, then `transfer()`', async () => { |  | ||||||
|                 const { opts, logs, blockTime } = await withdrawToAsync({ |  | ||||||
|                     toTokenAddress: wethTokenAddress, |  | ||||||
|                 }); |  | ||||||
|                 const exchangeAddress = await getExchangeForTokenAsync(opts.fromTokenAddress); |  | ||||||
|                 let calls: any = filterLogs<TokenToEthSwapInputArgs>(logs, ContractEvents.TokenToEthSwapInput); |  | ||||||
|                 expect(calls.length).to.eq(1); |  | ||||||
|                 expect(calls[0].args.exchange).to.eq(exchangeAddress); |  | ||||||
|                 expect(calls[0].args.tokensSold).to.bignumber.eq(opts.fromTokenBalance); |  | ||||||
|                 expect(calls[0].args.minEthBought).to.bignumber.eq(opts.amount); |  | ||||||
|                 expect(calls[0].args.deadline).to.bignumber.eq(blockTime); |  | ||||||
|                 calls = filterLogs<WethDepositArgs>( |  | ||||||
|                     logs.slice(calls[0].logIndex as number), |  | ||||||
|                     ContractEvents.WethDeposit, |  | ||||||
|                 ); |  | ||||||
|                 expect(calls.length).to.eq(1); |  | ||||||
|                 expect(calls[0].args.amount).to.bignumber.eq(opts.exchangeFillAmount); |  | ||||||
|                 calls = filterLogs<TokenTransferArgs>( |  | ||||||
|                     logs.slice(calls[0].logIndex as number), |  | ||||||
|                     ContractEvents.TokenTransfer, |  | ||||||
|                 ); |  | ||||||
|                 expect(calls.length).to.eq(1); |  | ||||||
|                 expect(calls[0].args.token).to.eq(opts.toTokenAddress); |  | ||||||
|                 expect(calls[0].args.from).to.eq(testContract.address); |  | ||||||
|                 expect(calls[0].args.to).to.eq(opts.toAddress); |  | ||||||
|                 expect(calls[0].args.amount).to.bignumber.eq(opts.exchangeFillAmount); |  | ||||||
|             }); |  | ||||||
|  |  | ||||||
|             it('sets allowance for "from" token', async () => { |  | ||||||
|                 const { opts, logs } = await withdrawToAsync({ |  | ||||||
|                     toTokenAddress: wethTokenAddress, |  | ||||||
|                 }); |  | ||||||
|                 const transfers = filterLogsToArguments<TokenApproveArgs>(logs, ContractEvents.TokenApprove); |  | ||||||
|                 const exchangeAddress = await getExchangeForTokenAsync(opts.fromTokenAddress); |  | ||||||
|                 expect(transfers.length).to.eq(1); |  | ||||||
|                 expect(transfers[0].spender).to.eq(exchangeAddress); |  | ||||||
|                 expect(transfers[0].allowance).to.bignumber.eq(constants.MAX_UINT256); |  | ||||||
|             }); |  | ||||||
|  |  | ||||||
|             it('sets allowance for "from" token on subsequent calls', async () => { |  | ||||||
|                 const { opts } = await withdrawToAsync({ |  | ||||||
|                     toTokenAddress: wethTokenAddress, |  | ||||||
|                 }); |  | ||||||
|                 const { logs } = await withdrawToAsync(opts); |  | ||||||
|                 const approvals = filterLogsToArguments<TokenApproveArgs>(logs, ContractEvents.TokenApprove); |  | ||||||
|                 const exchangeAddress = await getExchangeForTokenAsync(opts.fromTokenAddress); |  | ||||||
|                 expect(approvals.length).to.eq(1); |  | ||||||
|                 expect(approvals[0].spender).to.eq(exchangeAddress); |  | ||||||
|                 expect(approvals[0].allowance).to.bignumber.eq(constants.MAX_UINT256); |  | ||||||
|             }); |  | ||||||
|  |  | ||||||
|             it('fails if "from" token does not exist', async () => { |  | ||||||
|                 const tx = testContract |  | ||||||
|                     .bridgeTransferFrom( |  | ||||||
|                         randomAddress(), |  | ||||||
|                         randomAddress(), |  | ||||||
|                         randomAddress(), |  | ||||||
|                         getRandomInteger(1, 1e18), |  | ||||||
|                         hexUtils.leftPad(wethTokenAddress), |  | ||||||
|                     ) |  | ||||||
|                     .awaitTransactionSuccessAsync(); |  | ||||||
|                 return expect(tx).to.eventually.be.rejectedWith('NO_UNISWAP_EXCHANGE_FOR_TOKEN'); |  | ||||||
|             }); |  | ||||||
|  |  | ||||||
|             it('fails if `WETH.deposit()` fails', async () => { |  | ||||||
|                 const revertReason = 'FOOBAR'; |  | ||||||
|                 const tx = withdrawToAsync({ |  | ||||||
|                     toTokenAddress: wethTokenAddress, |  | ||||||
|                     toTokenRevertReason: revertReason, |  | ||||||
|                 }); |  | ||||||
|                 return expect(tx).to.eventually.be.rejectedWith(revertReason); |  | ||||||
|             }); |  | ||||||
|  |  | ||||||
|             it('fails if the exchange fails', async () => { |  | ||||||
|                 const revertReason = 'FOOBAR'; |  | ||||||
|                 const tx = withdrawToAsync({ |  | ||||||
|                     toTokenAddress: wethTokenAddress, |  | ||||||
|                     exchangeRevertReason: revertReason, |  | ||||||
|                 }); |  | ||||||
|                 return expect(tx).to.eventually.be.rejectedWith(revertReason); |  | ||||||
|             }); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         describe('ETH -> token', () => { |  | ||||||
|             it('calls  `WETH.withdraw()`, then `IUniswapExchange.ethToTokenTransferInput()`', async () => { |  | ||||||
|                 const { opts, logs, blockTime } = await withdrawToAsync({ |  | ||||||
|                     fromTokenAddress: wethTokenAddress, |  | ||||||
|                 }); |  | ||||||
|                 const exchangeAddress = await getExchangeForTokenAsync(opts.toTokenAddress); |  | ||||||
|                 let calls: any = filterLogs<WethWithdrawArgs>(logs, ContractEvents.WethWithdraw); |  | ||||||
|                 expect(calls.length).to.eq(1); |  | ||||||
|                 expect(calls[0].args.amount).to.bignumber.eq(opts.fromTokenBalance); |  | ||||||
|                 calls = filterLogs<EthToTokenTransferInputArgs>( |  | ||||||
|                     logs.slice(calls[0].logIndex as number), |  | ||||||
|                     ContractEvents.EthToTokenTransferInput, |  | ||||||
|                 ); |  | ||||||
|                 expect(calls.length).to.eq(1); |  | ||||||
|                 expect(calls[0].args.exchange).to.eq(exchangeAddress); |  | ||||||
|                 expect(calls[0].args.minTokensBought).to.bignumber.eq(opts.amount); |  | ||||||
|                 expect(calls[0].args.deadline).to.bignumber.eq(blockTime); |  | ||||||
|                 expect(calls[0].args.recipient).to.eq(opts.toAddress); |  | ||||||
|             }); |  | ||||||
|  |  | ||||||
|             it('does not set any allowance', async () => { |  | ||||||
|                 const { logs } = await withdrawToAsync({ |  | ||||||
|                     fromTokenAddress: wethTokenAddress, |  | ||||||
|                 }); |  | ||||||
|                 const approvals = filterLogsToArguments<TokenApproveArgs>(logs, ContractEvents.TokenApprove); |  | ||||||
|                 expect(approvals).to.be.empty(''); |  | ||||||
|             }); |  | ||||||
|  |  | ||||||
|             it('fails if "to" token does not exist', async () => { |  | ||||||
|                 const tx = testContract |  | ||||||
|                     .bridgeTransferFrom( |  | ||||||
|                         wethTokenAddress, |  | ||||||
|                         randomAddress(), |  | ||||||
|                         randomAddress(), |  | ||||||
|                         getRandomInteger(1, 1e18), |  | ||||||
|                         hexUtils.leftPad(randomAddress()), |  | ||||||
|                     ) |  | ||||||
|                     .awaitTransactionSuccessAsync(); |  | ||||||
|                 return expect(tx).to.eventually.be.rejectedWith('NO_UNISWAP_EXCHANGE_FOR_TOKEN'); |  | ||||||
|             }); |  | ||||||
|  |  | ||||||
|             it('fails if the `WETH.withdraw()` fails', async () => { |  | ||||||
|                 const revertReason = 'FOOBAR'; |  | ||||||
|                 const tx = withdrawToAsync({ |  | ||||||
|                     fromTokenAddress: wethTokenAddress, |  | ||||||
|                     fromTokenRevertReason: revertReason, |  | ||||||
|                 }); |  | ||||||
|                 return expect(tx).to.eventually.be.rejectedWith(revertReason); |  | ||||||
|             }); |  | ||||||
|  |  | ||||||
|             it('fails if the exchange fails', async () => { |  | ||||||
|                 const revertReason = 'FOOBAR'; |  | ||||||
|                 const tx = withdrawToAsync({ |  | ||||||
|                     fromTokenAddress: wethTokenAddress, |  | ||||||
|                     exchangeRevertReason: revertReason, |  | ||||||
|                 }); |  | ||||||
|                 return expect(tx).to.eventually.be.rejectedWith(revertReason); |  | ||||||
|             }); |  | ||||||
|         }); |  | ||||||
|     }); |  | ||||||
| }); |  | ||||||
| @@ -1,216 +0,0 @@ | |||||||
| import { |  | ||||||
|     blockchainTests, |  | ||||||
|     constants, |  | ||||||
|     expect, |  | ||||||
|     filterLogsToArguments, |  | ||||||
|     getRandomInteger, |  | ||||||
|     randomAddress, |  | ||||||
| } from '@0x/contracts-test-utils'; |  | ||||||
| import { AssetProxyId } from '@0x/types'; |  | ||||||
| import { AbiEncoder, BigNumber, hexUtils } from '@0x/utils'; |  | ||||||
| import { DecodedLogs } from 'ethereum-types'; |  | ||||||
| import * as _ from 'lodash'; |  | ||||||
|  |  | ||||||
| import { artifacts } from './artifacts'; |  | ||||||
|  |  | ||||||
| import { |  | ||||||
|     TestUniswapV2BridgeContract, |  | ||||||
|     TestUniswapV2BridgeEvents as ContractEvents, |  | ||||||
|     TestUniswapV2BridgeSwapExactTokensForTokensInputEventArgs as SwapExactTokensForTokensArgs, |  | ||||||
|     TestUniswapV2BridgeTokenApproveEventArgs as TokenApproveArgs, |  | ||||||
|     TestUniswapV2BridgeTokenTransferEventArgs as TokenTransferArgs, |  | ||||||
| } from './wrappers'; |  | ||||||
|  |  | ||||||
| blockchainTests.resets('UniswapV2 unit tests', env => { |  | ||||||
|     const FROM_TOKEN_DECIMALS = 6; |  | ||||||
|     const TO_TOKEN_DECIMALS = 18; |  | ||||||
|     const FROM_TOKEN_BASE = new BigNumber(10).pow(FROM_TOKEN_DECIMALS); |  | ||||||
|     const TO_TOKEN_BASE = new BigNumber(10).pow(TO_TOKEN_DECIMALS); |  | ||||||
|     let testContract: TestUniswapV2BridgeContract; |  | ||||||
|  |  | ||||||
|     before(async () => { |  | ||||||
|         testContract = await TestUniswapV2BridgeContract.deployFrom0xArtifactAsync( |  | ||||||
|             artifacts.TestUniswapV2Bridge, |  | ||||||
|             env.provider, |  | ||||||
|             env.txDefaults, |  | ||||||
|             artifacts, |  | ||||||
|         ); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     describe('isValidSignature()', () => { |  | ||||||
|         it('returns success bytes', async () => { |  | ||||||
|             const LEGACY_WALLET_MAGIC_VALUE = '0xb0671381'; |  | ||||||
|             const result = await testContract |  | ||||||
|                 .isValidSignature(hexUtils.random(), hexUtils.random(_.random(0, 32))) |  | ||||||
|                 .callAsync(); |  | ||||||
|             expect(result).to.eq(LEGACY_WALLET_MAGIC_VALUE); |  | ||||||
|         }); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     describe('bridgeTransferFrom()', () => { |  | ||||||
|         interface TransferFromOpts { |  | ||||||
|             tokenAddressesPath: string[]; |  | ||||||
|             toAddress: string; |  | ||||||
|             // Amount to pass into `bridgeTransferFrom()` |  | ||||||
|             amount: BigNumber; |  | ||||||
|             // Token balance of the bridge. |  | ||||||
|             fromTokenBalance: BigNumber; |  | ||||||
|             // Router reverts with this reason |  | ||||||
|             routerRevertReason: string; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         interface TransferFromResult { |  | ||||||
|             opts: TransferFromOpts; |  | ||||||
|             result: string; |  | ||||||
|             logs: DecodedLogs; |  | ||||||
|             blocktime: number; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         function createTransferFromOpts(opts?: Partial<TransferFromOpts>): TransferFromOpts { |  | ||||||
|             const amount = getRandomInteger(1, TO_TOKEN_BASE.times(100)); |  | ||||||
|             return { |  | ||||||
|                 tokenAddressesPath: Array(2).fill(constants.NULL_ADDRESS), |  | ||||||
|                 amount, |  | ||||||
|                 toAddress: randomAddress(), |  | ||||||
|                 fromTokenBalance: getRandomInteger(1, FROM_TOKEN_BASE.times(100)), |  | ||||||
|                 routerRevertReason: '', |  | ||||||
|                 ...opts, |  | ||||||
|             }; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         const bridgeDataEncoder = AbiEncoder.create('(address[])'); |  | ||||||
|  |  | ||||||
|         async function transferFromAsync(opts?: Partial<TransferFromOpts>): Promise<TransferFromResult> { |  | ||||||
|             const _opts = createTransferFromOpts(opts); |  | ||||||
|  |  | ||||||
|             for (let i = 0; i < _opts.tokenAddressesPath.length; i++) { |  | ||||||
|                 const createFromTokenFn = testContract.createToken(_opts.tokenAddressesPath[i]); |  | ||||||
|                 _opts.tokenAddressesPath[i] = await createFromTokenFn.callAsync(); |  | ||||||
|                 await createFromTokenFn.awaitTransactionSuccessAsync(); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             // Set the token balance for the token we're converting from. |  | ||||||
|             await testContract |  | ||||||
|                 .setTokenBalance(_opts.tokenAddressesPath[0], _opts.fromTokenBalance) |  | ||||||
|                 .awaitTransactionSuccessAsync(); |  | ||||||
|  |  | ||||||
|             // Set revert reason for the router. |  | ||||||
|             await testContract.setRouterRevertReason(_opts.routerRevertReason).awaitTransactionSuccessAsync(); |  | ||||||
|  |  | ||||||
|             // Call bridgeTransferFrom(). |  | ||||||
|             const bridgeTransferFromFn = testContract.bridgeTransferFrom( |  | ||||||
|                 // Output token |  | ||||||
|                 _opts.tokenAddressesPath[_opts.tokenAddressesPath.length - 1], |  | ||||||
|                 // Random maker address. |  | ||||||
|                 randomAddress(), |  | ||||||
|                 // Recipient address. |  | ||||||
|                 _opts.toAddress, |  | ||||||
|                 // Transfer amount. |  | ||||||
|                 _opts.amount, |  | ||||||
|                 // ABI-encode the input token address as the bridge data. // FIXME |  | ||||||
|                 bridgeDataEncoder.encode([_opts.tokenAddressesPath]), |  | ||||||
|             ); |  | ||||||
|             const result = await bridgeTransferFromFn.callAsync(); |  | ||||||
|             const receipt = await bridgeTransferFromFn.awaitTransactionSuccessAsync(); |  | ||||||
|             return { |  | ||||||
|                 opts: _opts, |  | ||||||
|                 result, |  | ||||||
|                 logs: (receipt.logs as any) as DecodedLogs, |  | ||||||
|                 blocktime: await env.web3Wrapper.getBlockTimestampAsync(receipt.blockNumber), |  | ||||||
|             }; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         it('returns magic bytes on success', async () => { |  | ||||||
|             const { result } = await transferFromAsync(); |  | ||||||
|             expect(result).to.eq(AssetProxyId.ERC20Bridge); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         it('performs transfer when both tokens are the same', async () => { |  | ||||||
|             const createTokenFn = testContract.createToken(constants.NULL_ADDRESS); |  | ||||||
|             const tokenAddress = await createTokenFn.callAsync(); |  | ||||||
|             await createTokenFn.awaitTransactionSuccessAsync(); |  | ||||||
|  |  | ||||||
|             const { opts, result, logs } = await transferFromAsync({ |  | ||||||
|                 tokenAddressesPath: [tokenAddress, tokenAddress], |  | ||||||
|             }); |  | ||||||
|             expect(result).to.eq(AssetProxyId.ERC20Bridge, 'asset proxy id'); |  | ||||||
|             const transfers = filterLogsToArguments<TokenTransferArgs>(logs, ContractEvents.TokenTransfer); |  | ||||||
|  |  | ||||||
|             expect(transfers.length).to.eq(1); |  | ||||||
|             expect(transfers[0].token).to.eq(tokenAddress, 'input token address'); |  | ||||||
|             expect(transfers[0].from).to.eq(testContract.address); |  | ||||||
|             expect(transfers[0].to).to.eq(opts.toAddress, 'recipient address'); |  | ||||||
|             expect(transfers[0].amount).to.bignumber.eq(opts.amount, 'amount'); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         describe('token -> token', async () => { |  | ||||||
|             it('calls UniswapV2Router01.swapExactTokensForTokens()', async () => { |  | ||||||
|                 const { opts, result, logs, blocktime } = await transferFromAsync(); |  | ||||||
|                 expect(result).to.eq(AssetProxyId.ERC20Bridge, 'asset proxy id'); |  | ||||||
|                 const transfers = filterLogsToArguments<SwapExactTokensForTokensArgs>( |  | ||||||
|                     logs, |  | ||||||
|                     ContractEvents.SwapExactTokensForTokensInput, |  | ||||||
|                 ); |  | ||||||
|  |  | ||||||
|                 expect(transfers.length).to.eq(1); |  | ||||||
|                 expect(transfers[0].toTokenAddress).to.eq( |  | ||||||
|                     opts.tokenAddressesPath[opts.tokenAddressesPath.length - 1], |  | ||||||
|                     'output token address', |  | ||||||
|                 ); |  | ||||||
|                 expect(transfers[0].to).to.eq(opts.toAddress, 'recipient address'); |  | ||||||
|                 expect(transfers[0].amountIn).to.bignumber.eq(opts.fromTokenBalance, 'input token amount'); |  | ||||||
|                 expect(transfers[0].amountOutMin).to.bignumber.eq(opts.amount, 'output token amount'); |  | ||||||
|                 expect(transfers[0].deadline).to.bignumber.eq(blocktime, 'deadline'); |  | ||||||
|             }); |  | ||||||
|  |  | ||||||
|             it('sets allowance for "from" token', async () => { |  | ||||||
|                 const { logs } = await transferFromAsync(); |  | ||||||
|                 const approvals = filterLogsToArguments<TokenApproveArgs>(logs, ContractEvents.TokenApprove); |  | ||||||
|                 const routerAddress = await testContract.getRouterAddress().callAsync(); |  | ||||||
|                 expect(approvals.length).to.eq(1); |  | ||||||
|                 expect(approvals[0].spender).to.eq(routerAddress); |  | ||||||
|                 expect(approvals[0].allowance).to.bignumber.eq(constants.MAX_UINT256); |  | ||||||
|             }); |  | ||||||
|  |  | ||||||
|             it('sets allowance for "from" token on subsequent calls', async () => { |  | ||||||
|                 const { opts } = await transferFromAsync(); |  | ||||||
|                 const { logs } = await transferFromAsync(opts); |  | ||||||
|                 const approvals = filterLogsToArguments<TokenApproveArgs>(logs, ContractEvents.TokenApprove); |  | ||||||
|                 const routerAddress = await testContract.getRouterAddress().callAsync(); |  | ||||||
|                 expect(approvals.length).to.eq(1); |  | ||||||
|                 expect(approvals[0].spender).to.eq(routerAddress); |  | ||||||
|                 expect(approvals[0].allowance).to.bignumber.eq(constants.MAX_UINT256); |  | ||||||
|             }); |  | ||||||
|  |  | ||||||
|             it('fails if the router fails', async () => { |  | ||||||
|                 const revertReason = 'FOOBAR'; |  | ||||||
|                 const tx = transferFromAsync({ |  | ||||||
|                     routerRevertReason: revertReason, |  | ||||||
|                 }); |  | ||||||
|                 return expect(tx).to.eventually.be.rejectedWith(revertReason); |  | ||||||
|             }); |  | ||||||
|         }); |  | ||||||
|         describe('token -> token -> token', async () => { |  | ||||||
|             it('calls UniswapV2Router01.swapExactTokensForTokens()', async () => { |  | ||||||
|                 const { opts, result, logs, blocktime } = await transferFromAsync({ |  | ||||||
|                     tokenAddressesPath: Array(3).fill(constants.NULL_ADDRESS), |  | ||||||
|                 }); |  | ||||||
|                 expect(result).to.eq(AssetProxyId.ERC20Bridge, 'asset proxy id'); |  | ||||||
|                 const transfers = filterLogsToArguments<SwapExactTokensForTokensArgs>( |  | ||||||
|                     logs, |  | ||||||
|                     ContractEvents.SwapExactTokensForTokensInput, |  | ||||||
|                 ); |  | ||||||
|  |  | ||||||
|                 expect(transfers.length).to.eq(1); |  | ||||||
|                 expect(transfers[0].toTokenAddress).to.eq( |  | ||||||
|                     opts.tokenAddressesPath[opts.tokenAddressesPath.length - 1], |  | ||||||
|                     'output token address', |  | ||||||
|                 ); |  | ||||||
|                 expect(transfers[0].to).to.eq(opts.toAddress, 'recipient address'); |  | ||||||
|                 expect(transfers[0].amountIn).to.bignumber.eq(opts.fromTokenBalance, 'input token amount'); |  | ||||||
|                 expect(transfers[0].amountOutMin).to.bignumber.eq(opts.amount, 'output token amount'); |  | ||||||
|                 expect(transfers[0].deadline).to.bignumber.eq(blocktime, 'deadline'); |  | ||||||
|             }); |  | ||||||
|         }); |  | ||||||
|     }); |  | ||||||
| }); |  | ||||||
| @@ -1,64 +0,0 @@ | |||||||
| /* |  | ||||||
|  * ----------------------------------------------------------------------------- |  | ||||||
|  * Warning: This file is auto-generated by contracts-gen. Don't edit manually. |  | ||||||
|  * ----------------------------------------------------------------------------- |  | ||||||
|  */ |  | ||||||
| export * from '../test/generated-wrappers/balancer_bridge'; |  | ||||||
| export * from '../test/generated-wrappers/bancor_bridge'; |  | ||||||
| export * from '../test/generated-wrappers/chai_bridge'; |  | ||||||
| export * from '../test/generated-wrappers/cream_bridge'; |  | ||||||
| export * from '../test/generated-wrappers/crypto_com_bridge'; |  | ||||||
| export * from '../test/generated-wrappers/curve_bridge'; |  | ||||||
| export * from '../test/generated-wrappers/d_o_d_o_bridge'; |  | ||||||
| export * from '../test/generated-wrappers/dex_forwarder_bridge'; |  | ||||||
| export * from '../test/generated-wrappers/dydx_bridge'; |  | ||||||
| export * from '../test/generated-wrappers/erc1155_proxy'; |  | ||||||
| export * from '../test/generated-wrappers/erc20_bridge_proxy'; |  | ||||||
| export * from '../test/generated-wrappers/erc20_proxy'; |  | ||||||
| export * from '../test/generated-wrappers/erc721_proxy'; |  | ||||||
| export * from '../test/generated-wrappers/eth2_dai_bridge'; |  | ||||||
| export * from '../test/generated-wrappers/i_asset_data'; |  | ||||||
| export * from '../test/generated-wrappers/i_asset_proxy'; |  | ||||||
| export * from '../test/generated-wrappers/i_asset_proxy_dispatcher'; |  | ||||||
| export * from '../test/generated-wrappers/i_authorizable'; |  | ||||||
| export * from '../test/generated-wrappers/i_balancer_pool'; |  | ||||||
| export * from '../test/generated-wrappers/i_bancor_network'; |  | ||||||
| export * from '../test/generated-wrappers/i_chai'; |  | ||||||
| export * from '../test/generated-wrappers/i_curve'; |  | ||||||
| export * from '../test/generated-wrappers/i_dydx'; |  | ||||||
| export * from '../test/generated-wrappers/i_dydx_bridge'; |  | ||||||
| export * from '../test/generated-wrappers/i_erc20_bridge'; |  | ||||||
| export * from '../test/generated-wrappers/i_eth2_dai'; |  | ||||||
| export * from '../test/generated-wrappers/i_gas_token'; |  | ||||||
| export * from '../test/generated-wrappers/i_kyber_network_proxy'; |  | ||||||
| export * from '../test/generated-wrappers/i_m_stable'; |  | ||||||
| export * from '../test/generated-wrappers/i_mooniswap'; |  | ||||||
| export * from '../test/generated-wrappers/i_shell'; |  | ||||||
| export * from '../test/generated-wrappers/i_uniswap_exchange'; |  | ||||||
| export * from '../test/generated-wrappers/i_uniswap_exchange_factory'; |  | ||||||
| export * from '../test/generated-wrappers/i_uniswap_v2_router01'; |  | ||||||
| export * from '../test/generated-wrappers/kyber_bridge'; |  | ||||||
| export * from '../test/generated-wrappers/m_stable_bridge'; |  | ||||||
| export * from '../test/generated-wrappers/mixin_asset_proxy_dispatcher'; |  | ||||||
| export * from '../test/generated-wrappers/mixin_authorizable'; |  | ||||||
| export * from '../test/generated-wrappers/mixin_gas_token'; |  | ||||||
| export * from '../test/generated-wrappers/mooniswap_bridge'; |  | ||||||
| export * from '../test/generated-wrappers/multi_asset_proxy'; |  | ||||||
| export * from '../test/generated-wrappers/ownable'; |  | ||||||
| export * from '../test/generated-wrappers/shell_bridge'; |  | ||||||
| export * from '../test/generated-wrappers/snow_swap_bridge'; |  | ||||||
| export * from '../test/generated-wrappers/static_call_proxy'; |  | ||||||
| export * from '../test/generated-wrappers/sushi_swap_bridge'; |  | ||||||
| export * from '../test/generated-wrappers/swerve_bridge'; |  | ||||||
| export * from '../test/generated-wrappers/test_bancor_bridge'; |  | ||||||
| export * from '../test/generated-wrappers/test_chai_bridge'; |  | ||||||
| export * from '../test/generated-wrappers/test_dex_forwarder_bridge'; |  | ||||||
| export * from '../test/generated-wrappers/test_dydx_bridge'; |  | ||||||
| export * from '../test/generated-wrappers/test_erc20_bridge'; |  | ||||||
| export * from '../test/generated-wrappers/test_eth2_dai_bridge'; |  | ||||||
| export * from '../test/generated-wrappers/test_kyber_bridge'; |  | ||||||
| export * from '../test/generated-wrappers/test_static_call_target'; |  | ||||||
| export * from '../test/generated-wrappers/test_uniswap_bridge'; |  | ||||||
| export * from '../test/generated-wrappers/test_uniswap_v2_bridge'; |  | ||||||
| export * from '../test/generated-wrappers/uniswap_bridge'; |  | ||||||
| export * from '../test/generated-wrappers/uniswap_v2_bridge'; |  | ||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user