Compare commits

..

81 Commits

Author SHA1 Message Date
Jacob Evans
bbd3c03969 Publish
- @0x/contracts-asset-proxy@2.2.6
 - @0x/contracts-coordinator@2.0.11
 - @0x/contracts-dev-utils@0.0.8
 - @0x/contracts-erc1155@1.1.13
 - @0x/contracts-erc20@2.2.12
 - @0x/contracts-erc721@2.1.13
 - @0x/contracts-exchange-forwarder@3.0.10
 - @0x/contracts-exchange-libs@3.0.6
 - @0x/contracts-exchange@2.1.12
 - @0x/contracts-extensions@4.0.6
 - @0x/contracts-multisig@3.1.12
 - @0x/contracts-test-utils@3.1.14
 - @0x/contracts-utils@3.2.2
 - 0x.js@7.0.0
 - @0x/abi-gen-templates@2.4.2
 - @0x/abi-gen-wrappers@5.3.0
 - @0x/abi-gen@4.1.1
 - @0x/assert@2.1.4
 - @0x/asset-buyer@6.1.12
 - @0x/asset-swapper@1.0.2
 - @0x/base-contract@5.3.2
 - @0x/connect@5.0.17
 - @0x/contract-addresses@3.1.0
 - @0x/contract-artifacts@2.2.0
 - @0x/contract-wrappers@11.1.0
 - @0x/dev-utils@2.3.1
 - @0x/fill-scenarios@3.0.17
 - @0x/instant@1.0.29
 - @0x/json-schemas@4.0.0
 - @0x/migrations@4.3.0
 - @0x/monorepo-scripts@1.0.35
 - @0x/order-utils@8.3.0
 - @0x/python-contract-wrappers@1.0.4
 - @0x/sol-compiler@3.1.13
 - @0x/sol-coverage@3.0.10
 - @0x/sol-doc@2.0.17
 - @0x/sol-profiler@3.1.12
 - @0x/sol-trace@2.0.18
 - @0x/sol-tracing-utils@6.0.17
 - @0x/sra-spec@2.0.15
 - @0x/subproviders@5.0.2
 - @0x/testnet-faucets@1.0.85
 - @0x/web3-wrapper@6.0.11
 - @0x/website@0.0.88
2019-08-22 14:13:00 +10:00
Jacob Evans
a4405c3d39 Updated CHANGELOGS 2019-08-22 14:12:40 +10:00
Xianny
0fe4f587d8 update ethereumjs-blockstream 6.0.0 -> ^7.0.0 (#2089) 2019-08-21 18:39:07 -07:00
Francesco Agosti
d3c714bd17 Merge pull request #2085 from 0xProject/feature/cfl-page-fix
Fix small issue on mobile nav
2019-08-21 14:54:17 -07:00
fragosti
c399b7a7d5 Add Asset Swapper to footer 2019-08-21 14:30:55 -07:00
fragosti
b9234e94fb Fix small issue on mobile nav 2019-08-21 14:30:55 -07:00
Francesco Agosti
417bb87785 Merge pull request #2081 from 0xProject/feature/cfl-page
CFL page
2019-08-20 18:24:40 -07:00
fragosti
0233ae3134 Use Asset Swapper on CFL page 2019-08-20 17:48:57 -07:00
fragosti
eed0c5dd59 Distinguish between asset swapper page and docs 2019-08-20 17:39:15 -07:00
fragosti
2b3b167095 Fix path of asset swapper link in product dropdown 2019-08-20 17:30:15 -07:00
fragosti
5d91d19808 Disable jsx-curly-spacing on cfl page 2019-08-20 17:24:49 -07:00
fragosti
0f374ddee9 Change AssetBuyer to AssetSwapper on instant page 2019-08-20 17:08:03 -07:00
Chris Kalani
a65a9913cd Fixed up some design nits and updated copy 2019-08-20 16:51:06 -07:00
fragosti
1ead32c666 Asset-Swapper -> asset-swapper 2019-08-20 15:35:39 -07:00
fragosti
d1af9fc780 use asset-swapper instead of Asset-Swapper 2019-08-20 15:31:50 -07:00
fragosti
0f06737fb6 Fix install prompt on mobile layout 2019-08-20 15:01:59 -07:00
fragosti
1676231532 Rename variables to be compatible with new backend 2019-08-20 14:36:57 -07:00
fragosti
b1caf697c8 Update titles and descriptions 2019-08-20 13:52:50 -07:00
fragosti
51481065fe Fix jumping around of editor 2019-08-20 11:56:09 -07:00
fragosti
e367da710c Fix massive i 2019-08-20 11:47:05 -07:00
fragosti
f493d6524d Change to crypto algo-traders 2019-08-20 11:22:21 -07:00
fragosti
e1b85da2a7 Make terminal text selectable 2019-08-20 11:16:28 -07:00
F. Eugene Aumson
22c6548ed1 abi-gen/test-cli: rm expected-output; use git diff (#2079)
* abi-gen/test-cli: rm expected-output; use git diff

* abi-gen/test-cli: Simplify git diff test script

* Remove abi-gen/test-cli/output from .gitignore
2019-08-20 14:08:54 -04:00
Fabio B
afb32c087d Merge pull request #2017 from jangerritharms/fix/broken-validator-signatures
Fix broken validator signatures
2019-08-20 18:44:50 +02:00
Jan-Gerrit Harms
bbc1ed1c64 Update test to adhere to v2.1 Validator revert 2019-08-20 14:53:10 +02:00
Jan-Gerrit Harms
3a46f1a27a Merge branch 'development' into fix/broken-validator-signatures 2019-08-20 09:30:21 +02:00
Jacob Evans
90cd364780 Merge pull request #2078 from 0xProject/fix/isValidSignature-2.1
Update development to 2.1 isValidSignature with magic salt
2019-08-20 16:34:48 +10:00
Jacob Evans
6795e6f078 Update comments with bytes4 values 2019-08-20 16:15:44 +10:00
fragosti
cfb3404349 Fix dogfood link 2019-08-19 20:49:35 -07:00
fragosti
0212f3ee78 Run linter 2019-08-19 19:28:42 -07:00
fragosti
6b2995a4ee Remove didError state from CFLmetrics component 2019-08-19 19:24:37 -07:00
fragosti
09e7ac54d4 Replace hummingbot link 2019-08-19 19:18:58 -07:00
fragosti
f69009d4a8 Add CFL to mobile dropdown and remove extensions for desktop 2019-08-19 19:16:26 -07:00
fragosti
206802ae33 Make layout more responsive 2019-08-19 19:08:48 -07:00
fragosti
91d4138fb8 Fix react key error 2019-08-19 18:22:17 -07:00
fragosti
cb455f951a Add loading state to CFL metrics 2019-08-19 18:19:01 -07:00
fragosti
5f25d20cd0 Improve tooltip and copy 2019-08-19 17:54:10 -07:00
fragosti
1f0e2cd910 Add info tooltip 2019-08-19 17:46:11 -07:00
Jacob Evans
1749d02701 Add python linter exception 2019-08-20 10:30:09 +10:00
fragosti
55ace3179c Add disclaimer component 2019-08-19 17:00:41 -07:00
fragosti
7866d9ccb4 Implement CFL metrics MVP 2019-08-19 16:52:40 -07:00
Brandon Millman
51f73d07fa Merge pull request #2080 from 0xProject/feature/website/adding-michael
Adding Michael Zhu to the team page
2019-08-19 13:02:18 -07:00
Francesco Agosti
63d84674ab Merge pull request #2074 from 0xProject/feature/website/remove-faq
Removing the old FAQ page and all references
2019-08-19 12:36:59 -07:00
Chris Kalani
14066997b2 Adding Michael Zhu to the team page 2019-08-19 12:34:19 -07:00
F. Eugene Aumson
28561e765a Update README.md (#2077)
* Update README.md

* top-level README.md: explain Python dependency

* top-level README.md: suggest node v6 OR v8
2019-08-19 13:55:06 -04:00
Jacob Evans
453fbbdc5d Update python doc strings 2019-08-19 15:55:28 +10:00
Jacob Evans
1e1e5ec10d Catch revert in IWallet/IValidator and return false 2019-08-19 15:45:32 +10:00
Jacob Evans
2088b0e459 Update python doc string 2019-08-19 15:13:09 +10:00
Jacob Evans
58400d9e01 Update python docs string 2019-08-19 14:40:46 +10:00
Jacob Evans
ac9375f1d2 Updated generated-wrappers? 2019-08-19 14:10:50 +10:00
Jacob Evans
db061c9355 Update Whitelist.sol 2019-08-19 12:15:46 +10:00
Jacob Evans
d5ce6c464b Update Wallet signature test 2019-08-19 11:55:48 +10:00
Jacob Evans
b06205bb7f Fix lint errors 2019-08-19 11:36:35 +10:00
Jacob Evans
f528a3e1de Update Order-utils to check magic values 2019-08-19 11:14:35 +10:00
Jacob Evans
bddfdacfad Update Interfaces for IValidator and IWallet 2019-08-19 11:08:06 +10:00
Jacob Evans
d3cdd3f235 Update development to 2.1 isValidSignature with magic salt 2019-08-19 10:35:26 +10:00
Chris Kalani
41ae45ea40 Fixed build error 2019-08-16 14:17:12 -07:00
Chris Kalani
657e0895ea Removing the olf FAW page and all references 2019-08-16 13:56:03 -07:00
Jan-Gerrit Harms
b2592d1cc2 Removed the order-watcher files that were accidentally added 2019-08-16 14:02:32 +02:00
Jan-Gerrit Harms
aa3524c3b2 Merge branch 'development' into fix/broken-validator-signatures 2019-08-16 13:04:27 +02:00
fragosti
39deb1a05f Make it possible to show figure in hero on the bottom on mobile 2019-08-15 17:23:20 -07:00
fragosti
302d08e290 Make website mobile friendly 2019-08-15 17:01:50 -07:00
fragosti
05489dd7f1 Implement basic CFL landing page 2019-08-15 16:33:52 -07:00
Jan-Gerrit Harms
55bd076602 Merge branch 'development' into fix/broken-validator-signatures 2019-08-14 09:37:20 +02:00
Jan-Gerrit Harms
7a224fe08f Merge branch 'development' into fix/broken-validator-signatures 2019-08-13 19:49:24 +02:00
Jan-Gerrit Harms
3bdeb82097 Updated CHANGELOG of 0x.js and @0x/order-watcher 2019-08-09 09:58:29 +02:00
Jan-Gerrit Harms
f49ab3f919 Merge branch 'development' into fix/broken-validator-signatures 2019-08-09 09:47:42 +02:00
Jan-Gerrit Harms
42d5bdd3ab Explicitly check exchangeAddress definedness 2019-08-09 09:39:49 +02:00
Jan-Gerrit Harms
7228cbfe92 Pass exchangeAddress instead of SignatureOpts 2019-08-09 09:36:32 +02:00
Jan-Gerrit Harms
11e2fc5bc4 Added documentation for additional parameters 2019-07-30 15:18:08 +02:00
Jan-Gerrit Harms
3e88f820b8 Prettified changelog 2019-07-30 15:11:24 +02:00
Jan-Gerrit Harms
163750f8c2 Updated internal usages of isValidSignatureAsync 2019-07-30 14:52:35 +02:00
Jan-Gerrit Harms
4aabc5d791 Fixed merge conflict in changelog 2019-07-30 14:43:24 +02:00
Jan-Gerrit Harms
c9a7b9dcc1 Appended to changelog in the order-utils package 2019-07-30 14:35:11 +02:00
Jan-Gerrit Harms
98075b5653 Revert "Updated changelog"
This reverts commit 57ae5be916.
2019-07-30 14:32:27 +02:00
Jan-Gerrit Harms
57ae5be916 Updated changelog 2019-07-30 14:26:46 +02:00
Jan-Gerrit Harms
8caf62997f Forgot formatting again 2019-07-30 13:26:27 +02:00
Jan-Gerrit Harms
f8656ad376 Added integration tests and fixed another bug 2019-07-30 13:25:36 +02:00
Jan-Gerrit Harms
29c6c2a2ad Add exports to 0x.js 2019-07-30 10:15:41 +02:00
Jan-Gerrit Harms
f2db67ef02 Fixed prettier errors 2019-07-30 09:50:54 +02:00
Jan-Gerrit Harms
72b8ef33d9 Fixes #1998, still needs Integration testing 2019-07-29 21:39:21 +02:00
2205 changed files with 163229 additions and 167315 deletions

View File

@@ -23,21 +23,37 @@ jobs:
# command: npm set prefix=/home/circleci/npm && echo 'export PATH=$HOME/circleci/npm/bin:$PATH' >> /home/circleci/.bashrc # command: npm set prefix=/home/circleci/npm && echo 'export PATH=$HOME/circleci/npm/bin:$PATH' >> /home/circleci/.bashrc
- run: - run:
name: install-yarn name: install-yarn
command: npm install --global yarn@1.17.0 command: npm install --global yarn@1.9.4
- 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:no_website
- run: yarn build:ts - run: yarn build:ts
- save_cache: - save_cache:
key: repo-{{ .Environment.CIRCLE_SHA1 }} key: repo-{{ .Environment.CIRCLE_SHA1 }}
paths: paths:
- ~/repo - ~/repo
- save_cache:
key: python-contract-wrappers-{{ .Environment.CIRCLE_SHA1 }}
paths:
- ~/repo/packages/python-contract-wrappers/generated
- store_artifacts:
path: ~/repo/packages/python-contract-wrappers/generated
- store_artifacts: - store_artifacts:
path: ~/repo/packages/abi-gen/test-cli/output path: ~/repo/packages/abi-gen/test-cli/output
- store_artifacts: - store_artifacts:
path: ~/repo/packages/abi-gen-wrappers/generated_docs path: ~/repo/packages/abi-gen-wrappers/generated_docs
build-website:
resource_class: medium+
docker:
- image: nikolaik/python-nodejs:python3.7-nodejs8
working_directory: ~/repo
steps:
- restore_cache:
keys:
- repo-{{ .Environment.CIRCLE_SHA1 }}
- run: cd packages/website && yarn build:prod
test-contracts-ganache: test-contracts-ganache:
resource_class: medium+ resource_class: medium+
docker: docker:
@@ -47,41 +63,19 @@ jobs:
- 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-extensions @0x/contracts-asset-proxy @0x/contracts-exchange @0x/contracts-exchange-forwarder @0x/contracts-coordinator @0x/contracts-tests @0x/contracts-staking - run: yarn wsrun test:circleci @0x/contracts-multisig @0x/contracts-utils @0x/contracts-exchange-libs @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-extensions @0x/contracts-asset-proxy @0x/contracts-exchange @0x/contracts-exchange-forwarder @0x/contracts-coordinator @0x/contracts-dev-utils
test-exchange-ganache-3.0: test-contracts-geth:
resource_class: medium+
docker: docker:
- image: nikolaik/python-nodejs:python3.7-nodejs8 - image: nikolaik/python-nodejs:python3.7-nodejs8
- image: 0xorg/devnet
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 # HACK(albrow): we need to sleep 10 seconds to ensure the devnet is
test-integrations-ganache-3.0: # initialized
resource_class: medium+ - run: sleep 10 && TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-multisig @0x/contracts-utils @0x/contracts-exchange-libs @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-extensions @0x/contracts-asset-proxy @0x/contracts-exchange @0x/contracts-exchange-forwarder @0x/contracts-coordinator @0x/contracts-dev-utils
docker:
- image: nikolaik/python-nodejs:python3.7-nodejs8
working_directory: ~/repo
steps:
- restore_cache:
keys:
- repo-{{ .Environment.CIRCLE_SHA1 }}
- run: yarn wsrun test:circleci @0x/contracts-integrations
test-contracts-rest-ganache-3.0:
resource_class: medium+
docker:
- image: nikolaik/python-nodejs:python3.7-nodejs8
working_directory: ~/repo
steps:
- restore_cache:
keys:
- repo-{{ .Environment.CIRCLE_SHA1 }}
- run: yarn wsrun test:circleci @0x/contracts-multisig @0x/contracts-utils @0x/contracts-exchange-libs @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-asset-proxy @0x/contracts-exchange-forwarder @0x/contracts-tests @0x/contracts-staking @0x/contracts-coordinator
# TODO(dorothy-zbornak): Re-enable after updating this package for
# 3.0. At that time, also remove exclusion from monorepo
# package.json's test script.
# - run: yarn wsrun test:circleci @0x/contracts-extensions
test-publish: test-publish:
resource_class: medium+ resource_class: medium+
docker: docker:
@@ -92,9 +86,7 @@ jobs:
- restore_cache: - restore_cache:
keys: keys:
- repo-{{ .Environment.CIRCLE_SHA1 }} - repo-{{ .Environment.CIRCLE_SHA1 }}
- run: - run: yarn test:publish:circleci
command: yarn test:publish:circleci
no_output_timeout: 1800
test-doc-generation: test-doc-generation:
docker: docker:
- image: nikolaik/python-nodejs:python3.7-nodejs8 - image: nikolaik/python-nodejs:python3.7-nodejs8
@@ -103,9 +95,7 @@ jobs:
- restore_cache: - restore_cache:
keys: keys:
- repo-{{ .Environment.CIRCLE_SHA1 }} - repo-{{ .Environment.CIRCLE_SHA1 }}
- run: - run: yarn test:generate_docs:circleci
command: yarn test:generate_docs:circleci
no_output_timeout: 1200
test-rest: test-rest:
docker: docker:
- image: nikolaik/python-nodejs:python3.7-nodejs8 - image: nikolaik/python-nodejs:python3.7-nodejs8
@@ -116,23 +106,15 @@ jobs:
- repo-{{ .Environment.CIRCLE_SHA1 }} - repo-{{ .Environment.CIRCLE_SHA1 }}
- run: yarn wsrun test:circleci @0x/contracts-test-utils - run: yarn wsrun test:circleci @0x/contracts-test-utils
- run: yarn wsrun test:circleci @0x/abi-gen - run: yarn wsrun test:circleci @0x/abi-gen
# TODO (xianny): Needs to be updated for 3.0 - run: yarn wsrun test:circleci @0x/asset-buyer
# - run: yarn wsrun test:circleci @0x/asset-buyer
# TODO: Needs to be updated for 3.0. At that time, also remove
# exclusion from monorepo package.json's test script.
# - run: yarn wsrun test:circleci @0x/asset-swapper
- run: yarn wsrun test:circleci @0x/contract-artifacts - run: yarn wsrun test:circleci @0x/contract-artifacts
- run: yarn wsrun test:circleci @0x/assert - run: yarn wsrun test:circleci @0x/assert
- run: yarn wsrun test:circleci @0x/base-contract - run: yarn wsrun test:circleci @0x/base-contract
# TODO (xianny): Needs to be updated for 3.0 - run: yarn wsrun test:circleci @0x/connect
# - run: yarn wsrun test:circleci @0x/connect
- run: yarn wsrun test:circleci @0x/contract-wrappers - run: yarn wsrun test:circleci @0x/contract-wrappers
- run: yarn wsrun test:circleci @0x/dev-utils - run: yarn wsrun test:circleci @0x/dev-utils
- run: yarn wsrun test:circleci @0x/json-schemas - run: yarn wsrun test:circleci @0x/json-schemas
- run: yarn wsrun test:circleci @0x/order-utils - run: yarn wsrun test:circleci @0x/order-utils
# TODO: Needs to be updated for 3.0. At that time, also remove
# exclusion from monorepo package.json's test script.
# - run: yarn wsrun test:circleci @0x/orderbook
- run: yarn wsrun test:circleci @0x/sol-compiler - run: yarn wsrun test:circleci @0x/sol-compiler
- run: yarn wsrun test:circleci @0x/sol-tracing-utils - run: yarn wsrun test:circleci @0x/sol-tracing-utils
- run: yarn wsrun test:circleci @0x/sol-doc - run: yarn wsrun test:circleci @0x/sol-doc
@@ -200,40 +182,20 @@ jobs:
working_directory: ~/repo working_directory: ~/repo
docker: docker:
- image: nikolaik/python-nodejs:python3.7-nodejs8 - image: nikolaik/python-nodejs:python3.7-nodejs8
- image: 0xorg/ganache-cli:4.4.0-beta.1 - image: 0xorg/ganache-cli:2.2.2
- image: 0xorg/launch-kit-backend:74bcc39
environment: environment:
VERSION: 4.4.0-beta.1 RPC_URL: http://localhost:8545
SNAPSHOT_NAME: 0x_ganache_snapshot-v3-beta NETWORK_ID: 50
- image: 0xorg/mesh:6.0.0-beta-0xv3
environment:
ETHEREUM_RPC_URL: 'http://localhost:8545'
ETHEREUM_NETWORK_ID: '50'
ETHEREUM_CHAIN_ID: '1337'
USE_BOOTSTRAP_LIST: 'true'
VERBOSITY: 3
PRIVATE_KEY_PATH: ''
BLOCK_POLLING_INTERVAL: '5s'
P2P_LISTEN_PORT: '60557'
command: |
sh -c "waitForGanache () { until printf 'POST /\r\nContent-Length: 26\r\n\r\n{\"method\":\"net_listening\"}' | nc localhost 8545 | grep true; do continue; done }; waitForGanache && ./mesh"
- image: 0xorg/launch-kit-backend:v3
environment:
RPC_URL: 'http://localhost:8545'
CHAIN_ID: 1337
WHITELIST_ALL_TOKENS: True WHITELIST_ALL_TOKENS: True
FEE_RECIPIENT: '0x0000000000000000000000000000000000000001'
MAKER_FEE_UNIT_AMOUNT: 0
TAKER_FEE_UNIT_AMOUNT: 0
MESH_ENDPOINT: 'ws://localhost:60557'
command: | command: |
sh -c "waitForMesh () { sleep 5; }; waitForMesh && node_modules/.bin/forever ts/lib/index.js" sh -c "until printf 'POST /\r\nContent-Length: 26\r\n\r\n{\"method\":\"net_listening\"}' | nc localhost 8545 | grep true; do continue; done; node_modules/.bin/forever ts/lib/index.js"
steps: steps:
- checkout - checkout
- restore_cache: - restore_cache:
key: installed-py-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }} key: installed-py-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
- restore_cache: - restore_cache:
keys: key: python-contract-wrappers-{{ .Environment.CIRCLE_SHA1 }}
- repo-{{ .Environment.CIRCLE_SHA1 }}
- run: - run:
command: | command: |
cd python-packages cd python-packages
@@ -248,14 +210,8 @@ jobs:
- run: - run:
command: | command: |
cd python-packages cd python-packages
./parallel coverage run setup.py test ./parallel_without_sra_client coverage run setup.py test
./build_docs ./build_docs
- run:
command: |
# copy generated wrappers into contract_wrappers/build,
# JUST so CircleCI will persist them as build artifacts.
cd python-packages/contract_wrappers/src/zero_ex
for i in contract_wrappers/[^__]*/; do mkdir -p ../../build/$i; cp $i/__init__.py ../../build/$i; done
- save_cache: - save_cache:
key: coverage-python-contract-addresses-{{ .Environment.CIRCLE_SHA1 }} key: coverage-python-contract-addresses-{{ .Environment.CIRCLE_SHA1 }}
paths: paths:
@@ -280,6 +236,10 @@ jobs:
key: coverage-python-sra-client-{{ .Environment.CIRCLE_SHA1 }} key: coverage-python-sra-client-{{ .Environment.CIRCLE_SHA1 }}
paths: paths:
- ~/repo/python-packages/sra_client/.coverage - ~/repo/python-packages/sra_client/.coverage
- store_artifacts:
path: ~/repo/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc20_token/__init__.py
- store_artifacts:
path: ~/repo/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/__init__.py
- store_artifacts: - store_artifacts:
path: ~/repo/python-packages/contract_addresses/build path: ~/repo/python-packages/contract_addresses/build
- store_artifacts: - store_artifacts:
@@ -329,8 +289,7 @@ jobs:
- restore_cache: - restore_cache:
key: installed-py-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }} key: installed-py-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
- restore_cache: - restore_cache:
keys: key: python-contract-wrappers-{{ .Environment.CIRCLE_SHA1 }}
- repo-{{ .Environment.CIRCLE_SHA1 }}
- run: - run:
command: | command: |
python -m ensurepip python -m ensurepip
@@ -339,7 +298,6 @@ jobs:
./install ./install
./lint ./lint
static-tests: static-tests:
resource_class: large
working_directory: ~/repo working_directory: ~/repo
docker: docker:
- image: nikolaik/python-nodejs:python3.7-nodejs8 - image: nikolaik/python-nodejs:python3.7-nodejs8
@@ -350,7 +308,6 @@ jobs:
- run: yarn lerna run lint - run: yarn lerna run lint
- run: yarn prettier:ci - run: yarn prettier:ci
- run: yarn deps_versions:ci - run: yarn deps_versions:ci
- run: yarn diff_md_docs:ci
- run: cd packages/0x.js && yarn build:umd:prod - run: cd packages/0x.js && yarn build:umd:prod
- run: yarn bundlewatch - run: yarn bundlewatch
submit-coverage: submit-coverage:
@@ -430,15 +387,17 @@ workflows:
main: main:
jobs: jobs:
- build - build
- test-exchange-ganache-3.0: - build-website:
requires: requires:
- build - build
- test-integrations-ganache-3.0: - test-contracts-ganache:
requires:
- build
- test-contracts-rest-ganache-3.0:
requires: requires:
- build - build
# TODO(albrow): Tests always fail on Geth right now because our fork
# is outdated. Uncomment once we have updated our Geth fork.
# - test-contracts-geth:
# requires:
# - build
- test-rest: - test-rest:
requires: requires:
- build - build
@@ -453,14 +412,12 @@ workflows:
- build - build
- submit-coverage: - submit-coverage:
requires: requires:
- test-contracts-rest-ganache-3.0
- test-exchange-ganache-3.0
- test-rest - test-rest
- static-tests - test-python
- test-python:
requires:
- build
- static-tests-python: - static-tests-python:
requires:
- test-python
- test-python:
requires: requires:
- build - build
# skip python tox run for now, as we don't yet have multiple test environments to support. # skip python tox run for now, as we don't yet have multiple test environments to support.

View File

@@ -13,6 +13,7 @@ contracts: ['contracts']
@0x/instant: ['packages/instant'] @0x/instant: ['packages/instant']
@0x/abi-gen-templates: ['packages/abi-gen-templates'] @0x/abi-gen-templates: ['packages/abi-gen-templates']
@0x/abi-gen: ['packages/abi-gen'] @0x/abi-gen: ['packages/abi-gen']
@0x/website: ['packages/website']
@0x/sol-coverage: ['packages/sol-coverage'] @0x/sol-coverage: ['packages/sol-coverage']
@0x/sol-profiler: ['packages/sol-profiler'] @0x/sol-profiler: ['packages/sol-profiler']
@0x/sol-trace: ['packages/sol-trace'] @0x/sol-trace: ['packages/sol-trace']
@@ -32,3 +33,7 @@ contracts: ['contracts']
@0x/json-schemas: ['packages/json-schemas'] @0x/json-schemas: ['packages/json-schemas']
@0x/ethereum-types: ['ethereum-types'] @0x/ethereum-types: ['ethereum-types']
@0x/connect: ['packages/connect'] @0x/connect: ['packages/connect']
@0x/fill-scenarios: ['packages/fill-scenarios']
@0x/dev-tools-pages: ['packages/dev-tools-pages']
@0x/testnet-faucets: ['packages/testnet-faucets']
@0x/monorepo-scripts: ['packages/monorepo-scripts']

80
.gitignore vendored
View File

@@ -40,12 +40,9 @@ build/Release
node_modules/ node_modules/
jspm_packages/ jspm_packages/
# TypeScript v1 declaration files # Typescript v1 declaration files
typings/ typings/
# NVM config
.nvmrc
# Optional npm cache directory # Optional npm cache directory
.npm .npm
.npmrc .npmrc
@@ -78,84 +75,43 @@ TODO.md
# VSCode file # VSCode file
.vscode .vscode
packages/website/public/bundle*
packages/dev-tools-pages/public/bundle*
# server cli
packages/testnet-faucets/server/
# generated contract artifacts/ # generated contract artifacts/
contracts/integrations/generated-artifacts/
contracts/integrations/test/generated-artifacts/
contracts/staking/generated-artifacts/
contracts/staking/test/generated-artifacts/
contracts/coordinator/generated-artifacts/ contracts/coordinator/generated-artifacts/
contracts/coordinator/test/generated-artifacts/
contracts/exchange/generated-artifacts/ contracts/exchange/generated-artifacts/
contracts/exchange/test/generated-artifacts/
contracts/asset-proxy/generated-artifacts/ contracts/asset-proxy/generated-artifacts/
contracts/asset-proxy/test/generated-artifacts/
contracts/multisig/generated-artifacts/ contracts/multisig/generated-artifacts/
contracts/multisig/test/generated-artifacts/
contracts/utils/generated-artifacts/ contracts/utils/generated-artifacts/
contracts/utils/test/generated-artifacts/
contracts/exchange-libs/generated-artifacts/ contracts/exchange-libs/generated-artifacts/
contracts/exchange-libs/test/generated-artifacts/
contracts/erc20/generated-artifacts/ contracts/erc20/generated-artifacts/
contracts/erc20/test/generated-artifacts/
contracts/erc721/generated-artifacts/ contracts/erc721/generated-artifacts/
contracts/erc721/test/generated-artifacts/
contracts/erc1155/generated-artifacts/ contracts/erc1155/generated-artifacts/
contracts/erc1155/test/generated-artifacts/
contracts/extensions/generated-artifacts/ contracts/extensions/generated-artifacts/
contracts/extensions/test/generated-artifacts/
contracts/exchange-forwarder/generated-artifacts/ contracts/exchange-forwarder/generated-artifacts/
contracts/exchange-forwarder/test/generated-artifacts/
contracts/dev-utils/generated-artifacts/ contracts/dev-utils/generated-artifacts/
contracts/dev-utils/test/generated-artifacts/
packages/sol-tracing-utils/test/fixtures/artifacts/ packages/sol-tracing-utils/test/fixtures/artifacts/
python-packages/contract_artifacts/src/zero_ex/contract_artifacts/artifacts/ python-packages/contract_artifacts/src/zero_ex/contract_artifacts/artifacts/
# generated truffle contract artifacts/
contracts/staking/build/
contracts/coordinator/build/
contracts/exchange/build/
contracts/asset-proxy/build/
contracts/multisig/build/
contracts/utils/build/
contracts/exchange-libs/build/
contracts/erc20/build/
contracts/erc721/build/
contracts/erc1155/build/
contracts/extensions/build/
contracts/exchange-forwarder/build/
contracts/dev-utils/build/
# generated contract wrappers # generated contract wrappers
packages/abi-gen-wrappers/src/generated-wrappers/
packages/python-contract-wrappers/generated/ packages/python-contract-wrappers/generated/
contracts/integrations/generated-wrappers/
contracts/integrations/test/generated-wrappers/
contracts/staking/generated-wrappers/
contracts/staking/test/generated-wrappers/
contracts/coordinator/generated-wrappers/ contracts/coordinator/generated-wrappers/
contracts/coordinator/test/generated-wrappers/
contracts/exchange/generated-wrappers/ contracts/exchange/generated-wrappers/
contracts/exchange/test/generated-wrappers/
contracts/asset-proxy/generated-wrappers/ contracts/asset-proxy/generated-wrappers/
contracts/asset-proxy/test/generated-wrappers/
contracts/multisig/generated-wrappers/ contracts/multisig/generated-wrappers/
contracts/multisig/test/generated-wrappers/
contracts/utils/generated-wrappers/ contracts/utils/generated-wrappers/
contracts/utils/test/generated-wrappers/
contracts/exchange-libs/generated-wrappers/ contracts/exchange-libs/generated-wrappers/
contracts/exchange-libs/test/generated-wrappers/
contracts/erc20/generated-wrappers/ contracts/erc20/generated-wrappers/
contracts/erc20/test/generated-wrappers/
contracts/erc721/generated-wrappers/ contracts/erc721/generated-wrappers/
contracts/erc721/test/generated-wrappers/
contracts/erc1155/generated-wrappers/ contracts/erc1155/generated-wrappers/
contracts/erc1155/test/generated-wrappers/
contracts/extensions/generated-wrappers/ contracts/extensions/generated-wrappers/
contracts/extensions/test/generated-wrappers/
contracts/exchange-forwarder/generated-wrappers/ contracts/exchange-forwarder/generated-wrappers/
contracts/exchange-forwarder/test/generated-wrappers/
contracts/dev-utils/generated-wrappers/ contracts/dev-utils/generated-wrappers/
contracts/dev-utils/test/generated-wrappers/
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/dev_utils/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc20_token/__init__.py python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc20_token/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/__init__.py python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/asset_proxy_owner/__init__.py python-packages/contract_wrappers/src/zero_ex/contract_wrappers/asset_proxy_owner/__init__.py
@@ -164,26 +120,25 @@ python-packages/contract_wrappers/src/zero_ex/contract_wrappers/coordinator_regi
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/dummy_erc20_token/__init__.py python-packages/contract_wrappers/src/zero_ex/contract_wrappers/dummy_erc20_token/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/dummy_erc721_token/__init__.py python-packages/contract_wrappers/src/zero_ex/contract_wrappers/dummy_erc721_token/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/dutch_auction/__init__.py python-packages/contract_wrappers/src/zero_ex/contract_wrappers/dutch_auction/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc1155_mintable/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc1155_proxy/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc20_proxy/__init__.py python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc20_proxy/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc721_proxy/__init__.py python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc721_proxy/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc721_token/__init__.py python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc721_token/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/eth_balance_checker/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/forwarder/__init__.py python-packages/contract_wrappers/src/zero_ex/contract_wrappers/forwarder/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/i_asset_proxy/__init__.py python-packages/contract_wrappers/src/zero_ex/contract_wrappers/i_asset_proxy/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/i_validator/__init__.py python-packages/contract_wrappers/src/zero_ex/contract_wrappers/i_validator/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/i_wallet/__init__.py python-packages/contract_wrappers/src/zero_ex/contract_wrappers/i_wallet/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/multi_asset_proxy/__init__.py python-packages/contract_wrappers/src/zero_ex/contract_wrappers/multi_asset_proxy/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/order_validator/__init__.py python-packages/contract_wrappers/src/zero_ex/contract_wrappers/order_validator/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/staking/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/staking_proxy/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/static_call_proxy/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/weth9/__init__.py python-packages/contract_wrappers/src/zero_ex/contract_wrappers/weth9/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/zrx_token/__init__.py python-packages/contract_wrappers/src/zero_ex/contract_wrappers/zrx_token/__init__.py
# solc-bin in sol-compiler # solc-bin in sol-compiler
packages/sol-compiler/solc_bin/ packages/sol-compiler/solc_bin/
# Monorepo scripts
packages/*/scripts/
# python stuff # python stuff
.eggs .eggs
.mypy_cache .mypy_cache
@@ -194,14 +149,5 @@ __pycache__
python-packages/*/src/*.egg-info python-packages/*/src/*.egg-info
python-packages/*/.coverage python-packages/*/.coverage
# python keeps package-local copies of json schemas and contract addresses # python keeps package-local copies of json schemas
python-packages/json_schemas/src/zero_ex/json_schemas/schemas python-packages/json_schemas/src/zero_ex/json_schemas/schemas
python-packages/contract_addresses/src/zero_ex/contract_addresses/addresses.json
# Doc README copy
packages/*/docs/README.md
.DS_Store
# the snapshot that gets built for migrations sure does have a ton of files
packages/migrations/0x_ganache_snapshot*

View File

@@ -1,78 +1,34 @@
lib lib
.nyc_output .nyc_output
/contracts/integrations/generated-wrappers
/contracts/integrations/test/generated-wrappers
/contracts/integrations/generated-artifacts
/contracts/integrations/test/generated-artifacts
/contracts/staking/generated-wrappers
/contracts/staking/test/generated-wrappers
/contracts/staking/generated-artifacts
/contracts/staking/test/generated-artifacts
/contracts/coordinator/generated-wrappers /contracts/coordinator/generated-wrappers
/contracts/coordinator/test/generated-wrappers
/contracts/coordinator/generated-artifacts /contracts/coordinator/generated-artifacts
/contracts/coordinator/test/generated-artifacts
/contracts/exchange/generated-wrappers /contracts/exchange/generated-wrappers
/contracts/exchange/test/generated-wrappers
/contracts/exchange/generated-artifacts /contracts/exchange/generated-artifacts
/contracts/exchange/test/generated-artifacts
/contracts/asset-proxy/generated-wrappers /contracts/asset-proxy/generated-wrappers
/contracts/asset-proxy/test/generated-wrappers
/contracts/asset-proxy/generated-artifacts /contracts/asset-proxy/generated-artifacts
/contracts/asset-proxy/test/generated-artifacts
/contracts/multisig/generated-wrappers /contracts/multisig/generated-wrappers
/contracts/multisig/test/generated-wrappers
/contracts/multisig/generated-artifacts /contracts/multisig/generated-artifacts
/contracts/multisig/test/generated-artifacts
/contracts/utils/generated-wrappers /contracts/utils/generated-wrappers
/contracts/utils/test/generated-wrappers
/contracts/utils/generated-artifacts /contracts/utils/generated-artifacts
/contracts/utils/test/generated-artifacts
/contracts/exchange-libs/generated-wrappers /contracts/exchange-libs/generated-wrappers
/contracts/exchange-libs/test/generated-wrappers
/contracts/exchange-libs/generated-artifacts /contracts/exchange-libs/generated-artifacts
/contracts/exchange-libs/test/generated-artifacts
/contracts/erc20/generated-wrappers /contracts/erc20/generated-wrappers
/contracts/erc20/test/generated-wrappers
/contracts/erc20/generated-artifacts /contracts/erc20/generated-artifacts
/contracts/erc20/test/generated-artifacts
/contracts/erc721/generated-wrappers /contracts/erc721/generated-wrappers
/contracts/erc721/test/generated-wrappers
/contracts/erc721/generated-artifacts /contracts/erc721/generated-artifacts
/contracts/erc721/test/generated-artifacts
/contracts/erc1155/generated-wrappers /contracts/erc1155/generated-wrappers
/contracts/erc1155/test/generated-wrappers
/contracts/erc1155/generated-artifacts /contracts/erc1155/generated-artifacts
/contracts/erc1155/test/generated-artifacts
/contracts/extensions/generated-wrappers /contracts/extensions/generated-wrappers
/contracts/extensions/test/generated-wrappers
/contracts/extensions/generated-artifacts /contracts/extensions/generated-artifacts
/contracts/extensions/test/generated-artifacts
/contracts/exchange-forwarder/generated-wrappers /contracts/exchange-forwarder/generated-wrappers
/contracts/exchange-forwarder/test/generated-wrappers
/contracts/exchange-forwarder/generated-artifacts /contracts/exchange-forwarder/generated-artifacts
/contracts/exchange-forwarder/test/generated-artifacts
/contracts/dev-utils/generated-wrappers /contracts/dev-utils/generated-wrappers
/contracts/dev-utils/test/generated-wrappers
/contracts/dev-utils/generated-artifacts /contracts/dev-utils/generated-artifacts
/contracts/dev-utils/test/generated-artifacts
/contracts/staking/build/
/contracts/coordinator/build/
/contracts/exchange/build/
/contracts/asset-proxy/build/
/contracts/multisig/build/
/contracts/utils/build/
/contracts/exchange-libs/build/
/contracts/erc20/build/
/contracts/erc721/build/
/contracts/erc1155/build/
/contracts/extensions/build/
/contracts/exchange-forwarder/build/
/contracts/dev-utils/build/
/packages/abi-gen/test-cli/output /packages/abi-gen/test-cli/output
/packages/json-schemas/schemas /packages/json-schemas/schemas
/python-packages/json_schemas/src/zero_ex/json_schemas/schemas /python-packages/json_schemas/src/zero_ex/json_schemas/schemas
/packages/sra-spec/public/ /packages/sra-spec/public/
/packages/dev-tools-pages/ts/**/data.json
package.json package.json
scripts/postpublish_utils.js scripts/postpublish_utils.js
packages/sol-coverage/test/fixtures/artifacts packages/sol-coverage/test/fixtures/artifacts
@@ -82,4 +38,3 @@ packages/sol-coverage/test/fixtures/artifacts
packages/abi-gen/test-cli/fixtures/artifacts/AbiGenDummy.json packages/abi-gen/test-cli/fixtures/artifacts/AbiGenDummy.json
packages/abi-gen/test-cli/fixtures/artifacts/LibDummy.json packages/abi-gen/test-cli/fixtures/artifacts/LibDummy.json
packages/abi-gen/test-cli/fixtures/artifacts/TestLibDummy.json packages/abi-gen/test-cli/fixtures/artifacts/TestLibDummy.json
packages/*/docs

View File

@@ -1,6 +1,6 @@
{ {
"printWidth": 120,
"tabWidth": 4, "tabWidth": 4,
"singleQuote": true, "printWidth": 120,
"trailingComma": "all" "trailingComma": all,
"singleQuote": true
} }

View File

@@ -7,6 +7,7 @@
# Website # Website
packages/asset-buyer/ @BMillman19 @fragosti @steveklebanoff packages/asset-buyer/ @BMillman19 @fragosti @steveklebanoff
packages/instant/ @BMillman19 @fragosti @steveklebanoff packages/instant/ @BMillman19 @fragosti @steveklebanoff
packages/website/ @BMillman19 @fragosti @fabioberger @steveklebanoff
# Dev tools & setup # Dev tools & setup
.circleci/ @LogvinovLeon .circleci/ @LogvinovLeon

View File

@@ -29,9 +29,9 @@ ALL PRs should be opened against `development`.
Branch names should be prefixed with `fix`, `feature` or `refactor`. Branch names should be prefixed with `fix`, `feature` or `refactor`.
- e.g `fix/missing-import` - e.g `fix/broken-wiki-link`
- If the PR only edits a single package, add it's name too - If the PR only edits a single package, add it's name too
- e.g `fix/subproviders/missing-import` - e.g `fix/website/broken-wiki-link`
### CHANGELOGs ### CHANGELOGs
@@ -55,7 +55,7 @@ If an entry without a `timestamp` already exists, this means other changes have
### Development Tooling ### Development Tooling
We strongly recommend you use the [VSCode](https://code.visualstudio.com/) text editor since most of our code is written in TypeScript and it offers amazing support for the language. We strongly recommend you use the [VSCode](https://code.visualstudio.com/) text editor since most of our code is written in Typescript and it offers amazing support for the language.
#### Linter #### Linter
@@ -89,7 +89,7 @@ A few of our coding conventions are not yet enforced by the linter/auto-formatte
1. Do not import from a project's `index.ts` (e.g import { Token } from '../src';). Always import from the source file itself. 1. Do not import from a project's `index.ts` (e.g import { Token } from '../src';). Always import from the source file itself.
1. Generic error variables should be named `err` instead of `e` or `error`. 1. Generic error variables should be named `err` instead of `e` or `error`.
1. If you _must_ cast a variable to any - try to type it back as fast as possible. (e.g., `const cw = ((zeroEx as any)._contractWrappers as ContractWrappers);`). This ensures subsequent code is type-safe. 1. If you _must_ cast a variable to any - try to type it back as fast as possible. (e.g., `const cw = ((zeroEx as any)._contractWrappers as ContractWrappers);`). This ensures subsequent code is type-safe.
1. Our enum conventions coincide with the recommended TypeScript conventions, using capitalized keys, and all-caps snake-case values. Eg `GetStats = 'GET_STATS'` 1. Our enum conventions coincide with the recommended Typescript conventions, using capitalized keys, and all-caps snake-case values. Eg `GetStats = 'GET_STATS'`
1. All public, exported methods/functions/classes must have associated Javadoc-style comments. 1. All public, exported methods/functions/classes must have associated Javadoc-style comments.
### Fix `submit-coverage` CI failure ### Fix `submit-coverage` CI failure

View File

@@ -6,16 +6,21 @@
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.
[website-url]: https://0x.org If you're developing on 0x now or are interested in using 0x infrastructure in the future, please join our [developer mailing list][dev-mailing-list-url] for updates.
[website-url]: https://0xproject.com
[whitepaper-url]: https://0xproject.com/pdfs/0x_white_paper.pdf
[dev-mailing-list-url]: http://eepurl.com/dx4cPf
[![CircleCI](https://circleci.com/gh/0xProject/0x-monorepo.svg?style=svg&circle-token=61bf7cd8c9b4e11b132089dfcffdd1be277d1e0c)](https://circleci.com/gh/0xProject/0x-monorepo) [![CircleCI](https://circleci.com/gh/0xProject/0x-monorepo.svg?style=svg&circle-token=61bf7cd8c9b4e11b132089dfcffdd1be277d1e0c)](https://circleci.com/gh/0xProject/0x-monorepo)
[![Coverage Status](https://coveralls.io/repos/github/0xProject/0x-monorepo/badge.svg?branch=development)](https://coveralls.io/github/0xProject/0x-monorepo?branch=development) [![Coverage Status](https://coveralls.io/repos/github/0xProject/0x-monorepo/badge.svg?branch=development)](https://coveralls.io/github/0xProject/0x-monorepo?branch=development)
[![Discord](https://img.shields.io/badge/chat-discord.chat-yellow.svg?style=flat)](https://discordapp.com/invite/d3FTX3M) [![Discord](https://img.shields.io/badge/chat-rocket.chat-yellow.svg?style=flat)](https://chat.0xproject.com)
[![Join the chat at https://gitter.im/0xProject/Lobby](https://badges.gitter.im/0xProject/Lobby.svg)](https://gitter.im/0xProject/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
## 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://0xproject.com/docs/order-utils) for a comprehensive list of core & community maintained packages. All packages maintained with this monorepo are listed below.
### Python Packages ### Python Packages
@@ -43,13 +48,12 @@ These packages are all under development. See [/contracts/README.md](/contracts/
| [`@0x/contracts-exchange-libs`](/contracts/exchange-libs) | [![npm](https://img.shields.io/npm/v/@0x/contracts-exchange-libs.svg)](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-exchange-libs`](/contracts/exchange-libs) | [![npm](https://img.shields.io/npm/v/@0x/contracts-exchange-libs.svg)](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) | [![npm](https://img.shields.io/npm/v/@0x/contracts-extensions.svg)](https://www.npmjs.com/package/@0x/contracts-extensions) | Contracts that interact with and extend the functionality of the core protocol | | [`@0x/contracts-extensions`](/contracts/extensions) | [![npm](https://img.shields.io/npm/v/@0x/contracts-extensions.svg)](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) | [![npm](https://img.shields.io/npm/v/@0x/contracts-multisig.svg)](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-multisig`](/contracts/multisig) | [![npm](https://img.shields.io/npm/v/@0x/contracts-multisig.svg)](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) | [![npm](https://img.shields.io/npm/v/@0x/contracts-test-utils.svg)](https://www.npmjs.com/package/@0x/contracts-test-utils) | TypeScript/Javascript shared utilities used for testing contracts | | [`@0x/contracts-test-utils`](/contracts/test-utils) | [![npm](https://img.shields.io/npm/v/@0x/contracts-test-utils.svg)](https://www.npmjs.com/package/@0x/contracts-test-utils) | Typescript/Javascript shared utilities used for testing contracts |
| [`@0x/contracts-utils`](/contracts/utils) | [![npm](https://img.shields.io/npm/v/@0x/contracts-utils.svg)](https://www.npmjs.com/package/@0x/contracts-utils) | Generic libraries and utilities used throughout all of the contracts | | [`@0x/contracts-utils`](/contracts/utils) | [![npm](https://img.shields.io/npm/v/@0x/contracts-utils.svg)](https://www.npmjs.com/package/@0x/contracts-utils) | Generic libraries and utilities used throughout all of the contracts |
| [`@0x/contracts-coordinator`](/contracts/coordinator) | [![npm](https://img.shields.io/npm/v/@0x/contracts-coordinator.svg)](https://www.npmjs.com/package/@0x/contracts-coordinator) | A contract that allows users to execute 0x transactions with permission from a Coordinator | | [`@0x/contracts-coordinator`](/contracts/coordinator) | [![npm](https://img.shields.io/npm/v/@0x/contracts-coordinator.svg)](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) | [![npm](https://img.shields.io/npm/v/@0x/contracts-dev-utils.svg)](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-dev-utils`](/contracts/dev-utils) | [![npm](https://img.shields.io/npm/v/@0x/contracts-dev-utils.svg)](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) | [![npm](https://img.shields.io/npm/v/@0x/contracts-staking.svg)](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
#### 0x-specific packages #### 0x-specific packages
@@ -93,12 +97,15 @@ These packages are all under development. See [/contracts/README.md](/contracts/
| [`@0x/assert`](/packages/assert) | [![npm](https://img.shields.io/npm/v/@0x/assert.svg)](https://www.npmjs.com/package/@0x/assert) | Type and schema assertions used by our packages | | [`@0x/assert`](/packages/assert) | [![npm](https://img.shields.io/npm/v/@0x/assert.svg)](https://www.npmjs.com/package/@0x/assert) | Type and schema assertions used by our packages |
| [`@0x/base-contract`](/packages/base-contract) | [![npm](https://img.shields.io/npm/v/@0x/base-contract.svg)](https://www.npmjs.com/package/@0x/base-contract) | BaseContract used by auto-generated `abi-gen` wrapper contracts | | [`@0x/base-contract`](/packages/base-contract) | [![npm](https://img.shields.io/npm/v/@0x/base-contract.svg)](https://www.npmjs.com/package/@0x/base-contract) | BaseContract used by auto-generated `abi-gen` wrapper contracts |
| [`@0x/dev-utils`](/packages/dev-utils) | [![npm](https://img.shields.io/npm/v/@0x/dev-utils.svg)](https://www.npmjs.com/package/@0x/dev-utils) | Dev utils to be shared across 0x packages | | [`@0x/dev-utils`](/packages/dev-utils) | [![npm](https://img.shields.io/npm/v/@0x/dev-utils.svg)](https://www.npmjs.com/package/@0x/dev-utils) | Dev utils to be shared across 0x packages |
| [`@0x/fill-scenarios`](/packages/fill-scenarios) | [![npm](https://img.shields.io/npm/v/@0x/fill-scenarios.svg)](https://www.npmjs.com/package/@0x/fill-scenarios) | 0x order fill scenario generator |
#### Private Packages #### Private Packages
| Package | Description | | Package | Description |
| ---------------------------------- | -------------------------------------------------------------------------------- | | -------------------------------------------------- | -------------------------------------------------------------------------------- |
| [`@0x/instant`](/packages/instant) | A free and flexible way to offer simple crypto purchasing in any app or website. | | [`@0x/instant`](/packages/instant) | A free and flexible way to offer simple crypto purchasing in any app or website. |
| [`@0x/testnet-faucets`](/packages/testnet-faucets) | A faucet micro-service that dispenses test ERC20 tokens or Ether |
| [`@0x/website`](/packages/website) | 0x website |
## Usage ## Usage

View File

@@ -1,2 +0,0 @@
# solhint can't parse `abi.decode` syntax.
contracts/src/ERC1155Proxy.sol

View File

@@ -1,80 +1,4 @@
[ [
{
"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, "timestamp": 1566446343,
"version": "2.2.6", "version": "2.2.6",
@@ -181,18 +105,6 @@
{ {
"note": "Update tests to use contract-built-in `awaitTransactionSuccessAsync`", "note": "Update tests to use contract-built-in `awaitTransactionSuccessAsync`",
"pr": 1797 "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 "timestamp": 1557507213

View File

@@ -5,33 +5,6 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG CHANGELOG
## 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_ ## v2.2.6 - _August 22, 2019_
* Dependencies updated * Dependencies updated
@@ -77,9 +50,6 @@ CHANGELOG
## v2.1.2 - _May 10, 2019_ ## v2.1.2 - _May 10, 2019_
* Update tests to use contract-built-in `awaitTransactionSuccessAsync` (#1797) * 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_ ## v2.1.1 - _April 11, 2019_

View File

@@ -1,6 +1,6 @@
## AssetProxy ## 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. 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 the 0x [wiki](https://0xproject.com/wiki#Deployed-Addresses) or the [DEPLOYS](./DEPLOYS.json) file within this package.
## Installation ## Installation
@@ -12,7 +12,7 @@ npm install @0x/contracts-asset-proxy --save
## Bug bounty ## 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). A bug bounty for the 2.0.0 contracts is ongoing! Instructions can be found [here](https://0xproject.com/wiki#Bug-Bounty).
## Contributing ## Contributing

View File

@@ -1,5 +1,5 @@
{ {
"artifactsDir": "./test/generated-artifacts", "artifactsDir": "./generated-artifacts",
"contractsDir": "./contracts", "contractsDir": "./contracts",
"useDockerisedSolc": false, "useDockerisedSolc": false,
"isOfflineMode": false, "isOfflineMode": false,
@@ -22,5 +22,17 @@
] ]
} }
} }
} },
"contracts": [
"src/ERC1155Proxy.sol",
"src/ERC20Proxy.sol",
"src/ERC721Proxy.sol",
"src/MixinAuthorizable.sol",
"src/MultiAssetProxy.sol",
"src/StaticCallProxy.sol",
"src/interfaces/IAssetData.sol",
"src/interfaces/IAssetProxy.sol",
"src/interfaces/IAuthorizable.sol",
"test/TestStaticCallTarget.sol"
]
} }

View File

@@ -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;
}
}
}

View File

@@ -1,6 +1,6 @@
/* /*
Copyright 2019 ZeroEx Intl. Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -19,18 +19,18 @@
pragma solidity ^0.5.9; pragma solidity ^0.5.9;
import "@0x/contracts-utils/contracts/src/LibBytes.sol"; import "@0x/contracts-utils/contracts/src/LibBytes.sol";
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol"; import "@0x/contracts-utils/contracts/src/SafeMath.sol";
import "@0x/contracts-erc1155/contracts/src/interfaces/IERC1155.sol"; import "@0x/contracts-erc1155/contracts/src/interfaces/IERC1155.sol";
import "../archive/MixinAuthorizable.sol"; import "./MixinAuthorizable.sol";
import "./interfaces/IAssetProxy.sol"; import "./interfaces/IAssetProxy.sol";
contract ERC1155Proxy is contract ERC1155Proxy is
MixinAuthorizable, MixinAuthorizable,
SafeMath,
IAssetProxy IAssetProxy
{ {
using LibBytes for bytes; using LibBytes for bytes;
using LibSafeMath for uint256;
// Id of this proxy. // Id of this proxy.
bytes4 constant internal PROXY_ID = bytes4(keccak256("ERC1155Assets(address,uint256[],uint256[],bytes)")); bytes4 constant internal PROXY_ID = bytes4(keccak256("ERC1155Assets(address,uint256[],uint256[],bytes)"));
@@ -71,7 +71,7 @@ contract ERC1155Proxy is
// to avoid copying over `ids` or `data`. This is possible if they are // 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 // identical to `values` and the offsets for each are pointing to the
// same location in the ABI encoded calldata. // same location in the ABI encoded calldata.
scaledValues[i] = values[i].safeMul(amount); scaledValues[i] = safeMul(values[i], amount);
} }
// Execute `safeBatchTransferFrom` call // Execute `safeBatchTransferFrom` call

View File

@@ -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);
}
}

View File

@@ -1,6 +1,6 @@
/* /*
Copyright 2019 ZeroEx Intl. Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -16,9 +16,9 @@
*/ */
pragma solidity ^0.5.9; pragma solidity ^0.5.5;
import "../archive/MixinAuthorizable.sol"; import "./MixinAuthorizable.sol";
contract ERC20Proxy is contract ERC20Proxy is

View File

@@ -1,6 +1,6 @@
/* /*
Copyright 2019 ZeroEx Intl. Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -16,9 +16,9 @@
*/ */
pragma solidity ^0.5.9; pragma solidity ^0.5.5;
import "../archive/MixinAuthorizable.sol"; import "./MixinAuthorizable.sol";
contract ERC721Proxy is contract ERC721Proxy is

View File

@@ -1,6 +1,6 @@
/* /*
Copyright 2019 ZeroEx Intl. Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -16,16 +16,16 @@
*/ */
pragma solidity ^0.5.9; pragma solidity ^0.5.5;
import "../archive/Ownable.sol"; import "@0x/contracts-utils/contracts/src/Ownable.sol";
import "../src/interfaces/IAssetProxy.sol"; import "./mixins/MAssetProxyDispatcher.sol";
import "../src/interfaces/IAssetProxyDispatcher.sol"; import "./interfaces/IAssetProxy.sol";
contract MixinAssetProxyDispatcher is contract MixinAssetProxyDispatcher is
Ownable, Ownable,
IAssetProxyDispatcher MAssetProxyDispatcher
{ {
// Mapping from Asset Proxy Id's to their respective Asset Proxy // Mapping from Asset Proxy Id's to their respective Asset Proxy
mapping (bytes4 => address) public assetProxies; mapping (bytes4 => address) public assetProxies;
@@ -69,7 +69,7 @@ contract MixinAssetProxyDispatcher is
/// @param from Address to transfer token from. /// @param from Address to transfer token from.
/// @param to Address to transfer token to. /// @param to Address to transfer token to.
/// @param amount Amount of token to transfer. /// @param amount Amount of token to transfer.
function _dispatchTransferFrom( function dispatchTransferFrom(
bytes memory assetData, bytes memory assetData,
address from, address from,
address to, address to,

View File

@@ -1,6 +1,6 @@
/* /*
Copyright 2019 ZeroEx Intl. Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -16,15 +16,15 @@
*/ */
pragma solidity ^0.5.9; pragma solidity ^0.5.5;
import "../archive/Ownable.sol"; import "@0x/contracts-utils/contracts/src/Ownable.sol";
import "../src/interfaces/IAuthorizable.sol"; import "./mixins/MAuthorizable.sol";
contract MixinAuthorizable is contract MixinAuthorizable is
Ownable, Ownable,
IAuthorizable MAuthorizable
{ {
/// @dev Only authorized addresses can invoke functions with this modifier. /// @dev Only authorized addresses can invoke functions with this modifier.
modifier onlyAuthorized { modifier onlyAuthorized {

View File

@@ -1,6 +1,6 @@
/* /*
Copyright 2019 ZeroEx Intl. Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -16,10 +16,10 @@
*/ */
pragma solidity ^0.5.9; pragma solidity ^0.5.5;
import "../archive/MixinAssetProxyDispatcher.sol"; import "./MixinAssetProxyDispatcher.sol";
import "../archive/MixinAuthorizable.sol"; import "./MixinAuthorizable.sol";
contract MultiAssetProxy is contract MultiAssetProxy is

View File

@@ -1,6 +1,6 @@
/* /*
Copyright 2019 ZeroEx Intl. Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@@ -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 "../interfaces/IERC20Bridge.sol";
import "../interfaces/IEth2Dai.sol";
// solhint-disable space-after-comma
contract Eth2DaiBridge is
IERC20Bridge,
IWallet
{
/* Mainnet addresses */
address constant public ETH2DAI_ADDRESS = 0x39755357759cE0d7f32dC8dC45414CCa409AE24e;
/// @dev Callback for `IERC20Bridge`. Tries to buy `amount` of
/// `toTokenAddress` tokens by selling the entirety of the opposing asset
/// (DAI or WETH) to the Eth2Dai contract, then transfers the bought
/// tokens to `to`.
/// @param toTokenAddress The token to give to `to` (either DAI or WETH).
/// @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 = _getEth2DaiContract();
// Grant an allowance to the exchange to spend `fromTokenAddress` token.
LibERC20Token.approve(fromTokenAddress, address(exchange), uint256(-1));
// Try to sell all of this contract's `fromTokenAddress` token balance.
uint256 boughtAmount = _getEth2DaiContract().sellAllAmount(
address(fromTokenAddress),
IERC20Token(fromTokenAddress).balanceOf(address(this)),
toTokenAddress,
amount
);
// Transfer the converted `toToken`s to `to`.
LibERC20Token.transfer(toTokenAddress, to, boughtAmount);
return BRIDGE_SUCCESS;
}
/// @dev `SignatureType.Wallet` callback, so that this bridge can be the maker
/// and sign for itself in orders. Always succeeds.
/// @return magicValue Magic success bytes, always.
function isValidSignature(
bytes32,
bytes calldata
)
external
view
returns (bytes4 magicValue)
{
return LEGACY_WALLET_MAGIC_VALUE;
}
/// @dev Overridable way to get the eth2dai contract.
/// @return exchange The Eth2Dai exchange contract.
function _getEth2DaiContract()
internal
view
returns (IEth2Dai exchange)
{
return IEth2Dai(ETH2DAI_ADDRESS);
}
}

View File

@@ -1,219 +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 "../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
{
/* Mainnet addresses */
address constant private UNISWAP_EXCHANGE_FACTORY_ADDRESS = 0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95;
address constant private WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
// Struct to hold `bridgeTransferFrom()` local variables in memory and to avoid
// stack overflows.
struct WithdrawToState {
IUniswapExchange exchange;
uint256 fromTokenBalance;
IEtherToken weth;
}
// 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 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.
WithdrawToState 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 = getWethContract();
// 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.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);
// Buy as much ETH with `fromTokenAddress` token as possible.
uint256 ethBought = 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(ethBought)();
// Transfer the WETH to `to`.
IEtherToken(toTokenAddress).transfer(to, ethBought);
// Convert from one token to another.
} else {
// Grant the exchange an allowance.
_grantExchangeAllowance(state.exchange, fromTokenAddress);
// Buy as much `toTokenAddress` token with `fromTokenAddress` token
// and transfer it to `to`.
state.exchange.tokenToTokenTransferInput(
// Sell all tokens we hold.
state.fromTokenBalance,
// Minimum buy amount.
amount,
// No minimum intermediate ETH buy amount.
0,
// Expires after this block.
block.timestamp,
// Recipient is `to`.
to,
// Convert to `toTokenAddress`.
toTokenAddress
);
}
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 Overridable way to get the weth contract.
/// @return token The WETH contract.
function getWethContract()
public
view
returns (IEtherToken token)
{
return IEtherToken(WETH_ADDRESS);
}
/// @dev Overridable way to get the uniswap exchange factory contract.
/// @return factory The exchange factory contract.
function getUniswapExchangeFactoryContract()
public
view
returns (IUniswapExchangeFactory factory)
{
return IUniswapExchangeFactory(UNISWAP_EXCHANGE_FACTORY_ADDRESS);
}
/// @dev Grants an unlimited allowance to the exchange for its token
/// on behalf of this contract.
/// @param exchange The Uniswap token exchange.
/// @param tokenAddress The token address for the exchange.
function _grantExchangeAllowance(IUniswapExchange exchange, address tokenAddress)
private
{
LibERC20Token.approve(tokenAddress, address(exchange), uint256(-1));
}
/// @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 == address(getWethContract())) {
exchangeTokenAddress = toTokenAddress;
}
exchange = getUniswapExchangeFactoryContract().getExchange(exchangeTokenAddress);
require(address(exchange) != address(0), "NO_UNISWAP_EXCHANGE_FOR_TOKEN");
return exchange;
}
}

View File

@@ -1,6 +1,6 @@
/* /*
Copyright 2019 ZeroEx Intl. Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -17,7 +17,7 @@
*/ */
// solhint-disable // solhint-disable
pragma solidity ^0.5.9; pragma solidity ^0.5.5;
pragma experimental ABIEncoderV2; pragma experimental ABIEncoderV2;
@@ -26,63 +26,33 @@ pragma experimental ABIEncoderV2;
// This argument is ABI encoded as one of the methods of this interface. // This argument is ABI encoded as one of the methods of this interface.
interface IAssetData { interface IAssetData {
/// @dev Function signature for encoding ERC20 assetData.
/// @param tokenAddress Address of ERC20Token contract.
function ERC20Token(address tokenAddress) function ERC20Token(address tokenAddress)
external; 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( function ERC721Token(
address tokenAddress, address tokenAddress,
uint256 tokenId uint256 tokenId
) )
external; 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( function ERC1155Assets(
address tokenAddress, address tokenAddress,
uint256[] calldata tokenIds, uint256[] calldata tokenIds,
uint256[] calldata values, uint256[] calldata tokenValues,
bytes calldata callbackData bytes calldata callbackData
) )
external; 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( function MultiAsset(
uint256[] calldata values, uint256[] calldata amounts,
bytes[] calldata nestedAssetData bytes[] calldata nestedAssetData
) )
external; 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( function StaticCall(
address staticCallTargetAddress, address callTarget,
bytes calldata staticCallData, bytes calldata staticCallData,
bytes32 expectedReturnDataHash bytes32 callResultHash
)
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; external;
} }

View File

@@ -1,6 +1,6 @@
/* /*
Copyright 2019 ZeroEx Intl. Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -16,7 +16,9 @@
*/ */
pragma solidity ^0.5.9; pragma solidity ^0.5.5;
import "./IAuthorizable.sol";
contract IAssetProxy { contract IAssetProxy {

View File

@@ -1,6 +1,6 @@
/* /*
Copyright 2019 ZeroEx Intl. Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -16,17 +16,11 @@
*/ */
pragma solidity ^0.5.9; pragma solidity ^0.5.5;
contract IAssetProxyDispatcher { 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. /// @dev Registers an asset proxy to its asset proxy id.
/// Once an asset proxy is registered, it cannot be unregistered. /// Once an asset proxy is registered, it cannot be unregistered.
/// @param assetProxy Address of new asset proxy to register. /// @param assetProxy Address of new asset proxy to register.

View File

@@ -1,6 +1,6 @@
/* /*
Copyright 2019 ZeroEx Intl. Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -16,7 +16,7 @@
*/ */
pragma solidity ^0.5.9; pragma solidity ^0.5.5;
import "@0x/contracts-utils/contracts/src/interfaces/IOwnable.sol"; import "@0x/contracts-utils/contracts/src/interfaces/IOwnable.sol";
@@ -24,18 +24,6 @@ import "@0x/contracts-utils/contracts/src/interfaces/IOwnable.sol";
contract IAuthorizable is contract IAuthorizable is
IOwnable 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. /// @dev Authorizes an address.
/// @param target Address to authorize. /// @param target Address to authorize.
function addAuthorizedAddress(address target) function addAuthorizedAddress(address target)

View File

@@ -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 IERC20Bridge {
// @dev Result of a successful bridge call.
bytes4 constant internal BRIDGE_SUCCESS = 0xdc1600f3;
/// @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 `0x37708e9b` if successful.
function bridgeTransferFrom(
address tokenAddress,
address from,
address to,
uint256 amount,
bytes calldata bridgeData
)
external
returns (bytes4 success);
}

View File

@@ -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);
}

View File

@@ -1,77 +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);
/// @dev Retrieves the token that is associated with this exchange.
/// @return tokenAddress The token address.
function toTokenAddress()
external
view
returns (address tokenAddress);
}

View File

@@ -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 (IUniswapExchange);
}

View File

@@ -0,0 +1,40 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.5;
contract LibAssetProxyIds {
// AssetProxy Ids are equiavalent the first 4 bytes of the keccak256 hash of the function signature assigned to each AssetProxy.
// ERC20Token(address)
bytes4 constant public ERC20_PROXY_ID = 0xf47261b0;
// ERC721Token(address,uint256)
bytes4 constant public ERC721_PROXY_ID = 0x02571792;
// ERC1155Assets(address,uint256[],uint256[],bytes)
bytes4 constant public ERC1155_PROXY_ID = 0xa7cb5fb7;
// MultiAsset(uint256[],bytes[])
bytes4 constant public MULTI_ASSET_PROXY_ID = 0x94cfcdd7;
// StaticCall(address,bytes,bytes32)
bytes4 constant public STATIC_CALL_PROXY_ID = 0xc339d10a;
}

View File

@@ -0,0 +1,45 @@
/*
Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.5;
import "../interfaces/IAssetProxyDispatcher.sol";
contract MAssetProxyDispatcher is
IAssetProxyDispatcher
{
// Logs registration of new asset proxy
event AssetProxyRegistered(
bytes4 id, // Id of new registered AssetProxy.
address assetProxy // Address of new registered AssetProxy.
);
/// @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;
}

View File

@@ -0,0 +1,41 @@
/*
Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.5;
import "../interfaces/IAuthorizable.sol";
contract MAuthorizable is
IAuthorizable
{
// 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 Only authorized addresses can invoke functions with this modifier.
modifier onlyAuthorized { revert(); _; }
}

View File

@@ -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)) }
}
}

View File

@@ -1,202 +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;
}
/// @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 _getEth2DaiContract()
internal
view
returns (IEth2Dai)
{
return IEth2Dai(address(this));
}
}

View File

@@ -1,6 +1,6 @@
/* /*
Copyright 2019 ZeroEx Intl. Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@@ -1,432 +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);
}
/// @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 (IUniswapExchange)
{
return IUniswapExchange(_testExchanges[tokenAddress]);
}
// @dev Use `wethToken`.
function getWethContract()
public
view
returns (IEtherToken)
{
return IEtherToken(address(wethToken));
}
// @dev This contract will double as the Uniswap contract.
function getUniswapExchangeFactoryContract()
public
view
returns (IUniswapExchangeFactory)
{
return IUniswapExchangeFactory(address(this));
}
}

View File

@@ -1,6 +1,6 @@
{ {
"name": "@0x/contracts-asset-proxy", "name": "@0x/contracts-asset-proxy",
"version": "2.3.0-beta.2", "version": "2.2.6",
"engines": { "engines": {
"node": ">=6.12" "node": ">=6.12"
}, },
@@ -12,7 +12,7 @@
"scripts": { "scripts": {
"build": "yarn pre_build && tsc -b", "build": "yarn pre_build && tsc -b",
"build:ci": "yarn build", "build:ci": "yarn build",
"pre_build": "run-s compile contracts:gen generate_contract_wrappers contracts:copy", "pre_build": "run-s compile generate_contract_wrappers",
"test": "yarn run_mocha", "test": "yarn run_mocha",
"rebuild_and_test": "run-s build test", "rebuild_and_test": "run-s build test",
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov", "test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
@@ -21,23 +21,20 @@
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit", "run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
"compile": "sol-compiler", "compile": "sol-compiler",
"watch": "sol-compiler -w", "watch": "sol-compiler -w",
"clean": "shx rm -rf lib test/generated-artifacts test/generated-wrappers generated-artifacts generated-wrappers", "clean": "shx rm -rf lib generated-artifacts generated-wrappers",
"generate_contract_wrappers": "abi-gen --debug --abis ${npm_package_config_abis} --output test/generated-wrappers --backend ethers", "generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../../node_modules/@0x/abi-gen-templates/contract.handlebars --partials '../../node_modules/@0x/abi-gen-templates/partials/**/*.handlebars' --output generated-wrappers --backend ethers",
"lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./test/generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude ./test/generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts", "lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
"fix": "tslint --fix --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./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 ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
"coverage:report:text": "istanbul report text", "coverage:report:text": "istanbul report text",
"coverage:report:html": "istanbul report html && open coverage/index.html", "coverage:report:html": "istanbul report html && open coverage/index.html",
"profiler: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", "coverage:report:lcov": "istanbul report lcov",
"test:circleci": "yarn test", "test:circleci": "yarn test",
"contracts:gen": "contracts-gen generate", "contracts:gen": "contracts-gen",
"contracts:copy": "contracts-gen copy", "lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol"
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol",
"compile:truffle": "truffle compile"
}, },
"config": { "config": {
"publicInterfaceContracts": "ERC1155Proxy,ERC20Proxy,ERC721Proxy,MultiAssetProxy,StaticCallProxy,ERC20BridgeProxy,Eth2DaiBridge,IAssetData,IAssetProxy,UniswapBridge,TestStaticCallTarget", "abis": "./generated-artifacts/@(ERC1155Proxy|ERC20Proxy|ERC721Proxy|IAssetData|IAssetProxy|IAuthorizable|MixinAuthorizable|MultiAssetProxy|StaticCallProxy|TestStaticCallTarget).json",
"abis": "./test/generated-artifacts/@(ERC1155Proxy|ERC20BridgeProxy|ERC20Proxy|ERC721Proxy|Eth2DaiBridge|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|IERC20Bridge|IEth2Dai|IUniswapExchange|IUniswapExchangeFactory|MixinAssetProxyDispatcher|MixinAuthorizable|MultiAssetProxy|Ownable|StaticCallProxy|TestERC20Bridge|TestEth2DaiBridge|TestStaticCallTarget|TestUniswapBridge|UniswapBridge).json",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually." "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
}, },
"repository": { "repository": {
@@ -50,14 +47,12 @@
}, },
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/protocol/README.md", "homepage": "https://github.com/0xProject/0x-monorepo/contracts/protocol/README.md",
"devDependencies": { "devDependencies": {
"@0x/abi-gen": "^4.4.0-beta.2", "@0x/abi-gen": "^4.1.1",
"@0x/contracts-gen": "^1.1.0-beta.2", "@0x/contracts-gen": "^1.0.13",
"@0x/contracts-test-utils": "^3.2.0-beta.2", "@0x/contracts-test-utils": "^3.1.14",
"@0x/contracts-utils": "^3.3.0-beta.2", "@0x/dev-utils": "^2.3.1",
"@0x/dev-utils": "^2.4.0-beta.2", "@0x/sol-compiler": "^3.1.13",
"@0x/sol-compiler": "^3.2.0-beta.2", "@0x/tslint-config": "^3.0.1",
"@0x/tslint-config": "^3.1.0-beta.2",
"@0x/types": "^2.5.0-beta.2",
"@types/lodash": "4.14.104", "@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7", "@types/mocha": "^5.2.7",
"@types/node": "*", "@types/node": "*",
@@ -65,27 +60,27 @@
"chai-as-promised": "^7.1.0", "chai-as-promised": "^7.1.0",
"chai-bignumber": "^3.0.0", "chai-bignumber": "^3.0.0",
"dirty-chai": "^2.0.1", "dirty-chai": "^2.0.1",
"ethereumjs-util": "^5.1.1",
"make-promises-safe": "^1.1.0", "make-promises-safe": "^1.1.0",
"mocha": "^6.2.0", "mocha": "^6.2.0",
"npm-run-all": "^4.1.2", "npm-run-all": "^4.1.2",
"shx": "^0.2.2", "shx": "^0.2.2",
"solhint": "^1.4.1", "solhint": "^1.4.1",
"truffle": "^5.0.32",
"tslint": "5.11.0", "tslint": "5.11.0",
"typescript": "3.0.1" "typescript": "3.0.1"
}, },
"dependencies": { "dependencies": {
"@0x/base-contract": "^5.5.0-beta.2", "@0x/base-contract": "^5.3.2",
"@0x/contracts-dev-utils": "^0.1.0-beta.2", "@0x/contracts-erc1155": "^1.1.13",
"@0x/contracts-erc1155": "^1.2.0-beta.2", "@0x/contracts-erc20": "^2.2.12",
"@0x/contracts-erc20": "^2.3.0-beta.2", "@0x/contracts-erc721": "^2.1.13",
"@0x/contracts-erc721": "^2.2.0-beta.2", "@0x/contracts-utils": "^3.2.2",
"@0x/order-utils": "^8.5.0-beta.2", "@0x/order-utils": "^8.3.0",
"@0x/typescript-typings": "^4.4.0-beta.2", "@0x/types": "^2.4.1",
"@0x/utils": "^4.6.0-beta.2", "@0x/typescript-typings": "^4.2.4",
"@0x/web3-wrapper": "^6.1.0-beta.2", "@0x/utils": "^4.5.0",
"ethereum-types": "^2.2.0-beta.2", "@0x/web3-wrapper": "^6.0.11",
"ethereum-types": "^2.1.4",
"ethereumjs-util": "^5.1.1",
"lodash": "^4.17.11" "lodash": "^4.17.11"
}, },
"publishConfig": { "publishConfig": {

View File

@@ -6,26 +6,24 @@
import { ContractArtifact } from 'ethereum-types'; import { ContractArtifact } from 'ethereum-types';
import * as ERC1155Proxy from '../generated-artifacts/ERC1155Proxy.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 ERC20Proxy from '../generated-artifacts/ERC20Proxy.json';
import * as ERC721Proxy from '../generated-artifacts/ERC721Proxy.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 IAssetData from '../generated-artifacts/IAssetData.json';
import * as IAssetProxy from '../generated-artifacts/IAssetProxy.json'; import * as IAssetProxy from '../generated-artifacts/IAssetProxy.json';
import * as IAuthorizable from '../generated-artifacts/IAuthorizable.json';
import * as MixinAuthorizable from '../generated-artifacts/MixinAuthorizable.json';
import * as MultiAssetProxy from '../generated-artifacts/MultiAssetProxy.json'; import * as MultiAssetProxy from '../generated-artifacts/MultiAssetProxy.json';
import * as StaticCallProxy from '../generated-artifacts/StaticCallProxy.json'; import * as StaticCallProxy from '../generated-artifacts/StaticCallProxy.json';
import * as TestStaticCallTarget from '../generated-artifacts/TestStaticCallTarget.json'; import * as TestStaticCallTarget from '../generated-artifacts/TestStaticCallTarget.json';
import * as UniswapBridge from '../generated-artifacts/UniswapBridge.json';
export const artifacts = { export const artifacts = {
ERC1155Proxy: ERC1155Proxy as ContractArtifact, ERC1155Proxy: ERC1155Proxy as ContractArtifact,
ERC20Proxy: ERC20Proxy as ContractArtifact, ERC20Proxy: ERC20Proxy as ContractArtifact,
ERC721Proxy: ERC721Proxy as ContractArtifact, ERC721Proxy: ERC721Proxy as ContractArtifact,
MixinAuthorizable: MixinAuthorizable as ContractArtifact,
MultiAssetProxy: MultiAssetProxy as ContractArtifact, MultiAssetProxy: MultiAssetProxy as ContractArtifact,
StaticCallProxy: StaticCallProxy as ContractArtifact, StaticCallProxy: StaticCallProxy as ContractArtifact,
ERC20BridgeProxy: ERC20BridgeProxy as ContractArtifact,
Eth2DaiBridge: Eth2DaiBridge as ContractArtifact,
IAssetData: IAssetData as ContractArtifact, IAssetData: IAssetData as ContractArtifact,
IAssetProxy: IAssetProxy as ContractArtifact, IAssetProxy: IAssetProxy as ContractArtifact,
UniswapBridge: UniswapBridge as ContractArtifact, IAuthorizable: IAuthorizable as ContractArtifact,
TestStaticCallTarget: TestStaticCallTarget as ContractArtifact, TestStaticCallTarget: TestStaticCallTarget as ContractArtifact,
}; };

View File

@@ -1,6 +1,3 @@
export * from './artifacts'; export * from './artifacts';
export * from './wrappers'; export * from './wrappers';
export * from '../test/utils';
export { ERC20Wrapper } from './erc20_wrapper';
export { ERC721Wrapper } from './erc721_wrapper';
export { ERC1155ProxyWrapper } from './erc1155_proxy_wrapper';

View File

@@ -4,13 +4,12 @@
* ----------------------------------------------------------------------------- * -----------------------------------------------------------------------------
*/ */
export * from '../generated-wrappers/erc1155_proxy'; export * from '../generated-wrappers/erc1155_proxy';
export * from '../generated-wrappers/erc20_bridge_proxy';
export * from '../generated-wrappers/erc20_proxy'; export * from '../generated-wrappers/erc20_proxy';
export * from '../generated-wrappers/erc721_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_data';
export * from '../generated-wrappers/i_asset_proxy'; export * from '../generated-wrappers/i_asset_proxy';
export * from '../generated-wrappers/i_authorizable';
export * from '../generated-wrappers/mixin_authorizable';
export * from '../generated-wrappers/multi_asset_proxy'; export * from '../generated-wrappers/multi_asset_proxy';
export * from '../generated-wrappers/static_call_proxy'; export * from '../generated-wrappers/static_call_proxy';
export * from '../generated-wrappers/test_static_call_target'; export * from '../generated-wrappers/test_static_call_target';
export * from '../generated-wrappers/uniswap_bridge';

View File

@@ -1,55 +0,0 @@
/*
* -----------------------------------------------------------------------------
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
* -----------------------------------------------------------------------------
*/
import { ContractArtifact } from 'ethereum-types';
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 IERC20Bridge from '../test/generated-artifacts/IERC20Bridge.json';
import * as IEth2Dai from '../test/generated-artifacts/IEth2Dai.json';
import * as IUniswapExchange from '../test/generated-artifacts/IUniswapExchange.json';
import * as IUniswapExchangeFactory from '../test/generated-artifacts/IUniswapExchangeFactory.json';
import * as MixinAssetProxyDispatcher from '../test/generated-artifacts/MixinAssetProxyDispatcher.json';
import * as MixinAuthorizable from '../test/generated-artifacts/MixinAuthorizable.json';
import * as MultiAssetProxy from '../test/generated-artifacts/MultiAssetProxy.json';
import * as Ownable from '../test/generated-artifacts/Ownable.json';
import * as StaticCallProxy from '../test/generated-artifacts/StaticCallProxy.json';
import * as TestERC20Bridge from '../test/generated-artifacts/TestERC20Bridge.json';
import * as TestEth2DaiBridge from '../test/generated-artifacts/TestEth2DaiBridge.json';
import * as TestStaticCallTarget from '../test/generated-artifacts/TestStaticCallTarget.json';
import * as TestUniswapBridge from '../test/generated-artifacts/TestUniswapBridge.json';
import * as UniswapBridge from '../test/generated-artifacts/UniswapBridge.json';
export const artifacts = {
MixinAssetProxyDispatcher: MixinAssetProxyDispatcher as ContractArtifact,
MixinAuthorizable: MixinAuthorizable as ContractArtifact,
Ownable: Ownable as ContractArtifact,
ERC1155Proxy: ERC1155Proxy as ContractArtifact,
ERC20BridgeProxy: ERC20BridgeProxy as ContractArtifact,
ERC20Proxy: ERC20Proxy as ContractArtifact,
ERC721Proxy: ERC721Proxy as ContractArtifact,
MultiAssetProxy: MultiAssetProxy as ContractArtifact,
StaticCallProxy: StaticCallProxy as ContractArtifact,
Eth2DaiBridge: Eth2DaiBridge as ContractArtifact,
UniswapBridge: UniswapBridge as ContractArtifact,
IAssetData: IAssetData as ContractArtifact,
IAssetProxy: IAssetProxy as ContractArtifact,
IAssetProxyDispatcher: IAssetProxyDispatcher as ContractArtifact,
IAuthorizable: IAuthorizable as ContractArtifact,
IERC20Bridge: IERC20Bridge as ContractArtifact,
IEth2Dai: IEth2Dai as ContractArtifact,
IUniswapExchange: IUniswapExchange as ContractArtifact,
IUniswapExchangeFactory: IUniswapExchangeFactory as ContractArtifact,
TestERC20Bridge: TestERC20Bridge as ContractArtifact,
TestEth2DaiBridge: TestEth2DaiBridge as ContractArtifact,
TestStaticCallTarget: TestStaticCallTarget as ContractArtifact,
TestUniswapBridge: TestUniswapBridge as ContractArtifact,
};

View File

@@ -1,13 +1,18 @@
import { chaiSetup, expectTransactionFailedAsync, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils'; import {
chaiSetup,
constants,
expectTransactionFailedAsync,
provider,
txDefaults,
web3Wrapper,
} from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils'; import { BlockchainLifecycle } from '@0x/dev-utils';
import { RevertReason } from '@0x/types'; import { RevertReason } from '@0x/types';
import { BigNumber } from '@0x/utils'; import { BigNumber } from '@0x/utils';
import * as chai from 'chai'; import * as chai from 'chai';
import * as _ from 'lodash'; import * as _ from 'lodash';
import { artifacts } from './artifacts'; import { artifacts, MixinAuthorizableContract } from '../src';
import { MixinAuthorizableContract } from './wrappers';
chaiSetup.configure(); chaiSetup.configure();
const expect = chai.expect; const expect = chai.expect;
@@ -22,11 +27,9 @@ describe('Authorizable', () => {
before(async () => { before(async () => {
await blockchainLifecycle.startAsync(); await blockchainLifecycle.startAsync();
}); });
after(async () => { after(async () => {
await blockchainLifecycle.revertAsync(); await blockchainLifecycle.revertAsync();
}); });
before(async () => { before(async () => {
const accounts = await web3Wrapper.getAvailableAddressesAsync(); const accounts = await web3Wrapper.getAvailableAddressesAsync();
[owner, address, notOwner] = _.slice(accounts, 0, 3); [owner, address, notOwner] = _.slice(accounts, 0, 3);
@@ -37,57 +40,74 @@ describe('Authorizable', () => {
artifacts, artifacts,
); );
}); });
beforeEach(async () => { beforeEach(async () => {
await blockchainLifecycle.startAsync(); await blockchainLifecycle.startAsync();
}); });
afterEach(async () => { afterEach(async () => {
await blockchainLifecycle.revertAsync(); await blockchainLifecycle.revertAsync();
}); });
describe('addAuthorizedAddress', () => { describe('addAuthorizedAddress', () => {
it('should revert if not called by owner', async () => { it('should throw if not called by owner', async () => {
await expectTransactionFailedAsync( return expectTransactionFailedAsync(
authorizable.addAuthorizedAddress(notOwner).sendTransactionAsync({ from: notOwner }), authorizable.addAuthorizedAddress.sendTransactionAsync(notOwner, { from: notOwner }),
RevertReason.OnlyContractOwner, RevertReason.OnlyContractOwner,
); );
}); });
it('should allow owner to add an authorized address', async () => { it('should allow owner to add an authorized address', async () => {
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner }); await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
const isAuthorized = await authorizable.authorized(address).callAsync(); address,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
const isAuthorized = await authorizable.authorized.callAsync(address);
expect(isAuthorized).to.be.true(); expect(isAuthorized).to.be.true();
}); });
it('should throw if owner attempts to authorize a duplicate address', async () => {
it('should revert if owner attempts to authorize a duplicate address', async () => { await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner }); address,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
return expectTransactionFailedAsync( return expectTransactionFailedAsync(
authorizable.addAuthorizedAddress(address).sendTransactionAsync({ from: owner }), authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }),
RevertReason.TargetAlreadyAuthorized, RevertReason.TargetAlreadyAuthorized,
); );
}); });
}); });
describe('removeAuthorizedAddress', () => { describe('removeAuthorizedAddress', () => {
it('should revert if not called by owner', async () => { it('should throw if not called by owner', async () => {
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner }); await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
await expectTransactionFailedAsync( address,
authorizable.removeAuthorizedAddress(address).sendTransactionAsync({ from: notOwner }), { from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
return expectTransactionFailedAsync(
authorizable.removeAuthorizedAddress.sendTransactionAsync(address, {
from: notOwner,
}),
RevertReason.OnlyContractOwner, RevertReason.OnlyContractOwner,
); );
}); });
it('should allow owner to remove an authorized address', async () => { it('should allow owner to remove an authorized address', async () => {
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner }); await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
await authorizable.removeAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner }); address,
const isAuthorized = await authorizable.authorized(address).callAsync(); { from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await authorizable.removeAuthorizedAddress.awaitTransactionSuccessAsync(
address,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
const isAuthorized = await authorizable.authorized.callAsync(address);
expect(isAuthorized).to.be.false(); expect(isAuthorized).to.be.false();
}); });
it('should revert if owner attempts to remove an address that is not authorized', async () => { it('should throw if owner attempts to remove an address that is not authorized', async () => {
return expectTransactionFailedAsync( return expectTransactionFailedAsync(
authorizable.removeAuthorizedAddress(address).sendTransactionAsync({ authorizable.removeAuthorizedAddress.sendTransactionAsync(address, {
from: owner, from: owner,
}), }),
RevertReason.TargetNotAuthorized, RevertReason.TargetNotAuthorized,
@@ -96,73 +116,100 @@ describe('Authorizable', () => {
}); });
describe('removeAuthorizedAddressAtIndex', () => { describe('removeAuthorizedAddressAtIndex', () => {
it('should revert if not called by owner', async () => { it('should throw if not called by owner', async () => {
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner }); await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
address,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
const index = new BigNumber(0); const index = new BigNumber(0);
await expectTransactionFailedAsync( return expectTransactionFailedAsync(
authorizable.removeAuthorizedAddressAtIndex(address, index).sendTransactionAsync({ authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, {
from: notOwner, from: notOwner,
}), }),
RevertReason.OnlyContractOwner, RevertReason.OnlyContractOwner,
); );
}); });
it('should throw if index is >= authorities.length', async () => {
it('should revert if index is >= authorities.length', async () => { await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner }); address,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
const index = new BigNumber(1); const index = new BigNumber(1);
return expectTransactionFailedAsync( return expectTransactionFailedAsync(
authorizable.removeAuthorizedAddressAtIndex(address, index).sendTransactionAsync({ authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, {
from: owner, from: owner,
}), }),
RevertReason.IndexOutOfBounds, RevertReason.IndexOutOfBounds,
); );
}); });
it('should throw if owner attempts to remove an address that is not authorized', async () => {
it('should revert if owner attempts to remove an address that is not authorized', async () => {
const index = new BigNumber(0); const index = new BigNumber(0);
return expectTransactionFailedAsync( return expectTransactionFailedAsync(
authorizable.removeAuthorizedAddressAtIndex(address, index).sendTransactionAsync({ authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, {
from: owner, from: owner,
}), }),
RevertReason.TargetNotAuthorized, RevertReason.TargetNotAuthorized,
); );
}); });
it('should throw if address at index does not match target', async () => {
it('should revert if address at index does not match target', async () => {
const address1 = address; const address1 = address;
const address2 = notOwner; const address2 = notOwner;
await authorizable.addAuthorizedAddress(address1).awaitTransactionSuccessAsync({ from: owner }); await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
await authorizable.addAuthorizedAddress(address2).awaitTransactionSuccessAsync({ from: owner }); address1,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
address2,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
const address1Index = new BigNumber(0); const address1Index = new BigNumber(0);
return expectTransactionFailedAsync( return expectTransactionFailedAsync(
authorizable.removeAuthorizedAddressAtIndex(address2, address1Index).sendTransactionAsync({ authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address2, address1Index, {
from: owner, from: owner,
}), }),
RevertReason.AuthorizedAddressMismatch, RevertReason.AuthorizedAddressMismatch,
); );
}); });
it('should allow owner to remove an authorized address', async () => { it('should allow owner to remove an authorized address', async () => {
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner }); await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
address,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
const index = new BigNumber(0); const index = new BigNumber(0);
await authorizable.removeAuthorizedAddressAtIndex(address, index).awaitTransactionSuccessAsync({ await authorizable.removeAuthorizedAddressAtIndex.awaitTransactionSuccessAsync(
from: owner, address,
}); index,
const isAuthorized = await authorizable.authorized(address).callAsync(); { from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
const isAuthorized = await authorizable.authorized.callAsync(address);
expect(isAuthorized).to.be.false(); expect(isAuthorized).to.be.false();
}); });
}); });
describe('getAuthorizedAddresses', () => { describe('getAuthorizedAddresses', () => {
it('should return all authorized addresses', async () => { it('should return all authorized addresses', async () => {
const initial = await authorizable.getAuthorizedAddresses().callAsync(); const initial = await authorizable.getAuthorizedAddresses.callAsync();
expect(initial).to.have.length(0); expect(initial).to.have.length(0);
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner }); await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
const afterAdd = await authorizable.getAuthorizedAddresses().callAsync(); address,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
const afterAdd = await authorizable.getAuthorizedAddresses.callAsync();
expect(afterAdd).to.have.length(1); expect(afterAdd).to.have.length(1);
expect(afterAdd).to.include(address); expect(afterAdd).to.include(address);
await authorizable.removeAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner }); await authorizable.removeAuthorizedAddress.awaitTransactionSuccessAsync(
const afterRemove = await authorizable.getAuthorizedAddresses().callAsync(); address,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
const afterRemove = await authorizable.getAuthorizedAddresses.callAsync();
expect(afterRemove).to.have.length(0); expect(afterRemove).to.have.length(0);
}); });
}); });

View File

@@ -1,4 +1,3 @@
import { DevUtilsContract } from '@0x/contracts-dev-utils';
import { import {
artifacts as erc1155Artifacts, artifacts as erc1155Artifacts,
DummyERC1155ReceiverBatchTokenReceivedEventArgs, DummyERC1155ReceiverBatchTokenReceivedEventArgs,
@@ -15,8 +14,8 @@ import {
txDefaults, txDefaults,
web3Wrapper, web3Wrapper,
} from '@0x/contracts-test-utils'; } from '@0x/contracts-test-utils';
import { SafeMathRevertErrors } from '@0x/contracts-utils';
import { BlockchainLifecycle } from '@0x/dev-utils'; import { BlockchainLifecycle } from '@0x/dev-utils';
import { assetDataUtils } from '@0x/order-utils';
import { AssetProxyId, RevertReason } from '@0x/types'; import { AssetProxyId, RevertReason } from '@0x/types';
import { BigNumber } from '@0x/utils'; import { BigNumber } from '@0x/utils';
import * as chai from 'chai'; import * as chai from 'chai';
@@ -24,10 +23,7 @@ import { LogWithDecodedArgs } from 'ethereum-types';
import * as ethUtil from 'ethereumjs-util'; import * as ethUtil from 'ethereumjs-util';
import * as _ from 'lodash'; import * as _ from 'lodash';
import { ERC1155ProxyWrapper } from '../src/erc1155_proxy_wrapper'; import { artifacts, ERC1155ProxyContract, ERC1155ProxyWrapper } from '../src';
import { ERC1155ProxyContract, IAssetDataContract } from '../src/wrappers';
import { artifacts } from './artifacts';
chaiSetup.configure(); chaiSetup.configure();
const expect = chai.expect; const expect = chai.expect;
@@ -63,8 +59,6 @@ describe('ERC1155Proxy', () => {
// tokens // tokens
let fungibleTokens: BigNumber[]; let fungibleTokens: BigNumber[];
let nonFungibleTokensOwnedBySpender: BigNumber[]; let nonFungibleTokensOwnedBySpender: BigNumber[];
// devUtils for encoding and decoding assetData
let devUtils: DevUtilsContract;
// tests // tests
before(async () => { before(async () => {
await blockchainLifecycle.startAsync(); await blockchainLifecycle.startAsync();
@@ -78,8 +72,16 @@ describe('ERC1155Proxy', () => {
const usedAddresses = ([owner, notAuthorized, authorized, spender, receiver] = _.slice(accounts, 0, 5)); const usedAddresses = ([owner, notAuthorized, authorized, spender, receiver] = _.slice(accounts, 0, 5));
erc1155ProxyWrapper = new ERC1155ProxyWrapper(provider, usedAddresses, owner); erc1155ProxyWrapper = new ERC1155ProxyWrapper(provider, usedAddresses, owner);
erc1155Proxy = await erc1155ProxyWrapper.deployProxyAsync(); erc1155Proxy = await erc1155ProxyWrapper.deployProxyAsync();
await erc1155Proxy.addAuthorizedAddress(authorized).awaitTransactionSuccessAsync({ from: owner }); await erc1155Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
await erc1155Proxy.addAuthorizedAddress(erc1155Proxy.address).awaitTransactionSuccessAsync({ from: owner }); authorized,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await erc1155Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
erc1155Proxy.address,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
// deploy & configure ERC1155 tokens and receiver // deploy & configure ERC1155 tokens and receiver
[erc1155Wrapper] = await erc1155ProxyWrapper.deployDummyContractsAsync(); [erc1155Wrapper] = await erc1155ProxyWrapper.deployDummyContractsAsync();
erc1155Contract = erc1155Wrapper.getContract(); erc1155Contract = erc1155Wrapper.getContract();
@@ -101,8 +103,6 @@ describe('ERC1155Proxy', () => {
tokenBalances.nonFungible[spender][erc1155Contract.address][nonFungibleTokenAsString][0]; tokenBalances.nonFungible[spender][erc1155Contract.address][nonFungibleTokenAsString][0];
nonFungibleTokensOwnedBySpender.push(nonFungibleTokenHeldBySpender); nonFungibleTokensOwnedBySpender.push(nonFungibleTokenHeldBySpender);
}); });
// set up devUtils
devUtils = new DevUtilsContract(constants.NULL_ADDRESS, provider, { from: owner });
}); });
beforeEach(async () => { beforeEach(async () => {
await blockchainLifecycle.startAsync(); await blockchainLifecycle.startAsync();
@@ -123,7 +123,7 @@ describe('ERC1155Proxy', () => {
); );
}); });
it('should have an id of 0xa7cb5fb7', async () => { it('should have an id of 0xa7cb5fb7', async () => {
const proxyId = await erc1155Proxy.getProxyId().callAsync(); const proxyId = await erc1155Proxy.getProxyId.callAsync();
const expectedProxyId = AssetProxyId.ERC1155; const expectedProxyId = AssetProxyId.ERC1155;
expect(proxyId).to.equal(expectedProxyId); expect(proxyId).to.equal(expectedProxyId);
}); });
@@ -638,14 +638,12 @@ describe('ERC1155Proxy', () => {
return value.times(valueMultiplier); return value.times(valueMultiplier);
}); });
const erc1155ContractAddress = erc1155Wrapper.getContract().address; const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = await devUtils const assetData = assetDataUtils.encodeERC1155AssetData(
.encodeERC1155AssetData(
erc1155ContractAddress, erc1155ContractAddress,
tokensToTransfer, tokensToTransfer,
valuesToTransfer, valuesToTransfer,
receiverCallbackData, receiverCallbackData,
) );
.callAsync();
const extraData = '0102030405060708091001020304050607080910010203040506070809100102'; const extraData = '0102030405060708091001020304050607080910010203040506070809100102';
const assetDataWithExtraData = `${assetData}${extraData}`; const assetDataWithExtraData = `${assetData}${extraData}`;
// check balances before transfer // check balances before transfer
@@ -698,20 +696,25 @@ describe('ERC1155Proxy', () => {
const tokenUri = ''; const tokenUri = '';
for (const tokenToCreate of tokensToCreate) { for (const tokenToCreate of tokensToCreate) {
// create token // create token
await erc1155Wrapper await erc1155Wrapper.getContract().createWithType.awaitTransactionSuccessAsync(
.getContract() tokenToCreate,
.createWithType(tokenToCreate, tokenUri) tokenUri,
.awaitTransactionSuccessAsync({ {
from: owner, from: owner,
}); },
constants.AWAIT_TRANSACTION_MINED_MS,
);
// mint balance for spender // mint balance for spender
await erc1155Wrapper await erc1155Wrapper.getContract().mintFungible.awaitTransactionSuccessAsync(
.getContract() tokenToCreate,
.mintFungible(tokenToCreate, [spender], [spenderInitialBalance]) [spender],
.awaitTransactionSuccessAsync({ [spenderInitialBalance],
{
from: owner, from: owner,
}); },
constants.AWAIT_TRANSACTION_MINED_MS,
);
} }
///// Step 2/5 ///// ///// Step 2/5 /////
// Check balances before transfer // Check balances before transfer
@@ -744,16 +747,18 @@ describe('ERC1155Proxy', () => {
const tokensToTransfer = [new BigNumber(1), new BigNumber(2)]; const tokensToTransfer = [new BigNumber(1), new BigNumber(2)];
const valuesToTransfer = tokensToTransfer; const valuesToTransfer = tokensToTransfer;
const valueMultiplier = new BigNumber(2); const valueMultiplier = new BigNumber(2);
const assetData = assetDataUtils.encodeERC1155AssetData(
// hand encode optimized assetData because our tooling (based on LibAssetData.sol/encodeERC1155AssetData) does not use optimized encoding erc1155ContractAddress,
const assetDataContract = new IAssetDataContract(constants.NULL_ADDRESS, provider); tokensToTransfer,
const selector = assetDataContract.getSelector('ERC1155Assets'); valuesToTransfer,
const assetDataWithoutContractAddress = receiverCallbackData,
);
// remove the function selector and contract address from check, as these change on each test
const offsetToTokenIds = 74;
const assetDataWithoutContractAddress = assetData.substr(offsetToTokenIds);
const expectedAssetDataWithoutContractAddress =
'0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040102030400000000000000000000000000000000000000000000000000000000'; '0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040102030400000000000000000000000000000000000000000000000000000000';
const assetData = `${selector}000000000000000000000000${erc1155ContractAddress.substr( expect(assetDataWithoutContractAddress).to.be.equal(expectedAssetDataWithoutContractAddress);
2,
)}${assetDataWithoutContractAddress}`;
///// Step 4/5 ///// ///// Step 4/5 /////
// Transfer token IDs [1, 2] and amounts [1, 2] with a multiplier of 2; // Transfer token IDs [1, 2] and amounts [1, 2] with a multiplier of 2;
// the expected trade will be token IDs [1, 2] and amounts [2, 4] // the expected trade will be token IDs [1, 2] and amounts [2, 4]
@@ -800,20 +805,25 @@ describe('ERC1155Proxy', () => {
const tokenUri = ''; const tokenUri = '';
for (const tokenToCreate of tokensToCreate) { for (const tokenToCreate of tokensToCreate) {
// create token // create token
await erc1155Wrapper await erc1155Wrapper.getContract().createWithType.awaitTransactionSuccessAsync(
.getContract() tokenToCreate,
.createWithType(tokenToCreate, tokenUri) tokenUri,
.awaitTransactionSuccessAsync({ {
from: owner, from: owner,
}); },
constants.AWAIT_TRANSACTION_MINED_MS,
);
// mint balance for spender // mint balance for spender
await erc1155Wrapper await erc1155Wrapper.getContract().mintFungible.awaitTransactionSuccessAsync(
.getContract() tokenToCreate,
.mintFungible(tokenToCreate, [spender], [spenderInitialBalance]) [spender],
.awaitTransactionSuccessAsync({ [spenderInitialBalance],
{
from: owner, from: owner,
}); },
constants.AWAIT_TRANSACTION_MINED_MS,
);
} }
///// Step 2/5 ///// ///// Step 2/5 /////
// Check balances before transfer // Check balances before transfer
@@ -857,14 +867,12 @@ describe('ERC1155Proxy', () => {
const valuesToTransfer = [new BigNumber(2), new BigNumber(2)]; const valuesToTransfer = [new BigNumber(2), new BigNumber(2)];
const valueMultiplier = new BigNumber(2); const valueMultiplier = new BigNumber(2);
// create callback data that is the encoded version of `valuesToTransfer` // create callback data that is the encoded version of `valuesToTransfer`
const generatedAssetData = await devUtils const generatedAssetData = assetDataUtils.encodeERC1155AssetData(
.encodeERC1155AssetData(
erc1155ContractAddress, erc1155ContractAddress,
tokensToTransfer, tokensToTransfer,
valuesToTransfer, valuesToTransfer,
receiverCallbackData, receiverCallbackData,
) );
.callAsync();
// remove the function selector and contract address from check, as these change on each test // remove the function selector and contract address from check, as these change on each test
const offsetToTokenIds = 74; const offsetToTokenIds = 74;
const assetDataSelectorAndContractAddress = generatedAssetData.substr(0, offsetToTokenIds); const assetDataSelectorAndContractAddress = generatedAssetData.substr(0, offsetToTokenIds);
@@ -929,20 +937,25 @@ describe('ERC1155Proxy', () => {
const tokenUri = ''; const tokenUri = '';
for (const tokenToCreate of tokensToCreate) { for (const tokenToCreate of tokensToCreate) {
// create token // create token
await erc1155Wrapper await erc1155Wrapper.getContract().createWithType.awaitTransactionSuccessAsync(
.getContract() tokenToCreate,
.createWithType(tokenToCreate, tokenUri) tokenUri,
.awaitTransactionSuccessAsync({ {
from: owner, from: owner,
}); },
constants.AWAIT_TRANSACTION_MINED_MS,
);
// mint balance for spender // mint balance for spender
await erc1155Wrapper await erc1155Wrapper.getContract().mintFungible.awaitTransactionSuccessAsync(
.getContract() tokenToCreate,
.mintFungible(tokenToCreate, [spender], [spenderInitialBalance]) [spender],
.awaitTransactionSuccessAsync({ [spenderInitialBalance],
{
from: owner, from: owner,
}); },
constants.AWAIT_TRANSACTION_MINED_MS,
);
} }
///// Step 2/5 ///// ///// Step 2/5 /////
// Check balances before transfer // Check balances before transfer
@@ -983,14 +996,12 @@ describe('ERC1155Proxy', () => {
const valuesToTransfer = [new BigNumber(1), new BigNumber(2)]; const valuesToTransfer = [new BigNumber(1), new BigNumber(2)];
const valueMultiplier = new BigNumber(2); const valueMultiplier = new BigNumber(2);
// create callback data that is the encoded version of `valuesToTransfer` // create callback data that is the encoded version of `valuesToTransfer`
const generatedAssetData = await devUtils const generatedAssetData = assetDataUtils.encodeERC1155AssetData(
.encodeERC1155AssetData(
erc1155ContractAddress, erc1155ContractAddress,
tokensToTransfer, tokensToTransfer,
valuesToTransfer, valuesToTransfer,
receiverCallbackData, receiverCallbackData,
) );
.callAsync();
// remove the function selector and contract address from check, as these change on each test // remove the function selector and contract address from check, as these change on each test
const offsetToTokenIds = 74; const offsetToTokenIds = 74;
const assetDataSelectorAndContractAddress = generatedAssetData.substr(0, offsetToTokenIds); const assetDataSelectorAndContractAddress = generatedAssetData.substr(0, offsetToTokenIds);
@@ -1048,14 +1059,12 @@ describe('ERC1155Proxy', () => {
const valuesToTransfer = [fungibleValueToTransferLarge]; const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall; const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address; const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = await devUtils const assetData = assetDataUtils.encodeERC1155AssetData(
.encodeERC1155AssetData(
erc1155ContractAddress, erc1155ContractAddress,
tokensToTransfer, tokensToTransfer,
valuesToTransfer, valuesToTransfer,
receiverCallbackData, receiverCallbackData,
) );
.callAsync();
// The asset data we just generated will look like this: // The asset data we just generated will look like this:
// a7cb5fb7 // a7cb5fb7
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082 // 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
@@ -1097,14 +1106,12 @@ describe('ERC1155Proxy', () => {
const valuesToTransfer = [fungibleValueToTransferLarge]; const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall; const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address; const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = await devUtils const assetData = assetDataUtils.encodeERC1155AssetData(
.encodeERC1155AssetData(
erc1155ContractAddress, erc1155ContractAddress,
tokensToTransfer, tokensToTransfer,
valuesToTransfer, valuesToTransfer,
receiverCallbackData, receiverCallbackData,
) );
.callAsync();
// The asset data we just generated will look like this: // The asset data we just generated will look like this:
// a7cb5fb7 // a7cb5fb7
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082 // 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
@@ -1150,14 +1157,12 @@ describe('ERC1155Proxy', () => {
const valuesToTransfer = [fungibleValueToTransferLarge]; const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall; const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address; const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = await devUtils const assetData = assetDataUtils.encodeERC1155AssetData(
.encodeERC1155AssetData(
erc1155ContractAddress, erc1155ContractAddress,
tokensToTransfer, tokensToTransfer,
valuesToTransfer, valuesToTransfer,
receiverCallbackData, receiverCallbackData,
) );
.callAsync();
// The asset data we just generated will look like this: // The asset data we just generated will look like this:
// a7cb5fb7 // a7cb5fb7
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082 // 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
@@ -1203,14 +1208,12 @@ describe('ERC1155Proxy', () => {
const valuesToTransfer = [fungibleValueToTransferLarge]; const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall; const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address; const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = await devUtils const assetData = assetDataUtils.encodeERC1155AssetData(
.encodeERC1155AssetData(
erc1155ContractAddress, erc1155ContractAddress,
tokensToTransfer, tokensToTransfer,
valuesToTransfer, valuesToTransfer,
receiverCallbackData, receiverCallbackData,
) );
.callAsync();
// The asset data we just generated will look like this: // The asset data we just generated will look like this:
// a7cb5fb7 // a7cb5fb7
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082 // 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
@@ -1256,14 +1259,12 @@ describe('ERC1155Proxy', () => {
const valuesToTransfer = [fungibleValueToTransferLarge]; const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall; const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address; const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = await devUtils const assetData = assetDataUtils.encodeERC1155AssetData(
.encodeERC1155AssetData(
erc1155ContractAddress, erc1155ContractAddress,
tokensToTransfer, tokensToTransfer,
valuesToTransfer, valuesToTransfer,
receiverCallbackData, receiverCallbackData,
) );
.callAsync();
// The asset data we just generated will look like this: // The asset data we just generated will look like this:
// a7cb5fb7 // a7cb5fb7
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082 // 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
@@ -1310,14 +1311,12 @@ describe('ERC1155Proxy', () => {
const valuesToTransfer = [fungibleValueToTransferLarge]; const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall; const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address; const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = await devUtils const assetData = assetDataUtils.encodeERC1155AssetData(
.encodeERC1155AssetData(
erc1155ContractAddress, erc1155ContractAddress,
tokensToTransfer, tokensToTransfer,
valuesToTransfer, valuesToTransfer,
receiverCallbackData, receiverCallbackData,
) );
.callAsync();
// The asset data we just generated will look like this: // The asset data we just generated will look like this:
// a7cb5fb7 // a7cb5fb7
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082 // 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
@@ -1359,14 +1358,12 @@ describe('ERC1155Proxy', () => {
const valuesToTransfer = [fungibleValueToTransferLarge]; const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall; const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address; const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = await devUtils const assetData = assetDataUtils.encodeERC1155AssetData(
.encodeERC1155AssetData(
erc1155ContractAddress, erc1155ContractAddress,
tokensToTransfer, tokensToTransfer,
valuesToTransfer, valuesToTransfer,
receiverCallbackData, receiverCallbackData,
) );
.callAsync();
// The asset data we just generated will look like this: // The asset data we just generated will look like this:
// a7cb5fb7 // a7cb5fb7
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082 // 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
@@ -1412,14 +1409,12 @@ describe('ERC1155Proxy', () => {
const valuesToTransfer = [fungibleValueToTransferLarge]; const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall; const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address; const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = await devUtils const assetData = assetDataUtils.encodeERC1155AssetData(
.encodeERC1155AssetData(
erc1155ContractAddress, erc1155ContractAddress,
tokensToTransfer, tokensToTransfer,
valuesToTransfer, valuesToTransfer,
receiverCallbackData, receiverCallbackData,
) );
.callAsync();
// The asset data we just generated will look like this: // The asset data we just generated will look like this:
// a7cb5fb7 // a7cb5fb7
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082 // 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
@@ -1461,14 +1456,12 @@ describe('ERC1155Proxy', () => {
const valuesToTransfer = [fungibleValueToTransferLarge]; const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall; const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address; const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = await devUtils const assetData = assetDataUtils.encodeERC1155AssetData(
.encodeERC1155AssetData(
erc1155ContractAddress, erc1155ContractAddress,
tokensToTransfer, tokensToTransfer,
valuesToTransfer, valuesToTransfer,
receiverCallbackData, receiverCallbackData,
) );
.callAsync();
// The asset data we just generated will look like this: // The asset data we just generated will look like this:
// a7cb5fb7 // a7cb5fb7
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082 // 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
@@ -1514,15 +1507,13 @@ describe('ERC1155Proxy', () => {
const valuesToTransfer = [fungibleValueToTransferLarge]; const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall; const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address; const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = await devUtils const assetData = assetDataUtils.encodeERC1155AssetData(
.encodeERC1155AssetData(
erc1155ContractAddress, erc1155ContractAddress,
tokensToTransfer, tokensToTransfer,
valuesToTransfer, valuesToTransfer,
receiverCallbackData, receiverCallbackData,
) );
.callAsync(); const txData = erc1155ProxyWrapper.getTransferFromAbiEncodedTxData(
const txData = await erc1155ProxyWrapper.getTransferFromAbiEncodedTxDataAsync(
spender, spender,
receiverContract, receiverContract,
erc1155Contract.address, erc1155Contract.address,
@@ -1547,15 +1538,13 @@ describe('ERC1155Proxy', () => {
const valuesToTransfer = [fungibleValueToTransferLarge]; const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall; const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address; const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = await devUtils const assetData = assetDataUtils.encodeERC1155AssetData(
.encodeERC1155AssetData(
erc1155ContractAddress, erc1155ContractAddress,
tokensToTransfer, tokensToTransfer,
valuesToTransfer, valuesToTransfer,
receiverCallbackData, receiverCallbackData,
) );
.callAsync(); const txData = erc1155ProxyWrapper.getTransferFromAbiEncodedTxData(
const txData = await erc1155ProxyWrapper.getTransferFromAbiEncodedTxDataAsync(
spender, spender,
receiverContract, receiverContract,
erc1155Contract.address, erc1155Contract.address,
@@ -1678,9 +1667,13 @@ describe('ERC1155Proxy', () => {
it('should propagate revert reason from erc1155 contract failure', async () => { it('should propagate revert reason from erc1155 contract failure', async () => {
// disable transfers // disable transfers
const shouldRejectTransfer = true; const shouldRejectTransfer = true;
await erc1155Receiver.setRejectTransferFlag(shouldRejectTransfer).awaitTransactionSuccessAsync({ await erc1155Receiver.setRejectTransferFlag.awaitTransactionSuccessAsync(
shouldRejectTransfer,
{
from: owner, from: owner,
}); },
constants.AWAIT_TRANSACTION_MINED_MS,
);
// setup test parameters // setup test parameters
const tokenHolders = [spender, receiverContract]; const tokenHolders = [spender, receiverContract];
const tokensToTransfer = fungibleTokens.slice(0, 1); const tokensToTransfer = fungibleTokens.slice(0, 1);
@@ -1755,14 +1748,9 @@ describe('ERC1155Proxy', () => {
nftNotOwnerBalance, nftNotOwnerBalance,
]; ];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
maxUintValue,
valueMultiplier,
);
// execute transfer // execute transfer
// note - this will overflow because we are trying to transfer `maxUintValue * 2` of the 2nd token // note - this will overflow because we are trying to transfer `maxUintValue * 2` of the 2nd token
await expect( await expectTransactionFailedAsync(
erc1155ProxyWrapper.transferFromAsync( erc1155ProxyWrapper.transferFromAsync(
spender, spender,
receiver, receiver,
@@ -1773,7 +1761,8 @@ describe('ERC1155Proxy', () => {
receiverCallbackData, receiverCallbackData,
authorized, authorized,
), ),
).to.revertWith(expectedError); RevertReason.Uint256Overflow,
);
}); });
it('should revert if transferring > 1 instances of a non-fungible token (valueMultiplier field >1)', async () => { it('should revert if transferring > 1 instances of a non-fungible token (valueMultiplier field >1)', async () => {
// setup test parameters // setup test parameters
@@ -1843,13 +1832,9 @@ describe('ERC1155Proxy', () => {
// check balances before transfer // check balances before transfer
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance]; const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.SubtractionUnderflow,
spenderInitialFungibleBalance,
valuesToTransfer[0].times(valueMultiplier),
);
// execute transfer // execute transfer
const tx = erc1155ProxyWrapper.transferFromAsync( await expectTransactionFailedAsync(
erc1155ProxyWrapper.transferFromAsync(
spender, spender,
receiver, receiver,
erc1155Contract.address, erc1155Contract.address,
@@ -1858,8 +1843,9 @@ describe('ERC1155Proxy', () => {
valueMultiplier, valueMultiplier,
receiverCallbackData, receiverCallbackData,
authorized, authorized,
),
RevertReason.Uint256Underflow,
); );
return expect(tx).to.revertWith(expectedError);
}); });
it('should revert if sender allowance is insufficient', async () => { it('should revert if sender allowance is insufficient', async () => {
// dremove allowance for ERC1155 proxy // dremove allowance for ERC1155 proxy

View File

@@ -1,290 +0,0 @@
import {
blockchainTests,
constants,
expect,
getRandomInteger,
hexLeftPad,
hexRightPad,
hexSlice,
Numberish,
randomAddress,
} from '@0x/contracts-test-utils';
import { AuthorizableRevertErrors } from '@0x/contracts-utils';
import { AssetProxyId } from '@0x/types';
import { AbiEncoder, BigNumber, 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 = hexRightPad(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 = hexSlice(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: hexLeftPad('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: hexRightPad('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);
});
});
});

View File

@@ -1,191 +0,0 @@
import {
blockchainTests,
constants,
expect,
filterLogsToArguments,
getRandomInteger,
hexLeftPad,
hexRandom,
Numberish,
randomAddress,
} from '@0x/contracts-test-utils';
import { AssetProxyId } from '@0x/types';
import { BigNumber, 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(hexRandom(), hexRandom(_.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: hexLeftPad(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.
hexLeftPad(_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: hexLeftPad(0) });
const tx = withdrawToAsync(opts);
return expect(tx).to.revertWith(new RawRevertError(hexLeftPad(0)));
});
it('succeeds if `toTokenAddress.transfer()` returns true', async () => {
await withdrawToAsync({ toTokenTransferReturnData: hexLeftPad(1) });
});
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,3 @@
import { DevUtilsContract } from '@0x/contracts-dev-utils';
import { import {
chaiSetup, chaiSetup,
constants, constants,
@@ -9,14 +8,13 @@ import {
web3Wrapper, web3Wrapper,
} from '@0x/contracts-test-utils'; } from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils'; import { BlockchainLifecycle } from '@0x/dev-utils';
import { assetDataUtils } from '@0x/order-utils';
import { AssetProxyId, RevertReason } from '@0x/types'; import { AssetProxyId, RevertReason } from '@0x/types';
import { AbiEncoder, BigNumber } from '@0x/utils'; import { AbiEncoder, BigNumber } from '@0x/utils';
import * as chai from 'chai'; import * as chai from 'chai';
import * as ethUtil from 'ethereumjs-util'; import * as ethUtil from 'ethereumjs-util';
import { artifacts } from './artifacts'; import { artifacts, IAssetProxyContract, StaticCallProxyContract, TestStaticCallTargetContract } from '../src';
import { IAssetProxyContract, StaticCallProxyContract, TestStaticCallTargetContract } from './wrappers';
chaiSetup.configure(); chaiSetup.configure();
const expect = chai.expect; const expect = chai.expect;
@@ -27,7 +25,6 @@ describe('StaticCallProxy', () => {
let fromAddress: string; let fromAddress: string;
let toAddress: string; let toAddress: string;
let devUtils: DevUtilsContract;
let staticCallProxy: IAssetProxyContract; let staticCallProxy: IAssetProxyContract;
let staticCallTarget: TestStaticCallTargetContract; let staticCallTarget: TestStaticCallTargetContract;
@@ -46,14 +43,7 @@ describe('StaticCallProxy', () => {
txDefaults, txDefaults,
artifacts, artifacts,
); );
devUtils = new DevUtilsContract(constants.NULL_ADDRESS, provider); staticCallProxy = new IAssetProxyContract(staticCallProxyWithoutTransferFrom.address, provider, txDefaults);
staticCallProxy = new IAssetProxyContract(
staticCallProxyWithoutTransferFrom.address,
provider,
txDefaults,
{},
StaticCallProxyContract.deployedBytecode,
);
staticCallTarget = await TestStaticCallTargetContract.deployFrom0xArtifactAsync( staticCallTarget = await TestStaticCallTargetContract.deployFrom0xArtifactAsync(
artifacts.TestStaticCallTarget, artifacts.TestStaticCallTarget,
provider, provider,
@@ -81,21 +71,26 @@ describe('StaticCallProxy', () => {
); );
}); });
it('should have an id of 0xc339d10a', async () => { it('should have an id of 0xc339d10a', async () => {
const proxyId = await staticCallProxy.getProxyId().callAsync(); const proxyId = await staticCallProxy.getProxyId.callAsync();
const expectedProxyId = AssetProxyId.StaticCall; const expectedProxyId = AssetProxyId.StaticCall;
expect(proxyId).to.equal(expectedProxyId); expect(proxyId).to.equal(expectedProxyId);
}); });
}); });
describe('transferFrom', () => { describe('transferFrom', () => {
it('should revert if assetData lies outside the bounds of calldata', async () => { it('should revert if assetData lies outside the bounds of calldata', async () => {
const staticCallData = staticCallTarget.noInputFunction().getABIEncodedTransactionData(); const staticCallData = staticCallTarget.noInputFunction.getABIEncodedTransactionData();
const expectedResultHash = constants.KECCAK256_NULL; const expectedResultHash = constants.KECCAK256_NULL;
const assetData = await devUtils const assetData = assetDataUtils.encodeStaticCallAssetData(
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash) staticCallTarget.address,
.callAsync(); staticCallData,
const txData = staticCallProxy expectedResultHash,
.transferFrom(assetData, fromAddress, toAddress, amount) );
.getABIEncodedTransactionData(); const txData = staticCallProxy.transferFrom.getABIEncodedTransactionData(
assetData,
fromAddress,
toAddress,
amount,
);
const offsetToAssetData = '0000000000000000000000000000000000000000000000000000000000000080'; const offsetToAssetData = '0000000000000000000000000000000000000000000000000000000000000080';
const txDataEndBuffer = ethUtil.toBuffer((txData.length - 2) / 2 - 4); const txDataEndBuffer = ethUtil.toBuffer((txData.length - 2) / 2 - 4);
const paddedTxDataEndBuffer = ethUtil.setLengthLeft(txDataEndBuffer, 32); const paddedTxDataEndBuffer = ethUtil.setLengthLeft(txDataEndBuffer, 32);
@@ -113,21 +108,23 @@ describe('StaticCallProxy', () => {
it('should revert if the length of assetData is less than 100 bytes', async () => { it('should revert if the length of assetData is less than 100 bytes', async () => {
const staticCallData = constants.NULL_BYTES; const staticCallData = constants.NULL_BYTES;
const expectedResultHash = constants.KECCAK256_NULL; const expectedResultHash = constants.KECCAK256_NULL;
const assetData = (await devUtils const assetData = assetDataUtils
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash) .encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash)
.callAsync()).slice(0, -128); .slice(0, -128);
const assetDataByteLen = (assetData.length - 2) / 2; const assetDataByteLen = (assetData.length - 2) / 2;
expect((assetDataByteLen - 4) % 32).to.equal(0); expect((assetDataByteLen - 4) % 32).to.equal(0);
await expectTransactionFailedWithoutReasonAsync( await expectTransactionFailedWithoutReasonAsync(
staticCallProxy.transferFrom(assetData, fromAddress, toAddress, amount).sendTransactionAsync(), staticCallProxy.transferFrom.sendTransactionAsync(assetData, fromAddress, toAddress, amount),
); );
}); });
it('should revert if the offset to `staticCallData` points to outside of assetData', async () => { it('should revert if the offset to `staticCallData` points to outside of assetData', async () => {
const staticCallData = staticCallTarget.noInputFunction().getABIEncodedTransactionData(); const staticCallData = staticCallTarget.noInputFunction.getABIEncodedTransactionData();
const expectedResultHash = constants.KECCAK256_NULL; const expectedResultHash = constants.KECCAK256_NULL;
const assetData = await devUtils const assetData = assetDataUtils.encodeStaticCallAssetData(
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash) staticCallTarget.address,
.callAsync(); staticCallData,
expectedResultHash,
);
const offsetToStaticCallData = '0000000000000000000000000000000000000000000000000000000000000060'; const offsetToStaticCallData = '0000000000000000000000000000000000000000000000000000000000000060';
const assetDataEndBuffer = ethUtil.toBuffer((assetData.length - 2) / 2 - 4); const assetDataEndBuffer = ethUtil.toBuffer((assetData.length - 2) / 2 - 4);
const paddedAssetDataEndBuffer = ethUtil.setLengthLeft(assetDataEndBuffer, 32); const paddedAssetDataEndBuffer = ethUtil.setLengthLeft(assetDataEndBuffer, 32);
@@ -138,88 +135,90 @@ describe('StaticCallProxy', () => {
invalidOffsetToStaticCallData, invalidOffsetToStaticCallData,
)}${newStaticCallData}`; )}${newStaticCallData}`;
await expectTransactionFailedWithoutReasonAsync( await expectTransactionFailedWithoutReasonAsync(
staticCallProxy.transferFrom(badAssetData, fromAddress, toAddress, amount).sendTransactionAsync(), staticCallProxy.transferFrom.sendTransactionAsync(badAssetData, fromAddress, toAddress, amount),
); );
}); });
it('should revert if the callTarget attempts to write to state', async () => { it('should revert if the callTarget attempts to write to state', async () => {
const staticCallData = staticCallTarget.updateState().getABIEncodedTransactionData(); const staticCallData = staticCallTarget.updateState.getABIEncodedTransactionData();
const expectedResultHash = constants.KECCAK256_NULL; const expectedResultHash = constants.KECCAK256_NULL;
const assetData = await devUtils const assetData = assetDataUtils.encodeStaticCallAssetData(
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash) staticCallTarget.address,
.callAsync(); staticCallData,
expectedResultHash,
);
await expectTransactionFailedWithoutReasonAsync( await expectTransactionFailedWithoutReasonAsync(
staticCallProxy.transferFrom(assetData, fromAddress, toAddress, amount).sendTransactionAsync(), staticCallProxy.transferFrom.sendTransactionAsync(assetData, fromAddress, toAddress, amount),
); );
}); });
it('should revert with data provided by the callTarget if the staticcall reverts', async () => { it('should revert with data provided by the callTarget if the staticcall reverts', async () => {
const staticCallData = staticCallTarget.assertEvenNumber(new BigNumber(1)).getABIEncodedTransactionData(); const staticCallData = staticCallTarget.assertEvenNumber.getABIEncodedTransactionData(new BigNumber(1));
const expectedResultHash = constants.KECCAK256_NULL; const expectedResultHash = constants.KECCAK256_NULL;
const assetData = await devUtils const assetData = assetDataUtils.encodeStaticCallAssetData(
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash) staticCallTarget.address,
.callAsync(); staticCallData,
expectedResultHash,
);
await expectTransactionFailedAsync( await expectTransactionFailedAsync(
staticCallProxy.transferFrom(assetData, fromAddress, toAddress, amount).sendTransactionAsync(), staticCallProxy.transferFrom.sendTransactionAsync(assetData, fromAddress, toAddress, amount),
RevertReason.TargetNotEven, RevertReason.TargetNotEven,
); );
}); });
it('should revert if the hash of the output is different than expected expected', async () => { it('should revert if the hash of the output is different than expected expected', async () => {
const staticCallData = staticCallTarget.isOddNumber(new BigNumber(0)).getABIEncodedTransactionData(); const staticCallData = staticCallTarget.isOddNumber.getABIEncodedTransactionData(new BigNumber(0));
const trueAsBuffer = ethUtil.toBuffer('0x0000000000000000000000000000000000000000000000000000000000000001'); const trueAsBuffer = ethUtil.toBuffer('0x0000000000000000000000000000000000000000000000000000000000000001');
const expectedResultHash = ethUtil.bufferToHex(ethUtil.sha3(trueAsBuffer)); const expectedResultHash = ethUtil.bufferToHex(ethUtil.sha3(trueAsBuffer));
const assetData = await devUtils const assetData = assetDataUtils.encodeStaticCallAssetData(
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash) staticCallTarget.address,
.callAsync(); staticCallData,
expectedResultHash,
);
await expectTransactionFailedAsync( await expectTransactionFailedAsync(
staticCallProxy.transferFrom(assetData, fromAddress, toAddress, amount).sendTransactionAsync(), staticCallProxy.transferFrom.sendTransactionAsync(assetData, fromAddress, toAddress, amount),
RevertReason.UnexpectedStaticCallResult, RevertReason.UnexpectedStaticCallResult,
); );
}); });
it('should be successful if a function call with no inputs and no outputs is successful', async () => { it('should be successful if a function call with no inputs and no outputs is successful', async () => {
const staticCallData = staticCallTarget.noInputFunction().getABIEncodedTransactionData(); const staticCallData = staticCallTarget.noInputFunction.getABIEncodedTransactionData();
const expectedResultHash = constants.KECCAK256_NULL; const expectedResultHash = constants.KECCAK256_NULL;
const assetData = await devUtils const assetData = assetDataUtils.encodeStaticCallAssetData(
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash) staticCallTarget.address,
.callAsync(); staticCallData,
await staticCallProxy expectedResultHash,
.transferFrom(assetData, fromAddress, toAddress, amount) );
.awaitTransactionSuccessAsync(); await staticCallProxy.transferFrom.awaitTransactionSuccessAsync(assetData, fromAddress, toAddress, amount);
}); });
it('should be successful if the staticCallTarget is not a contract and no return value is expected', async () => { it('should be successful if the staticCallTarget is not a contract and no return value is expected', async () => {
const staticCallData = '0x0102030405060708'; const staticCallData = '0x0102030405060708';
const expectedResultHash = constants.KECCAK256_NULL; const expectedResultHash = constants.KECCAK256_NULL;
const assetData = await devUtils const assetData = assetDataUtils.encodeStaticCallAssetData(toAddress, staticCallData, expectedResultHash);
.encodeStaticCallAssetData(toAddress, staticCallData, expectedResultHash) await staticCallProxy.transferFrom.awaitTransactionSuccessAsync(assetData, fromAddress, toAddress, amount);
.callAsync();
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 () => { 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 staticCallData = staticCallTarget.isOddNumber.getABIEncodedTransactionData(new BigNumber(1));
const trueAsBuffer = ethUtil.toBuffer('0x0000000000000000000000000000000000000000000000000000000000000001'); const trueAsBuffer = ethUtil.toBuffer('0x0000000000000000000000000000000000000000000000000000000000000001');
const expectedResultHash = ethUtil.bufferToHex(ethUtil.sha3(trueAsBuffer)); const expectedResultHash = ethUtil.bufferToHex(ethUtil.sha3(trueAsBuffer));
const assetData = await devUtils const assetData = assetDataUtils.encodeStaticCallAssetData(
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash) staticCallTarget.address,
.callAsync(); staticCallData,
await staticCallProxy expectedResultHash,
.transferFrom(assetData, fromAddress, toAddress, amount) );
.awaitTransactionSuccessAsync(); await staticCallProxy.transferFrom.awaitTransactionSuccessAsync(assetData, fromAddress, toAddress, amount);
}); });
it('should be successful if a function with one dynamic input is successful', async () => { it('should be successful if a function with one dynamic input is successful', async () => {
const dynamicInput = '0x0102030405060708'; const dynamicInput = '0x0102030405060708';
const staticCallData = staticCallTarget.dynamicInputFunction(dynamicInput).getABIEncodedTransactionData(); const staticCallData = staticCallTarget.dynamicInputFunction.getABIEncodedTransactionData(dynamicInput);
const expectedResultHash = constants.KECCAK256_NULL; const expectedResultHash = constants.KECCAK256_NULL;
const assetData = await devUtils const assetData = assetDataUtils.encodeStaticCallAssetData(
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash) staticCallTarget.address,
.callAsync(); staticCallData,
await staticCallProxy expectedResultHash,
.transferFrom(assetData, fromAddress, toAddress, amount) );
.awaitTransactionSuccessAsync(); await staticCallProxy.transferFrom.awaitTransactionSuccessAsync(assetData, fromAddress, toAddress, amount);
}); });
it('should be successful if a function call returns a complex type', async () => { it('should be successful if a function call returns a complex type', async () => {
const a = new BigNumber(1); const a = new BigNumber(1);
const b = new BigNumber(2); const b = new BigNumber(2);
const staticCallData = staticCallTarget.returnComplexType(a, b).getABIEncodedTransactionData(); const staticCallData = staticCallTarget.returnComplexType.getABIEncodedTransactionData(a, b);
const abiEncoder = new AbiEncoder.DynamicBytes({ const abiEncoder = new AbiEncoder.DynamicBytes({
name: '', name: '',
type: 'bytes', type: 'bytes',
@@ -232,12 +231,12 @@ describe('StaticCallProxy', () => {
const expectedResultHash = ethUtil.bufferToHex( const expectedResultHash = ethUtil.bufferToHex(
ethUtil.sha3(ethUtil.toBuffer(encodedExpectedResultWithOffset)), ethUtil.sha3(ethUtil.toBuffer(encodedExpectedResultWithOffset)),
); );
const assetData = await devUtils const assetData = assetDataUtils.encodeStaticCallAssetData(
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash) staticCallTarget.address,
.callAsync(); staticCallData,
await staticCallProxy expectedResultHash,
.transferFrom(assetData, fromAddress, toAddress, amount) );
.awaitTransactionSuccessAsync(); await staticCallProxy.transferFrom.awaitTransactionSuccessAsync(assetData, fromAddress, toAddress, amount);
}); });
}); });
}); });

View File

@@ -1,370 +0,0 @@
import {
blockchainTests,
constants,
expect,
filterLogs,
filterLogsToArguments,
getRandomInteger,
hexLeftPad,
hexRandom,
Numberish,
randomAddress,
} from '@0x/contracts-test-utils';
import { AssetProxyId } from '@0x/types';
import { BigNumber } 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(hexRandom(), hexRandom(_.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.
hexLeftPad(_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(0);
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),
hexLeftPad(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),
hexLeftPad(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),
hexLeftPad(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);
});
});
});
});

View File

@@ -1,4 +1,3 @@
import { DevUtilsContract } from '@0x/contracts-dev-utils';
import { artifacts as erc1155Artifacts, ERC1155MintableContract, Erc1155Wrapper } from '@0x/contracts-erc1155'; import { artifacts as erc1155Artifacts, ERC1155MintableContract, Erc1155Wrapper } from '@0x/contracts-erc1155';
import { import {
constants, constants,
@@ -8,14 +7,13 @@ import {
LogDecoder, LogDecoder,
txDefaults, txDefaults,
} from '@0x/contracts-test-utils'; } from '@0x/contracts-test-utils';
import { assetDataUtils } from '@0x/order-utils';
import { BigNumber } from '@0x/utils'; import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper'; import { Web3Wrapper } from '@0x/web3-wrapper';
import { Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types'; import { Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
import * as _ from 'lodash'; import * as _ from 'lodash';
import { artifacts } from './artifacts'; import { artifacts, ERC1155ProxyContract, IAssetProxyContract } from '../../src';
import { ERC1155ProxyContract, IAssetProxyContract } from './wrappers';
export class ERC1155ProxyWrapper { export class ERC1155ProxyWrapper {
private readonly _tokenOwnerAddresses: string[]; private readonly _tokenOwnerAddresses: string[];
@@ -28,7 +26,6 @@ export class ERC1155ProxyWrapper {
private readonly _logDecoder: LogDecoder; private readonly _logDecoder: LogDecoder;
private readonly _dummyTokenWrappers: Erc1155Wrapper[]; private readonly _dummyTokenWrappers: Erc1155Wrapper[];
private readonly _assetProxyInterface: IAssetProxyContract; private readonly _assetProxyInterface: IAssetProxyContract;
private readonly _devUtils: DevUtilsContract;
private _proxyContract?: ERC1155ProxyContract; private _proxyContract?: ERC1155ProxyContract;
private _proxyIdIfExists?: string; private _proxyIdIfExists?: string;
private _initialTokenIdsByOwner: ERC1155HoldingsByOwner = { fungible: {}, nonFungible: {} }; private _initialTokenIdsByOwner: ERC1155HoldingsByOwner = { fungible: {}, nonFungible: {} };
@@ -40,7 +37,6 @@ export class ERC1155ProxyWrapper {
this._logDecoder = new LogDecoder(this._web3Wrapper, allArtifacts); this._logDecoder = new LogDecoder(this._web3Wrapper, allArtifacts);
this._dummyTokenWrappers = []; this._dummyTokenWrappers = [];
this._assetProxyInterface = new IAssetProxyContract(constants.NULL_ADDRESS, provider); this._assetProxyInterface = new IAssetProxyContract(constants.NULL_ADDRESS, provider);
this._devUtils = new DevUtilsContract(constants.NULL_ADDRESS, provider);
this._tokenOwnerAddresses = tokenOwnerAddresses; this._tokenOwnerAddresses = tokenOwnerAddresses;
this._contractOwnerAddress = contractOwnerAddress; this._contractOwnerAddress = contractOwnerAddress;
this._fungibleTokenIds = []; this._fungibleTokenIds = [];
@@ -76,7 +72,7 @@ export class ERC1155ProxyWrapper {
txDefaults, txDefaults,
artifacts, artifacts,
); );
this._proxyIdIfExists = await this._proxyContract.getProxyId().callAsync(); this._proxyIdIfExists = await this._proxyContract.getProxyId.callAsync();
return this._proxyContract; return this._proxyContract;
} }
/** /**
@@ -99,7 +95,7 @@ export class ERC1155ProxyWrapper {
* @param extraData extra data to append to `transferFrom` transaction. Optional. * @param extraData extra data to append to `transferFrom` transaction. Optional.
* @return abi encoded tx data. * @return abi encoded tx data.
*/ */
public async getTransferFromAbiEncodedTxDataAsync( public getTransferFromAbiEncodedTxData(
from: string, from: string,
to: string, to: string,
contractAddress: string, contractAddress: string,
@@ -109,17 +105,23 @@ export class ERC1155ProxyWrapper {
receiverCallbackData: string, receiverCallbackData: string,
authorizedSender: string, authorizedSender: string,
assetData_?: string, assetData_?: string,
): Promise<string> { ): string {
this._validateProxyContractExistsOrThrow(); this._validateProxyContractExistsOrThrow();
const assetData = const assetData =
assetData_ === undefined assetData_ === undefined
? await this._devUtils ? assetDataUtils.encodeERC1155AssetData(
.encodeERC1155AssetData(contractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData) contractAddress,
.callAsync() tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
)
: assetData_; : assetData_;
const data = this._assetProxyInterface const data = this._assetProxyInterface.transferFrom.getABIEncodedTransactionData(
.transferFrom(assetData, from, to, valueMultiplier) assetData,
.getABIEncodedTransactionData(); from,
to,
valueMultiplier,
);
return data; return data;
} }
/** /**
@@ -167,13 +169,19 @@ export class ERC1155ProxyWrapper {
this._validateProxyContractExistsOrThrow(); this._validateProxyContractExistsOrThrow();
const assetData = const assetData =
assetData_ === undefined assetData_ === undefined
? await this._devUtils ? assetDataUtils.encodeERC1155AssetData(
.encodeERC1155AssetData(contractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData) contractAddress,
.callAsync() tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
)
: assetData_; : assetData_;
const data = this._assetProxyInterface const data = this._assetProxyInterface.transferFrom.getABIEncodedTransactionData(
.transferFrom(assetData, from, to, valueMultiplier) assetData,
.getABIEncodedTransactionData(); from,
to,
valueMultiplier,
);
const txHash = await this._web3Wrapper.sendTransactionAsync({ const txHash = await this._web3Wrapper.sendTransactionAsync({
to: (this._proxyContract as ERC1155ProxyContract).address, to: (this._proxyContract as ERC1155ProxyContract).address,
data, data,
@@ -328,22 +336,6 @@ export class ERC1155ProxyWrapper {
}; };
return holdingsByOwner; 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`. * @dev Checks if proxy is approved to transfer tokens on behalf of `userAddress`.
* @param userAddress owner of ERC1155 tokens. * @param userAddress owner of ERC1155 tokens.
@@ -354,7 +346,7 @@ export class ERC1155ProxyWrapper {
this._validateProxyContractExistsOrThrow(); this._validateProxyContractExistsOrThrow();
const tokenContract = this._getContractFromAddress(contractAddress); const tokenContract = this._getContractFromAddress(contractAddress);
const operator = (this._proxyContract as ERC1155ProxyContract).address; const operator = (this._proxyContract as ERC1155ProxyContract).address;
const didApproveAll = await tokenContract.isApprovedForAll(userAddress, operator).callAsync(); const didApproveAll = await tokenContract.isApprovedForAll.callAsync(userAddress, operator);
return didApproveAll; return didApproveAll;
} }
public getFungibleTokenIds(): BigNumber[] { public getFungibleTokenIds(): BigNumber[] {

View File

@@ -1,20 +1,17 @@
import { DevUtilsContract } from '@0x/contracts-dev-utils';
import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contracts-erc20'; import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contracts-erc20';
import { constants, ERC20BalancesByOwner, txDefaults } from '@0x/contracts-test-utils'; import { constants, ERC20BalancesByOwner, txDefaults } from '@0x/contracts-test-utils';
import { assetDataUtils } from '@0x/order-utils';
import { BigNumber } from '@0x/utils'; import { BigNumber } from '@0x/utils';
import { ZeroExProvider } from 'ethereum-types'; import { ZeroExProvider } from 'ethereum-types';
import * as _ from 'lodash'; import * as _ from 'lodash';
import { artifacts } from './artifacts'; import { artifacts, ERC20ProxyContract } from '../../src';
import { ERC20ProxyContract } from './wrappers';
export class ERC20Wrapper { export class ERC20Wrapper {
private readonly _tokenOwnerAddresses: string[]; private readonly _tokenOwnerAddresses: string[];
private readonly _contractOwnerAddress: string; private readonly _contractOwnerAddress: string;
private readonly _provider: ZeroExProvider; private readonly _provider: ZeroExProvider;
private readonly _dummyTokenContracts: DummyERC20TokenContract[]; private readonly _dummyTokenContracts: DummyERC20TokenContract[];
private readonly _devUtils: DevUtilsContract;
private _proxyContract?: ERC20ProxyContract; private _proxyContract?: ERC20ProxyContract;
private _proxyIdIfExists?: string; private _proxyIdIfExists?: string;
/** /**
@@ -29,7 +26,6 @@ export class ERC20Wrapper {
this._provider = provider; this._provider = provider;
this._tokenOwnerAddresses = tokenOwnerAddresses; this._tokenOwnerAddresses = tokenOwnerAddresses;
this._contractOwnerAddress = contractOwnerAddress; this._contractOwnerAddress = contractOwnerAddress;
this._devUtils = new DevUtilsContract(constants.NULL_ADDRESS, provider);
} }
public async deployDummyTokensAsync( public async deployDummyTokensAsync(
numberToDeploy: number, numberToDeploy: number,
@@ -58,7 +54,7 @@ export class ERC20Wrapper {
txDefaults, txDefaults,
artifacts, artifacts,
); );
this._proxyIdIfExists = await this._proxyContract.getProxyId().callAsync(); this._proxyIdIfExists = await this._proxyContract.getProxyId.callAsync();
return this._proxyContract; return this._proxyContract;
} }
public getProxyId(): string { public getProxyId(): string {
@@ -70,39 +66,50 @@ export class ERC20Wrapper {
this._validateProxyContractExistsOrThrow(); this._validateProxyContractExistsOrThrow();
for (const dummyTokenContract of this._dummyTokenContracts) { for (const dummyTokenContract of this._dummyTokenContracts) {
for (const tokenOwnerAddress of this._tokenOwnerAddresses) { for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
await dummyTokenContract await dummyTokenContract.setBalance.awaitTransactionSuccessAsync(
.setBalance(tokenOwnerAddress, constants.INITIAL_ERC20_BALANCE) tokenOwnerAddress,
.awaitTransactionSuccessAsync({ from: this._contractOwnerAddress }); constants.INITIAL_ERC20_BALANCE,
await dummyTokenContract { from: this._contractOwnerAddress },
.approve((this._proxyContract as ERC20ProxyContract).address, constants.INITIAL_ERC20_ALLOWANCE) constants.AWAIT_TRANSACTION_MINED_MS,
.awaitTransactionSuccessAsync({ from: tokenOwnerAddress }); );
await dummyTokenContract.approve.awaitTransactionSuccessAsync(
(this._proxyContract as ERC20ProxyContract).address,
constants.INITIAL_ERC20_ALLOWANCE,
{ from: tokenOwnerAddress },
constants.AWAIT_TRANSACTION_MINED_MS,
);
} }
} }
} }
public async getBalanceAsync(userAddress: string, assetData: string): Promise<BigNumber> { public async getBalanceAsync(userAddress: string, assetData: string): Promise<BigNumber> {
const tokenContract = await this._getTokenContractFromAssetDataAsync(assetData); const tokenContract = this._getTokenContractFromAssetData(assetData);
const balance = new BigNumber(await tokenContract.balanceOf(userAddress).callAsync()); const balance = new BigNumber(await tokenContract.balanceOf.callAsync(userAddress));
return balance; return balance;
} }
public async setBalanceAsync(userAddress: string, assetData: string, amount: BigNumber): Promise<void> { public async setBalanceAsync(userAddress: string, assetData: string, amount: BigNumber): Promise<void> {
const tokenContract = await this._getTokenContractFromAssetDataAsync(assetData); const tokenContract = this._getTokenContractFromAssetData(assetData);
await tokenContract await tokenContract.setBalance.awaitTransactionSuccessAsync(
.setBalance(userAddress, amount) userAddress,
.awaitTransactionSuccessAsync( amount,
{ from: this._contractOwnerAddress }, { from: this._contractOwnerAddress },
{ pollingIntervalMs: constants.AWAIT_TRANSACTION_MINED_MS }, constants.AWAIT_TRANSACTION_MINED_MS,
); );
} }
public async getProxyAllowanceAsync(userAddress: string, assetData: string): Promise<BigNumber> { public async getProxyAllowanceAsync(userAddress: string, assetData: string): Promise<BigNumber> {
const tokenContract = await this._getTokenContractFromAssetDataAsync(assetData); const tokenContract = this._getTokenContractFromAssetData(assetData);
const proxyAddress = (this._proxyContract as ERC20ProxyContract).address; const proxyAddress = (this._proxyContract as ERC20ProxyContract).address;
const allowance = new BigNumber(await tokenContract.allowance(userAddress, proxyAddress).callAsync()); const allowance = new BigNumber(await tokenContract.allowance.callAsync(userAddress, proxyAddress));
return allowance; return allowance;
} }
public async setAllowanceAsync(userAddress: string, assetData: string, amount: BigNumber): Promise<void> { public async setAllowanceAsync(userAddress: string, assetData: string, amount: BigNumber): Promise<void> {
const tokenContract = await this._getTokenContractFromAssetDataAsync(assetData); const tokenContract = this._getTokenContractFromAssetData(assetData);
const proxyAddress = (this._proxyContract as ERC20ProxyContract).address; const proxyAddress = (this._proxyContract as ERC20ProxyContract).address;
await tokenContract.approve(proxyAddress, amount).awaitTransactionSuccessAsync({ from: userAddress }); await tokenContract.approve.awaitTransactionSuccessAsync(
proxyAddress,
amount,
{ from: userAddress },
constants.AWAIT_TRANSACTION_MINED_MS,
);
} }
public async getBalancesAsync(): Promise<ERC20BalancesByOwner> { public async getBalancesAsync(): Promise<ERC20BalancesByOwner> {
this._validateDummyTokenContractsExistOrThrow(); this._validateDummyTokenContractsExistOrThrow();
@@ -111,7 +118,7 @@ export class ERC20Wrapper {
const balanceInfo: Array<{ tokenOwnerAddress: string; tokenAddress: string }> = []; const balanceInfo: Array<{ tokenOwnerAddress: string; tokenAddress: string }> = [];
for (const dummyTokenContract of this._dummyTokenContracts) { for (const dummyTokenContract of this._dummyTokenContracts) {
for (const tokenOwnerAddress of this._tokenOwnerAddresses) { for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
balances.push(await dummyTokenContract.balanceOf(tokenOwnerAddress).callAsync()); balances.push(await dummyTokenContract.balanceOf.callAsync(tokenOwnerAddress));
balanceInfo.push({ balanceInfo.push({
tokenOwnerAddress, tokenOwnerAddress,
tokenAddress: dummyTokenContract.address, tokenAddress: dummyTokenContract.address,
@@ -144,8 +151,9 @@ export class ERC20Wrapper {
const tokenAddresses = _.map(this._dummyTokenContracts, dummyTokenContract => dummyTokenContract.address); const tokenAddresses = _.map(this._dummyTokenContracts, dummyTokenContract => dummyTokenContract.address);
return tokenAddresses; return tokenAddresses;
} }
private async _getTokenContractFromAssetDataAsync(assetData: string): Promise<DummyERC20TokenContract> { private _getTokenContractFromAssetData(assetData: string): DummyERC20TokenContract {
const [proxyId, tokenAddress] = await this._devUtils.decodeERC20AssetData(assetData).callAsync(); // tslint:disable-line:no-unused-variable const erc20ProxyData = assetDataUtils.decodeERC20AssetData(assetData);
const tokenAddress = erc20ProxyData.tokenAddress;
const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === tokenAddress); const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === tokenAddress);
if (tokenContractIfExists === undefined) { if (tokenContractIfExists === undefined) {
throw new Error(`Token: ${tokenAddress} was not deployed through ERC20Wrapper`); throw new Error(`Token: ${tokenAddress} was not deployed through ERC20Wrapper`);

View File

@@ -5,9 +5,7 @@ import { BigNumber } from '@0x/utils';
import { ZeroExProvider } from 'ethereum-types'; import { ZeroExProvider } from 'ethereum-types';
import * as _ from 'lodash'; import * as _ from 'lodash';
import { artifacts } from './artifacts'; import { artifacts, ERC721ProxyContract } from '../../src';
import { ERC721ProxyContract } from './wrappers';
export class ERC721Wrapper { export class ERC721Wrapper {
private readonly _tokenOwnerAddresses: string[]; private readonly _tokenOwnerAddresses: string[];
@@ -46,7 +44,7 @@ export class ERC721Wrapper {
txDefaults, txDefaults,
artifacts, artifacts,
); );
this._proxyIdIfExists = await this._proxyContract.getProxyId().callAsync(); this._proxyIdIfExists = await this._proxyContract.getProxyId.callAsync();
return this._proxyContract; return this._proxyContract;
} }
public getProxyId(): string { public getProxyId(): string {
@@ -73,14 +71,14 @@ export class ERC721Wrapper {
} }
this._initialTokenIdsByOwner[tokenOwnerAddress][dummyTokenContract.address].push(tokenId); this._initialTokenIdsByOwner[tokenOwnerAddress][dummyTokenContract.address].push(tokenId);
await this.approveProxyForAllAsync(dummyTokenContract.address, tokenOwnerAddress, true); await this.approveProxyAsync(dummyTokenContract.address, tokenId);
} }
} }
} }
} }
public async doesTokenExistAsync(tokenAddress: string, tokenId: BigNumber): Promise<boolean> { public async doesTokenExistAsync(tokenAddress: string, tokenId: BigNumber): Promise<boolean> {
const tokenContract = this._getTokenContractFromAssetData(tokenAddress); const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
const owner = await tokenContract.ownerOf(tokenId).callAsync(); const owner = await tokenContract.ownerOf.callAsync(tokenId);
const doesExist = owner !== constants.NULL_ADDRESS; const doesExist = owner !== constants.NULL_ADDRESS;
return doesExist; return doesExist;
} }
@@ -88,21 +86,26 @@ export class ERC721Wrapper {
const proxyAddress = (this._proxyContract as ERC721ProxyContract).address; const proxyAddress = (this._proxyContract as ERC721ProxyContract).address;
await this.approveAsync(proxyAddress, tokenAddress, tokenId); await this.approveAsync(proxyAddress, tokenAddress, tokenId);
} }
public async approveProxyForAllAsync( public async approveProxyForAllAsync(tokenAddress: string, tokenId: BigNumber, isApproved: boolean): Promise<void> {
tokenAddress: string,
ownerAddress: string,
isApproved: boolean,
): Promise<void> {
const tokenContract = this._getTokenContractFromAssetData(tokenAddress); const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
const tokenOwner = await this.ownerOfAsync(tokenAddress, tokenId);
const proxyAddress = (this._proxyContract as ERC721ProxyContract).address; const proxyAddress = (this._proxyContract as ERC721ProxyContract).address;
await tokenContract.setApprovalForAll(proxyAddress, isApproved).awaitTransactionSuccessAsync({ await tokenContract.setApprovalForAll.awaitTransactionSuccessAsync(
from: ownerAddress, proxyAddress,
}); isApproved,
{ from: tokenOwner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
} }
public async approveAsync(to: string, tokenAddress: string, tokenId: BigNumber): Promise<void> { public async approveAsync(to: string, tokenAddress: string, tokenId: BigNumber): Promise<void> {
const tokenContract = this._getTokenContractFromAssetData(tokenAddress); const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
const tokenOwner = await this.ownerOfAsync(tokenAddress, tokenId); const tokenOwner = await this.ownerOfAsync(tokenAddress, tokenId);
await tokenContract.approve(to, tokenId).awaitTransactionSuccessAsync({ from: tokenOwner }); await tokenContract.approve.awaitTransactionSuccessAsync(
to,
tokenId,
{ from: tokenOwner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
} }
public async transferFromAsync( public async transferFromAsync(
tokenAddress: string, tokenAddress: string,
@@ -111,28 +114,40 @@ export class ERC721Wrapper {
userAddress: string, userAddress: string,
): Promise<void> { ): Promise<void> {
const tokenContract = this._getTokenContractFromAssetData(tokenAddress); const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
await tokenContract.transferFrom(currentOwner, userAddress, tokenId).awaitTransactionSuccessAsync({ await tokenContract.transferFrom.awaitTransactionSuccessAsync(
from: currentOwner, currentOwner,
}); userAddress,
tokenId,
{ from: currentOwner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
} }
public async mintAsync(tokenAddress: string, tokenId: BigNumber, userAddress: string): Promise<void> { public async mintAsync(tokenAddress: string, tokenId: BigNumber, userAddress: string): Promise<void> {
const tokenContract = this._getTokenContractFromAssetData(tokenAddress); const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
await tokenContract.mint(userAddress, tokenId).awaitTransactionSuccessAsync({ await tokenContract.mint.awaitTransactionSuccessAsync(
from: this._contractOwnerAddress, userAddress,
}); tokenId,
{ from: this._contractOwnerAddress },
constants.AWAIT_TRANSACTION_MINED_MS,
);
} }
public async burnAsync(tokenAddress: string, tokenId: BigNumber, owner: string): Promise<void> { public async burnAsync(tokenAddress: string, tokenId: BigNumber, owner: string): Promise<void> {
const tokenContract = this._getTokenContractFromAssetData(tokenAddress); const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
await tokenContract.burn(owner, tokenId).awaitTransactionSuccessAsync({ from: this._contractOwnerAddress }); await tokenContract.burn.awaitTransactionSuccessAsync(
owner,
tokenId,
{ from: this._contractOwnerAddress },
constants.AWAIT_TRANSACTION_MINED_MS,
);
} }
public async ownerOfAsync(tokenAddress: string, tokenId: BigNumber): Promise<string> { public async ownerOfAsync(tokenAddress: string, tokenId: BigNumber): Promise<string> {
const tokenContract = this._getTokenContractFromAssetData(tokenAddress); const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
const owner = await tokenContract.ownerOf(tokenId).callAsync(); const owner = await tokenContract.ownerOf.callAsync(tokenId);
return owner; return owner;
} }
public async isOwnerAsync(userAddress: string, tokenAddress: string, tokenId: BigNumber): Promise<boolean> { public async isOwnerAsync(userAddress: string, tokenAddress: string, tokenId: BigNumber): Promise<boolean> {
const tokenContract = this._getTokenContractFromAssetData(tokenAddress); const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
const tokenOwner = await tokenContract.ownerOf(tokenId).callAsync(); const tokenOwner = await tokenContract.ownerOf.callAsync(tokenId);
const isOwner = tokenOwner === userAddress; const isOwner = tokenOwner === userAddress;
return isOwner; return isOwner;
} }
@@ -140,13 +155,13 @@ export class ERC721Wrapper {
this._validateProxyContractExistsOrThrow(); this._validateProxyContractExistsOrThrow();
const tokenContract = this._getTokenContractFromAssetData(tokenAddress); const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
const operator = (this._proxyContract as ERC721ProxyContract).address; const operator = (this._proxyContract as ERC721ProxyContract).address;
const didApproveAll = await tokenContract.isApprovedForAll(userAddress, operator).callAsync(); const didApproveAll = await tokenContract.isApprovedForAll.callAsync(userAddress, operator);
return didApproveAll; return didApproveAll;
} }
public async isProxyApprovedAsync(tokenAddress: string, tokenId: BigNumber): Promise<boolean> { public async isProxyApprovedAsync(tokenAddress: string, tokenId: BigNumber): Promise<boolean> {
this._validateProxyContractExistsOrThrow(); this._validateProxyContractExistsOrThrow();
const tokenContract = this._getTokenContractFromAssetData(tokenAddress); const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
const approvedAddress = await tokenContract.getApproved(tokenId).callAsync(); const approvedAddress = await tokenContract.getApproved.callAsync(tokenId);
const proxyAddress = (this._proxyContract as ERC721ProxyContract).address; const proxyAddress = (this._proxyContract as ERC721ProxyContract).address;
const isProxyAnApprovedOperator = approvedAddress === proxyAddress; const isProxyAnApprovedOperator = approvedAddress === proxyAddress;
return isProxyAnApprovedOperator; return isProxyAnApprovedOperator;
@@ -163,7 +178,7 @@ export class ERC721Wrapper {
dummyTokenContract.address dummyTokenContract.address
]; ];
for (const tokenId of initialTokenOwnerIds) { for (const tokenId of initialTokenOwnerIds) {
tokenOwnerAddresses.push(await dummyTokenContract.ownerOf(tokenId).callAsync()); tokenOwnerAddresses.push(await dummyTokenContract.ownerOf.callAsync(tokenId));
tokenInfo.push({ tokenInfo.push({
tokenId, tokenId,
tokenAddress: dummyTokenContract.address, tokenAddress: dummyTokenContract.address,

View File

@@ -0,0 +1,3 @@
export * from './erc20_wrapper';
export * from './erc721_wrapper';
export * from './erc1155_proxy_wrapper';

View File

@@ -1,28 +0,0 @@
/*
* -----------------------------------------------------------------------------
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
* -----------------------------------------------------------------------------
*/
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_erc20_bridge';
export * from '../test/generated-wrappers/i_eth2_dai';
export * from '../test/generated-wrappers/i_uniswap_exchange';
export * from '../test/generated-wrappers/i_uniswap_exchange_factory';
export * from '../test/generated-wrappers/mixin_asset_proxy_dispatcher';
export * from '../test/generated-wrappers/mixin_authorizable';
export * from '../test/generated-wrappers/multi_asset_proxy';
export * from '../test/generated-wrappers/ownable';
export * from '../test/generated-wrappers/static_call_proxy';
export * from '../test/generated-wrappers/test_erc20_bridge';
export * from '../test/generated-wrappers/test_eth2_dai_bridge';
export * from '../test/generated-wrappers/test_static_call_target';
export * from '../test/generated-wrappers/test_uniswap_bridge';
export * from '../test/generated-wrappers/uniswap_bridge';

View File

@@ -1,96 +0,0 @@
/**
* Use this file to configure your truffle project. It's seeded with some
* common settings for different networks and features like migrations,
* compilation and testing. Uncomment the ones you need or modify
* them to suit your project as necessary.
*
* More information about configuration can be found at:
*
* truffleframework.com/docs/advanced/configuration
*
* To deploy via Infura you'll need a wallet provider (like truffle-hdwallet-provider)
* to sign your transactions before they're sent to a remote public node. Infura accounts
* are available for free at: infura.io/register.
*
* You'll also need a mnemonic - the twelve word phrase the wallet uses to generate
* public/private key pairs. If you're publishing your code to GitHub make sure you load this
* phrase from a file you've .gitignored so it doesn't accidentally become public.
*
*/
// const HDWalletProvider = require('truffle-hdwallet-provider');
// const infuraKey = "fj4jll3k.....";
//
// const fs = require('fs');
// const mnemonic = fs.readFileSync(".secret").toString().trim();
module.exports = {
/**
* Networks define how you connect to your ethereum client and let you set the
* defaults web3 uses to send transactions. If you don't specify one truffle
* will spin up a development blockchain for you on port 9545 when you
* run `develop` or `test`. You can ask a truffle command to use a specific
* network from the command line, e.g
*
* $ truffle test --network <network-name>
*/
networks: {
// Useful for testing. The `development` name is special - truffle uses it by default
// if it's defined here and no other network is specified at the command line.
// You should run a client (like ganache-cli, geth or parity) in a separate terminal
// tab if you use this network and you must also set the `host`, `port` and `network_id`
// options below to some value.
//
// development: {
// host: "127.0.0.1", // Localhost (default: none)
// port: 8545, // Standard Ethereum port (default: none)
// network_id: "*", // Any network (default: none)
// },
// Another network with more advanced options...
// advanced: {
// port: 8777, // Custom port
// network_id: 1342, // Custom network
// gas: 8500000, // Gas sent with each transaction (default: ~6700000)
// gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei)
// from: <address>, // Account to send txs from (default: accounts[0])
// websockets: true // Enable EventEmitter interface for web3 (default: false)
// },
// Useful for deploying to a public network.
// NB: It's important to wrap the provider as a function.
// ropsten: {
// provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/v3/YOUR-PROJECT-ID`),
// network_id: 3, // Ropsten's id
// gas: 5500000, // Ropsten has a lower block limit than mainnet
// confirmations: 2, // # of confs to wait between deployments. (default: 0)
// timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50)
// skipDryRun: true // Skip dry run before migrations? (default: false for public nets )
// },
// Useful for private networks
// private: {
// provider: () => new HDWalletProvider(mnemonic, `https://network.io`),
// network_id: 2111, // This network is yours, in the cloud.
// production: true // Treats this network as if it was a public net. (default: false)
// }
},
// Set default mocha options here, use special reporters etc.
mocha: {
// timeout: 100000
},
// Configure your compilers
compilers: {
solc: {
version: '0.5.9',
settings: {
evmVersion: 'constantinople',
optimizer: {
enabled: true,
runs: 1000000,
details: { yul: true, deduplicate: true, cse: true, constantOptimizer: true },
},
},
},
},
};

View File

@@ -4,39 +4,15 @@
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"], "include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
"files": [ "files": [
"generated-artifacts/ERC1155Proxy.json", "generated-artifacts/ERC1155Proxy.json",
"generated-artifacts/ERC20BridgeProxy.json",
"generated-artifacts/ERC20Proxy.json", "generated-artifacts/ERC20Proxy.json",
"generated-artifacts/ERC721Proxy.json", "generated-artifacts/ERC721Proxy.json",
"generated-artifacts/Eth2DaiBridge.json",
"generated-artifacts/IAssetData.json", "generated-artifacts/IAssetData.json",
"generated-artifacts/IAssetProxy.json", "generated-artifacts/IAssetProxy.json",
"generated-artifacts/IAuthorizable.json",
"generated-artifacts/MixinAuthorizable.json",
"generated-artifacts/MultiAssetProxy.json", "generated-artifacts/MultiAssetProxy.json",
"generated-artifacts/StaticCallProxy.json", "generated-artifacts/StaticCallProxy.json",
"generated-artifacts/TestStaticCallTarget.json", "generated-artifacts/TestStaticCallTarget.json"
"generated-artifacts/UniswapBridge.json",
"test/generated-artifacts/ERC1155Proxy.json",
"test/generated-artifacts/ERC20BridgeProxy.json",
"test/generated-artifacts/ERC20Proxy.json",
"test/generated-artifacts/ERC721Proxy.json",
"test/generated-artifacts/Eth2DaiBridge.json",
"test/generated-artifacts/IAssetData.json",
"test/generated-artifacts/IAssetProxy.json",
"test/generated-artifacts/IAssetProxyDispatcher.json",
"test/generated-artifacts/IAuthorizable.json",
"test/generated-artifacts/IERC20Bridge.json",
"test/generated-artifacts/IEth2Dai.json",
"test/generated-artifacts/IUniswapExchange.json",
"test/generated-artifacts/IUniswapExchangeFactory.json",
"test/generated-artifacts/MixinAssetProxyDispatcher.json",
"test/generated-artifacts/MixinAuthorizable.json",
"test/generated-artifacts/MultiAssetProxy.json",
"test/generated-artifacts/Ownable.json",
"test/generated-artifacts/StaticCallProxy.json",
"test/generated-artifacts/TestERC20Bridge.json",
"test/generated-artifacts/TestEth2DaiBridge.json",
"test/generated-artifacts/TestStaticCallTarget.json",
"test/generated-artifacts/TestUniswapBridge.json",
"test/generated-artifacts/UniswapBridge.json"
], ],
"exclude": ["./deploy/solc/solc_bin"] "exclude": ["./deploy/solc/solc_bin"]
} }

View File

@@ -1,10 +0,0 @@
# Blacklist all files
.*
*
# Whitelist lib
!lib/**/*
# Whitelist Solidity contracts
!contracts/src/**/*
# Blacklist tests in lib
/lib/test/*
# Package specific ignore

View File

@@ -1,2 +0,0 @@
# solhint can't parse `abi.decode` syntax.
contracts/src/MixinCoordinatorApprovalVerifier.sol

View File

@@ -1,95 +1,4 @@
[ [
{
"version": "2.1.0-beta.2",
"changes": [
{
"note": "Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils",
"pr": 2330
},
{
"note": "Introduced new export CoordinatorRevertErrors",
"pr": 2321
},
{
"note": "Added dependency on @0x/contracts-utils",
"pr": 2321
}
],
"timestamp": 1574030254
},
{
"version": "2.1.0-beta.1",
"changes": [
{
"note": "Dependencies updated"
}
],
"timestamp": 1573159180
},
{
"version": "2.1.0-beta.0",
"changes": [
{
"note": "Add chainId to domain separator",
"pr": 1742
},
{
"note": "Inherit Exchange domain constants from `exchange-libs` to reduce code duplication",
"pr": 1742
},
{
"note": "Update domain separator",
"pr": 1742
},
{
"note": "Refactor contract to use new ITransactions interface",
"pr": 1753
},
{
"note": "Add verifyingContractIfExists arg to LibEIP712CoordinatorDomain constructor",
"pr": 1753
},
{
"note": "Remove LibZeroExTransaction contract",
"pr": 1753
},
{
"note": "Update tests for arbitrary fee tokens (ZEIP-28).",
"pr": 1819
},
{
"note": "Update for new `marketXOrders` consolidation.",
"pr": 2042
},
{
"note": "Use built in selectors instead of hard coded constants",
"pr": 2055
},
{
"note": "Compile and export all contracts, artifacts, and wrappers by default",
"pr": 2055
}
],
"timestamp": 1570135330
},
{
"timestamp": 1568744790,
"version": "2.0.13",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1567521715,
"version": "2.0.12",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{ {
"timestamp": 1566446343, "timestamp": 1566446343,
"version": "2.0.11", "version": "2.0.11",

View File

@@ -5,37 +5,6 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG CHANGELOG
## v2.1.0-beta.2 - _November 17, 2019_
* Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330)
* Introduced new export CoordinatorRevertErrors (#2321)
* Added dependency on @0x/contracts-utils (#2321)
## v2.1.0-beta.1 - _November 7, 2019_
* Dependencies updated
## v2.1.0-beta.0 - _October 3, 2019_
* Add chainId to domain separator (#1742)
* Inherit Exchange domain constants from `exchange-libs` to reduce code duplication (#1742)
* Update domain separator (#1742)
* Refactor contract to use new ITransactions interface (#1753)
* Add verifyingContractIfExists arg to LibEIP712CoordinatorDomain constructor (#1753)
* Remove LibZeroExTransaction contract (#1753)
* Update tests for arbitrary fee tokens (ZEIP-28). (#1819)
* Update for new `marketXOrders` consolidation. (#2042)
* Use built in selectors instead of hard coded constants (#2055)
* Compile and export all contracts, artifacts, and wrappers by default (#2055)
## v2.0.13 - _September 17, 2019_
* Dependencies updated
## v2.0.12 - _September 3, 2019_
* Dependencies updated
## v2.0.11 - _August 22, 2019_ ## v2.0.11 - _August 22, 2019_
* Dependencies updated * Dependencies updated

View File

@@ -1,6 +1,6 @@
## Coordinator ## Coordinator
This package contains a contract that allows users to call arbitrary functions on the Exchange contract with permission from one or more Coordinators. Addresses of the deployed contracts can be found in this 0x [guide](https://0x.org/docs/guides/0x-cheat-sheet) or the [DEPLOYS](./DEPLOYS.json) file within this package. This package contains a contract that allows users to call arbitrary functions on the Exchange contract with permission from one or more Coordinators. Addresses of the deployed contracts can be found in the 0x [wiki](https://0xproject.com/wiki#Deployed-Addresses) or the [DEPLOYS](./DEPLOYS.json) file within this package.
## Installation ## Installation
@@ -12,7 +12,7 @@ npm install @0x/contracts-coordinator --save
## Bug bounty ## 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). A bug bounty for the 2.0.0 contracts is ongoing! Instructions can be found [here](https://0xproject.com/wiki#Bug-Bounty).
## Contributing ## Contributing

View File

@@ -1,5 +1,5 @@
{ {
"artifactsDir": "./test/generated-artifacts", "artifactsDir": "./generated-artifacts",
"contractsDir": "./contracts", "contractsDir": "./contracts",
"useDockerisedSolc": false, "useDockerisedSolc": false,
"compilerSettings": { "compilerSettings": {
@@ -21,5 +21,6 @@
] ]
} }
} }
} },
"contracts": ["src/Coordinator.sol", "src/registry/CoordinatorRegistry.sol"]
} }

View File

@@ -1,6 +1,6 @@
/* /*
Copyright 2019 ZeroEx Intl. Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -16,12 +16,10 @@
*/ */
pragma solidity ^0.5.9; pragma solidity ^0.5.5;
pragma experimental ABIEncoderV2; pragma experimental "ABIEncoderV2";
import "@0x/contracts-exchange-libs/contracts/src/LibEIP712ExchangeDomain.sol";
import "./libs/LibConstants.sol"; import "./libs/LibConstants.sol";
import "./libs/LibEIP712CoordinatorDomain.sol";
import "./MixinSignatureValidator.sol"; import "./MixinSignatureValidator.sol";
import "./MixinCoordinatorApprovalVerifier.sol"; import "./MixinCoordinatorApprovalVerifier.sol";
import "./MixinCoordinatorCore.sol"; import "./MixinCoordinatorCore.sol";
@@ -34,12 +32,8 @@ contract Coordinator is
MixinCoordinatorApprovalVerifier, MixinCoordinatorApprovalVerifier,
MixinCoordinatorCore MixinCoordinatorCore
{ {
/// @param exchange Address of the 0x Exchange contract. constructor (address _exchange)
/// @param chainId Chain ID of the network this contract is deployed on.
constructor (address exchange, uint256 chainId)
public public
LibConstants(exchange) LibConstants(_exchange)
LibEIP712CoordinatorDomain(chainId, address(0))
LibEIP712ExchangeDomain(chainId, exchange)
{} {}
} }

View File

@@ -1,6 +1,6 @@
/* /*
Copyright 2019 ZeroEx Intl. Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -16,28 +16,26 @@
*/ */
pragma solidity ^0.5.9; pragma solidity ^0.5.5;
pragma experimental ABIEncoderV2; pragma experimental "ABIEncoderV2";
import "@0x/contracts-exchange-libs/contracts/src/LibEIP712ExchangeDomain.sol"; import "@0x/contracts-exchange-libs/contracts/src/LibExchangeSelectors.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol"; import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol";
import "@0x/contracts-utils/contracts/src/LibAddressArray.sol";
import "@0x/contracts-utils/contracts/src/LibBytes.sol"; import "@0x/contracts-utils/contracts/src/LibBytes.sol";
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol"; import "@0x/contracts-utils/contracts/src/LibAddressArray.sol";
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
import "./libs/LibCoordinatorApproval.sol"; import "./libs/LibCoordinatorApproval.sol";
import "./libs/LibCoordinatorRichErrors.sol"; import "./libs/LibZeroExTransaction.sol";
import "./interfaces/ICoordinatorSignatureValidator.sol"; import "./mixins/MSignatureValidator.sol";
import "./interfaces/ICoordinatorApprovalVerifier.sol"; import "./mixins/MCoordinatorApprovalVerifier.sol";
// solhint-disable avoid-tx-origin // solhint-disable avoid-tx-origin
contract MixinCoordinatorApprovalVerifier is contract MixinCoordinatorApprovalVerifier is
LibExchangeSelectors,
LibCoordinatorApproval, LibCoordinatorApproval,
LibEIP712ExchangeDomain, LibZeroExTransaction,
ICoordinatorSignatureValidator, MSignatureValidator,
ICoordinatorApprovalVerifier MCoordinatorApprovalVerifier
{ {
using LibBytes for bytes; using LibBytes for bytes;
using LibAddressArray for address[]; using LibAddressArray for address[];
@@ -47,12 +45,13 @@ contract MixinCoordinatorApprovalVerifier is
/// @param transaction 0x transaction containing salt, signerAddress, and data. /// @param transaction 0x transaction containing salt, signerAddress, and data.
/// @param txOrigin Required signer of Ethereum transaction calling this function. /// @param txOrigin Required signer of Ethereum transaction calling this function.
/// @param transactionSignature Proof that the transaction has been signed by the signer. /// @param transactionSignature Proof that the transaction has been signed by the signer.
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each /// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
/// order in the transaction's Exchange calldata. /// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order in the transaction's Exchange calldata.
function assertValidCoordinatorApprovals( function assertValidCoordinatorApprovals(
LibZeroExTransaction.ZeroExTransaction memory transaction, LibZeroExTransaction.ZeroExTransaction memory transaction,
address txOrigin, address txOrigin,
bytes memory transactionSignature, bytes memory transactionSignature,
uint256[] memory approvalExpirationTimeSeconds,
bytes[] memory approvalSignatures bytes[] memory approvalSignatures
) )
public public
@@ -64,11 +63,12 @@ contract MixinCoordinatorApprovalVerifier is
// No approval is required for non-fill methods // No approval is required for non-fill methods
if (orders.length > 0) { if (orders.length > 0) {
// Revert if approval is invalid for transaction orders // Revert if approval is invalid for transaction orders
_assertValidTransactionOrdersApproval( assertValidTransactionOrdersApproval(
transaction, transaction,
orders, orders,
txOrigin, txOrigin,
transactionSignature, transactionSignature,
approvalExpirationTimeSeconds,
approvalSignatures approvalSignatures
); );
} }
@@ -76,7 +76,7 @@ contract MixinCoordinatorApprovalVerifier is
/// @dev Decodes the orders from Exchange calldata representing any fill method. /// @dev Decodes the orders from Exchange calldata representing any fill method.
/// @param data Exchange calldata representing a fill method. /// @param data Exchange calldata representing a fill method.
/// @return orders The orders from the Exchange calldata. /// @return The orders from the Exchange calldata.
function decodeOrdersFromFillData(bytes memory data) function decodeOrdersFromFillData(bytes memory data)
public public
pure pure
@@ -84,8 +84,9 @@ contract MixinCoordinatorApprovalVerifier is
{ {
bytes4 selector = data.readBytes4(0); bytes4 selector = data.readBytes4(0);
if ( if (
selector == IExchange(address(0)).fillOrder.selector || selector == FILL_ORDER_SELECTOR ||
selector == IExchange(address(0)).fillOrKillOrder.selector selector == FILL_ORDER_NO_THROW_SELECTOR ||
selector == FILL_OR_KILL_ORDER_SELECTOR
) { ) {
// Decode single order // Decode single order
(LibOrder.Order memory order) = abi.decode( (LibOrder.Order memory order) = abi.decode(
@@ -95,13 +96,13 @@ contract MixinCoordinatorApprovalVerifier is
orders = new LibOrder.Order[](1); orders = new LibOrder.Order[](1);
orders[0] = order; orders[0] = order;
} else if ( } else if (
selector == IExchange(address(0)).batchFillOrders.selector || selector == BATCH_FILL_ORDERS_SELECTOR ||
selector == IExchange(address(0)).batchFillOrdersNoThrow.selector || selector == BATCH_FILL_ORDERS_NO_THROW_SELECTOR ||
selector == IExchange(address(0)).batchFillOrKillOrders.selector || selector == BATCH_FILL_OR_KILL_ORDERS_SELECTOR ||
selector == IExchange(address(0)).marketBuyOrdersNoThrow.selector || selector == MARKET_BUY_ORDERS_SELECTOR ||
selector == IExchange(address(0)).marketBuyOrdersFillOrKill.selector || selector == MARKET_BUY_ORDERS_NO_THROW_SELECTOR ||
selector == IExchange(address(0)).marketSellOrdersNoThrow.selector || selector == MARKET_SELL_ORDERS_SELECTOR ||
selector == IExchange(address(0)).marketSellOrdersFillOrKill.selector selector == MARKET_SELL_ORDERS_NO_THROW_SELECTOR
) { ) {
// Decode all orders // Decode all orders
// solhint-disable indent // solhint-disable indent
@@ -109,10 +110,7 @@ contract MixinCoordinatorApprovalVerifier is
data.slice(4, data.length), data.slice(4, data.length),
(LibOrder.Order[]) (LibOrder.Order[])
); );
} else if ( } else if (selector == MATCH_ORDERS_SELECTOR) {
selector == IExchange(address(0)).matchOrders.selector ||
selector == IExchange(address(0)).matchOrdersWithMaximalFill.selector
) {
// Decode left and right orders // Decode left and right orders
(LibOrder.Order memory leftOrder, LibOrder.Order memory rightOrder) = abi.decode( (LibOrder.Order memory leftOrder, LibOrder.Order memory rightOrder) = abi.decode(
data.slice(4, data.length), data.slice(4, data.length),
@@ -132,24 +130,27 @@ contract MixinCoordinatorApprovalVerifier is
/// @param orders Array of order structs containing order specifications. /// @param orders Array of order structs containing order specifications.
/// @param txOrigin Required signer of Ethereum transaction calling this function. /// @param txOrigin Required signer of Ethereum transaction calling this function.
/// @param transactionSignature Proof that the transaction has been signed by the signer. /// @param transactionSignature Proof that the transaction has been signed by the signer.
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order. /// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order.
function _assertValidTransactionOrdersApproval( function assertValidTransactionOrdersApproval(
LibZeroExTransaction.ZeroExTransaction memory transaction, LibZeroExTransaction.ZeroExTransaction memory transaction,
LibOrder.Order[] memory orders, LibOrder.Order[] memory orders,
address txOrigin, address txOrigin,
bytes memory transactionSignature, bytes memory transactionSignature,
uint256[] memory approvalExpirationTimeSeconds,
bytes[] memory approvalSignatures bytes[] memory approvalSignatures
) )
internal internal
view view
{ {
// Verify that Ethereum tx signer is the same as the approved txOrigin // Verify that Ethereum tx signer is the same as the approved txOrigin
if (tx.origin != txOrigin) { require(
LibRichErrors.rrevert(LibCoordinatorRichErrors.InvalidOriginError(txOrigin)); tx.origin == txOrigin,
} "INVALID_ORIGIN"
);
// Hash 0x transaction // Hash 0x transaction
bytes32 transactionHash = LibZeroExTransaction.getTypedDataHash(transaction, EIP712_EXCHANGE_DOMAIN_HASH); bytes32 transactionHash = getTransactionHash(transaction);
// Create empty list of approval signers // Create empty list of approval signers
address[] memory approvalSignerAddresses = new address[](0); address[] memory approvalSignerAddresses = new address[](0);
@@ -157,12 +158,21 @@ contract MixinCoordinatorApprovalVerifier is
uint256 signaturesLength = approvalSignatures.length; uint256 signaturesLength = approvalSignatures.length;
for (uint256 i = 0; i != signaturesLength; i++) { for (uint256 i = 0; i != signaturesLength; i++) {
// Create approval message // Create approval message
uint256 currentApprovalExpirationTimeSeconds = approvalExpirationTimeSeconds[i];
CoordinatorApproval memory approval = CoordinatorApproval({ CoordinatorApproval memory approval = CoordinatorApproval({
txOrigin: txOrigin, txOrigin: txOrigin,
transactionHash: transactionHash, transactionHash: transactionHash,
transactionSignature: transactionSignature transactionSignature: transactionSignature,
approvalExpirationTimeSeconds: currentApprovalExpirationTimeSeconds
}); });
// Ensure approval has not expired
require(
// solhint-disable-next-line not-rely-on-time
currentApprovalExpirationTimeSeconds > block.timestamp,
"APPROVAL_EXPIRED"
);
// Hash approval message and recover signer address // Hash approval message and recover signer address
bytes32 approvalHash = getCoordinatorApprovalHash(approval); bytes32 approvalHash = getCoordinatorApprovalHash(approval);
address approvalSignerAddress = getSignerAddress(approvalHash, approvalSignatures[i]); address approvalSignerAddress = getSignerAddress(approvalHash, approvalSignatures[i]);
@@ -184,12 +194,10 @@ contract MixinCoordinatorApprovalVerifier is
// Ensure feeRecipient of order has approved this 0x transaction // Ensure feeRecipient of order has approved this 0x transaction
address approverAddress = orders[i].feeRecipientAddress; address approverAddress = orders[i].feeRecipientAddress;
bool isOrderApproved = approvalSignerAddresses.contains(approverAddress); bool isOrderApproved = approvalSignerAddresses.contains(approverAddress);
if (!isOrderApproved) { require(
LibRichErrors.rrevert(LibCoordinatorRichErrors.InvalidApprovalSignatureError( isOrderApproved,
transactionHash, "INVALID_APPROVAL_SIGNATURE"
approverAddress );
));
}
} }
} }
} }

View File

@@ -1,6 +1,6 @@
/* /*
Copyright 2019 ZeroEx Intl. Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -16,57 +16,50 @@
*/ */
pragma solidity ^0.5.9; pragma solidity ^0.5.5;
pragma experimental ABIEncoderV2; pragma experimental "ABIEncoderV2";
import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol"; import "./libs/LibZeroExTransaction.sol";
import "@0x/contracts-utils/contracts/src/Refundable.sol";
import "./libs/LibConstants.sol"; import "./libs/LibConstants.sol";
import "./mixins/MCoordinatorApprovalVerifier.sol";
import "./interfaces/ICoordinatorCore.sol"; import "./interfaces/ICoordinatorCore.sol";
import "./interfaces/ICoordinatorApprovalVerifier.sol";
// solhint-disable no-empty-blocks
contract MixinCoordinatorCore is contract MixinCoordinatorCore is
Refundable,
LibConstants, LibConstants,
ICoordinatorApprovalVerifier, MCoordinatorApprovalVerifier,
ICoordinatorCore ICoordinatorCore
{ {
/// @dev Executes a 0x transaction that has been signed by the feeRecipients that correspond to each order in the transaction's Exchange calldata.
/// @dev A payable fallback function that makes this contract "payable". This is necessary to allow
/// this contract to gracefully handle refunds from the Exchange.
function ()
external
payable
{}
/// @dev Executes a 0x transaction that has been signed by the feeRecipients that correspond to
/// each order in the transaction's Exchange calldata.
/// @param transaction 0x transaction containing salt, signerAddress, and data. /// @param transaction 0x transaction containing salt, signerAddress, and data.
/// @param txOrigin Required signer of Ethereum transaction calling this function. /// @param txOrigin Required signer of Ethereum transaction calling this function.
/// @param transactionSignature Proof that the transaction has been signed by the signer. /// @param transactionSignature Proof that the transaction has been signed by the signer.
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each /// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
/// order in the transaction's Exchange calldata. /// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order in the transaction's Exchange calldata.
function executeTransaction( function executeTransaction(
LibZeroExTransaction.ZeroExTransaction memory transaction, LibZeroExTransaction.ZeroExTransaction memory transaction,
address txOrigin, address txOrigin,
bytes memory transactionSignature, bytes memory transactionSignature,
uint256[] memory approvalExpirationTimeSeconds,
bytes[] memory approvalSignatures bytes[] memory approvalSignatures
) )
public public
payable
refundFinalBalance
{ {
// Validate that the 0x transaction has been approves by each feeRecipient // Validate that the 0x transaction has been approves by each feeRecipient
assertValidCoordinatorApprovals( assertValidCoordinatorApprovals(
transaction, transaction,
txOrigin, txOrigin,
transactionSignature, transactionSignature,
approvalExpirationTimeSeconds,
approvalSignatures approvalSignatures
); );
// Execute the transaction // Execute the transaction
EXCHANGE.executeTransaction.value(msg.value)(transaction, transactionSignature); EXCHANGE.executeTransaction(
transaction.salt,
transaction.signerAddress,
transaction.data,
transactionSignature
);
} }
} }

View File

@@ -1,6 +1,6 @@
/* /*
Copyright 2019 ZeroEx Intl. Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -16,48 +16,38 @@
*/ */
pragma solidity ^0.5.9; pragma solidity ^0.5.5;
import "@0x/contracts-utils/contracts/src/LibBytes.sol"; import "@0x/contracts-utils/contracts/src/LibBytes.sol";
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol"; import "./mixins/MSignatureValidator.sol";
import "./interfaces/ICoordinatorSignatureValidator.sol";
import "./libs/LibCoordinatorRichErrors.sol";
contract MixinSignatureValidator is contract MixinSignatureValidator is
ICoordinatorSignatureValidator MSignatureValidator
{ {
using LibBytes for bytes; using LibBytes for bytes;
/// @dev Recovers the address of a signer given a hash and signature. /// @dev Recovers the address of a signer given a hash and signature.
/// @param hash Any 32 byte hash. /// @param hash Any 32 byte hash.
/// @param signature Proof that the hash has been signed by signer. /// @param signature Proof that the hash has been signed by signer.
/// @return signerAddress Address of the signer.
function getSignerAddress(bytes32 hash, bytes memory signature) function getSignerAddress(bytes32 hash, bytes memory signature)
public public
pure pure
returns (address signerAddress) returns (address signerAddress)
{ {
uint256 signatureLength = signature.length; require(
if (signatureLength == 0) { signature.length > 0,
LibRichErrors.rrevert(LibCoordinatorRichErrors.SignatureError( "LENGTH_GREATER_THAN_0_REQUIRED"
LibCoordinatorRichErrors.SignatureErrorCodes.INVALID_LENGTH, );
hash,
signature
));
}
// Pop last byte off of signature byte array. // Pop last byte off of signature byte array.
uint8 signatureTypeRaw = uint8(signature[signature.length - 1]); uint8 signatureTypeRaw = uint8(signature.popLastByte());
// Ensure signature is supported // Ensure signature is supported
if (signatureTypeRaw >= uint8(SignatureType.NSignatureTypes)) { require(
LibRichErrors.rrevert(LibCoordinatorRichErrors.SignatureError( signatureTypeRaw < uint8(SignatureType.NSignatureTypes),
LibCoordinatorRichErrors.SignatureErrorCodes.UNSUPPORTED, "SIGNATURE_UNSUPPORTED"
hash, );
signature
));
}
SignatureType signatureType = SignatureType(signatureTypeRaw); SignatureType signatureType = SignatureType(signatureTypeRaw);
@@ -67,32 +57,25 @@ contract MixinSignatureValidator is
// it an explicit option. This aids testing and analysis. It is // it an explicit option. This aids testing and analysis. It is
// also the initialization value for the enum type. // also the initialization value for the enum type.
if (signatureType == SignatureType.Illegal) { if (signatureType == SignatureType.Illegal) {
LibRichErrors.rrevert(LibCoordinatorRichErrors.SignatureError( revert("SIGNATURE_ILLEGAL");
LibCoordinatorRichErrors.SignatureErrorCodes.ILLEGAL,
hash,
signature
));
// Always invalid signature. // Always invalid signature.
// Like Illegal, this is always implicitly available and therefore // Like Illegal, this is always implicitly available and therefore
// offered explicitly. It can be implicitly created by providing // offered explicitly. It can be implicitly created by providing
// a correctly formatted but incorrect signature. // a correctly formatted but incorrect signature.
} else if (signatureType == SignatureType.Invalid) { } else if (signatureType == SignatureType.Invalid) {
LibRichErrors.rrevert(LibCoordinatorRichErrors.SignatureError( require(
LibCoordinatorRichErrors.SignatureErrorCodes.INVALID, signature.length == 0,
hash, "LENGTH_0_REQUIRED"
signature );
)); revert("SIGNATURE_INVALID");
// Signature using EIP712 // Signature using EIP712
} else if (signatureType == SignatureType.EIP712) { } else if (signatureType == SignatureType.EIP712) {
if (signatureLength != 66) { require(
LibRichErrors.rrevert(LibCoordinatorRichErrors.SignatureError( signature.length == 65,
LibCoordinatorRichErrors.SignatureErrorCodes.INVALID_LENGTH, "LENGTH_65_REQUIRED"
hash, );
signature
));
}
uint8 v = uint8(signature[0]); uint8 v = uint8(signature[0]);
bytes32 r = signature.readBytes32(1); bytes32 r = signature.readBytes32(1);
bytes32 s = signature.readBytes32(33); bytes32 s = signature.readBytes32(33);
@@ -106,13 +89,10 @@ contract MixinSignatureValidator is
// Signed using web3.eth_sign // Signed using web3.eth_sign
} else if (signatureType == SignatureType.EthSign) { } else if (signatureType == SignatureType.EthSign) {
if (signatureLength != 66) { require(
LibRichErrors.rrevert(LibCoordinatorRichErrors.SignatureError( signature.length == 65,
LibCoordinatorRichErrors.SignatureErrorCodes.INVALID_LENGTH, "LENGTH_65_REQUIRED"
hash, );
signature
));
}
uint8 v = uint8(signature[0]); uint8 v = uint8(signature[0]);
bytes32 r = signature.readBytes32(1); bytes32 r = signature.readBytes32(1);
bytes32 s = signature.readBytes32(33); bytes32 s = signature.readBytes32(33);
@@ -133,10 +113,6 @@ contract MixinSignatureValidator is
// that we currently support. In this case returning false // that we currently support. In this case returning false
// may lead the caller to incorrectly believe that the // may lead the caller to incorrectly believe that the
// signature was invalid.) // signature was invalid.)
LibRichErrors.rrevert(LibCoordinatorRichErrors.SignatureError( revert("SIGNATURE_UNSUPPORTED");
LibCoordinatorRichErrors.SignatureErrorCodes.UNSUPPORTED,
hash,
signature
));
} }
} }

View File

@@ -1,6 +1,6 @@
/* /*
Copyright 2019 ZeroEx Intl. Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -16,11 +16,11 @@
*/ */
pragma solidity ^0.5.9; pragma solidity ^0.5.5;
pragma experimental ABIEncoderV2; pragma experimental "ABIEncoderV2";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol"; import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol"; import "../libs/LibZeroExTransaction.sol";
contract ICoordinatorApprovalVerifier { contract ICoordinatorApprovalVerifier {
@@ -30,12 +30,13 @@ contract ICoordinatorApprovalVerifier {
/// @param transaction 0x transaction containing salt, signerAddress, and data. /// @param transaction 0x transaction containing salt, signerAddress, and data.
/// @param txOrigin Required signer of Ethereum transaction calling this function. /// @param txOrigin Required signer of Ethereum transaction calling this function.
/// @param transactionSignature Proof that the transaction has been signed by the signer. /// @param transactionSignature Proof that the transaction has been signed by the signer.
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each /// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
/// order in the transaction's Exchange calldata. /// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order in the transaction's Exchange calldata.
function assertValidCoordinatorApprovals( function assertValidCoordinatorApprovals(
LibZeroExTransaction.ZeroExTransaction memory transaction, LibZeroExTransaction.ZeroExTransaction memory transaction,
address txOrigin, address txOrigin,
bytes memory transactionSignature, bytes memory transactionSignature,
uint256[] memory approvalExpirationTimeSeconds,
bytes[] memory approvalSignatures bytes[] memory approvalSignatures
) )
public public
@@ -43,7 +44,7 @@ contract ICoordinatorApprovalVerifier {
/// @dev Decodes the orders from Exchange calldata representing any fill method. /// @dev Decodes the orders from Exchange calldata representing any fill method.
/// @param data Exchange calldata representing a fill method. /// @param data Exchange calldata representing a fill method.
/// @return orders The orders from the Exchange calldata. /// @return The orders from the Exchange calldata.
function decodeOrdersFromFillData(bytes memory data) function decodeOrdersFromFillData(bytes memory data)
public public
pure pure

View File

@@ -1,6 +1,6 @@
/* /*
Copyright 2019 ZeroEx Intl. Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -16,27 +16,26 @@
*/ */
pragma solidity ^0.5.9; pragma solidity ^0.5.5;
pragma experimental ABIEncoderV2; pragma experimental "ABIEncoderV2";
import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol"; import "../libs/LibZeroExTransaction.sol";
contract ICoordinatorCore { contract ICoordinatorCore {
/// @dev Executes a 0x transaction that has been signed by the feeRecipients that correspond to /// @dev Executes a 0x transaction that has been signed by the feeRecipients that correspond to each order in the transaction's Exchange calldata.
/// each order in the transaction's Exchange calldata.
/// @param transaction 0x transaction containing salt, signerAddress, and data. /// @param transaction 0x transaction containing salt, signerAddress, and data.
/// @param txOrigin Required signer of Ethereum transaction calling this function. /// @param txOrigin Required signer of Ethereum transaction calling this function.
/// @param transactionSignature Proof that the transaction has been signed by the signer. /// @param transactionSignature Proof that the transaction has been signed by the signer.
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each /// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
/// order in the transaction's Exchange calldata. /// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order in the transaction's Exchange calldata.
function executeTransaction( function executeTransaction(
LibZeroExTransaction.ZeroExTransaction memory transaction, LibZeroExTransaction.ZeroExTransaction memory transaction,
address txOrigin, address txOrigin,
bytes memory transactionSignature, bytes memory transactionSignature,
uint256[] memory approvalExpirationTimeSeconds,
bytes[] memory approvalSignatures bytes[] memory approvalSignatures
) )
public public;
payable;
} }

View File

@@ -1,45 +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 ICoordinatorSignatureValidator {
// Allowed signature types.
enum SignatureType {
Illegal, // 0x00, default value
Invalid, // 0x01
EIP712, // 0x02
EthSign, // 0x03
Wallet, // 0x04
Validator, // 0x05
PreSigned, // 0x06
EIP1271Wallet, // 0x07
NSignatureTypes // 0x08, number of signature types. Always leave at end.
}
/// @dev Recovers the address of a signer given a hash and signature.
/// @param hash Any 32 byte hash.
/// @param signature Proof that the hash has been signed by signer.
/// @return signerAddress Address of the signer.
function getSignerAddress(bytes32 hash, bytes memory signature)
public
pure
returns (address signerAddress);
}

View File

@@ -1,6 +1,6 @@
/* /*
Copyright 2019 ZeroEx Intl. Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -18,10 +18,14 @@
pragma solidity ^0.5.5; pragma solidity ^0.5.5;
import "../src/libs/LibExchangeRichErrorDecoder.sol";
contract ISignatureValidator {
// solhint-disable no-empty-blocks /// @dev Recovers the address of a signer given a hash and signature.
contract TestLibExchangeRichErrorDecoder is /// @param hash Any 32 byte hash.
LibExchangeRichErrorDecoder /// @param signature Proof that the hash has been signed by signer.
{} function getSignerAddress(bytes32 hash, bytes memory signature)
public
pure
returns (address signerAddress);
}

View File

@@ -1,6 +1,6 @@
/* /*
Copyright 2019 ZeroEx Intl. Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -15,24 +15,21 @@
limitations under the License. limitations under the License.
*/ */
pragma solidity ^0.5.5;
pragma solidity ^0.5.9;
import "@0x/contracts-utils/contracts/src/LibEIP1271.sol";
contract IEIP1271Wallet is contract ITransactions {
LibEIP1271
{ /// @dev Executes an exchange method call in the context of signer.
/// @dev Verifies that a signature is valid. /// @param salt Arbitrary number to ensure uniqueness of transaction hash.
/// @param data Arbitrary signed data. /// @param signerAddress Address of transaction signer.
/// @param signature Proof that data has been signed. /// @param data AbiV2 encoded calldata.
/// @return magicValue bytes4(0x20c13b0b) if the signature check succeeds. /// @param signature Proof of signer transaction by signer.
function isValidSignature( function executeTransaction(
uint256 salt,
address signerAddress,
bytes calldata data, bytes calldata data,
bytes calldata signature bytes calldata signature
) )
external external;
view
returns (bytes4 magicValue);
} }

View File

@@ -1,6 +1,6 @@
/* /*
Copyright 2019 ZeroEx Intl. Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -16,21 +16,19 @@
*/ */
pragma solidity ^0.5.9; pragma solidity ^0.5.5;
import "@0x/contracts-exchange/contracts/src/interfaces/ITransactions.sol"; import "../interfaces/ITransactions.sol";
// solhint-disable var-name-mixedcase
contract LibConstants { contract LibConstants {
// The 0x Exchange contract. // solhint-disable-next-line var-name-mixedcase
ITransactions internal EXCHANGE; ITransactions internal EXCHANGE;
/// @param exchange Address of the 0x Exchange contract. constructor (address _exchange)
constructor (address exchange)
public public
{ {
EXCHANGE = ITransactions(exchange); EXCHANGE = ITransactions(_exchange);
} }
} }

View File

@@ -1,6 +1,6 @@
/* /*
Copyright 2019 ZeroEx Intl. Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -16,52 +16,49 @@
*/ */
pragma solidity ^0.5.9; pragma solidity ^0.5.5;
pragma experimental ABIEncoderV2; pragma experimental "ABIEncoderV2";
import "./LibEIP712CoordinatorDomain.sol"; import "./LibEIP712Domain.sol";
contract LibCoordinatorApproval is contract LibCoordinatorApproval is
LibEIP712CoordinatorDomain LibEIP712Domain
{ {
// Hash for the EIP712 Coordinator approval message // Hash for the EIP712 Coordinator approval message
// keccak256(abi.encodePacked( // keccak256(abi.encodePacked(
// "CoordinatorApproval(", // "CoordinatorApproval(",
// "address txOrigin,", // "address txOrigin,",
// "bytes32 transactionHash,", // "bytes32 transactionHash,",
// "bytes transactionSignature", // "bytes transactionSignature,",
// "uint256 approvalExpirationTimeSeconds",
// ")" // ")"
// )); // ));
bytes32 constant public EIP712_COORDINATOR_APPROVAL_SCHEMA_HASH = bytes32 constant internal EIP712_COORDINATOR_APPROVAL_SCHEMA_HASH = 0x2fbcdbaa76bc7589916958ae919dfbef04d23f6bbf26de6ff317b32c6cc01e05;
0xa6511c04ca44625d50986f8c36bedc09366207a17b96e347094053a9f8507168;
struct CoordinatorApproval { struct CoordinatorApproval {
address txOrigin; // Required signer of Ethereum transaction that is submitting approval. address txOrigin; // Required signer of Ethereum transaction that is submitting approval.
bytes32 transactionHash; // EIP712 hash of the transaction. bytes32 transactionHash; // EIP712 hash of the transaction.
bytes transactionSignature; // Signature of the 0x transaction. bytes transactionSignature; // Signature of the 0x transaction.
uint256 approvalExpirationTimeSeconds; // Timestamp in seconds for which the approval expires.
} }
/// @dev Calculates the EIP712 hash of the Coordinator approval mesasage using the domain /// @dev Calculated the EIP712 hash of the Coordinator approval mesasage using the domain separator of this contract.
/// separator of this contract. /// @param approval Coordinator approval message containing the transaction hash, transaction signature, and expiration of the approval.
/// @param approval Coordinator approval message containing the transaction hash, and transaction /// @return EIP712 hash of the Coordinator approval message with the domain separator of this contract.
/// signature.
/// @return approvalHash EIP712 hash of the Coordinator approval message with the domain
/// separator of this contract.
function getCoordinatorApprovalHash(CoordinatorApproval memory approval) function getCoordinatorApprovalHash(CoordinatorApproval memory approval)
public public
view view
returns (bytes32 approvalHash) returns (bytes32 approvalHash)
{ {
approvalHash = _hashEIP712CoordinatorMessage(_hashCoordinatorApproval(approval)); approvalHash = hashEIP712CoordinatorMessage(hashCoordinatorApproval(approval));
return approvalHash; return approvalHash;
} }
/// @dev Calculates the EIP712 hash of the Coordinator approval mesasage with no domain separator. /// @dev Calculated the EIP712 hash of the Coordinator approval mesasage with no domain separator.
/// @param approval Coordinator approval message containing the transaction hash, and transaction /// @param approval Coordinator approval message containing the transaction hash, transaction signature, and expiration of the approval.
// signature. /// @return EIP712 hash of the Coordinator approval message with no domain separator.
/// @return result EIP712 hash of the Coordinator approval message with no domain separator. function hashCoordinatorApproval(CoordinatorApproval memory approval)
function _hashCoordinatorApproval(CoordinatorApproval memory approval)
internal internal
pure pure
returns (bytes32 result) returns (bytes32 result)
@@ -70,6 +67,7 @@ contract LibCoordinatorApproval is
bytes memory transactionSignature = approval.transactionSignature; bytes memory transactionSignature = approval.transactionSignature;
address txOrigin = approval.txOrigin; address txOrigin = approval.txOrigin;
bytes32 transactionHash = approval.transactionHash; bytes32 transactionHash = approval.transactionHash;
uint256 approvalExpirationTimeSeconds = approval.approvalExpirationTimeSeconds;
// Assembly for more efficiently computing: // Assembly for more efficiently computing:
// keccak256(abi.encodePacked( // keccak256(abi.encodePacked(
@@ -77,6 +75,7 @@ contract LibCoordinatorApproval is
// approval.txOrigin, // approval.txOrigin,
// approval.transactionHash, // approval.transactionHash,
// keccak256(approval.transactionSignature) // keccak256(approval.transactionSignature)
// approval.approvalExpirationTimeSeconds,
// )); // ));
assembly { assembly {
@@ -90,8 +89,9 @@ contract LibCoordinatorApproval is
mstore(add(memPtr, 32), txOrigin) // txOrigin mstore(add(memPtr, 32), txOrigin) // txOrigin
mstore(add(memPtr, 64), transactionHash) // transactionHash mstore(add(memPtr, 64), transactionHash) // transactionHash
mstore(add(memPtr, 96), transactionSignatureHash) // transactionSignatureHash mstore(add(memPtr, 96), transactionSignatureHash) // transactionSignatureHash
mstore(add(memPtr, 128), approvalExpirationTimeSeconds) // approvalExpirationTimeSeconds
// Compute hash // Compute hash
result := keccak256(memPtr, 128) result := keccak256(memPtr, 160)
} }
return result; return result;
} }

View File

@@ -1,87 +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;
library LibCoordinatorRichErrors {
enum SignatureErrorCodes {
INVALID_LENGTH,
UNSUPPORTED,
ILLEGAL,
INVALID
}
// bytes4(keccak256("SignatureError(uint8,bytes32,bytes)"))
bytes4 internal constant SIGNATURE_ERROR_SELECTOR =
0x779c5223;
// bytes4(keccak256("InvalidOriginError(address)"))
bytes4 internal constant INVALID_ORIGIN_ERROR_SELECTOR =
0xa458d7ff;
// bytes4(keccak256("InvalidApprovalSignatureError(bytes32,address)"))
bytes4 internal constant INVALID_APPROVAL_SIGNATURE_ERROR_SELECTOR =
0xd789b640;
// solhint-disable func-name-mixedcase
function SignatureError(
SignatureErrorCodes errorCode,
bytes32 hash,
bytes memory signature
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
SIGNATURE_ERROR_SELECTOR,
errorCode,
hash,
signature
);
}
function InvalidOriginError(
address expectedOrigin
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
INVALID_ORIGIN_ERROR_SELECTOR,
expectedOrigin
);
}
function InvalidApprovalSignatureError(
bytes32 transactionHash,
address approverAddress
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
INVALID_APPROVAL_SIGNATURE_ERROR_SELECTOR,
transactionHash,
approverAddress
);
}
}

View File

@@ -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-utils/contracts/src/LibEIP712.sol";
contract LibEIP712CoordinatorDomain {
// EIP712 Domain Name value for the Coordinator
string constant public EIP712_COORDINATOR_DOMAIN_NAME = "0x Protocol Coordinator";
// EIP712 Domain Version value for the Coordinator
string constant public EIP712_COORDINATOR_DOMAIN_VERSION = "3.0.0";
// Hash of the EIP712 Domain Separator data for the Coordinator
// solhint-disable-next-line var-name-mixedcase
bytes32 public EIP712_COORDINATOR_DOMAIN_HASH;
/// @param chainId Chain ID of the network this contract is deployed on.
/// @param verifyingContractAddressIfExists Address of the verifying contract (null if the address of this contract)
constructor (
uint256 chainId,
address verifyingContractAddressIfExists
)
public
{
address verifyingContractAddress = verifyingContractAddressIfExists == address(0)
? address(this)
: verifyingContractAddressIfExists;
EIP712_COORDINATOR_DOMAIN_HASH = LibEIP712.hashEIP712Domain(
EIP712_COORDINATOR_DOMAIN_NAME,
EIP712_COORDINATOR_DOMAIN_VERSION,
chainId,
verifyingContractAddress
);
}
/// @dev Calculates EIP712 encoding for a hash struct in the EIP712 domain
/// of this contract.
/// @param hashStruct The EIP712 hash struct.
/// @return result EIP712 hash applied to this EIP712 Domain.
function _hashEIP712CoordinatorMessage(bytes32 hashStruct)
internal
view
returns (bytes32 result)
{
return LibEIP712.hashEIP712Message(EIP712_COORDINATOR_DOMAIN_HASH, hashStruct);
}
}

View File

@@ -0,0 +1,131 @@
/*
Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.5;
import "./LibConstants.sol";
contract LibEIP712Domain is
LibConstants
{
// EIP191 header for EIP712 prefix
string constant internal EIP191_HEADER = "\x19\x01";
// EIP712 Domain Name value for the Coordinator
string constant internal EIP712_COORDINATOR_DOMAIN_NAME = "0x Protocol Coordinator";
// EIP712 Domain Version value for the Coordinator
string constant internal EIP712_COORDINATOR_DOMAIN_VERSION = "1.0.0";
// EIP712 Domain Name value for the Exchange
string constant internal EIP712_EXCHANGE_DOMAIN_NAME = "0x Protocol";
// EIP712 Domain Version value for the Exchange
string constant internal EIP712_EXCHANGE_DOMAIN_VERSION = "2";
// Hash of the EIP712 Domain Separator Schema
bytes32 constant internal EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH = keccak256(abi.encodePacked(
"EIP712Domain(",
"string name,",
"string version,",
"address verifyingContract",
")"
));
// Hash of the EIP712 Domain Separator data for the Coordinator
// solhint-disable-next-line var-name-mixedcase
bytes32 public EIP712_COORDINATOR_DOMAIN_HASH;
// Hash of the EIP712 Domain Separator data for the Exchange
// solhint-disable-next-line var-name-mixedcase
bytes32 public EIP712_EXCHANGE_DOMAIN_HASH;
constructor ()
public
{
EIP712_COORDINATOR_DOMAIN_HASH = keccak256(abi.encodePacked(
EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH,
keccak256(bytes(EIP712_COORDINATOR_DOMAIN_NAME)),
keccak256(bytes(EIP712_COORDINATOR_DOMAIN_VERSION)),
uint256(address(this))
));
EIP712_EXCHANGE_DOMAIN_HASH = keccak256(abi.encodePacked(
EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH,
keccak256(bytes(EIP712_EXCHANGE_DOMAIN_NAME)),
keccak256(bytes(EIP712_EXCHANGE_DOMAIN_VERSION)),
uint256(address(EXCHANGE))
));
}
/// @dev Calculates EIP712 encoding for a hash struct in the EIP712 domain
/// of this contract.
/// @param hashStruct The EIP712 hash struct.
/// @return EIP712 hash applied to this EIP712 Domain.
function hashEIP712CoordinatorMessage(bytes32 hashStruct)
internal
view
returns (bytes32 result)
{
return hashEIP712Message(EIP712_COORDINATOR_DOMAIN_HASH, hashStruct);
}
/// @dev Calculates EIP712 encoding for a hash struct in the EIP712 domain
/// of the Exchange contract.
/// @param hashStruct The EIP712 hash struct.
/// @return EIP712 hash applied to the Exchange EIP712 Domain.
function hashEIP712ExchangeMessage(bytes32 hashStruct)
internal
view
returns (bytes32 result)
{
return hashEIP712Message(EIP712_EXCHANGE_DOMAIN_HASH, hashStruct);
}
/// @dev Calculates EIP712 encoding for a hash struct with a given domain hash.
/// @param eip712DomainHash Hash of the domain domain separator data.
/// @param hashStruct The EIP712 hash struct.
/// @return EIP712 hash applied to the Exchange EIP712 Domain.
function hashEIP712Message(bytes32 eip712DomainHash, bytes32 hashStruct)
internal
pure
returns (bytes32 result)
{
// Assembly for more efficient computing:
// keccak256(abi.encodePacked(
// EIP191_HEADER,
// EIP712_DOMAIN_HASH,
// hashStruct
// ));
assembly {
// Load free memory pointer
let memPtr := mload(64)
mstore(memPtr, 0x1901000000000000000000000000000000000000000000000000000000000000) // EIP191 header
mstore(add(memPtr, 2), eip712DomainHash) // EIP712 domain hash
mstore(add(memPtr, 34), hashStruct) // Hash of struct
// Compute hash
result := keccak256(memPtr, 66)
}
return result;
}
}

View File

@@ -0,0 +1,95 @@
/*
Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.5;
pragma experimental "ABIEncoderV2";
import "./LibEIP712Domain.sol";
contract LibZeroExTransaction is
LibEIP712Domain
{
// Hash for the EIP712 0x transaction schema
// keccak256(abi.encodePacked(
// "ZeroExTransaction(",
// "uint256 salt,",
// "address signerAddress,",
// "bytes data",
// ")"
// ));
bytes32 constant internal EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH = 0x213c6f636f3ea94e701c0adf9b2624aa45a6c694f9a292c094f9a81c24b5df4c;
struct ZeroExTransaction {
uint256 salt; // Arbitrary number to ensure uniqueness of transaction hash.
address signerAddress; // Address of transaction signer.
bytes data; // AbiV2 encoded calldata.
}
/// @dev Calculates the EIP712 hash of a 0x transaction using the domain separator of the Exchange contract.
/// @param transaction 0x transaction containing salt, signerAddress, and data.
/// @return EIP712 hash of the transaction with the domain separator of this contract.
function getTransactionHash(ZeroExTransaction memory transaction)
public
view
returns (bytes32 transactionHash)
{
// Hash the transaction with the domain separator of the Exchange contract.
transactionHash = hashEIP712ExchangeMessage(hashZeroExTransaction(transaction));
return transactionHash;
}
/// @dev Calculates EIP712 hash of the 0x transaction with no domain separator.
/// @param transaction 0x transaction containing salt, signerAddress, and data.
/// @return EIP712 hash of the transaction with no domain separator.
function hashZeroExTransaction(ZeroExTransaction memory transaction)
internal
pure
returns (bytes32 result)
{
bytes32 schemaHash = EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH;
bytes memory data = transaction.data;
uint256 salt = transaction.salt;
address signerAddress = transaction.signerAddress;
// Assembly for more efficiently computing:
// keccak256(abi.encodePacked(
// EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH,
// transaction.salt,
// uint256(transaction.signerAddress),
// keccak256(transaction.data)
// ));
assembly {
// Compute hash of data
let dataHash := keccak256(add(data, 32), mload(data))
// Load free memory pointer
let memPtr := mload(64)
mstore(memPtr, schemaHash) // hash of schema
mstore(add(memPtr, 32), salt) // salt
mstore(add(memPtr, 64), and(signerAddress, 0xffffffffffffffffffffffffffffffffffffffff)) // signerAddress
mstore(add(memPtr, 96), dataHash) // hash of data
// Compute hash
result := keccak256(memPtr, 128)
}
return result;
}
}

View File

@@ -0,0 +1,46 @@
/*
Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.5;
pragma experimental "ABIEncoderV2";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "../interfaces/ICoordinatorApprovalVerifier.sol";
contract MCoordinatorApprovalVerifier is
ICoordinatorApprovalVerifier
{
/// @dev Validates that the feeRecipients of a batch of order have approved a 0x transaction.
/// @param transaction 0x transaction containing salt, signerAddress, and data.
/// @param orders Array of order structs containing order specifications.
/// @param txOrigin Required signer of Ethereum transaction calling this function.
/// @param transactionSignature Proof that the transaction has been signed by the signer.
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order.
function assertValidTransactionOrdersApproval(
LibZeroExTransaction.ZeroExTransaction memory transaction,
LibOrder.Order[] memory orders,
address txOrigin,
bytes memory transactionSignature,
uint256[] memory approvalExpirationTimeSeconds,
bytes[] memory approvalSignatures
)
internal
view;
}

View File

@@ -1,6 +1,6 @@
/* /*
Copyright 2019 ZeroEx Intl. Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -16,27 +16,20 @@
*/ */
pragma solidity ^0.5.9; pragma solidity ^0.5.5;
pragma experimental ABIEncoderV2;
import "../src/Staking.sol"; import "../interfaces/ISignatureValidator.sol";
contract TestExchangeManager is contract MSignatureValidator is
Staking ISignatureValidator
{ {
function setValidExchange(address exchange) // Allowed signature types.
external enum SignatureType {
{ Illegal, // 0x00, default value
validExchanges[exchange] = true; Invalid, // 0x01
} EIP712, // 0x02
EthSign, // 0x03
function onlyExchangeFunction() NSignatureTypes // 0x04, number of signature types. Always leave at end.
external
view
onlyExchange
returns (bool)
{
return true;
} }
} }

View File

@@ -1,6 +1,6 @@
/* /*
Copyright 2019 ZeroEx Intl. Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -16,7 +16,7 @@
*/ */
pragma solidity ^0.5.9; pragma solidity ^0.5.5;
import "./MixinCoordinatorRegistryCore.sol"; import "./MixinCoordinatorRegistryCore.sol";

View File

@@ -1,6 +1,6 @@
/* /*
Copyright 2019 ZeroEx Intl. Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -16,7 +16,7 @@
*/ */
pragma solidity ^0.5.9; pragma solidity ^0.5.5;
import "./interfaces/ICoordinatorRegistryCore.sol"; import "./interfaces/ICoordinatorRegistryCore.sol";
@@ -29,7 +29,7 @@ contract MixinCoordinatorRegistryCore is
mapping (address => string) internal coordinatorEndpoints; mapping (address => string) internal coordinatorEndpoints;
/// @dev Called by a Coordinator operator to set the endpoint of their Coordinator. /// @dev Called by a Coordinator operator to set the endpoint of their Coordinator.
/// @param coordinatorEndpoint Endpoint of the Coordinator as a string. /// @param coordinatorEndpoint endpoint of the Coordinator.
function setCoordinatorEndpoint(string calldata coordinatorEndpoint) external { function setCoordinatorEndpoint(string calldata coordinatorEndpoint) external {
address coordinatorOperator = msg.sender; address coordinatorOperator = msg.sender;
coordinatorEndpoints[coordinatorOperator] = coordinatorEndpoint; coordinatorEndpoints[coordinatorOperator] = coordinatorEndpoint;
@@ -37,8 +37,7 @@ contract MixinCoordinatorRegistryCore is
} }
/// @dev Gets the endpoint for a Coordinator. /// @dev Gets the endpoint for a Coordinator.
/// @param coordinatorOperator Operator of the Coordinator endpoint. /// @param coordinatorOperator operator of the Coordinator endpoint.
/// @return coordinatorEndpoint Endpoint of the Coordinator as a string.
function getCoordinatorEndpoint(address coordinatorOperator) function getCoordinatorEndpoint(address coordinatorOperator)
external external
view view

View File

@@ -1,6 +1,6 @@
/* /*
Copyright 2019 ZeroEx Intl. Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -16,7 +16,7 @@
*/ */
pragma solidity ^0.5.9; pragma solidity ^0.5.5;
// solhint-disable no-empty-blocks // solhint-disable no-empty-blocks
@@ -29,12 +29,11 @@ contract ICoordinatorRegistryCore
); );
/// @dev Called by a Coordinator operator to set the endpoint of their Coordinator. /// @dev Called by a Coordinator operator to set the endpoint of their Coordinator.
/// @param coordinatorEndpoint Endpoint of the Coordinator as a string. /// @param coordinatorEndpoint endpoint of the Coordinator.
function setCoordinatorEndpoint(string calldata coordinatorEndpoint) external; function setCoordinatorEndpoint(string calldata coordinatorEndpoint) external;
/// @dev Gets the endpoint for a Coordinator. /// @dev Gets the endpoint for a Coordinator.
/// @param coordinatorOperator Operator of the Coordinator endpoint. /// @param coordinatorOperator operator of the Coordinator endpoint.
/// @return coordinatorEndpoint Endpoint of the Coordinator as a string.
function getCoordinatorEndpoint(address coordinatorOperator) function getCoordinatorEndpoint(address coordinatorOperator)
external external
view view

View File

@@ -1,6 +1,6 @@
{ {
"name": "@0x/contracts-coordinator", "name": "@0x/contracts-coordinator",
"version": "2.1.0-beta.2", "version": "2.0.11",
"engines": { "engines": {
"node": ">=6.12" "node": ">=6.12"
}, },
@@ -12,7 +12,7 @@
"scripts": { "scripts": {
"build": "yarn pre_build && tsc -b", "build": "yarn pre_build && tsc -b",
"build:ci": "yarn build", "build:ci": "yarn build",
"pre_build": "run-s compile contracts:gen generate_contract_wrappers contracts:copy", "pre_build": "run-s compile generate_contract_wrappers",
"test": "yarn run_mocha", "test": "yarn run_mocha",
"rebuild_and_test": "run-s build test", "rebuild_and_test": "run-s build test",
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov", "test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
@@ -21,23 +21,20 @@
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit", "run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
"compile": "sol-compiler", "compile": "sol-compiler",
"watch": "sol-compiler -w", "watch": "sol-compiler -w",
"clean": "shx rm -rf lib test/generated-artifacts test/generated-wrappers generated-artifacts generated-wrappers", "clean": "shx rm -rf lib generated-artifacts generated-wrappers",
"generate_contract_wrappers": "abi-gen --debug --abis ${npm_package_config_abis} --output test/generated-wrappers --backend ethers", "generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../../node_modules/@0x/abi-gen-templates/contract.handlebars --partials '../../node_modules/@0x/abi-gen-templates/partials/**/*.handlebars' --output generated-wrappers --backend ethers",
"lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./test/generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude ./test/generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts", "lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
"fix": "tslint --fix --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./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 ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
"coverage:report:text": "istanbul report text", "coverage:report:text": "istanbul report text",
"coverage:report:html": "istanbul report html && open coverage/index.html", "coverage:report:html": "istanbul report html && open coverage/index.html",
"profiler: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", "coverage:report:lcov": "istanbul report lcov",
"test:circleci": "yarn test", "test:circleci": "yarn test",
"contracts:gen": "contracts-gen generate", "contracts:gen": "contracts-gen",
"contracts:copy": "contracts-gen copy", "lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol"
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol",
"compile:truffle": "truffle compile"
}, },
"config": { "config": {
"publicInterfaceContracts": "Coordinator,CoordinatorRegistry,LibCoordinatorApproval,LibCoordinatorRichErrors,LibEIP712CoordinatorDomain,LibConstants", "abis": "./generated-artifacts/@(Coordinator|CoordinatorRegistry).json",
"abis": "./test/generated-artifacts/@(Coordinator|CoordinatorRegistry|ICoordinatorApprovalVerifier|ICoordinatorCore|ICoordinatorRegistryCore|ICoordinatorSignatureValidator|LibConstants|LibCoordinatorApproval|LibCoordinatorRichErrors|LibEIP712CoordinatorDomain|MixinCoordinatorApprovalVerifier|MixinCoordinatorCore|MixinCoordinatorRegistryCore|MixinSignatureValidator).json",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually." "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
}, },
"repository": { "repository": {
@@ -50,18 +47,12 @@
}, },
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md", "homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
"devDependencies": { "devDependencies": {
"@0x/abi-gen": "^4.4.0-beta.2", "@0x/abi-gen": "^4.1.1",
"@0x/contracts-asset-proxy": "^2.3.0-beta.2", "@0x/contracts-gen": "^1.0.13",
"@0x/contracts-dev-utils": "^0.1.0-beta.2", "@0x/contracts-test-utils": "^3.1.14",
"@0x/contracts-erc20": "^2.3.0-beta.2", "@0x/dev-utils": "^2.3.1",
"@0x/contracts-exchange": "^2.2.0-beta.2", "@0x/sol-compiler": "^3.1.13",
"@0x/contracts-gen": "^1.1.0-beta.2", "@0x/tslint-config": "^3.0.1",
"@0x/contracts-test-utils": "^3.2.0-beta.2",
"@0x/dev-utils": "^2.4.0-beta.2",
"@0x/order-utils": "^8.5.0-beta.2",
"@0x/sol-compiler": "^3.2.0-beta.2",
"@0x/tslint-config": "^3.1.0-beta.2",
"@0x/web3-wrapper": "^6.1.0-beta.2",
"@types/lodash": "4.14.104", "@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7", "@types/mocha": "^5.2.7",
"@types/node": "*", "@types/node": "*",
@@ -69,23 +60,29 @@
"chai-as-promised": "^7.1.0", "chai-as-promised": "^7.1.0",
"chai-bignumber": "^3.0.0", "chai-bignumber": "^3.0.0",
"dirty-chai": "^2.0.1", "dirty-chai": "^2.0.1",
"lodash": "^4.17.11",
"make-promises-safe": "^1.1.0", "make-promises-safe": "^1.1.0",
"mocha": "^6.2.0", "mocha": "^6.2.0",
"npm-run-all": "^4.1.2", "npm-run-all": "^4.1.2",
"shx": "^0.2.2", "shx": "^0.2.2",
"solhint": "^1.4.1", "solhint": "^1.4.1",
"truffle": "^5.0.32",
"tslint": "5.11.0", "tslint": "5.11.0",
"typescript": "3.0.1" "typescript": "3.0.1"
}, },
"dependencies": { "dependencies": {
"@0x/base-contract": "^5.5.0-beta.2", "@0x/base-contract": "^5.3.2",
"@0x/contracts-utils": "^3.3.0-beta.2", "@0x/contracts-asset-proxy": "^2.2.6",
"@0x/types": "^2.5.0-beta.2", "@0x/contracts-erc20": "^2.2.12",
"@0x/typescript-typings": "^4.4.0-beta.2", "@0x/contracts-exchange": "^2.1.12",
"@0x/utils": "^4.6.0-beta.2", "@0x/contracts-exchange-libs": "^3.0.6",
"ethereum-types": "^2.2.0-beta.2" "@0x/contracts-utils": "^3.2.2",
"@0x/order-utils": "^8.3.0",
"@0x/types": "^2.4.1",
"@0x/typescript-typings": "^4.2.4",
"@0x/utils": "^4.5.0",
"@0x/web3-wrapper": "^6.0.11",
"ethereum-types": "^2.1.4",
"ethereumjs-util": "^5.1.1",
"lodash": "^4.17.11"
}, },
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"

View File

@@ -7,15 +7,7 @@ import { ContractArtifact } from 'ethereum-types';
import * as Coordinator from '../generated-artifacts/Coordinator.json'; import * as Coordinator from '../generated-artifacts/Coordinator.json';
import * as CoordinatorRegistry from '../generated-artifacts/CoordinatorRegistry.json'; import * as CoordinatorRegistry from '../generated-artifacts/CoordinatorRegistry.json';
import * as LibConstants from '../generated-artifacts/LibConstants.json';
import * as LibCoordinatorApproval from '../generated-artifacts/LibCoordinatorApproval.json';
import * as LibCoordinatorRichErrors from '../generated-artifacts/LibCoordinatorRichErrors.json';
import * as LibEIP712CoordinatorDomain from '../generated-artifacts/LibEIP712CoordinatorDomain.json';
export const artifacts = { export const artifacts = {
Coordinator: Coordinator as ContractArtifact, Coordinator: Coordinator as ContractArtifact,
CoordinatorRegistry: CoordinatorRegistry as ContractArtifact, CoordinatorRegistry: CoordinatorRegistry as ContractArtifact,
LibCoordinatorApproval: LibCoordinatorApproval as ContractArtifact,
LibCoordinatorRichErrors: LibCoordinatorRichErrors as ContractArtifact,
LibEIP712CoordinatorDomain: LibEIP712CoordinatorDomain as ContractArtifact,
LibConstants: LibConstants as ContractArtifact,
}; };

View File

@@ -1,28 +0,0 @@
import { hexConcat } from '@0x/contracts-test-utils';
import { eip712Utils } from '@0x/order-utils';
import { SignedZeroExTransaction } from '@0x/types';
import { signTypedDataUtils } from '@0x/utils';
export const hashUtils = {
async getApprovalHashBufferAsync(
transaction: SignedZeroExTransaction,
verifyingContract: string,
txOrigin: string,
): Promise<Buffer> {
const typedData = await eip712Utils.createCoordinatorApprovalTypedDataAsync(
transaction,
verifyingContract,
txOrigin,
);
const hashBuffer = signTypedDataUtils.generateTypedDataHash(typedData);
return hashBuffer;
},
async getApprovalHashHexAsync(
transaction: SignedZeroExTransaction,
verifyingContract: string,
txOrigin: string,
): Promise<string> {
const hashHex = hexConcat(await hashUtils.getApprovalHashBufferAsync(transaction, verifyingContract, txOrigin));
return hashHex;
},
};

View File

@@ -1,5 +1,3 @@
export * from './artifacts'; export * from './artifacts';
export * from './wrappers'; export * from './wrappers';
export import CoordinatorRevertErrors = require('./revert_errors'); export * from '../test/utils';
export { ApprovalFactory } from './approval_factory';
export { SignedCoordinatorApproval } from './types';

View File

@@ -1,52 +0,0 @@
import { BigNumber, RevertError } from '@0x/utils';
// tslint:disable:max-classes-per-file
export enum SignatureErrorCodes {
InvalidLength,
Unsupported,
Illegal,
Invalid,
}
export class SignatureError extends RevertError {
constructor(errorCode?: SignatureErrorCodes, hash?: string, signature?: string) {
super('SignatureError', 'SignatureError(uint8 errorCode, bytes32 hash, bytes signature)', {
errorCode,
hash,
signature,
});
}
}
export class InvalidOriginError extends RevertError {
constructor(expectedOrigin?: string) {
super('InvalidOriginError', 'InvalidOriginError(address expectedOrigin)', { expectedOrigin });
}
}
export class ApprovalExpiredError extends RevertError {
constructor(transactionHash?: string, approvalExpirationTime?: BigNumber | number | string) {
super('ApprovalExpiredError', 'ApprovalExpiredError(bytes32 transactionHash, uint256 approvalExpirationTime)', {
transactionHash,
approvalExpirationTime,
});
}
}
export class InvalidApprovalSignatureError extends RevertError {
constructor(transactionHash?: string, approverAddress?: string) {
super(
'InvalidApprovalSignatureError',
'InvalidApprovalSignatureError(bytes32 transactionHash, address approverAddress)',
{ transactionHash, approverAddress },
);
}
}
const types = [SignatureError, InvalidOriginError, ApprovalExpiredError, InvalidApprovalSignatureError];
// Register the types we've defined.
for (const type of types) {
RevertError.registerType(type);
}

View File

@@ -5,7 +5,3 @@
*/ */
export * from '../generated-wrappers/coordinator'; export * from '../generated-wrappers/coordinator';
export * from '../generated-wrappers/coordinator_registry'; export * from '../generated-wrappers/coordinator_registry';
export * from '../generated-wrappers/lib_constants';
export * from '../generated-wrappers/lib_coordinator_approval';
export * from '../generated-wrappers/lib_coordinator_rich_errors';
export * from '../generated-wrappers/lib_e_i_p712_coordinator_domain';

View File

@@ -1,37 +0,0 @@
/*
* -----------------------------------------------------------------------------
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
* -----------------------------------------------------------------------------
*/
import { ContractArtifact } from 'ethereum-types';
import * as Coordinator from '../test/generated-artifacts/Coordinator.json';
import * as CoordinatorRegistry from '../test/generated-artifacts/CoordinatorRegistry.json';
import * as ICoordinatorApprovalVerifier from '../test/generated-artifacts/ICoordinatorApprovalVerifier.json';
import * as ICoordinatorCore from '../test/generated-artifacts/ICoordinatorCore.json';
import * as ICoordinatorRegistryCore from '../test/generated-artifacts/ICoordinatorRegistryCore.json';
import * as ICoordinatorSignatureValidator from '../test/generated-artifacts/ICoordinatorSignatureValidator.json';
import * as LibConstants from '../test/generated-artifacts/LibConstants.json';
import * as LibCoordinatorApproval from '../test/generated-artifacts/LibCoordinatorApproval.json';
import * as LibCoordinatorRichErrors from '../test/generated-artifacts/LibCoordinatorRichErrors.json';
import * as LibEIP712CoordinatorDomain from '../test/generated-artifacts/LibEIP712CoordinatorDomain.json';
import * as MixinCoordinatorApprovalVerifier from '../test/generated-artifacts/MixinCoordinatorApprovalVerifier.json';
import * as MixinCoordinatorCore from '../test/generated-artifacts/MixinCoordinatorCore.json';
import * as MixinCoordinatorRegistryCore from '../test/generated-artifacts/MixinCoordinatorRegistryCore.json';
import * as MixinSignatureValidator from '../test/generated-artifacts/MixinSignatureValidator.json';
export const artifacts = {
Coordinator: Coordinator as ContractArtifact,
MixinCoordinatorApprovalVerifier: MixinCoordinatorApprovalVerifier as ContractArtifact,
MixinCoordinatorCore: MixinCoordinatorCore as ContractArtifact,
MixinSignatureValidator: MixinSignatureValidator as ContractArtifact,
ICoordinatorApprovalVerifier: ICoordinatorApprovalVerifier as ContractArtifact,
ICoordinatorCore: ICoordinatorCore as ContractArtifact,
ICoordinatorSignatureValidator: ICoordinatorSignatureValidator as ContractArtifact,
LibConstants: LibConstants as ContractArtifact,
LibCoordinatorApproval: LibCoordinatorApproval as ContractArtifact,
LibCoordinatorRichErrors: LibCoordinatorRichErrors as ContractArtifact,
LibEIP712CoordinatorDomain: LibEIP712CoordinatorDomain as ContractArtifact,
CoordinatorRegistry: CoordinatorRegistry as ContractArtifact,
MixinCoordinatorRegistryCore: MixinCoordinatorRegistryCore as ContractArtifact,
ICoordinatorRegistryCore: ICoordinatorRegistryCore as ContractArtifact,
};

View File

@@ -0,0 +1,523 @@
import { ERC20ProxyContract, ERC20Wrapper } from '@0x/contracts-asset-proxy';
import { DummyERC20TokenContract } from '@0x/contracts-erc20';
import {
artifacts as exchangeArtifacts,
ExchangeCancelEventArgs,
ExchangeCancelUpToEventArgs,
ExchangeContract,
ExchangeFillEventArgs,
} from '@0x/contracts-exchange';
import {
chaiSetup,
constants as devConstants,
expectTransactionFailedAsync,
getLatestBlockTimestampAsync,
OrderFactory,
provider,
TransactionFactory,
txDefaults,
web3Wrapper,
} from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { assetDataUtils, orderHashUtils } from '@0x/order-utils';
import { RevertReason, SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
import * as chai from 'chai';
import { LogWithDecodedArgs } from 'ethereum-types';
import { ApprovalFactory, artifacts, constants, CoordinatorContract, exchangeDataEncoder } from '../src';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
web3Wrapper.abiDecoder.addABI(exchangeArtifacts.Exchange.compilerOutput.abi);
// tslint:disable:no-unnecessary-type-assertion
describe('Coordinator tests', () => {
let makerAddress: string;
let owner: string;
let takerAddress: string;
let feeRecipientAddress: string;
let erc20Proxy: ERC20ProxyContract;
let erc20TokenA: DummyERC20TokenContract;
let erc20TokenB: DummyERC20TokenContract;
let zrxToken: DummyERC20TokenContract;
let coordinatorContract: CoordinatorContract;
let exchange: ExchangeContract;
let erc20Wrapper: ERC20Wrapper;
let orderFactory: OrderFactory;
let takerTransactionFactory: TransactionFactory;
let makerTransactionFactory: TransactionFactory;
let approvalFactory: ApprovalFactory;
before(async () => {
await blockchainLifecycle.startAsync();
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
before(async () => {
const accounts = await web3Wrapper.getAvailableAddressesAsync();
const usedAddresses = ([owner, makerAddress, takerAddress, feeRecipientAddress] = accounts.slice(0, 4));
erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner);
erc20Proxy = await erc20Wrapper.deployProxyAsync();
const numDummyErc20ToDeploy = 3;
[erc20TokenA, erc20TokenB, zrxToken] = await erc20Wrapper.deployDummyTokensAsync(
numDummyErc20ToDeploy,
devConstants.DUMMY_TOKEN_DECIMALS,
);
await erc20Wrapper.setBalancesAndAllowancesAsync();
exchange = await ExchangeContract.deployFrom0xArtifactAsync(
exchangeArtifacts.Exchange,
provider,
txDefaults,
artifacts,
assetDataUtils.encodeERC20AssetData(zrxToken.address),
);
await web3Wrapper.awaitTransactionSuccessAsync(
await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { from: owner }),
devConstants.AWAIT_TRANSACTION_MINED_MS,
);
await web3Wrapper.awaitTransactionSuccessAsync(
await exchange.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, { from: owner }),
devConstants.AWAIT_TRANSACTION_MINED_MS,
);
coordinatorContract = await CoordinatorContract.deployFrom0xArtifactAsync(
artifacts.Coordinator,
provider,
txDefaults,
artifacts,
exchange.address,
);
// Configure order defaults
const defaultOrderParams = {
...devConstants.STATIC_ORDER_PARAMS,
exchangeAddress: exchange.address,
senderAddress: coordinatorContract.address,
makerAddress,
feeRecipientAddress,
makerAssetData: assetDataUtils.encodeERC20AssetData(erc20TokenA.address),
takerAssetData: assetDataUtils.encodeERC20AssetData(erc20TokenB.address),
};
const makerPrivateKey = devConstants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)];
const takerPrivateKey = devConstants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(takerAddress)];
const feeRecipientPrivateKey = devConstants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(feeRecipientAddress)];
orderFactory = new OrderFactory(makerPrivateKey, defaultOrderParams);
makerTransactionFactory = new TransactionFactory(makerPrivateKey, exchange.address);
takerTransactionFactory = new TransactionFactory(takerPrivateKey, exchange.address);
approvalFactory = new ApprovalFactory(feeRecipientPrivateKey, coordinatorContract.address);
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('single order fills', () => {
for (const fnName of constants.SINGLE_FILL_FN_NAMES) {
it(`${fnName} should fill the order with a signed approval`, async () => {
const orders = [await orderFactory.newSignedOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = takerTransactionFactory.newSignedTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory.newSignedApproval(
transaction,
takerAddress,
approvalExpirationTimeSeconds,
);
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
await coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
takerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval.signature],
{ from: takerAddress },
),
devConstants.AWAIT_TRANSACTION_MINED_MS,
);
const fillLogs = transactionReceipt.logs.filter(
log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill',
);
expect(fillLogs.length).to.eq(1);
const fillLogArgs = (fillLogs[0] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
expect(fillLogArgs.makerAddress).to.eq(makerAddress);
expect(fillLogArgs.takerAddress).to.eq(takerAddress);
expect(fillLogArgs.senderAddress).to.eq(coordinatorContract.address);
expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
expect(fillLogArgs.makerAssetData).to.eq(orders[0].makerAssetData);
expect(fillLogArgs.takerAssetData).to.eq(orders[0].takerAssetData);
expect(fillLogArgs.makerAssetFilledAmount).to.bignumber.eq(orders[0].makerAssetAmount);
expect(fillLogArgs.takerAssetFilledAmount).to.bignumber.eq(orders[0].takerAssetAmount);
expect(fillLogArgs.makerFeePaid).to.bignumber.eq(orders[0].makerFee);
expect(fillLogArgs.takerFeePaid).to.bignumber.eq(orders[0].takerFee);
expect(fillLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(orders[0]));
});
it(`${fnName} should fill the order if called by approver`, async () => {
const orders = [await orderFactory.newSignedOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = takerTransactionFactory.newSignedTransaction(data);
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
await coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
feeRecipientAddress,
transaction.signature,
[],
[],
{ from: feeRecipientAddress },
),
devConstants.AWAIT_TRANSACTION_MINED_MS,
);
const fillLogs = transactionReceipt.logs.filter(
log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill',
);
expect(fillLogs.length).to.eq(1);
const fillLogArgs = (fillLogs[0] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
expect(fillLogArgs.makerAddress).to.eq(makerAddress);
expect(fillLogArgs.takerAddress).to.eq(takerAddress);
expect(fillLogArgs.senderAddress).to.eq(coordinatorContract.address);
expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
expect(fillLogArgs.makerAssetData).to.eq(orders[0].makerAssetData);
expect(fillLogArgs.takerAssetData).to.eq(orders[0].takerAssetData);
expect(fillLogArgs.makerAssetFilledAmount).to.bignumber.eq(orders[0].makerAssetAmount);
expect(fillLogArgs.takerAssetFilledAmount).to.bignumber.eq(orders[0].takerAssetAmount);
expect(fillLogArgs.makerFeePaid).to.bignumber.eq(orders[0].makerFee);
expect(fillLogArgs.takerFeePaid).to.bignumber.eq(orders[0].takerFee);
expect(fillLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(orders[0]));
});
it(`${fnName} should revert with no approval signature`, async () => {
const orders = [await orderFactory.newSignedOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = takerTransactionFactory.newSignedTransaction(data);
await expectTransactionFailedAsync(
coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
takerAddress,
transaction.signature,
[],
[],
{
from: takerAddress,
gas: devConstants.MAX_EXECUTE_TRANSACTION_GAS,
},
),
RevertReason.InvalidApprovalSignature,
);
});
it(`${fnName} should revert with an invalid approval signature`, async () => {
const orders = [await orderFactory.newSignedOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = takerTransactionFactory.newSignedTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory.newSignedApproval(
transaction,
takerAddress,
approvalExpirationTimeSeconds,
);
const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`;
await expectTransactionFailedAsync(
coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
takerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[signature],
{ from: takerAddress },
),
RevertReason.InvalidApprovalSignature,
);
});
it(`${fnName} should revert with an expired approval`, async () => {
const orders = [await orderFactory.newSignedOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = takerTransactionFactory.newSignedTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER);
const approval = approvalFactory.newSignedApproval(
transaction,
takerAddress,
approvalExpirationTimeSeconds,
);
await expectTransactionFailedAsync(
coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
takerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval.signature],
{ from: takerAddress },
),
RevertReason.ApprovalExpired,
);
});
it(`${fnName} should revert if not called by tx signer or approver`, async () => {
const orders = [await orderFactory.newSignedOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = takerTransactionFactory.newSignedTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory.newSignedApproval(
transaction,
takerAddress,
approvalExpirationTimeSeconds,
);
await expectTransactionFailedAsync(
coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
takerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval.signature],
{ from: owner },
),
RevertReason.InvalidOrigin,
);
});
}
});
describe('batch order fills', () => {
for (const fnName of [...constants.MARKET_FILL_FN_NAMES, ...constants.BATCH_FILL_FN_NAMES]) {
it(`${fnName} should fill the orders with a signed approval`, async () => {
const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = takerTransactionFactory.newSignedTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory.newSignedApproval(
transaction,
takerAddress,
approvalExpirationTimeSeconds,
);
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
await coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
takerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval.signature],
{ from: takerAddress, gas: devConstants.MAX_EXECUTE_TRANSACTION_GAS },
),
devConstants.AWAIT_TRANSACTION_MINED_MS,
);
const fillLogs = transactionReceipt.logs.filter(
log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill',
);
expect(fillLogs.length).to.eq(orders.length);
orders.forEach((order, index) => {
const fillLogArgs = (fillLogs[index] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
expect(fillLogArgs.makerAddress).to.eq(makerAddress);
expect(fillLogArgs.takerAddress).to.eq(takerAddress);
expect(fillLogArgs.senderAddress).to.eq(coordinatorContract.address);
expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
expect(fillLogArgs.makerAssetData).to.eq(order.makerAssetData);
expect(fillLogArgs.takerAssetData).to.eq(order.takerAssetData);
expect(fillLogArgs.makerAssetFilledAmount).to.bignumber.eq(order.makerAssetAmount);
expect(fillLogArgs.takerAssetFilledAmount).to.bignumber.eq(order.takerAssetAmount);
expect(fillLogArgs.makerFeePaid).to.bignumber.eq(order.makerFee);
expect(fillLogArgs.takerFeePaid).to.bignumber.eq(order.takerFee);
expect(fillLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(order));
});
});
it(`${fnName} should fill the orders if called by approver`, async () => {
const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = takerTransactionFactory.newSignedTransaction(data);
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
await coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
feeRecipientAddress,
transaction.signature,
[],
[],
{ from: feeRecipientAddress, gas: devConstants.MAX_EXECUTE_TRANSACTION_GAS },
),
devConstants.AWAIT_TRANSACTION_MINED_MS,
);
const fillLogs = transactionReceipt.logs.filter(
log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill',
);
expect(fillLogs.length).to.eq(orders.length);
orders.forEach((order, index) => {
const fillLogArgs = (fillLogs[index] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
expect(fillLogArgs.makerAddress).to.eq(makerAddress);
expect(fillLogArgs.takerAddress).to.eq(takerAddress);
expect(fillLogArgs.senderAddress).to.eq(coordinatorContract.address);
expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
expect(fillLogArgs.makerAssetData).to.eq(order.makerAssetData);
expect(fillLogArgs.takerAssetData).to.eq(order.takerAssetData);
expect(fillLogArgs.makerAssetFilledAmount).to.bignumber.eq(order.makerAssetAmount);
expect(fillLogArgs.takerAssetFilledAmount).to.bignumber.eq(order.takerAssetAmount);
expect(fillLogArgs.makerFeePaid).to.bignumber.eq(order.makerFee);
expect(fillLogArgs.takerFeePaid).to.bignumber.eq(order.takerFee);
expect(fillLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(order));
});
});
it(`${fnName} should revert with an invalid approval signature`, async () => {
const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = takerTransactionFactory.newSignedTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory.newSignedApproval(
transaction,
takerAddress,
approvalExpirationTimeSeconds,
);
const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`;
await expectTransactionFailedAsync(
coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
takerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[signature],
{ from: takerAddress },
),
RevertReason.InvalidApprovalSignature,
);
});
it(`${fnName} should revert with an expired approval`, async () => {
const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = takerTransactionFactory.newSignedTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER);
const approval = approvalFactory.newSignedApproval(
transaction,
takerAddress,
approvalExpirationTimeSeconds,
);
await expectTransactionFailedAsync(
coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
takerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval.signature],
{ from: takerAddress },
),
RevertReason.ApprovalExpired,
);
});
it(`${fnName} should revert if not called by tx signer or approver`, async () => {
const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = takerTransactionFactory.newSignedTransaction(data);
const currentTimestamp = await getLatestBlockTimestampAsync();
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
const approval = approvalFactory.newSignedApproval(
transaction,
takerAddress,
approvalExpirationTimeSeconds,
);
await expectTransactionFailedAsync(
coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
takerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval.signature],
{ from: owner },
),
RevertReason.InvalidOrigin,
);
});
}
});
describe('cancels', () => {
it('cancelOrder call should be successful without an approval', async () => {
const orders = [await orderFactory.newSignedOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(constants.CANCEL_ORDER, orders);
const transaction = makerTransactionFactory.newSignedTransaction(data);
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
await coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
makerAddress,
transaction.signature,
[],
[],
{
from: makerAddress,
},
),
);
const cancelLogs = transactionReceipt.logs.filter(
log => (log as LogWithDecodedArgs<ExchangeCancelEventArgs>).event === 'Cancel',
);
expect(cancelLogs.length).to.eq(1);
const cancelLogArgs = (cancelLogs[0] as LogWithDecodedArgs<ExchangeCancelEventArgs>).args;
expect(cancelLogArgs.makerAddress).to.eq(makerAddress);
expect(cancelLogArgs.senderAddress).to.eq(coordinatorContract.address);
expect(cancelLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
expect(cancelLogArgs.makerAssetData).to.eq(orders[0].makerAssetData);
expect(cancelLogArgs.takerAssetData).to.eq(orders[0].takerAssetData);
expect(cancelLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(orders[0]));
});
it('batchCancelOrders call should be successful without an approval', async () => {
const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(constants.BATCH_CANCEL_ORDERS, orders);
const transaction = makerTransactionFactory.newSignedTransaction(data);
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
await coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
makerAddress,
transaction.signature,
[],
[],
{
from: makerAddress,
},
),
);
const cancelLogs = transactionReceipt.logs.filter(
log => (log as LogWithDecodedArgs<ExchangeCancelEventArgs>).event === 'Cancel',
);
expect(cancelLogs.length).to.eq(orders.length);
orders.forEach((order, index) => {
const cancelLogArgs = (cancelLogs[index] as LogWithDecodedArgs<ExchangeCancelEventArgs>).args;
expect(cancelLogArgs.makerAddress).to.eq(makerAddress);
expect(cancelLogArgs.senderAddress).to.eq(coordinatorContract.address);
expect(cancelLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
expect(cancelLogArgs.makerAssetData).to.eq(order.makerAssetData);
expect(cancelLogArgs.takerAssetData).to.eq(order.takerAssetData);
expect(cancelLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(order));
});
});
it('cancelOrdersUpTo call should be successful without an approval', async () => {
const orders: SignedOrder[] = [];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(constants.CANCEL_ORDERS_UP_TO, orders);
const transaction = makerTransactionFactory.newSignedTransaction(data);
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
await coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
makerAddress,
transaction.signature,
[],
[],
{
from: makerAddress,
},
),
);
const cancelLogs = transactionReceipt.logs.filter(
log => (log as LogWithDecodedArgs<ExchangeCancelUpToEventArgs>).event === 'CancelUpTo',
);
expect(cancelLogs.length).to.eq(1);
const cancelLogArgs = (cancelLogs[0] as LogWithDecodedArgs<ExchangeCancelUpToEventArgs>).args;
expect(cancelLogArgs.makerAddress).to.eq(makerAddress);
expect(cancelLogArgs.senderAddress).to.eq(coordinatorContract.address);
expect(cancelLogArgs.orderEpoch).to.bignumber.eq(new BigNumber(1));
});
});
});
// tslint:disable:max-file-line-count

View File

@@ -1,73 +1,81 @@
import { blockchainTests, expect, verifyEvents } from '@0x/contracts-test-utils'; import { artifacts as exchangeArtifacts } from '@0x/contracts-exchange';
import { chaiSetup, provider, web3Wrapper } from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import * as chai from 'chai';
import { LogWithDecodedArgs } from 'ethereum-types';
import { artifacts } from './artifacts'; import { CoordinatorRegistryCoordinatorEndpointSetEventArgs } from '../src';
import { CoordinatorRegistryContract, CoordinatorRegistryCoordinatorEndpointSetEventArgs } from './wrappers'; import { CoordinatorRegistryWrapper } from './utils/coordinator_registry_wrapper';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
web3Wrapper.abiDecoder.addABI(exchangeArtifacts.Exchange.compilerOutput.abi);
// tslint:disable:no-unnecessary-type-assertion // tslint:disable:no-unnecessary-type-assertion
blockchainTests.resets('Coordinator Registry tests', env => { describe('Coordinator Registry tests', () => {
let coordinatorRegistry: CoordinatorRegistryContract;
let coordinatorOperator: string; let coordinatorOperator: string;
const coordinatorEndpoint = 'http://sometec.0x.org'; const coordinatorEndpoint = 'http://sometec.0x.org';
const nilCoordinatorEndpoint = ''; const nilCoordinatorEndpoint = '';
let coordinatorRegistryWrapper: CoordinatorRegistryWrapper;
// tests // tests
before(async () => {
await blockchainLifecycle.startAsync();
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
before(async () => { before(async () => {
// setup accounts (skip owner) // setup accounts (skip owner)
const accounts = await env.getAccountAddressesAsync(); const accounts = await web3Wrapper.getAvailableAddressesAsync();
[, coordinatorOperator] = accounts; [, coordinatorOperator] = accounts;
// deploy coordinator registry // deploy coordinator registry
coordinatorRegistry = await CoordinatorRegistryContract.deployFrom0xArtifactAsync( coordinatorRegistryWrapper = new CoordinatorRegistryWrapper(provider);
artifacts.CoordinatorRegistry, await coordinatorRegistryWrapper.deployCoordinatorRegistryAsync();
env.provider, });
env.txDefaults, beforeEach(async () => {
artifacts, await blockchainLifecycle.startAsync();
); });
afterEach(async () => {
await blockchainLifecycle.revertAsync();
}); });
describe('core', () => { describe('core', () => {
it('Should successfully set a Coordinator endpoint', async () => { it('Should successfully set a Coordinator endpoint', async () => {
await coordinatorRegistry.setCoordinatorEndpoint(coordinatorEndpoint).awaitTransactionSuccessAsync({ await coordinatorRegistryWrapper.setCoordinatorEndpointAsync(coordinatorOperator, coordinatorEndpoint);
from: coordinatorOperator, const recordedCoordinatorEndpoint = await coordinatorRegistryWrapper.getCoordinatorEndpointAsync(
}); coordinatorOperator,
const recordedCoordinatorEndpoint = await coordinatorRegistry );
.getCoordinatorEndpoint(coordinatorOperator)
.callAsync();
expect(recordedCoordinatorEndpoint).to.be.equal(coordinatorEndpoint); expect(recordedCoordinatorEndpoint).to.be.equal(coordinatorEndpoint);
}); });
it('Should successfully unset a Coordinator endpoint', async () => { it('Should successfully unset a Coordinator endpoint', async () => {
// set Coordinator endpoint // set Coordinator endpoint
await coordinatorRegistry.setCoordinatorEndpoint(coordinatorEndpoint).awaitTransactionSuccessAsync({ await coordinatorRegistryWrapper.setCoordinatorEndpointAsync(coordinatorOperator, coordinatorEndpoint);
from: coordinatorOperator, let recordedCoordinatorEndpoint = await coordinatorRegistryWrapper.getCoordinatorEndpointAsync(
}); coordinatorOperator,
let recordedCoordinatorEndpoint = await coordinatorRegistry );
.getCoordinatorEndpoint(coordinatorOperator)
.callAsync();
expect(recordedCoordinatorEndpoint).to.be.equal(coordinatorEndpoint); expect(recordedCoordinatorEndpoint).to.be.equal(coordinatorEndpoint);
// unset Coordinator endpoint // unset Coordinator endpoint
await coordinatorRegistry.setCoordinatorEndpoint(nilCoordinatorEndpoint).awaitTransactionSuccessAsync({ await coordinatorRegistryWrapper.setCoordinatorEndpointAsync(coordinatorOperator, nilCoordinatorEndpoint);
from: coordinatorOperator, recordedCoordinatorEndpoint = await coordinatorRegistryWrapper.getCoordinatorEndpointAsync(
}); coordinatorOperator,
recordedCoordinatorEndpoint = await coordinatorRegistry );
.getCoordinatorEndpoint(coordinatorOperator)
.callAsync();
expect(recordedCoordinatorEndpoint).to.be.equal(nilCoordinatorEndpoint); expect(recordedCoordinatorEndpoint).to.be.equal(nilCoordinatorEndpoint);
}); });
it('Should emit an event when setting Coordinator endpoint', async () => { it('Should emit an event when setting Coordinator endpoint', async () => {
// set Coordinator endpoint // set Coordinator endpoint
const txReceipt = await coordinatorRegistry const txReceipt = await coordinatorRegistryWrapper.setCoordinatorEndpointAsync(
.setCoordinatorEndpoint(coordinatorEndpoint)
.awaitTransactionSuccessAsync({
from: coordinatorOperator,
});
const recordedCoordinatorEndpoint = await coordinatorRegistry
.getCoordinatorEndpoint(coordinatorOperator)
.callAsync();
expect(recordedCoordinatorEndpoint).to.be.equal(coordinatorEndpoint);
// validate event
const expectedEvent: CoordinatorRegistryCoordinatorEndpointSetEventArgs = {
coordinatorOperator, coordinatorOperator,
coordinatorEndpoint, coordinatorEndpoint,
}; );
verifyEvents(txReceipt, [expectedEvent], 'CoordinatorEndpointSet'); const recordedCoordinatorEndpoint = await coordinatorRegistryWrapper.getCoordinatorEndpointAsync(
coordinatorOperator,
);
expect(recordedCoordinatorEndpoint).to.be.equal(coordinatorEndpoint);
// validate event
expect(txReceipt.logs.length).to.be.equal(1);
const log = txReceipt.logs[0] as LogWithDecodedArgs<CoordinatorRegistryCoordinatorEndpointSetEventArgs>;
expect(log.args.coordinatorOperator).to.be.equal(coordinatorOperator);
expect(log.args.coordinatorEndpoint).to.be.equal(coordinatorEndpoint);
}); });
}); });
}); });

View File

@@ -1,55 +1,79 @@
import { blockchainTests, constants, expect, randomAddress, transactionHashUtils } from '@0x/contracts-test-utils'; import { addressUtils, chaiSetup, constants, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { transactionHashUtils } from '@0x/order-utils';
import { BigNumber } from '@0x/utils'; import { BigNumber } from '@0x/utils';
import * as chai from 'chai';
import { hashUtils } from '../src/hash_utils'; import { artifacts, CoordinatorContract, hashUtils } from '../src';
import { artifacts } from './artifacts'; chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
import { CoordinatorContract } from './wrappers'; describe('Libs tests', () => {
blockchainTests.resets('Libs tests', env => {
let coordinatorContract: CoordinatorContract; let coordinatorContract: CoordinatorContract;
let chainId: number; const exchangeAddress = addressUtils.generatePseudoRandomAddress();
const exchangeAddress = randomAddress();
before(async () => { before(async () => {
chainId = await env.getChainIdAsync(); await blockchainLifecycle.startAsync();
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
before(async () => {
coordinatorContract = await CoordinatorContract.deployFrom0xArtifactAsync( coordinatorContract = await CoordinatorContract.deployFrom0xArtifactAsync(
artifacts.Coordinator, artifacts.Coordinator,
env.provider, provider,
env.txDefaults, txDefaults,
artifacts, artifacts,
exchangeAddress, exchangeAddress,
new BigNumber(chainId),
); );
}); });
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('getTransactionHash', () => {
it('should return the correct transaction hash', async () => {
const tx = {
verifyingContractAddress: exchangeAddress,
salt: new BigNumber(0),
signerAddress: constants.NULL_ADDRESS,
data: '0x1234',
};
const expectedTxHash = transactionHashUtils.getTransactionHashHex(tx);
const txHash = await coordinatorContract.getTransactionHash.callAsync(tx);
expect(expectedTxHash).to.eq(txHash);
});
});
describe('getApprovalHash', () => { describe('getApprovalHash', () => {
it('should return the correct approval hash', async () => { it('should return the correct approval hash', async () => {
const signedTx = { const signedTx = {
salt: constants.ZERO_AMOUNT, verifyingContractAddress: exchangeAddress,
gasPrice: constants.ZERO_AMOUNT, salt: new BigNumber(0),
expirationTimeSeconds: constants.ZERO_AMOUNT,
signerAddress: constants.NULL_ADDRESS, signerAddress: constants.NULL_ADDRESS,
data: '0x1234', data: '0x1234',
signature: '0x5678', signature: '0x5678',
domain: {
verifyingContract: exchangeAddress,
chainId,
},
}; };
const approvalExpirationTimeSeconds = new BigNumber(0);
const txOrigin = constants.NULL_ADDRESS; const txOrigin = constants.NULL_ADDRESS;
const approval = { const approval = {
txOrigin, txOrigin,
transactionHash: transactionHashUtils.getTransactionHashHex(signedTx), transactionHash: transactionHashUtils.getTransactionHashHex(signedTx),
transactionSignature: signedTx.signature, transactionSignature: signedTx.signature,
approvalExpirationTimeSeconds,
}; };
const expectedApprovalHash = await hashUtils.getApprovalHashHexAsync( const expectedApprovalHash = hashUtils.getApprovalHashHex(
signedTx, signedTx,
coordinatorContract.address, coordinatorContract.address,
txOrigin, txOrigin,
approvalExpirationTimeSeconds,
); );
const approvalHash = await coordinatorContract.getCoordinatorApprovalHash(approval).callAsync(); const approvalHash = await coordinatorContract.getCoordinatorApprovalHash.callAsync(approval);
expect(expectedApprovalHash).to.eq(approvalHash); expect(expectedApprovalHash).to.eq(approvalHash);
}); });
}); });

File diff suppressed because it is too large Load Diff

View File

@@ -1,33 +1,37 @@
import { hexConcat, signingUtils } from '@0x/contracts-test-utils'; import { signingUtils } from '@0x/contracts-test-utils';
import { SignatureType, SignedZeroExTransaction } from '@0x/types'; import { SignatureType, SignedZeroExTransaction } from '@0x/types';
import { BigNumber } from '@0x/utils';
import * as ethUtil from 'ethereumjs-util';
import { hashUtils } from './hash_utils'; import { hashUtils, SignedCoordinatorApproval } from './index';
import { SignedCoordinatorApproval } from './types';
export class ApprovalFactory { export class ApprovalFactory {
private readonly _privateKey: Buffer; private readonly _privateKey: Buffer;
private readonly _verifyingContractAddress: string; private readonly _verifyingContractAddress: string;
constructor(privateKey: Buffer, verifyingContract: string) { constructor(privateKey: Buffer, verifyingContractAddress: string) {
this._privateKey = privateKey; this._privateKey = privateKey;
this._verifyingContractAddress = verifyingContract; this._verifyingContractAddress = verifyingContractAddress;
} }
public async newSignedApprovalAsync( public newSignedApproval(
transaction: SignedZeroExTransaction, transaction: SignedZeroExTransaction,
txOrigin: string, txOrigin: string,
approvalExpirationTimeSeconds: BigNumber,
signatureType: SignatureType = SignatureType.EthSign, signatureType: SignatureType = SignatureType.EthSign,
): Promise<SignedCoordinatorApproval> { ): SignedCoordinatorApproval {
const approvalHashBuff = await hashUtils.getApprovalHashBufferAsync( const approvalHashBuff = hashUtils.getApprovalHashBuffer(
transaction, transaction,
this._verifyingContractAddress, this._verifyingContractAddress,
txOrigin, txOrigin,
approvalExpirationTimeSeconds,
); );
const signatureBuff = signingUtils.signMessage(approvalHashBuff, this._privateKey, signatureType); const signatureBuff = signingUtils.signMessage(approvalHashBuff, this._privateKey, signatureType);
const signedApproval = { const signedApproval = {
txOrigin, txOrigin,
transaction, transaction,
signature: hexConcat(signatureBuff), approvalExpirationTimeSeconds,
signature: ethUtil.addHexPrefix(signatureBuff.toString('hex')),
}; };
return signedApproval; return signedApproval;
} }

View File

@@ -0,0 +1,12 @@
import { BigNumber } from '@0x/utils';
export const constants = {
SINGLE_FILL_FN_NAMES: ['fillOrder', 'fillOrKillOrder', 'fillOrderNoThrow'],
BATCH_FILL_FN_NAMES: ['batchFillOrders', 'batchFillOrKillOrders', 'batchFillOrdersNoThrow'],
MARKET_FILL_FN_NAMES: ['marketBuyOrders', 'marketBuyOrdersNoThrow', 'marketSellOrders', 'marketSellOrdersNoThrow'],
MATCH_ORDERS: 'matchOrders',
CANCEL_ORDER: 'cancelOrder',
BATCH_CANCEL_ORDERS: 'batchCancelOrders',
CANCEL_ORDERS_UP_TO: 'cancelOrdersUpTo',
TIME_BUFFER: new BigNumber(1000),
};

View File

@@ -0,0 +1,65 @@
import { LogDecoder, txDefaults } from '@0x/contracts-test-utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { TransactionReceiptWithDecodedLogs, ZeroExProvider } from 'ethereum-types';
import { artifacts, CoordinatorRegistryContract } from '../../src';
export class CoordinatorRegistryWrapper {
private readonly _web3Wrapper: Web3Wrapper;
private readonly _provider: ZeroExProvider;
private readonly _logDecoder: LogDecoder;
private _coordinatorRegistryContract?: CoordinatorRegistryContract;
/**
* Instanitates an CoordinatorRegistryWrapper
* @param provider Web3 provider to use for all JSON RPC requests
* Instance of CoordinatorRegistryWrapper
*/
constructor(provider: ZeroExProvider) {
this._web3Wrapper = new Web3Wrapper(provider);
this._provider = provider;
this._logDecoder = new LogDecoder(this._web3Wrapper, artifacts);
}
public async deployCoordinatorRegistryAsync(): Promise<CoordinatorRegistryContract> {
this._coordinatorRegistryContract = await CoordinatorRegistryContract.deployFrom0xArtifactAsync(
artifacts.CoordinatorRegistry,
this._provider,
txDefaults,
artifacts,
);
if (this._coordinatorRegistryContract === undefined) {
throw new Error(`Failed to deploy Coordinator Registry contract.`);
}
return this._coordinatorRegistryContract;
}
public async setCoordinatorEndpointAsync(
coordinatorOperator: string,
coordinatorEndpoint: string,
): Promise<TransactionReceiptWithDecodedLogs> {
this._assertCoordinatorRegistryDeployed();
const txReceipt = await this._logDecoder.getTxWithDecodedLogsAsync(
await (this
._coordinatorRegistryContract as CoordinatorRegistryContract).setCoordinatorEndpoint.sendTransactionAsync(
coordinatorEndpoint,
{
from: coordinatorOperator,
},
),
);
return txReceipt;
}
public async getCoordinatorEndpointAsync(coordinatorOperator: string): Promise<string> {
this._assertCoordinatorRegistryDeployed();
const coordinatorEndpoint = await (this
._coordinatorRegistryContract as CoordinatorRegistryContract).getCoordinatorEndpoint.callAsync(
coordinatorOperator,
);
return coordinatorEndpoint;
}
private _assertCoordinatorRegistryDeployed(): void {
if (this._coordinatorRegistryContract === undefined) {
throw new Error(
'The Coordinator Registry contract was not deployed through the CoordinatorRegistryWrapper. Call `deployCoordinatorRegistryAsync` to deploy.',
);
}
}
}

View File

@@ -0,0 +1,47 @@
import { IExchangeContract } from '@0x/contracts-exchange';
import { constants as devConstants, provider } from '@0x/contracts-test-utils';
import { SignedOrder } from '@0x/types';
import { constants } from './index';
export const exchangeDataEncoder = {
encodeOrdersToExchangeData(fnName: string, orders: SignedOrder[]): string {
const exchangeInstance = new IExchangeContract(devConstants.NULL_ADDRESS, provider);
let data;
if (constants.SINGLE_FILL_FN_NAMES.indexOf(fnName) !== -1) {
data = (exchangeInstance as any)[fnName].getABIEncodedTransactionData(
orders[0],
orders[0].takerAssetAmount,
orders[0].signature,
);
} else if (constants.BATCH_FILL_FN_NAMES.indexOf(fnName) !== -1) {
data = (exchangeInstance as any)[fnName].getABIEncodedTransactionData(
orders,
orders.map(order => order.takerAssetAmount),
orders.map(order => order.signature),
);
} else if (constants.MARKET_FILL_FN_NAMES.indexOf(fnName) !== -1) {
data = (exchangeInstance as any)[fnName].getABIEncodedTransactionData(
orders,
orders.map(order => order.takerAssetAmount).reduce((prev, curr) => prev.plus(curr)),
orders.map(order => order.signature),
);
} else if (fnName === constants.MATCH_ORDERS) {
data = exchangeInstance.matchOrders.getABIEncodedTransactionData(
orders[0],
orders[1],
orders[0].signature,
orders[1].signature,
);
} else if (fnName === constants.CANCEL_ORDER) {
data = exchangeInstance.cancelOrder.getABIEncodedTransactionData(orders[0]);
} else if (fnName === constants.BATCH_CANCEL_ORDERS) {
data = exchangeInstance.batchCancelOrders.getABIEncodedTransactionData(orders);
} else if (fnName === constants.CANCEL_ORDERS_UP_TO) {
data = exchangeInstance.cancelOrdersUpTo.getABIEncodedTransactionData(devConstants.ZERO_AMOUNT);
} else {
throw new Error(`Error: ${fnName} not a supported function`);
}
return data;
},
};

View File

@@ -0,0 +1,33 @@
import { eip712Utils } from '@0x/order-utils';
import { SignedZeroExTransaction } from '@0x/types';
import { BigNumber, signTypedDataUtils } from '@0x/utils';
import * as _ from 'lodash';
export const hashUtils = {
getApprovalHashBuffer(
transaction: SignedZeroExTransaction,
verifyingContractAddress: string,
txOrigin: string,
approvalExpirationTimeSeconds: BigNumber,
): Buffer {
const typedData = eip712Utils.createCoordinatorApprovalTypedData(
transaction,
verifyingContractAddress,
txOrigin,
approvalExpirationTimeSeconds,
);
const hashBuffer = signTypedDataUtils.generateTypedDataHash(typedData);
return hashBuffer;
},
getApprovalHashHex(
transaction: SignedZeroExTransaction,
verifyingContractAddress: string,
txOrigin: string,
approvalExpirationTimeSeconds: BigNumber,
): string {
const hashHex = `0x${hashUtils
.getApprovalHashBuffer(transaction, verifyingContractAddress, txOrigin, approvalExpirationTimeSeconds)
.toString('hex')}`;
return hashHex;
},
};

Some files were not shown because too many files have changed in this diff Show More