Compare commits
46 Commits
@0x/contra
...
@0xproject
Author | SHA1 | Date | |
---|---|---|---|
|
8fb5c29b4b | ||
|
40a061a5ca | ||
|
2ef82592a3 | ||
|
14b5448d70 | ||
|
e7d45e47bf | ||
|
28268d4355 | ||
|
50fa02c1d1 | ||
|
b36ff9103d | ||
|
f032c2466c | ||
|
b63ddc9be4 | ||
|
171430b617 | ||
|
d561043774 | ||
|
b1871e9ddd | ||
|
b4a5e7258c | ||
|
3c75debdf9 | ||
|
84d1053f73 | ||
|
f9df42f5d9 | ||
|
4414ef0a0f | ||
|
b7729ada38 | ||
|
3da67feeb2 | ||
|
8a0d563a32 | ||
|
612fc4a949 | ||
|
bf915ce403 | ||
|
0f9ea9773e | ||
|
447a3a6c26 | ||
|
f1cc16c44d | ||
|
aae16b6343 | ||
|
834e1538d1 | ||
|
afc489bc2c | ||
|
3e061e7364 | ||
|
6395c2a8b2 | ||
|
c8225288cd | ||
|
4a108aa67d | ||
|
fd9b3e0dcf | ||
|
f94b647e61 | ||
|
2eccc3efaf | ||
|
972341725e | ||
|
40b10fd29d | ||
|
b31fcffc76 | ||
|
ec222ea0cd | ||
|
5533220da0 | ||
|
6ee7024457 | ||
|
f839ac9c58 | ||
|
1a8b1460a6 | ||
|
feac0779a4 | ||
|
d9eeb0421c |
@@ -1,460 +1,219 @@
|
||||
version: 2
|
||||
|
||||
jobs:
|
||||
build:
|
||||
resource_class: medium+
|
||||
docker:
|
||||
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||
environment:
|
||||
CONTRACTS_COMMIT_HASH: '9ed05f5'
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- checkout
|
||||
- run: echo 'export PATH=$HOME/CIRCLE_PROJECT_REPONAME/node_modules/.bin:$PATH' >> $BASH_ENV
|
||||
# HACK(feuGeneA): commented out this hack as we're changing
|
||||
# from a circleci-maintained container to a different
|
||||
# container, and this hack may not apply anymore, as
|
||||
# suggested by the non-existance of `/home/circleci/.bashrc`
|
||||
# when running the command below.
|
||||
# - run:
|
||||
# # HACK(albrow): Without this, yarn commands will sometimes
|
||||
# # fail with a "permission denied" error.
|
||||
# name: Set npm path
|
||||
# command: npm set prefix=/home/circleci/npm && echo 'export PATH=$HOME/circleci/npm/bin:$PATH' >> /home/circleci/.bashrc
|
||||
- run:
|
||||
name: install-yarn
|
||||
command: npm install --global yarn@1.17.0
|
||||
- run:
|
||||
name: yarn
|
||||
command: yarn --frozen-lockfile --ignore-engines install || yarn --frozen-lockfile --ignore-engines install
|
||||
- setup_remote_docker
|
||||
- run: yarn build:ci
|
||||
- run: yarn build:ts
|
||||
- save_cache:
|
||||
key: repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo
|
||||
- store_artifacts:
|
||||
path: ~/repo/packages/abi-gen/test-cli/output
|
||||
- store_artifacts:
|
||||
path: ~/repo/packages/contract-wrappers/generated_docs
|
||||
test-contracts-ganache:
|
||||
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-extensions @0x/contracts-asset-proxy @0x/contracts-exchange @0x/contracts-exchange-forwarder @0x/contracts-coordinator @0x/contracts-tests @0x/contracts-staking
|
||||
test-exchange-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-exchange
|
||||
test-integrations-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-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 @0x/contracts-erc20-bridge-sampler
|
||||
# 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:
|
||||
resource_class: medium+
|
||||
docker:
|
||||
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||
- image: 0xorg/verdaccio
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run:
|
||||
command: yarn test:publish:circleci
|
||||
no_output_timeout: 1800
|
||||
test-doc-generation:
|
||||
docker:
|
||||
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run:
|
||||
command: yarn test:generate_docs:circleci
|
||||
no_output_timeout: 1200
|
||||
test-rest:
|
||||
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-test-utils
|
||||
- run: yarn wsrun test:circleci @0x/abi-gen
|
||||
- run: yarn wsrun test:circleci @0x/asset-swapper
|
||||
- run: yarn wsrun test:circleci @0x/contract-artifacts
|
||||
- run: yarn wsrun test:circleci @0x/assert
|
||||
- run: yarn wsrun test:circleci @0x/base-contract
|
||||
- run: yarn wsrun test:circleci @0x/connect
|
||||
- run: yarn wsrun test:circleci @0x/contract-wrappers-test
|
||||
- run: yarn wsrun test:circleci @0x/dev-utils
|
||||
- run: yarn wsrun test:circleci @0x/json-schemas
|
||||
- run: yarn wsrun test:circleci @0x/order-utils
|
||||
- run: yarn wsrun test:circleci @0x/orderbook
|
||||
- run: yarn wsrun test:circleci @0x/sol-compiler
|
||||
- run: yarn wsrun test:circleci @0x/sol-tracing-utils
|
||||
- run: yarn wsrun test:circleci @0x/sol-doc
|
||||
- run: yarn wsrun test:circleci @0x/subproviders
|
||||
- run: yarn wsrun test:circleci @0x/web3-wrapper
|
||||
- run: yarn wsrun test:circleci @0x/utils
|
||||
- run: yarn wsrun test:circleci @0x/instant
|
||||
- save_cache:
|
||||
key: coverage-abi-gen-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/abi-gen/coverage/lcov.info
|
||||
- save_cache:
|
||||
key: coverage-assert-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/assert/coverage/lcov.info
|
||||
- save_cache:
|
||||
key: coverage-asset-swapper-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/asset-swapper/coverage/lcov.info
|
||||
- save_cache:
|
||||
key: coverage-base-contract-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/base-contract/coverage/lcov.info
|
||||
- save_cache:
|
||||
key: coverage-connect-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/connect/coverage/lcov.info
|
||||
- save_cache:
|
||||
key: coverage-contract-wrappers-test-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/contract-wrappers-test/coverage/lcov.info
|
||||
- save_cache:
|
||||
key: coverage-dev-utils-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/dev-utils/coverage/lcov.info
|
||||
- save_cache:
|
||||
key: coverage-json-schemas-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/json-schemas/coverage/lcov.info
|
||||
- save_cache:
|
||||
key: coverage-order-utils-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/order-utils/coverage/lcov.info
|
||||
- save_cache:
|
||||
key: coverage-sol-compiler-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/sol-compiler/coverage/lcov.info
|
||||
- save_cache:
|
||||
key: coverage-sol-tracing-utils-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/sol-tracing-utils/coverage/lcov.info
|
||||
- save_cache:
|
||||
key: coverage-sol-doc-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/sol-doc/coverage/lcov.info
|
||||
- save_cache:
|
||||
key: coverage-subproviders-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/subproviders/coverage/lcov.info
|
||||
- save_cache:
|
||||
key: coverage-web3-wrapper-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/web3-wrapper/coverage/lcov.info
|
||||
test-python:
|
||||
working_directory: ~/repo
|
||||
docker:
|
||||
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||
- image: 0xorg/ganache-cli:4.4.0-beta.1
|
||||
environment:
|
||||
VERSION: latest
|
||||
SNAPSHOT_NAME: 0x_ganache_snapshot-v3-beta
|
||||
- image: 0xorg/mesh: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
|
||||
FEE_RECIPIENT: '0x0000000000000000000000000000000000000001'
|
||||
MAKER_FEE_UNIT_AMOUNT: 0
|
||||
TAKER_FEE_UNIT_AMOUNT: 0
|
||||
MESH_ENDPOINT: 'ws://localhost:60557'
|
||||
command: |
|
||||
sh -c "waitForMesh () { sleep 5; }; waitForMesh && node_modules/.bin/forever ts/lib/index.js"
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
key: installed-py-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run:
|
||||
command: |
|
||||
cd python-packages
|
||||
python -m ensurepip
|
||||
./pre_install
|
||||
./install
|
||||
- save_cache:
|
||||
key: installed-py-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- '/usr/local/bin'
|
||||
- '/usr/local/lib/python3.7/site-packages'
|
||||
- run:
|
||||
command: |
|
||||
cd python-packages
|
||||
./parallel coverage run setup.py test
|
||||
./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:
|
||||
key: coverage-python-contract-addresses-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/python-packages/contract_addresses/.coverage
|
||||
- save_cache:
|
||||
key: coverage-python-contract-artifacts-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/python-packages/contract_artifacts/.coverage
|
||||
- save_cache:
|
||||
key: coverage-python-contract-demo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/python-packages/contract_demo/.coverage
|
||||
- save_cache:
|
||||
key: coverage-python-json-schemas-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/python-packages/json_schemas/.coverage
|
||||
- save_cache:
|
||||
key: coverage-python-order-utils-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/python-packages/order_utils/.coverage
|
||||
- save_cache:
|
||||
key: coverage-python-sra-client-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/python-packages/sra_client/.coverage
|
||||
- store_artifacts:
|
||||
path: ~/repo/python-packages/contract_addresses/build
|
||||
- store_artifacts:
|
||||
path: ~/repo/python-packages/contract_artifacts/build
|
||||
- store_artifacts:
|
||||
path: ~/repo/python-packages/contract_wrappers/build
|
||||
- store_artifacts:
|
||||
path: ~/repo/python-packages/json_schemas/build
|
||||
- store_artifacts:
|
||||
path: ~/repo/python-packages/middlewares/build
|
||||
- store_artifacts:
|
||||
path: ~/repo/python-packages/order_utils/build
|
||||
- store_artifacts:
|
||||
path: ~/repo/python-packages/sra_client/build
|
||||
test-rest-python:
|
||||
working_directory: ~/repo
|
||||
docker:
|
||||
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
key: installed-py-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run:
|
||||
command: |
|
||||
cd python-packages/order_utils
|
||||
python -m ensurepip
|
||||
python -m pip install .
|
||||
- save_cache:
|
||||
key: installed-py-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- '/usr/local/bin'
|
||||
- '/usr/local/lib/python3.7/site-packages'
|
||||
- '.eggs'
|
||||
- '.mypy_cache'
|
||||
- '.pytest_cache'
|
||||
- '.tox'
|
||||
- run:
|
||||
command: |
|
||||
cd python-packages/order_utils
|
||||
tox
|
||||
static-tests-python:
|
||||
working_directory: ~/repo
|
||||
docker:
|
||||
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
key: installed-py-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run:
|
||||
command: |
|
||||
python -m ensurepip
|
||||
cd python-packages
|
||||
./pre_install
|
||||
./install
|
||||
./lint
|
||||
static-tests:
|
||||
resource_class: large
|
||||
working_directory: ~/repo
|
||||
docker:
|
||||
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: yarn lerna run lint
|
||||
- run: yarn prettier:ci
|
||||
- run: yarn deps_versions:ci
|
||||
- run: yarn diff_md_docs:ci
|
||||
- run: cd packages/0x.js && yarn build:umd:prod
|
||||
- run: yarn bundlewatch
|
||||
submit-coverage:
|
||||
docker:
|
||||
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-abi-gen-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-assert-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-asset-swapper-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-base-contract-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-connect-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-contract-wrappers-test-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-dev-utils-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-json-schemas-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-order-utils-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-sol-compiler-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-sol-tracing-utils-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-sol-doc-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-subproviders-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-web3-wrapper-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-contracts-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-python-json-schemas-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-python-contract-addresses-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-python-contract-artifacts-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-python-contract-demo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-python-sra-client-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-python-order-utils-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: yarn report_coverage
|
||||
build:
|
||||
docker:
|
||||
- image: circleci/node:6.12
|
||||
environment:
|
||||
CONTRACTS_COMMIT_HASH: '9ed05f5'
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- checkout
|
||||
- run: echo 'export PATH=$HOME/CIRCLE_PROJECT_REPONAME/node_modules/.bin:$PATH' >> $BASH_ENV
|
||||
- restore_cache:
|
||||
key: dependency-cache-{{ checksum "package.json" }}
|
||||
- run:
|
||||
name: yarn
|
||||
command: yarn --frozen-lockfile
|
||||
- save_cache:
|
||||
key: dependency-cache-{{ checksum "package.json" }}
|
||||
paths:
|
||||
- ./node_modules
|
||||
- run: wget https://s3.amazonaws.com/testrpc-shapshots/${CONTRACTS_COMMIT_HASH}.zip
|
||||
- run: unzip ${CONTRACTS_COMMIT_HASH}.zip -d testrpc_snapshot
|
||||
- run: node ./node_modules/lerna/bin/lerna.js bootstrap
|
||||
- run: yarn build
|
||||
- save_cache:
|
||||
key: repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo
|
||||
test-installation:
|
||||
docker:
|
||||
- image: circleci/node:6.12
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: yarn test:installation
|
||||
test-0xjs:
|
||||
docker:
|
||||
- image: circleci/node:6.12
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run:
|
||||
name: testrpc
|
||||
command: npm run testrpc -- --db testrpc_snapshot
|
||||
background: true
|
||||
- run: yarn wsrun test:circleci 0x.js
|
||||
- save_cache:
|
||||
key: coverage-0xjs-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/0x.js/coverage/lcov.info
|
||||
test-contracts:
|
||||
docker:
|
||||
- image: circleci/node:6.12
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run:
|
||||
name: testrpc
|
||||
command: npm run testrpc -- --db testrpc_snapshot
|
||||
background: true
|
||||
- run: yarn wsrun test:circleci contracts
|
||||
- save_cache:
|
||||
key: coverage-contracts-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/contracts/coverage/lcov.info
|
||||
test-sol-compiler:
|
||||
docker:
|
||||
- image: circleci/node:6.12
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run:
|
||||
name: testrpc
|
||||
command: npm run testrpc -- --db testrpc_snapshot
|
||||
background: true
|
||||
- run: yarn wsrun test:circleci @0xproject/sol-compiler
|
||||
- save_cache:
|
||||
key: coverage-sol-compiler-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/sol-compiler/coverage/lcov.info
|
||||
test-rest:
|
||||
docker:
|
||||
- image: circleci/node:6.12
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run:
|
||||
name: testrpc
|
||||
command: npm run testrpc -- --db testrpc_snapshot
|
||||
background: true
|
||||
- run: yarn wsrun test:circleci --exclude contracts --exclude 0x.js --exclude @0xproject/sol-compiler --stages --exclude-missing
|
||||
- save_cache:
|
||||
key: coverage-assert-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/assert/coverage/lcov.info
|
||||
- save_cache:
|
||||
key: coverage-connect-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/connect/coverage/lcov.info
|
||||
- save_cache:
|
||||
key: coverage-dev-utils-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/dev-utils/coverage/lcov.info
|
||||
- save_cache:
|
||||
key: coverage-json-schemas-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/json-schemas/coverage/lcov.info
|
||||
- save_cache:
|
||||
key: coverage-subproviders-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/subproviders/coverage/lcov.info
|
||||
- save_cache:
|
||||
key: coverage-sol-cov-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/sol-cov/coverage/lcov.info
|
||||
- save_cache:
|
||||
key: coverage-metacoin-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/metacoin/coverage/lcov.info
|
||||
lint:
|
||||
working_directory: ~/repo
|
||||
docker:
|
||||
- image: circleci/node:6.12
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: yarn lerna:run lint
|
||||
prettier:
|
||||
working_directory: ~/repo
|
||||
docker:
|
||||
- image: circleci/node:6.12
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: yarn prettier:ci
|
||||
submit-coverage:
|
||||
docker:
|
||||
- image: circleci/node:6.12
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-contracts-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-assert-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-connect-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-dev-utils-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-json-schemas-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-subproviders-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-sol-cov-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-sol-compiler-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-0xjs-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-metacoin-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: yarn report_coverage
|
||||
workflows:
|
||||
version: 2
|
||||
main:
|
||||
jobs:
|
||||
version: 2
|
||||
main:
|
||||
jobs:
|
||||
- build
|
||||
- test-installation:
|
||||
requires:
|
||||
- build
|
||||
- test-exchange-ganache-3.0:
|
||||
requires:
|
||||
- build
|
||||
- test-integrations-ganache-3.0:
|
||||
requires:
|
||||
- build
|
||||
- test-contracts-rest-ganache-3.0:
|
||||
requires:
|
||||
- build
|
||||
- test-rest:
|
||||
requires:
|
||||
- build
|
||||
- static-tests:
|
||||
requires:
|
||||
- build
|
||||
- test-publish:
|
||||
requires:
|
||||
- build
|
||||
- test-doc-generation:
|
||||
requires:
|
||||
- build
|
||||
- submit-coverage:
|
||||
requires:
|
||||
- test-contracts-rest-ganache-3.0
|
||||
- test-exchange-ganache-3.0
|
||||
- test-rest
|
||||
- static-tests
|
||||
- test-python:
|
||||
requires:
|
||||
- build
|
||||
- static-tests-python:
|
||||
requires:
|
||||
- build
|
||||
# skip python tox run for now, as we don't yet have multiple test environments to support.
|
||||
# - test-rest-python
|
||||
- test-0xjs:
|
||||
requires:
|
||||
- build
|
||||
- test-contracts:
|
||||
requires:
|
||||
- build
|
||||
- test-sol-compiler:
|
||||
requires:
|
||||
- build
|
||||
- test-rest:
|
||||
requires:
|
||||
- build
|
||||
- prettier:
|
||||
requires:
|
||||
- build
|
||||
- lint:
|
||||
requires:
|
||||
- build
|
||||
- submit-coverage:
|
||||
requires:
|
||||
- test-0xjs
|
||||
- test-sol-compiler
|
||||
- test-rest
|
||||
- test-contracts
|
||||
|
7
.gitattributes
vendored
7
.gitattributes
vendored
@@ -1,8 +1 @@
|
||||
*.sol linguist-language=Solidity
|
||||
|
||||
# Automatically collapse generated files in GitHub.
|
||||
*.svg linguist-generated=true
|
||||
packages/contract-artifacts/artifacts/*json linguist-generated=true
|
||||
packages/abi-gen-wrappers/src/generated-wrappers/*.ts linguist-generated=true
|
||||
packages/contract-wrappers/src/generated-wrappers/*.ts linguist-generated=true
|
||||
|
||||
|
33
.github/autolabeler.yml
vendored
33
.github/autolabeler.yml
vendored
@@ -1,33 +0,0 @@
|
||||
python: ['python-packages']
|
||||
contracts: ['contracts']
|
||||
@0x/sol-doc: ['packages/sol-doc']
|
||||
@0x/sol-resolver: ['packages/sol-resolver']
|
||||
@0x/contracts-gen: ['packages/contracts-gen']
|
||||
@0x/sra-spec: ['packages/sra-spec']
|
||||
@0x/subproviders: ['packages/subproviders']
|
||||
@0x/contract-addresses: ['packages/contract-addresses']
|
||||
@0x/migrations: ['packages/migrations']
|
||||
@0x/web3-wrapper: ['packages/web3-wrapper']
|
||||
@0x/sol-compiler: ['packages/sol-compiler']
|
||||
@0x/types: ['packages/types']
|
||||
@0x/instant: ['packages/instant']
|
||||
@0x/abi-gen-templates: ['packages/abi-gen-templates']
|
||||
@0x/abi-gen: ['packages/abi-gen']
|
||||
@0x/sol-coverage: ['packages/sol-coverage']
|
||||
@0x/sol-profiler: ['packages/sol-profiler']
|
||||
@0x/sol-trace: ['packages/sol-trace']
|
||||
@0x/sol-tracing-utils: ['packages/sol-tracing-utils']
|
||||
@0x/utils: ['packages/utils']
|
||||
@0x/tslint-config: ['packages/tslint-config']
|
||||
@0x/asset-swapper: ['packages/asset-swapper']
|
||||
@0x/order-utils: ['packages/order-utils']
|
||||
@0x/assert: ['packages/assert']
|
||||
@0x/base-contract: ['packages/base-contract']
|
||||
@0x/typescript-typings: ['packages/typescript-typings']
|
||||
0x.js: ['packages/0x.js']
|
||||
@0x/contract-artifacts: ['packages/contract-artifacts']
|
||||
@0x/dev-utils: ['packages/dev-utils']
|
||||
@0x/contract-wrappers: ['packages/contract-wrappers']
|
||||
@0x/json-schemas: ['packages/json-schemas']
|
||||
@0x/ethereum-types: ['ethereum-types']
|
||||
@0x/connect: ['packages/connect']
|
19
.github/stale.yml
vendored
19
.github/stale.yml
vendored
@@ -1,19 +0,0 @@
|
||||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 30
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 30
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- pinned
|
||||
- security
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: stale
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. Thank you
|
||||
for your contributions.
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: >
|
||||
This issue has been automatically closed because no activity occured in 7 days after being marked as stale. If it's still relevant - feel free to reopen. Thank you
|
||||
for your contributions.
|
157
.gitignore
vendored
157
.gitignore
vendored
@@ -11,10 +11,6 @@ pids
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# SQLite database files
|
||||
*.db
|
||||
*.sqlite
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
@@ -40,15 +36,11 @@ build/Release
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
# Typescript v1 declaration files
|
||||
typings/
|
||||
|
||||
# NVM config
|
||||
.nvmrc
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
.npmrc
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
@@ -75,140 +67,31 @@ generated_docs/
|
||||
|
||||
TODO.md
|
||||
|
||||
# VSCode file
|
||||
.vscode
|
||||
packages/website/public/bundle*
|
||||
packages/react-docs/example/public/bundle*
|
||||
|
||||
# server cli
|
||||
packages/testnet-faucets/server/
|
||||
|
||||
# generated contract artifacts/
|
||||
contracts/erc20-bridge-sampler/generated-artifacts/
|
||||
contracts/erc20-bridge-sampler/test/generated-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/test/generated-artifacts/
|
||||
contracts/exchange/generated-artifacts/
|
||||
contracts/exchange/test/generated-artifacts/
|
||||
contracts/asset-proxy/generated-artifacts/
|
||||
contracts/asset-proxy/test/generated-artifacts/
|
||||
contracts/multisig/generated-artifacts/
|
||||
contracts/multisig/test/generated-artifacts/
|
||||
contracts/utils/generated-artifacts/
|
||||
contracts/utils/test/generated-artifacts/
|
||||
contracts/exchange-libs/generated-artifacts/
|
||||
contracts/exchange-libs/test/generated-artifacts/
|
||||
contracts/erc20/generated-artifacts/
|
||||
contracts/erc20/test/generated-artifacts/
|
||||
contracts/erc721/generated-artifacts/
|
||||
contracts/erc721/test/generated-artifacts/
|
||||
contracts/erc1155/generated-artifacts/
|
||||
contracts/erc1155/test/generated-artifacts/
|
||||
contracts/extensions/generated-artifacts/
|
||||
contracts/extensions/test/generated-artifacts/
|
||||
contracts/exchange-forwarder/generated-artifacts/
|
||||
contracts/exchange-forwarder/test/generated-artifacts/
|
||||
contracts/dev-utils/generated-artifacts/
|
||||
contracts/dev-utils/test/generated-artifacts/
|
||||
packages/sol-tracing-utils/test/fixtures/artifacts/
|
||||
python-packages/contract_artifacts/src/zero_ex/contract_artifacts/artifacts/
|
||||
|
||||
# generated truffle contract artifacts/
|
||||
contracts/erc20-bridge-sampler/build/
|
||||
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/sol-cov/test/fixtures/artifacts/
|
||||
packages/metacoin/artifacts/
|
||||
packages/order-watcher/test/artifacts/
|
||||
packages/contract-wrappers/test/artifacts/
|
||||
|
||||
# generated contract wrappers
|
||||
packages/python-contract-wrappers/generated/
|
||||
contracts/erc20-bridge-sampler/generated-wrappers/
|
||||
contracts/erc20-bridge-sampler/test/generated-wrappers/
|
||||
contracts/integrations/generated-wrappers/
|
||||
contracts/integrations/test/generated-wrappers/
|
||||
contracts/staking/generated-wrappers/
|
||||
contracts/staking/test/generated-wrappers/
|
||||
contracts/coordinator/generated-wrappers/
|
||||
contracts/coordinator/test/generated-wrappers/
|
||||
contracts/exchange/generated-wrappers/
|
||||
contracts/exchange/test/generated-wrappers/
|
||||
contracts/asset-proxy/generated-wrappers/
|
||||
contracts/asset-proxy/test/generated-wrappers/
|
||||
contracts/multisig/generated-wrappers/
|
||||
contracts/multisig/test/generated-wrappers/
|
||||
contracts/utils/generated-wrappers/
|
||||
contracts/utils/test/generated-wrappers/
|
||||
contracts/exchange-libs/generated-wrappers/
|
||||
contracts/exchange-libs/test/generated-wrappers/
|
||||
contracts/erc20/generated-wrappers/
|
||||
contracts/erc20/test/generated-wrappers/
|
||||
contracts/erc721/generated-wrappers/
|
||||
contracts/erc721/test/generated-wrappers/
|
||||
contracts/erc1155/generated-wrappers/
|
||||
contracts/erc1155/test/generated-wrappers/
|
||||
contracts/extensions/generated-wrappers/
|
||||
contracts/extensions/test/generated-wrappers/
|
||||
contracts/exchange-forwarder/generated-wrappers/
|
||||
contracts/exchange-forwarder/test/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/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/coordinator/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/coordinator_registry/__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/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_bridge_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_token/__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_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/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/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/zrx_token/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/zrx_vault/__init__.py
|
||||
packages/0x.js/src/contract_wrappers/generated/
|
||||
packages/contracts/src/contract_wrappers/generated/
|
||||
packages/contract-wrappers/src/contract_wrappers/generated/
|
||||
packages/metacoin/src/contract_wrappers
|
||||
packages/fill-scenarios/src/generated_contract_wrappers/
|
||||
packages/order-watcher/src/generated_contract_wrappers/
|
||||
packages/migrations/src/contract_wrappers
|
||||
|
||||
# solc-bin in sol-compiler
|
||||
packages/sol-compiler/solc_bin/
|
||||
|
||||
# python stuff
|
||||
.eggs
|
||||
.mypy_cache
|
||||
.tox
|
||||
python-packages/*/build
|
||||
python-packages/*/dist
|
||||
__pycache__
|
||||
python-packages/*/src/*.egg-info
|
||||
python-packages/*/.coverage
|
||||
# Monorepo scripts
|
||||
packages/*/scripts/
|
||||
|
||||
# python keeps package-local copies of json schemas and contract addresses
|
||||
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*
|
||||
ganache.log
|
||||
|
@@ -1,89 +1,10 @@
|
||||
lib
|
||||
.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/test/generated-wrappers
|
||||
/contracts/coordinator/generated-artifacts
|
||||
/contracts/coordinator/test/generated-artifacts
|
||||
/contracts/exchange/generated-wrappers
|
||||
/contracts/exchange/test/generated-wrappers
|
||||
/contracts/exchange/generated-artifacts
|
||||
/contracts/exchange/test/generated-artifacts
|
||||
/contracts/asset-proxy/generated-wrappers
|
||||
/contracts/asset-proxy/test/generated-wrappers
|
||||
/contracts/asset-proxy/generated-artifacts
|
||||
/contracts/asset-proxy/test/generated-artifacts
|
||||
/contracts/multisig/generated-wrappers
|
||||
/contracts/multisig/test/generated-wrappers
|
||||
/contracts/multisig/generated-artifacts
|
||||
/contracts/multisig/test/generated-artifacts
|
||||
/contracts/utils/generated-wrappers
|
||||
/contracts/utils/test/generated-wrappers
|
||||
/contracts/utils/generated-artifacts
|
||||
/contracts/utils/test/generated-artifacts
|
||||
/contracts/exchange-libs/generated-wrappers
|
||||
/contracts/exchange-libs/test/generated-wrappers
|
||||
/contracts/exchange-libs/generated-artifacts
|
||||
/contracts/exchange-libs/test/generated-artifacts
|
||||
/contracts/erc20/generated-wrappers
|
||||
/contracts/erc20/test/generated-wrappers
|
||||
/contracts/erc20/generated-artifacts
|
||||
/contracts/erc20/test/generated-artifacts
|
||||
/contracts/erc20-bridge-sampler/generated-wrappers
|
||||
/contracts/erc20-bridge-sampler/test/generated-wrappers
|
||||
/contracts/erc20-bridge-sampler/generated-artifacts
|
||||
/contracts/erc20-bridge-sampler/test/generated-artifacts
|
||||
/contracts/erc721/generated-wrappers
|
||||
/contracts/erc721/test/generated-wrappers
|
||||
/contracts/erc721/generated-artifacts
|
||||
/contracts/erc721/test/generated-artifacts
|
||||
/contracts/erc1155/generated-wrappers
|
||||
/contracts/erc1155/test/generated-wrappers
|
||||
/contracts/erc1155/generated-artifacts
|
||||
/contracts/erc1155/test/generated-artifacts
|
||||
/contracts/extensions/generated-wrappers
|
||||
/contracts/extensions/test/generated-wrappers
|
||||
/contracts/extensions/generated-artifacts
|
||||
/contracts/extensions/test/generated-artifacts
|
||||
/contracts/exchange-forwarder/generated-wrappers
|
||||
/contracts/exchange-forwarder/test/generated-wrappers
|
||||
/contracts/exchange-forwarder/generated-artifacts
|
||||
/contracts/exchange-forwarder/test/generated-artifacts
|
||||
/contracts/dev-utils/generated-wrappers
|
||||
/contracts/dev-utils/test/generated-wrappers
|
||||
/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/json-schemas/schemas
|
||||
/python-packages/json_schemas/src/zero_ex/json_schemas/schemas
|
||||
/packages/sra-spec/public/
|
||||
/packages/0x.js/test/artifacts
|
||||
/packages/contracts/src/artifacts
|
||||
/packages/metacoin/artifacts
|
||||
/packages/contract-wrappers/test/artifacts
|
||||
/packages/order-watcher/test/artifacts
|
||||
/packages/migrations/artifacts/1.0.0
|
||||
package.json
|
||||
scripts/postpublish_utils.js
|
||||
packages/sol-coverage/test/fixtures/artifacts
|
||||
.pytest_cache
|
||||
.mypy_cache
|
||||
.tox
|
||||
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/TestLibDummy.json
|
||||
packages/*/docs
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"printWidth": 120,
|
||||
"tabWidth": 4,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all"
|
||||
"printWidth": 120,
|
||||
"trailingComma": all,
|
||||
"singleQuote": true
|
||||
}
|
||||
|
38
CODEOWNERS
38
CODEOWNERS
@@ -1,38 +0,0 @@
|
||||
# See https://help.github.com/articles/about-codeowners/
|
||||
# for more info about CODEOWNERS file
|
||||
|
||||
# It uses the same pattern rule for gitignore file
|
||||
# https://git-scm.com/docs/gitignore#_pattern_format
|
||||
|
||||
# Website
|
||||
packages/asset-swapper/ @BMillman19 @fragosti @dave4506
|
||||
packages/instant/ @BMillman19 @fragosti @dave4506
|
||||
|
||||
# Dev tools & setup
|
||||
.circleci/ @LogvinovLeon
|
||||
packages/abi-gen/ @feuGeneA
|
||||
packages/base-contract/ @xianny
|
||||
packages/connect/ @fragosti
|
||||
packages/abi-gen-templates/ @feuGeneA @xianny
|
||||
packages/contract-addresses/ @albrow
|
||||
packages/contract-artifacts/ @albrow
|
||||
packages/dev-utils/ @LogvinovLeon @fabioberger
|
||||
packages/devnet/ @albrow
|
||||
packages/ethereum-types/ @LogvinovLeon
|
||||
packages/monorepo-scripts/ @fabioberger
|
||||
packages/order-utils/ @fabioberger @LogvinovLeon
|
||||
packages/python-contract-wrappers/ @feuGeneA
|
||||
packages/sol-compiler/ @LogvinovLeon
|
||||
packages/sol-coverage/ @LogvinovLeon
|
||||
packages/sol-profiler/ @LogvinovLeon
|
||||
packages/sol-trace/ @LogvinovLeon
|
||||
packages/sol-tracing-utils/ @LogvinovLeon
|
||||
packages/sol-resolver/ @LogvinovLeon
|
||||
packages/subproviders/ @fabioberger @dekz
|
||||
packages/verdaccio/ @albrow
|
||||
packages/web3-wrapper/ @LogvinovLeon @fabioberger
|
||||
python-packages/ @feuGeneA
|
||||
packages/utils/ @hysz
|
||||
|
||||
# Protocol/smart contracts
|
||||
contracts/ @abandeali1 @hysz
|
@@ -8,19 +8,19 @@ In the interest of fostering an open and welcoming environment, we as contributo
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment include:
|
||||
|
||||
- Using welcoming and inclusive language
|
||||
- Being respectful of differing viewpoints and experiences
|
||||
- Gracefully accepting constructive criticism
|
||||
- Focusing on what is best for the community
|
||||
- Showing empathy towards other community members
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
- The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||
- Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
- Public or private harassment
|
||||
- Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||
- Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
|
108
CONTRIBUTING.md
108
CONTRIBUTING.md
@@ -1,105 +1,49 @@
|
||||
## 0x Contribution Guide
|
||||
|
||||
We welcome contributions from anyone on the internet and are grateful for even the smallest contributions. This document will help get you setup to start contributing back to 0x.
|
||||
Thank you for your interest in contributing to 0x protocol! We welcome contributions from anyone on the internet, and are grateful for even the smallest of fixes!
|
||||
|
||||
### Getting started
|
||||
### How to contribute
|
||||
|
||||
1. Fork `0xproject/0x-monorepo`
|
||||
2. Clone your fork
|
||||
3. Follow the [installation & build steps](https://github.com/0xProject/0x-monorepo#install-dependencies) in the repo's top-level README.
|
||||
4. Setup the recommended [Development Tooling](#development-tooling).
|
||||
5. Open a PR with the `[WIP]` flag against the `development` branch and describe the change you are intending to undertake in the PR description. (see [our branch naming conventions](#branch-structure))
|
||||
If you'd like to contribute to 0x protocol, please fork the repo, fix, commit and send a pull request against the `development` branch for the maintainers to review and merge into the main code base. If you wish to submit more complex changes though, please check with a core dev first on [our RocketChat #dev channel](http://chat.0xproject.com) to ensure those changes are in-line with the general philosophy of the project and/or to get some early feedback which can make both your efforts easier as well as our review and merge procedures quick and simple.
|
||||
|
||||
Before removing the `[WIP]` tag and submitting the PR for review, make sure:
|
||||
We encourage a “PR early” approach so create the PR as early as possible even without the fix/feature ready, so that devs and other contributors know you have picked up the issue. These early PRs should indicate an 'in progress' status by adding the '[WIP]' prefix to the PR title. Please make sure your contributions adhere to our coding guidelines:
|
||||
|
||||
- It passes our linter checks (`yarn lint`)
|
||||
- It is properly formatted with Prettier (`yarn prettier`)
|
||||
- It passes our continuous integration tests (See: [Enabling code coverage checks on your fork](#enabling-code-coverage-checks-on-your-fork) for instructions on getting the `submit-coverage` test to pass on forks)
|
||||
- You've created/updated the corresponding [CHANGELOG](#CHANGELOGs) entries.
|
||||
- Your changes have sufficient test coverage (e.g regression tests have been added for bug fixes)
|
||||
* Pull requests adding features or refactoring should be opened against the `development` branch
|
||||
* Pull requests fixing bugs in the latest release version should be opened again the `master` branch
|
||||
* Write [good commit messages](https://chris.beams.io/posts/git-commit/)
|
||||
|
||||
### Branch structure
|
||||
### Code quality
|
||||
|
||||
We have two main branches:
|
||||
Because 0x.js is used by multiple relayers in production and their businesses depend on it, we strive for exceptional code quality. Please follow the existing code standards and conventions. `tslint` and `prettier` (described below) will help you.
|
||||
|
||||
- `master` represents the most recently released (published on npm) version of the codebase.
|
||||
- `development` represents the current development state of the codebase.
|
||||
If you're adding functionality, please also add tests and make sure they pass. We have an automatic coverage reporting tool, so we'll see it if they are missing ;)
|
||||
If you're adding a new public function/member, make sure you document it with Java doc-style comments. We use typedoc to generate [awesome documentation](https://0xproject.com/docs/0xjs) from the comments within our source code.
|
||||
|
||||
ALL PRs should be opened against `development`.
|
||||
If the sub-package you are modifying has a `CHANGELOG.md` file, make sure to add an entry in it for the change made to the package. For published packages, only changes that modify the public interface or behavior of the package need a CHANGELOG entry.
|
||||
|
||||
Branch names should be prefixed with `fix`, `feature` or `refactor`.
|
||||
### Styleguide
|
||||
|
||||
- e.g `fix/missing-import`
|
||||
- If the PR only edits a single package, add it's name too
|
||||
- e.g `fix/subproviders/missing-import`
|
||||
We use [TSLint](https://palantir.github.io/tslint/) with [custom configs](https://github.com/0xProject/0x-monorepo/tree/development/packages/tslint-config) to keep our code style consistent.
|
||||
|
||||
### CHANGELOGs
|
||||
To lint your code just run: `yarn lint`
|
||||
|
||||
At 0x we use [Semantic Versioning](http://semver.org/) for all our published packages. If a change you make corresponds to a semver bump, you must modify the package's `CHANGELOG.json` file accordingly.
|
||||
|
||||
Each CHANGELOG entry that corresponds to a published package will have a `timestamp`. If no entry exists without a `timestamp`, you must first create a new one:
|
||||
|
||||
```
|
||||
{
|
||||
"version": "1.0.1", <- The updated package version
|
||||
"changes": [
|
||||
{
|
||||
"note": "", <- Describe your change
|
||||
"PR": 100 <- Your PR number
|
||||
}
|
||||
]
|
||||
},
|
||||
```
|
||||
|
||||
If an entry without a `timestamp` already exists, this means other changes have been introduced by other collaborators since the last publish. Add your changes to the list of notes and adjust the version if your PR introduces a greater semver change (i.e current changes required a patch bump, but your changes require a major version bump).
|
||||
|
||||
### 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.
|
||||
|
||||
#### Linter
|
||||
|
||||
We use [TSLint](https://palantir.github.io/tslint/) with [custom configs](https://github.com/0xProject/0x-monorepo/tree/development/packages/tslint-config) to keep our code-style consistent.
|
||||
|
||||
Use `yarn:lint` to lint the entire monorepo, and `PKG={PACKAGE_NAME} yarn lint` to lint a specific package.
|
||||
|
||||
If you want to change a rule, or add a custom rule, please make these changes to our [tslint-config](https://github.com/0xProject/0x-monorepo/tree/development/packages/tslint-config) package. All other packages have it as a dependency.
|
||||
|
||||
Integrate it into your text editor:
|
||||
|
||||
- VSCode: [vscode-tslint](https://marketplace.visualstudio.com/items?itemName=eg2.tslint)
|
||||
- Atom: [linter-tslint](https://atom.io/packages/linter-tslint)
|
||||
|
||||
#### Auto-formatter
|
||||
|
||||
We use [Prettier](https://prettier.io/) to auto-format our code. Be sure to either add a [text editor integration](https://prettier.io/docs/en/editors.html) or a [pre-commit hook](https://prettier.io/docs/en/precommit.html) to properly format your code changes.
|
||||
We also use [Prettier](https://prettier.io/) to auto-format our code. Be sure to either add a [text editor integration](https://prettier.io/docs/en/editors.html) or a [pre-commit hook](https://prettier.io/docs/en/precommit.html) to properly format your code changes.
|
||||
|
||||
If using the Atom text editor, we recommend you install the following packages:
|
||||
|
||||
- VSCode: [prettier-vscode](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)
|
||||
- Atom: [prettier-atom](https://atom.io/packages/prettier-atom)
|
||||
* [atom-typescript](https://atom.io/packages/atom-typescript)
|
||||
* [linter-tslint](https://atom.io/packages/linter-tslint)
|
||||
* [prettier-atom](https://atom.io/packages/prettier-atom)
|
||||
* [language-ethereum](https://atom.io/packages/language-ethereum)
|
||||
|
||||
## Unenforced coding conventions
|
||||
Our CI will also run TSLint and Prettier as a part of the test run when you submit your PR. Make sure that the CI tests pass for your contribution.
|
||||
|
||||
A few of our coding conventions are not yet enforced by the linter/auto-formatter. Be careful to follow these conventions in your PR's.
|
||||
### Branch structure & versioning
|
||||
|
||||
1. Unused anonymous function parameters should be named with an underscore + number (e.g \_1, \_2, etc...)
|
||||
1. There should be a new-line between methods in a class and between test cases.
|
||||
1. If a string literal has the same value in two or more places, it should be a single constant referenced in both places.
|
||||
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. 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. All public, exported methods/functions/classes must have associated Javadoc-style comments.
|
||||
We use [semantic versioning](http://semver.org/), but before a package reaches v1.0.0 all breaking changes as well as new features will be minor version bumps.
|
||||
|
||||
### Fix `submit-coverage` CI failure
|
||||
We have two main branches: `master` and `development`.
|
||||
|
||||
If you simply fork the repo and then create a PR from it, your PR will fail the `submit-coverage` check on CI. This is because the 0x CircleCI configuration sets the `COVERALLS_REPO_TOKEN` environment variable to the token for `0xProject/0x-monorepo`, but when running the check against your fork the token needs to match your repo's name `your-username/0x-monorepo`.
|
||||
`master` represents the most recent released (published on npm) version.
|
||||
|
||||
To facilitate this check, after creating your fork, but before creating the branch for your PR, do the following:
|
||||
|
||||
1. Log in to [coveralls.io](https://coveralls.io/), go to `Add Repos`, and enable your fork. Then go to the settings for that repo, and copy the `Repo Token` identifier.
|
||||
2. Log in to [CircleCI](https://circleci.com/login), go to `Add Projects`, click the `Set Up Project` button corresponding to your fork, and then click `Start Building`. (Aside from step 3 below, no actual set up is needed, since it will use the `.circleci/config.yml` file in 0x-monorepo, so you can ignore all of the instruction/explanation given on the page with the `Start Building` button.)
|
||||
3. In CircleCI, configure your project to add an environment variable, with name `COVERALLS_REPO_TOKEN`, and for the value paste in the `Repo Token` you copied in step 1.
|
||||
|
||||
Now, when you push to your branch, CircleCI will automatically run all of the checks in your own instance, and the coverage check will work since it has the proper `Repo Token`, and the PR will magically refer to your own checks rather than running them in the 0x CircleCI instance.
|
||||
`development` represents the development state and is a default branch to which you will submit a PR. We use this structure so that we can push hotfixes to the currently released version without needing to publish all the changes made towards the next release. If a hotfix is implemented on `master`, it is back-ported to `development`.
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
<!--- Before submitting please check to see if this issue was already reported -->
|
||||
|
||||
<!--- Prefix your issue title with the package name it relates to (e.g., `0x.js: ` or `general:`) -->
|
||||
<!--- Provide a general summary of the issue in the Title above -->
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
@@ -46,14 +46,10 @@
|
||||
|
||||
<!--- Include as many relevant details about the environment you experienced the bug in -->
|
||||
|
||||
| Package | Version |
|
||||
| ------: | :------ |
|
||||
|
||||
|
||||
<!-- For example:
|
||||
| `0x.js` | 2.0.4 |
|
||||
| `Exchange Contract` | v2 |
|
||||
-->
|
||||
| Package | Version |
|
||||
| ------------------: | :------ |
|
||||
| `0x.js` | 0.25.0 |
|
||||
| `Exchange Contract` | v1 |
|
||||
|
||||
| Network |
|
||||
| ------- |
|
||||
|
4
LICENSE
4
LICENSE
@@ -1,4 +1,4 @@
|
||||
Copyright 2017 ZeroEx Intl.
|
||||
Copyright 2017 ZeroEx Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -10,4 +10,4 @@ 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.
|
||||
limitations under the License.
|
@@ -1,26 +1,41 @@
|
||||
<!--- Thank you for taking the time to submit a Pull Request -->
|
||||
|
||||
<!--- Provide a general summary of the issue in the Title above -->
|
||||
|
||||
## Description
|
||||
|
||||
<!--- Describe your changes in detail -->
|
||||
|
||||
## Testing instructions
|
||||
## Motivation and Context
|
||||
|
||||
<!--- Please describe how reviewers can test your changes -->
|
||||
<!--- Why is this change required? What problem does it solve? -->
|
||||
|
||||
<!--- If it fixes an open issue, please link to the issue here. -->
|
||||
|
||||
## How Has This Been Tested?
|
||||
|
||||
<!--- Please describe in detail how you tested your changes. -->
|
||||
|
||||
<!--- Include details of your testing environment, and the tests you ran to -->
|
||||
|
||||
<!--- see how your change affects other areas of the code, etc. -->
|
||||
|
||||
## Types of changes
|
||||
|
||||
<!--- What types of changes does your code introduce? Uncomment all the bullets that apply: -->
|
||||
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
|
||||
|
||||
<!-- * Bug fix (non-breaking change which fixes an issue) -->
|
||||
|
||||
<!-- * New feature (non-breaking change which adds functionality) -->
|
||||
|
||||
<!-- * Breaking change (fix or feature that would cause existing functionality to change) -->
|
||||
* [ ] Bug fix (non-breaking change which fixes an issue)
|
||||
* [ ] New feature (non-breaking change which adds functionality)
|
||||
* [ ] Breaking change (fix or feature that would cause existing functionality to change)
|
||||
|
||||
## Checklist:
|
||||
|
||||
<!--- The following points should be used to indicate the progress of your PR. Put an `x` in all the boxes that apply right now, and come back over time and check them off as you make progress. If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
|
||||
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
|
||||
|
||||
- [ ] Prefix PR title with `[WIP]` if necessary.
|
||||
- [ ] Add tests to cover changes as needed.
|
||||
- [ ] Update documentation as needed.
|
||||
- [ ] Add new entries to the relevant CHANGELOG.jsons.
|
||||
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
|
||||
|
||||
* [ ] Change requires a change to the documentation.
|
||||
* [ ] Added tests to cover my changes.
|
||||
* [ ] Added new entries to the relevant CHANGELOG.jsons.
|
||||
* [ ] Labeled this PR with the 'WIP' label if it is a work in progress.
|
||||
* [ ] Labeled this PR with the labels corresponding to the changed package.
|
||||
|
162
README.md
162
README.md
@@ -1,112 +1,74 @@
|
||||
<img src="https://github.com/0xProject/branding/blob/master/0x%20Logo/PNG/0x-Logo-Black.png" width="150px" >
|
||||
<img src="https://github.com/0xProject/branding/blob/master/0x_Black_CMYK.png" width="200px" >
|
||||
|
||||
---
|
||||
|
||||
[0x][website-url] is an open protocol that facilitates trustless, low friction exchange of Ethereum-based assets. For more information on how it works, check out the [0x protocol specification](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md).
|
||||
[0x][website-url] is an open protocol that facilitates trustless, low friction exchange of Ethereum-based assets. A full description of the protocol may be found in our [whitepaper][whitepaper-url].
|
||||
|
||||
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
|
||||
[website-url]: https://0xproject.com/
|
||||
[whitepaper-url]: https://0xproject.com/pdfs/0x_white_paper.pdf
|
||||
|
||||
[](https://circleci.com/gh/0xProject/0x-monorepo)
|
||||
[](https://coveralls.io/github/0xProject/0x-monorepo?branch=development)
|
||||
[](https://discordapp.com/invite/d3FTX3M)
|
||||
[](https://chat.0xproject.com)
|
||||
[](https://gitter.im/0xProject/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](https://opensource.org/licenses/Apache-2.0)
|
||||
|
||||
## Packages
|
||||
### Published 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.
|
||||
| Package | Version | Description |
|
||||
| --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [`0x.js`](/packages/0x.js) | [](https://www.npmjs.com/package/0x.js) | A Javascript library for interacting with the 0x protocol |
|
||||
| [`@0xproject/abi-gen`](/packages/abi-gen) | [](https://www.npmjs.com/package/@0xproject/abi-gen) | Tool to generate TS wrappers from smart contract ABIs |
|
||||
| [`@0xproject/assert`](/packages/assert) | [](https://www.npmjs.com/package/@0xproject/assert) | Type and schema assertions used by our packages |
|
||||
| [`@0xproject/base-contract`](/packages/base-contract) | [](https://www.npmjs.com/package/@0xproject/base-contract) | BaseContract used by auto-generated `abi-gen` wrapper contracts |
|
||||
| [`@0xproject/connect`](/packages/connect) | [](https://www.npmjs.com/package/@0xproject/connect) | A Javascript library for interacting with the Standard Relayer API |
|
||||
| [`@0xproject/sol-compiler`](/packages/sol-compiler) | [](https://www.npmjs.com/package/@0xproject/sol-compiler) | A thin wrapper around Solc.js that outputs artifacts, resolves imports, only re-compiles when needed, and other niceties. |
|
||||
| [`@0xproject/dev-utils`](/packages/dev-utils) | [](https://www.npmjs.com/package/@0xproject/dev-utils) | Dev utils to be shared across 0x projects and packages |
|
||||
| [`@0xproject/json-schemas`](/packages/json-schemas) | [](https://www.npmjs.com/package/@0xproject/json-schemas) | 0x-related json schemas |
|
||||
| [`@0xproject/monorepo-scripts`](/packages/monorepo-scripts) | [](https://www.npmjs.com/package/@0xproject/monorepo-scripts) | Monorepo scripts |
|
||||
| [`@0xproject/react-docs`](/packages/react-docs) | [](https://www.npmjs.com/package/@0xproject/react-docs) | React documentation component for rendering TypeDoc & Doxity generated JSON |
|
||||
| [`@0xproject/react-shared`](/packages/react-shared) | [](https://www.npmjs.com/package/@0xproject/react-shared) | 0x shared react components |
|
||||
| [`@0xproject/sra-report`](/packages/sra-report) | [](https://www.npmjs.com/package/@0xproject/sra-report) | Generate reports for standard relayer API compliance |
|
||||
| [`@0xproject/sol-cov`](/packages/sol-cov) | [](https://www.npmjs.com/package/@0xproject/sol-cov) | Solidity test coverage tool |
|
||||
| [`@0xproject/subproviders`](/packages/subproviders) | [](https://www.npmjs.com/package/@0xproject/subproviders) | Useful web3 subproviders (e.g LedgerSubprovider) |
|
||||
| [`@0xproject/tslint-config`](/packages/tslint-config) | [](https://www.npmjs.com/package/@0xproject/tslint-config) | Custom 0x development TSLint rules |
|
||||
| [`@0xproject/types`](/packages/types) | [](https://www.npmjs.com/package/@0xproject/types) | Shared type declarations |
|
||||
| [`@0xproject/typescript-typings`](/packages/typescript-typings) | [](https://www.npmjs.com/package/@0xproject/typescript-typings) | Repository of types for external packages |
|
||||
| [`@0xproject/utils`](/packages/utils) | [](https://www.npmjs.com/package/@0xproject/utils) | Shared utilities |
|
||||
| [`@0xproject/web3-wrapper`](/packages/web3-wrapper) | [](https://www.npmjs.com/package/@0xproject/web3-wrapper) | Web3 wrapper |
|
||||
|
||||
### Python Packages
|
||||
### Private Packages
|
||||
|
||||
| Package | Version | Description |
|
||||
| -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
|
||||
| [`0x-contract-addresses`](/python-packages/contract_addresses) | [](https://pypi.org/project/0x-contract-addresses/) | A tiny utility library for getting known deployed contract addresses for a particular network |
|
||||
| [`0x-contract-artifacts`](/python-packages/contract_artifacts) | [](https://pypi.org/project/0x-contract-artifacts/) | 0x smart contract compilation artifacts |
|
||||
| [`0x-contract-wrappers`](/python-packages/contract_wrappers) | [](https://pypi.org/project/0x-contract-wrappers/) | 0x smart contract wrappers |
|
||||
| [`0x-json-schemas`](/python-packages/json_schemas) | [](https://pypi.org/project/0x-json-schemas/) | 0x-related JSON schemas |
|
||||
| [`0x-order-utils`](/python-packages/order_utils) | [](https://pypi.org/project/0x-order-utils/) | A set of utilities for generating, parsing, signing and validating 0x orders |
|
||||
| [`0x-sra-client`](/python-packages/sra_client) | [](https://pypi.org/project/0x-sra-client/) | A Python client for interacting with servers conforming to the Standard Relayer API specification |
|
||||
|
||||
### Solidity Packages
|
||||
|
||||
These packages are all under development. See [/contracts/README.md](/contracts/README.md) for a list of deployed packages.
|
||||
|
||||
| Package | Version | Description |
|
||||
| ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [`@0x/contracts-asset-proxy`](/contracts/asset-proxy) | [](https://www.npmjs.com/package/@0x/contracts-asset-proxy) | [`AssetProxy`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#assetproxy) contracts used within the protocol |
|
||||
| [`@0x/contracts-erc20`](/contracts/erc20) | [](https://www.npmjs.com/package/@0x/contracts-erc20) | Implementations of various ERC20 tokens |
|
||||
| [`@0x/contracts-erc721`](/contracts/erc721) | [](https://www.npmjs.com/package/@0x/contracts-erc721) | Implementations of various ERC721 tokens |
|
||||
| [`@0x/contracts-erc1155`](/contracts/erc1155) | [](https://www.npmjs.com/package/@0x/contracts-erc1155) | Implementations of various ERC1155 tokens |
|
||||
| [`@0x/contracts-exchange`](/contracts/exchange) | [](https://www.npmjs.com/package/@0x/contracts-exchange) | The [`Exchange`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#exchange) contract used for settling trades within the protocol |
|
||||
| [`@0x/contracts-exchange-forwarder`](/contracts/exchange-forwarder) | [](https://www.npmjs.com/package/@0x/contracts-exchange-forwarder) | A [`Forwarder`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/forwarder-specification.md) contract used to simplify UX for interacting with the protocol |
|
||||
| [`@0x/contracts-exchange-libs`](/contracts/exchange-libs) | [](https://www.npmjs.com/package/@0x/contracts-exchange-libs) | Protocol specific libraries used within the [`Exchange`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#exchange) contract |
|
||||
| [`@0x/contracts-extensions`](/contracts/extensions) | [](https://www.npmjs.com/package/@0x/contracts-extensions) | Contracts that interact with and extend the functionality of the core protocol |
|
||||
| [`@0x/contracts-multisig`](/contracts/multisig) | [](https://www.npmjs.com/package/@0x/contracts-multisig) | Various implementations of multisignature wallets, including the [`AssetProxyOwner`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#assetproxyowner) contract that has permissions to upgrade the protocol |
|
||||
| [`@0x/contracts-test-utils`](/contracts/test-utils) | [](https://www.npmjs.com/package/@0x/contracts-test-utils) | TypeScript/Javascript shared utilities used for testing contracts |
|
||||
| [`@0x/contracts-utils`](/contracts/utils) | [](https://www.npmjs.com/package/@0x/contracts-utils) | Generic libraries and utilities used throughout all of the contracts |
|
||||
| [`@0x/contracts-coordinator`](/contracts/coordinator) | [](https://www.npmjs.com/package/@0x/contracts-coordinator) | A contract that allows users to execute 0x transactions with permission from a Coordinator |
|
||||
| [`@0x/contracts-dev-utils`](/contracts/dev-utils) | [](https://www.npmjs.com/package/@0x/contracts-dev-utils) | A contract contains utility functions for developers (such as validating many orders using a single eth_call) |
|
||||
| [`@0x/contracts-staking`](/contracts/staking) | [](https://www.npmjs.com/package/@0x/contracts-staking) | Implements the stake-based liquidity incentives defined by [`ZEIP-31`](https://github.com/0xProject/ZEIPs/issues/31) |
|
||||
|
||||
### TypeScript/Javascript Packages
|
||||
|
||||
#### 0x-specific packages
|
||||
|
||||
| Package | Version | Description |
|
||||
| -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
|
||||
| [`0x.js`](/packages/0x.js) | [](https://www.npmjs.com/package/0x.js) | An aggregate package combining many smaller utility packages for interacting with the 0x protocol |
|
||||
| [`@0x/contract-addresses`](/packages/contract-addresses) | [](https://www.npmjs.com/package/@0x/contract-addresses) | A tiny utility library for getting known deployed contract addresses for a particular network. |
|
||||
| [`@0x/contract-wrappers`](/packages/contract-wrappers) | [](https://www.npmjs.com/package/@0x/contract-wrappers) | JS/TS wrappers for interacting with the 0x smart contracts |
|
||||
| [`@0x/order-utils`](/packages/order-utils) | [](https://www.npmjs.com/package/@0x/order-utils) | A set of utilities for generating, parsing, signing and validating 0x orders |
|
||||
| [`@0x/json-schemas`](/packages/json-schemas) | [](https://www.npmjs.com/package/@0x/json-schemas) | 0x-related JSON schemas | |
|
||||
| [`@0x/migrations`](/packages/migrations) | [](https://www.npmjs.com/package/@0x/migrations) | Migration tool for deploying 0x smart contracts on private testnets |
|
||||
| [`@0x/contract-artifacts`](/packages/contract-artifacts) | [](https://www.npmjs.com/package/@0x/contract-artifacts) | 0x smart contract compilation artifacts | |
|
||||
| [`@0x/sra-spec`](/packages/sra-spec) | [](https://www.npmjs.com/package/@0x/sra-spec) | OpenAPI specification for the Standard Relayer API |
|
||||
| [`@0x/connect`](/packages/connect) | [](https://www.npmjs.com/package/@0x/connect) | An HTTP/WS client for interacting with the Standard Relayer API |
|
||||
| [`@0x/asset-swapper`](/packages/asset-swapper) | [](https://www.npmjs.com/package/@0x/asset-swapper) | Convenience package for discovering and performing swaps for any ERC20 Assets |
|
||||
|
||||
#### Ethereum tooling
|
||||
|
||||
| Package | Version | Description |
|
||||
| -------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [`@0x/web3-wrapper`](/packages/web3-wrapper) | [](https://www.npmjs.com/package/@0x/web3-wrapper) | An Ethereum JSON RPC client |
|
||||
| [`@0x/sol-compiler`](/packages/sol-compiler) | [](https://www.npmjs.com/package/@0x/sol-compiler) | A wrapper around solc-js that adds smart re-compilation, ability to compile an entire project, Solidity version specific compilation, standard input description support and much more. |
|
||||
| [`@0x/sol-coverage`](/packages/sol-coverage) | [](https://www.npmjs.com/package/@0x/sol-coverage) | A solidity test coverage tool |
|
||||
| [`@0x/sol-profiler`](/packages/sol-profiler) | [](https://www.npmjs.com/package/@0x/sol-profiler) | A solidity gas cost profiler |
|
||||
| [`@0x/sol-trace`](/packages/sol-trace) | [](https://www.npmjs.com/package/@0x/sol-trace) | A solidity stack trace tool |
|
||||
| [`@0x/sol-resolver`](/packages/sol-resolver) | [](https://www.npmjs.com/package/@0x/sol-resolver) | Import resolver for smart contracts dependencies |
|
||||
| [`@0x/subproviders`](/packages/subproviders) | [](https://www.npmjs.com/package/@0x/subproviders) | Web3 provider middlewares (e.g. LedgerSubprovider) |
|
||||
| [`@0x/sol-doc`](/packages/sol-doc) | [](https://www.npmjs.com/package/@0x/sol-doc) | Solidity documentation generator |
|
||||
|
||||
#### Utilities
|
||||
|
||||
| Package | Version | Description |
|
||||
| -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- |
|
||||
| [`@0x/abi-gen`](/packages/abi-gen) | [](https://www.npmjs.com/package/@0x/abi-gen) | Tool to generate TS wrappers from smart contract ABIs |
|
||||
| [`@0x/tslint-config`](/packages/tslint-config) | [](https://www.npmjs.com/package/@0x/tslint-config) | Custom TSLint rules used by the 0x core team |
|
||||
| [`@0x/types`](/packages/types) | [](https://www.npmjs.com/package/@0x/types) | Shared type declarations |
|
||||
| [`@0x/typescript-typings`](/packages/typescript-typings) | [](https://www.npmjs.com/package/@0x/typescript-typings) | Repository of types for external packages |
|
||||
| [`@0x/utils`](/packages/utils) | [](https://www.npmjs.com/package/@0x/utils) | Shared utilities |
|
||||
| [`@0x/assert`](/packages/assert) | [](https://www.npmjs.com/package/@0x/assert) | Type and schema assertions used by our packages |
|
||||
| [`@0x/base-contract`](/packages/base-contract) | [](https://www.npmjs.com/package/@0x/base-contract) | BaseContract used by auto-generated `abi-gen` wrapper contracts |
|
||||
| [`@0x/dev-utils`](/packages/dev-utils) | [](https://www.npmjs.com/package/@0x/dev-utils) | Dev utils to be shared across 0x packages |
|
||||
|
||||
#### Private Packages
|
||||
|
||||
| Package | Description |
|
||||
| ---------------------------------- | -------------------------------------------------------------------------------- |
|
||||
| [`@0x/instant`](/packages/instant) | A free and flexible way to offer simple crypto purchasing in any app or website. |
|
||||
| Package | Description |
|
||||
| --------------------------------------------------------------- | ---------------------------------------------------------------- |
|
||||
| [`@0xproject/contracts`](/packages/contracts) | 0x solidity smart contracts & tests |
|
||||
| [`@0xproject/react-docs-example`](/packages/react-docs-example) | Example documentation site created with `@0xproject/react-docs` |
|
||||
| [`@0xproject/testnet-faucets`](/packages/testnet-faucets) | A faucet micro-service that dispenses test ERC20 tokens or Ether |
|
||||
| [`@0xproject/website`](/packages/website) | 0x website & Portal DApp |
|
||||
|
||||
## Usage
|
||||
|
||||
Node version 6.x or 8.x is required.
|
||||
Dedicated documentation pages:
|
||||
|
||||
* [0x.js Library](https://0xproject.com/docs/0xjs)
|
||||
* [0x Connect](https://0xproject.com/docs/connect)
|
||||
* [Smart contracts](https://0xproject.com/docs/contracts)
|
||||
* [Subproviders](https://0xproject.com/docs/subproviders)
|
||||
* [Sol Compiler](https://0xproject.com/docs/sol-compiler)
|
||||
* [Web3-wrapper](https://0xproject.com/docs/web3-wrapper)
|
||||
* [JSON-schemas](https://0xproject.com/docs/json-schemas)
|
||||
* [Sol-cov](https://0xproject.com/docs/sol-cov)
|
||||
* [Standard Relayer API](https://github.com/0xProject/standard-relayer-api/blob/master/README.md)
|
||||
|
||||
Node version >= 6.12 is required.
|
||||
|
||||
Most of the packages require additional typings for external dependencies.
|
||||
You can include those by prepending the `@0x/typescript-typings` package to your [`typeRoots`](http://www.typescriptlang.org/docs/handbook/tsconfig-json.html) config.
|
||||
You can include those by prepending @0xproject/typescript-typings package to your [`typeRoots`](http://www.typescriptlang.org/docs/handbook/tsconfig-json.html) config.
|
||||
|
||||
```json
|
||||
"typeRoots": ["node_modules/@0x/typescript-typings/types", "node_modules/@types"],
|
||||
"typeRoots": ["node_modules/@0xproject/typescript-typings/types", "node_modules/@types"],
|
||||
```
|
||||
|
||||
## Contributing
|
||||
@@ -117,10 +79,10 @@ We strongly recommend that the community help us make improvements and determine
|
||||
|
||||
### Install dependencies
|
||||
|
||||
Make sure you are using Yarn v1.9.4. To install using brew:
|
||||
If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them:
|
||||
|
||||
```bash
|
||||
brew install yarn@1.9.4
|
||||
yarn config set workspaces-experimental true
|
||||
```
|
||||
|
||||
Then install dependencies
|
||||
@@ -129,8 +91,6 @@ Then install dependencies
|
||||
yarn install
|
||||
```
|
||||
|
||||
You will also need to have Python 3 installed, in order to build and run the tests of `abi-gen`'s command-line interface, which is integrated with the yarn build, yarn test, and yarn lint commands described below. More specifically, your local pip should resolve to the Python 3 version of pip, not a Python 2.x version.
|
||||
|
||||
### Build
|
||||
|
||||
To build all packages:
|
||||
@@ -142,13 +102,7 @@ yarn build
|
||||
To build a specific package:
|
||||
|
||||
```bash
|
||||
PKG=@0x/web3-wrapper yarn build
|
||||
```
|
||||
|
||||
To build all contracts packages:
|
||||
|
||||
```bash
|
||||
yarn build:contracts
|
||||
PKG=@0xproject/web3-wrapper yarn build
|
||||
```
|
||||
|
||||
### Watch
|
||||
@@ -165,7 +119,7 @@ To watch a specific package and all it's dependent packages:
|
||||
PKG=[NPM_PACKAGE_NAME] yarn watch
|
||||
|
||||
e.g
|
||||
PKG=@0x/web3-wrapper yarn watch
|
||||
PKG=@0xproject/web3-wrapper yarn watch
|
||||
```
|
||||
|
||||
### Clean
|
||||
@@ -221,11 +175,5 @@ yarn test
|
||||
Run a specific package's test:
|
||||
|
||||
```bash
|
||||
PKG=@0x/web3-wrapper yarn test
|
||||
```
|
||||
|
||||
Run all contracts packages tests:
|
||||
|
||||
```bash
|
||||
yarn test:contracts
|
||||
PKG=@0xproject/web3-wrapper yarn test
|
||||
```
|
||||
|
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"extends": "default",
|
||||
"rules": {
|
||||
"avoid-low-level-calls": false,
|
||||
"avoid-tx-origin": "warn",
|
||||
"bracket-align": false,
|
||||
"code-complexity": false,
|
||||
"compiler-fixed": false,
|
||||
"const-name-snakecase": "error",
|
||||
"expression-indent": "error",
|
||||
"function-max-lines": false,
|
||||
"func-order": "error",
|
||||
"indent": ["error", 4],
|
||||
"max-line-length": ["warn", 160],
|
||||
"no-inline-assembly": false,
|
||||
"quotes": ["error", "double"],
|
||||
"separate-by-one-line-in-contract": "error",
|
||||
"space-after-comma": "error",
|
||||
"statement-indent": "error"
|
||||
}
|
||||
}
|
@@ -1,16 +0,0 @@
|
||||
#### Deployed Contract Packages
|
||||
|
||||
| Contract | Package | Version | Git Tag |
|
||||
| --------------- | ------------------------------------------------------------------- | -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| AssetProxyOwner | [`@0x/contracts-multisig`](/contracts/multisig) | [v1.0.2](https://www.npmjs.com/package/@0x/contracts-multisig/v/1.0.2) | [@0x/contracts-multisig@1.0.2](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-multisig@1.0.2) |
|
||||
| ERC20Proxy | [`@0x/contracts-asset-proxy`](/contracts/asset-proxy) | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-asset-proxy/v/1.0.1) | [@0x/contracts-asset-proxy@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-asset-proxy@1.0.1) |
|
||||
| ERC721Proxy | [`@0x/contracts-asset-proxy`](/contracts/asset-proxy) | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-asset-proxy/v/1.0.1) | [@0x/contracts-asset-proxy@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-asset-proxy@1.0.1) |
|
||||
| Exchange | [`@0x/contracts-exchange`](/contracts/exchange) | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-exchange/v/1.0.1) | [@0x/contracts-exchange@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-exchange@1.0.1) |
|
||||
| DutchAuction | [`@0x/contracts-extensions`](/contracts/extensions) | [v1.0.2](https://www.npmjs.com/package/@0x/contracts-extensions/v/1.0.2) | [@0x/contracts-extensions@1.0.2](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-extensions@1.0.2) |
|
||||
| Forwarder | [`@0x/contracts-exchange-forwarder`](/contracts/exchange-forwarder) | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-exchange-forwarder/v/1.0.1) | [@0x/contracts-exchange-forwarder@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-exchange-forwarder@1.0.1) |
|
||||
| MultiAssetProxy | [`@0x/contracts-asset-proxy`](/contracts/asset-proxy) | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-asset-proxy/v/1.0.1) | [@0x/contracts-asset-proxy@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-asset-proxy@1.0.1) |
|
||||
| ZRXToken | [`@0x/contracts-erc20`](/contracts/erc20) | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-erc20/v/1.0.1) | [@0x/contracts-erc20@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-erc20@1.0.1) |
|
||||
|
||||
#### Development
|
||||
|
||||
Building solidity files will update the contract artifact in `{package-name}/generated-artifacts/{contract}.json`, but does not automatically update the `contract-artifacts` or `contract-wrappers` packages, which are generated from the artifact JSON. See `contract-artifacts/README.md` for instructions on updating these packages.
|
@@ -1,48 +0,0 @@
|
||||
# Contracts testing options
|
||||
|
||||
## Revert stack traces
|
||||
|
||||
If you want to see helpful stack traces (incl. line number, code snippet) for smart contract reverts, run the tests with:
|
||||
|
||||
```
|
||||
yarn test:trace
|
||||
```
|
||||
|
||||
**Note:** This currently slows down the test runs and is therefore not enabled by default.
|
||||
|
||||
## Backing Ethereum node
|
||||
|
||||
By default, our tests run against an in-process [Ganache](https://github.com/trufflesuite/ganache-core) instance. In order to run the tests against [Geth](https://github.com/ethereum/go-ethereum), first follow the instructions in the README for the devnet package to start the devnet Geth node. Then run:
|
||||
|
||||
```bash
|
||||
TEST_PROVIDER=geth yarn test
|
||||
```
|
||||
|
||||
## Code coverage
|
||||
|
||||
In order to see the Solidity code coverage output generated by `@0x/sol-coverage`, run:
|
||||
|
||||
```
|
||||
yarn test:coverage
|
||||
```
|
||||
|
||||
## Gas profiler
|
||||
|
||||
In order to profile the gas costs for a specific smart contract call/transaction, you can run the tests in `profiler` mode.
|
||||
|
||||
**Note:** Traces emitted by ganache have incorrect gas costs so we recommend using Geth for profiling.
|
||||
|
||||
```
|
||||
TEST_PROVIDER=geth yarn test:profiler
|
||||
```
|
||||
|
||||
You'll see a warning that you need to explicitly enable and disable the profiler before and after the block of code you want to profile.
|
||||
|
||||
```typescript
|
||||
import { profiler } from './utils/profiler';
|
||||
profiler.start();
|
||||
// Some call to a smart contract
|
||||
profiler.stop();
|
||||
```
|
||||
|
||||
Without explicitly starting and stopping the profiler, the profiler output will be too busy, and therefore unusable.
|
@@ -1,10 +0,0 @@
|
||||
# Blacklist all files
|
||||
.*
|
||||
*
|
||||
# Whitelist lib
|
||||
!lib/**/*
|
||||
# Whitelist Solidity contracts
|
||||
!contracts/src/**/*
|
||||
# Blacklist tests in lib
|
||||
/lib/test/*
|
||||
# Package specific ignore
|
@@ -1,2 +0,0 @@
|
||||
# solhint can't parse `abi.decode` syntax.
|
||||
contracts/src/ERC1155Proxy.sol
|
@@ -1,350 +0,0 @@
|
||||
[
|
||||
{
|
||||
"version": "2.3.0-beta.4",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Implement `KyberBridge`.",
|
||||
"pr": 2352
|
||||
}
|
||||
],
|
||||
"timestamp": 1575290197
|
||||
},
|
||||
{
|
||||
"version": "2.3.0-beta.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
],
|
||||
"timestamp": 1574238768
|
||||
},
|
||||
{
|
||||
"version": "2.3.0-beta.2",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils",
|
||||
"pr": 2330
|
||||
}
|
||||
],
|
||||
"timestamp": 1574030254
|
||||
},
|
||||
{
|
||||
"version": "2.3.0-beta.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "ERC20Wrapper and ERC1155ProxyWrapper constructors now require an instance of DevUtilsContract",
|
||||
"pr": 2034
|
||||
}
|
||||
],
|
||||
"timestamp": 1573159180
|
||||
},
|
||||
{
|
||||
"version": "2.3.0-beta.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Disallow the zero address from being made an authorized address in MixinAuthorizable, and created an archive directory that includes an old version of Ownable",
|
||||
"pr": 2019
|
||||
},
|
||||
{
|
||||
"note": "Remove `LibAssetProxyIds` contract",
|
||||
"pr": 2055
|
||||
},
|
||||
{
|
||||
"note": "Compile and export all contracts, artifacts, and wrappers by default",
|
||||
"pr": 2055
|
||||
},
|
||||
{
|
||||
"note": "Remove unused dependency on IAuthorizable in IAssetProxy",
|
||||
"pr": 1910
|
||||
},
|
||||
{
|
||||
"note": "Add `ERC20BridgeProxy`",
|
||||
"pr": 2220
|
||||
},
|
||||
{
|
||||
"note": "Add `Eth2DaiBridge`",
|
||||
"pr": 2221
|
||||
},
|
||||
{
|
||||
"note": "Add `UniswapBridge`",
|
||||
"pr": 2233
|
||||
},
|
||||
{
|
||||
"note": "Replaced `SafeMath` with `LibSafeMath`",
|
||||
"pr": 2254
|
||||
}
|
||||
],
|
||||
"timestamp": 1570135330
|
||||
},
|
||||
{
|
||||
"timestamp": 1568744790,
|
||||
"version": "2.2.8",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1567521715,
|
||||
"version": "2.2.7",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1566446343,
|
||||
"version": "2.2.6",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1565296576,
|
||||
"version": "2.2.5",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "2.2.4",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Updated calls to <contract wrapper>.deployFrom0xArtifactAsync to include artifact dependencies.",
|
||||
"pr": 1995
|
||||
}
|
||||
],
|
||||
"timestamp": 1564607468
|
||||
},
|
||||
{
|
||||
"timestamp": 1563957393,
|
||||
"version": "2.2.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1563193019,
|
||||
"version": "2.2.2",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1563047529,
|
||||
"version": "2.2.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "2.2.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add `LibAssetProxyIds` contract",
|
||||
"pr": 1835
|
||||
},
|
||||
{
|
||||
"note": "Updated ERC1155 Asset Proxy. Less optimization. More explicit handling of edge cases.",
|
||||
"pr": 1852
|
||||
},
|
||||
{
|
||||
"note": "Implement StaticCallProxy",
|
||||
"pr": 1863
|
||||
}
|
||||
],
|
||||
"timestamp": 1563006338
|
||||
},
|
||||
{
|
||||
"timestamp": 1558712885,
|
||||
"version": "2.1.5",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1557961111,
|
||||
"version": "2.1.4",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1557799313,
|
||||
"version": "2.1.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "2.1.2",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Update tests to use contract-built-in `awaitTransactionSuccessAsync`",
|
||||
"pr": 1797
|
||||
},
|
||||
{
|
||||
"note": "Make `ERC721Wrapper.setApprovalForAll()` take an owner address instead of a token ID",
|
||||
"pr": 1819
|
||||
},
|
||||
{
|
||||
"note": "Automatically set unlimited proxy allowances in `ERC721.setBalancesAndAllowancesAsync()`",
|
||||
"pr": 1819
|
||||
},
|
||||
{
|
||||
"note": "Add `setProxyAllowanceForAllAsync()` to `ERC1155ProxyWrapper`.",
|
||||
"pr": 1819
|
||||
}
|
||||
],
|
||||
"timestamp": 1557507213
|
||||
},
|
||||
{
|
||||
"version": "2.1.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
],
|
||||
"timestamp": 1554997931
|
||||
},
|
||||
{
|
||||
"version": "2.1.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Run Web3ProviderEngine without excess block polling",
|
||||
"pr": 1695
|
||||
}
|
||||
],
|
||||
"timestamp": 1553183790
|
||||
},
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Do not reexport external dependencies",
|
||||
"pr": 1682
|
||||
},
|
||||
{
|
||||
"note": "Add ERC1155Proxy",
|
||||
"pr": 1661
|
||||
},
|
||||
{
|
||||
"note": "Bumped solidity version to ^0.5.5",
|
||||
"pr": 1701
|
||||
},
|
||||
{
|
||||
"note": "Integration testing for ERC1155Proxy",
|
||||
"pr": 1673
|
||||
}
|
||||
],
|
||||
"timestamp": 1553091633
|
||||
},
|
||||
{
|
||||
"timestamp": 1551479279,
|
||||
"version": "1.0.9",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1551299797,
|
||||
"version": "1.0.8",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1551220833,
|
||||
"version": "1.0.7",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1551130135,
|
||||
"version": "1.0.6",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1549733923,
|
||||
"version": "1.0.5",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "1.0.4",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
],
|
||||
"timestamp": 1549547375
|
||||
},
|
||||
{
|
||||
"version": "1.0.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Fake publish to enable pinning"
|
||||
}
|
||||
],
|
||||
"timestamp": 1549504360
|
||||
},
|
||||
{
|
||||
"timestamp": 1549452781,
|
||||
"version": "1.0.2",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1549373905,
|
||||
"version": "1.0.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Move all AssetProxy contracts out of contracts-protocol to new package",
|
||||
"pr": 1539
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
@@ -1,145 +0,0 @@
|
||||
<!--
|
||||
changelogUtils.file is auto-generated using the monorepo-scripts package. Don't edit directly.
|
||||
Edit the package's CHANGELOG.json file only.
|
||||
-->
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v2.3.0-beta.4 - _December 2, 2019_
|
||||
|
||||
* Implement `KyberBridge`. (#2352)
|
||||
|
||||
## v2.3.0-beta.3 - _November 20, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.3.0-beta.2 - _November 17, 2019_
|
||||
|
||||
* Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330)
|
||||
|
||||
## v2.3.0-beta.1 - _November 7, 2019_
|
||||
|
||||
* ERC20Wrapper and ERC1155ProxyWrapper constructors now require an instance of DevUtilsContract (#2034)
|
||||
|
||||
## v2.3.0-beta.0 - _October 3, 2019_
|
||||
|
||||
* Disallow the zero address from being made an authorized address in MixinAuthorizable, and created an archive directory that includes an old version of Ownable (#2019)
|
||||
* Remove `LibAssetProxyIds` contract (#2055)
|
||||
* Compile and export all contracts, artifacts, and wrappers by default (#2055)
|
||||
* Remove unused dependency on IAuthorizable in IAssetProxy (#1910)
|
||||
* Add `ERC20BridgeProxy` (#2220)
|
||||
* Add `Eth2DaiBridge` (#2221)
|
||||
* Add `UniswapBridge` (#2233)
|
||||
* Replaced `SafeMath` with `LibSafeMath` (#2254)
|
||||
|
||||
## v2.2.8 - _September 17, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.2.7 - _September 3, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.2.6 - _August 22, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.2.5 - _August 8, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.2.4 - _July 31, 2019_
|
||||
|
||||
* Updated calls to <contract wrapper>.deployFrom0xArtifactAsync to include artifact dependencies. (#1995)
|
||||
|
||||
## v2.2.3 - _July 24, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.2.2 - _July 15, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.2.1 - _July 13, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.2.0 - _July 13, 2019_
|
||||
|
||||
* Add `LibAssetProxyIds` contract (#1835)
|
||||
* Updated ERC1155 Asset Proxy. Less optimization. More explicit handling of edge cases. (#1852)
|
||||
* Implement StaticCallProxy (#1863)
|
||||
|
||||
## v2.1.5 - _May 24, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.1.4 - _May 15, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.1.3 - _May 14, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.1.2 - _May 10, 2019_
|
||||
|
||||
* Update tests to use contract-built-in `awaitTransactionSuccessAsync` (#1797)
|
||||
* Make `ERC721Wrapper.setApprovalForAll()` take an owner address instead of a token ID (#1819)
|
||||
* Automatically set unlimited proxy allowances in `ERC721.setBalancesAndAllowancesAsync()` (#1819)
|
||||
* Add `setProxyAllowanceForAllAsync()` to `ERC1155ProxyWrapper`. (#1819)
|
||||
|
||||
## v2.1.1 - _April 11, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.1.0 - _March 21, 2019_
|
||||
|
||||
* Run Web3ProviderEngine without excess block polling (#1695)
|
||||
|
||||
## v2.0.0 - _March 20, 2019_
|
||||
|
||||
* Do not reexport external dependencies (#1682)
|
||||
* Add ERC1155Proxy (#1661)
|
||||
* Bumped solidity version to ^0.5.5 (#1701)
|
||||
* Integration testing for ERC1155Proxy (#1673)
|
||||
|
||||
## v1.0.9 - _March 1, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.8 - _February 27, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.7 - _February 26, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.6 - _February 25, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.5 - _February 9, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.4 - _February 7, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.3 - _February 7, 2019_
|
||||
|
||||
* Fake publish to enable pinning
|
||||
|
||||
## v1.0.2 - _February 6, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.1 - _February 5, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.0 - _Invalid date_
|
||||
|
||||
* Move all AssetProxy contracts out of contracts-protocol to new package (#1539)
|
@@ -1,47 +0,0 @@
|
||||
[
|
||||
{
|
||||
"name": "MultiAssetProxy",
|
||||
"version": "1.0.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add MultiAssetProxy implementation",
|
||||
"pr": 1224,
|
||||
"networks": {
|
||||
"3": "0xab8fbd189c569ccdee3a4d929bb7f557be4028f6",
|
||||
"4": "0xb34cde0ad3a83d04abebc0b66e75196f22216621",
|
||||
"42": "0xf6313a772c222f51c28f2304c0703b8cf5428fd8"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "ERC20Proxy",
|
||||
"version": "1.0.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "protocol v2 deploy",
|
||||
"networks": {
|
||||
"1": "0x2240dab907db71e64d3e0dba4800c83b5c502d4e",
|
||||
"3": "0xb1408f4c245a23c31b98d2c626777d4c0d766caa",
|
||||
"4": "0x3e809c563c15a295e832e37053798ddc8d6c8dab",
|
||||
"42": "0xf1ec01d6236d3cd881a0bf0130ea25fe4234003e"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "ERC721Proxy",
|
||||
"version": "1.0.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "protocol v2 deploy",
|
||||
"networks": {
|
||||
"1": "0x208e41fb445f1bb1b6780d58356e81405f3e6127",
|
||||
"3": "0xe654aac058bfbf9f83fcaee7793311dd82f6ddb4",
|
||||
"4": "0x8e1ff02637cb5e39f2fa36c14706aa348b065b09",
|
||||
"42": "0x2a9127c745688a165106c11cd4d647d2220af821"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
@@ -1,73 +0,0 @@
|
||||
## AssetProxy
|
||||
|
||||
This package contains the implementations of all of the [`AssetProxy`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#assetproxy) contracts available within the 0x protocol. These contracts are responsible for decoding the `assetData` sent to them and performing the actual transfer of assets. Addresses of the deployed contracts can be found in this 0x [guide](https://0x.org/docs/guides/0x-cheat-sheet) or the [DEPLOYS](./DEPLOYS.json) file within this package.
|
||||
|
||||
## Installation
|
||||
|
||||
**Install**
|
||||
|
||||
```bash
|
||||
npm install @0x/contracts-asset-proxy --save
|
||||
```
|
||||
|
||||
## Bug bounty
|
||||
|
||||
A bug bounty for the 2.0.0 contracts is ongoing! Instructions can be found [here](https://0x.org/docs/guides/bug-bounty-program).
|
||||
|
||||
## Contributing
|
||||
|
||||
We strongly recommend that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository.
|
||||
|
||||
For proposals regarding the 0x protocol's smart contract architecture, message format, or additional functionality, go to the [0x Improvement Proposals (ZEIPs)](https://github.com/0xProject/ZEIPs) repository and follow the contribution guidelines provided therein.
|
||||
|
||||
Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started.
|
||||
|
||||
### Install Dependencies
|
||||
|
||||
If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them:
|
||||
|
||||
```bash
|
||||
yarn config set workspaces-experimental true
|
||||
```
|
||||
|
||||
Then install dependencies
|
||||
|
||||
```bash
|
||||
yarn install
|
||||
```
|
||||
|
||||
### Build
|
||||
|
||||
To build this package and all other monorepo packages that it depends on, run the following from the monorepo root directory:
|
||||
|
||||
```bash
|
||||
PKG=@0x/contracts-asset-proxy yarn build
|
||||
```
|
||||
|
||||
Or continuously rebuild on change:
|
||||
|
||||
```bash
|
||||
PKG=@0x/contracts-asset-proxy yarn watch
|
||||
```
|
||||
|
||||
### Clean
|
||||
|
||||
```bash
|
||||
yarn clean
|
||||
```
|
||||
|
||||
### Lint
|
||||
|
||||
```bash
|
||||
yarn lint
|
||||
```
|
||||
|
||||
### Run Tests
|
||||
|
||||
```bash
|
||||
yarn test
|
||||
```
|
||||
|
||||
#### Testing options
|
||||
|
||||
Contracts testing options like coverage, profiling, revert traces or backing node choosing - are described [here](../TESTING.md).
|
@@ -1,26 +0,0 @@
|
||||
{
|
||||
"artifactsDir": "./test/generated-artifacts",
|
||||
"contractsDir": "./contracts",
|
||||
"useDockerisedSolc": false,
|
||||
"isOfflineMode": false,
|
||||
"compilerSettings": {
|
||||
"evmVersion": "constantinople",
|
||||
"optimizer": {
|
||||
"enabled": true,
|
||||
"runs": 1000000,
|
||||
"details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
|
||||
},
|
||||
"outputSelection": {
|
||||
"*": {
|
||||
"*": [
|
||||
"abi",
|
||||
"devdoc",
|
||||
"evm.bytecode.object",
|
||||
"evm.bytecode.sourceMap",
|
||||
"evm.deployedBytecode.object",
|
||||
"evm.deployedBytecode.sourceMap"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,172 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
|
||||
import "../archive/Ownable.sol";
|
||||
import "../src/interfaces/IAssetProxy.sol";
|
||||
import "../src/interfaces/IAssetProxyDispatcher.sol";
|
||||
|
||||
|
||||
contract MixinAssetProxyDispatcher is
|
||||
Ownable,
|
||||
IAssetProxyDispatcher
|
||||
{
|
||||
// Mapping from Asset Proxy Id's to their respective Asset Proxy
|
||||
mapping (bytes4 => address) public assetProxies;
|
||||
|
||||
/// @dev Registers an asset proxy to its asset proxy id.
|
||||
/// Once an asset proxy is registered, it cannot be unregistered.
|
||||
/// @param assetProxy Address of new asset proxy to register.
|
||||
function registerAssetProxy(address assetProxy)
|
||||
external
|
||||
onlyOwner
|
||||
{
|
||||
// Ensure that no asset proxy exists with current id.
|
||||
bytes4 assetProxyId = IAssetProxy(assetProxy).getProxyId();
|
||||
address currentAssetProxy = assetProxies[assetProxyId];
|
||||
require(
|
||||
currentAssetProxy == address(0),
|
||||
"ASSET_PROXY_ALREADY_EXISTS"
|
||||
);
|
||||
|
||||
// Add asset proxy and log registration.
|
||||
assetProxies[assetProxyId] = assetProxy;
|
||||
emit AssetProxyRegistered(
|
||||
assetProxyId,
|
||||
assetProxy
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Gets an asset proxy.
|
||||
/// @param assetProxyId Id of the asset proxy.
|
||||
/// @return The asset proxy registered to assetProxyId. Returns 0x0 if no proxy is registered.
|
||||
function getAssetProxy(bytes4 assetProxyId)
|
||||
external
|
||||
view
|
||||
returns (address)
|
||||
{
|
||||
return assetProxies[assetProxyId];
|
||||
}
|
||||
|
||||
/// @dev Forwards arguments to assetProxy and calls `transferFrom`. Either succeeds or throws.
|
||||
/// @param assetData Byte array encoded for the asset.
|
||||
/// @param from Address to transfer token from.
|
||||
/// @param to Address to transfer token to.
|
||||
/// @param amount Amount of token to transfer.
|
||||
function _dispatchTransferFrom(
|
||||
bytes memory assetData,
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount
|
||||
)
|
||||
internal
|
||||
{
|
||||
// Do nothing if no amount should be transferred.
|
||||
if (amount > 0 && from != to) {
|
||||
// Ensure assetData length is valid
|
||||
require(
|
||||
assetData.length > 3,
|
||||
"LENGTH_GREATER_THAN_3_REQUIRED"
|
||||
);
|
||||
|
||||
// Lookup assetProxy. We do not use `LibBytes.readBytes4` for gas efficiency reasons.
|
||||
bytes4 assetProxyId;
|
||||
assembly {
|
||||
assetProxyId := and(mload(
|
||||
add(assetData, 32)),
|
||||
0xFFFFFFFF00000000000000000000000000000000000000000000000000000000
|
||||
)
|
||||
}
|
||||
address assetProxy = assetProxies[assetProxyId];
|
||||
|
||||
// Ensure that assetProxy exists
|
||||
require(
|
||||
assetProxy != address(0),
|
||||
"ASSET_PROXY_DOES_NOT_EXIST"
|
||||
);
|
||||
|
||||
// We construct calldata for the `assetProxy.transferFrom` ABI.
|
||||
// The layout of this calldata is in the table below.
|
||||
//
|
||||
// | Area | Offset | Length | Contents |
|
||||
// | -------- |--------|---------|-------------------------------------------- |
|
||||
// | Header | 0 | 4 | function selector |
|
||||
// | Params | | 4 * 32 | function parameters: |
|
||||
// | | 4 | | 1. offset to assetData (*) |
|
||||
// | | 36 | | 2. from |
|
||||
// | | 68 | | 3. to |
|
||||
// | | 100 | | 4. amount |
|
||||
// | Data | | | assetData: |
|
||||
// | | 132 | 32 | assetData Length |
|
||||
// | | 164 | ** | assetData Contents |
|
||||
|
||||
assembly {
|
||||
/////// Setup State ///////
|
||||
// `cdStart` is the start of the calldata for `assetProxy.transferFrom` (equal to free memory ptr).
|
||||
let cdStart := mload(64)
|
||||
// `dataAreaLength` is the total number of words needed to store `assetData`
|
||||
// As-per the ABI spec, this value is padded up to the nearest multiple of 32,
|
||||
// and includes 32-bytes for length.
|
||||
let dataAreaLength := and(add(mload(assetData), 63), 0xFFFFFFFFFFFE0)
|
||||
// `cdEnd` is the end of the calldata for `assetProxy.transferFrom`.
|
||||
let cdEnd := add(cdStart, add(132, dataAreaLength))
|
||||
|
||||
|
||||
/////// Setup Header Area ///////
|
||||
// This area holds the 4-byte `transferFromSelector`.
|
||||
// bytes4(keccak256("transferFrom(bytes,address,address,uint256)")) = 0xa85e59e4
|
||||
mstore(cdStart, 0xa85e59e400000000000000000000000000000000000000000000000000000000)
|
||||
|
||||
/////// Setup Params Area ///////
|
||||
// Each parameter is padded to 32-bytes. The entire Params Area is 128 bytes.
|
||||
// Notes:
|
||||
// 1. The offset to `assetData` is the length of the Params Area (128 bytes).
|
||||
// 2. A 20-byte mask is applied to addresses to zero-out the unused bytes.
|
||||
mstore(add(cdStart, 4), 128)
|
||||
mstore(add(cdStart, 36), and(from, 0xffffffffffffffffffffffffffffffffffffffff))
|
||||
mstore(add(cdStart, 68), and(to, 0xffffffffffffffffffffffffffffffffffffffff))
|
||||
mstore(add(cdStart, 100), amount)
|
||||
|
||||
/////// Setup Data Area ///////
|
||||
// This area holds `assetData`.
|
||||
let dataArea := add(cdStart, 132)
|
||||
// solhint-disable-next-line no-empty-blocks
|
||||
for {} lt(dataArea, cdEnd) {} {
|
||||
mstore(dataArea, mload(assetData))
|
||||
dataArea := add(dataArea, 32)
|
||||
assetData := add(assetData, 32)
|
||||
}
|
||||
|
||||
/////// Call `assetProxy.transferFrom` using the constructed calldata ///////
|
||||
let success := call(
|
||||
gas, // forward all gas
|
||||
assetProxy, // call address of asset proxy
|
||||
0, // don't send any ETH
|
||||
cdStart, // pointer to start of input
|
||||
sub(cdEnd, cdStart), // length of input
|
||||
cdStart, // write output over input
|
||||
512 // reserve 512 bytes for output
|
||||
)
|
||||
if iszero(success) {
|
||||
revert(cdStart, returndatasize())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,117 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
|
||||
import "../archive/Ownable.sol";
|
||||
import "../src/interfaces/IAuthorizable.sol";
|
||||
|
||||
|
||||
contract MixinAuthorizable is
|
||||
Ownable,
|
||||
IAuthorizable
|
||||
{
|
||||
/// @dev Only authorized addresses can invoke functions with this modifier.
|
||||
modifier onlyAuthorized {
|
||||
require(
|
||||
authorized[msg.sender],
|
||||
"SENDER_NOT_AUTHORIZED"
|
||||
);
|
||||
_;
|
||||
}
|
||||
|
||||
mapping (address => bool) public authorized;
|
||||
address[] public authorities;
|
||||
|
||||
/// @dev Authorizes an address.
|
||||
/// @param target Address to authorize.
|
||||
function addAuthorizedAddress(address target)
|
||||
external
|
||||
onlyOwner
|
||||
{
|
||||
require(
|
||||
!authorized[target],
|
||||
"TARGET_ALREADY_AUTHORIZED"
|
||||
);
|
||||
|
||||
authorized[target] = true;
|
||||
authorities.push(target);
|
||||
emit AuthorizedAddressAdded(target, msg.sender);
|
||||
}
|
||||
|
||||
/// @dev Removes authorizion of an address.
|
||||
/// @param target Address to remove authorization from.
|
||||
function removeAuthorizedAddress(address target)
|
||||
external
|
||||
onlyOwner
|
||||
{
|
||||
require(
|
||||
authorized[target],
|
||||
"TARGET_NOT_AUTHORIZED"
|
||||
);
|
||||
|
||||
delete authorized[target];
|
||||
for (uint256 i = 0; i < authorities.length; i++) {
|
||||
if (authorities[i] == target) {
|
||||
authorities[i] = authorities[authorities.length - 1];
|
||||
authorities.length -= 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
emit AuthorizedAddressRemoved(target, msg.sender);
|
||||
}
|
||||
|
||||
/// @dev Removes authorizion of an address.
|
||||
/// @param target Address to remove authorization from.
|
||||
/// @param index Index of target in authorities array.
|
||||
function removeAuthorizedAddressAtIndex(
|
||||
address target,
|
||||
uint256 index
|
||||
)
|
||||
external
|
||||
onlyOwner
|
||||
{
|
||||
require(
|
||||
authorized[target],
|
||||
"TARGET_NOT_AUTHORIZED"
|
||||
);
|
||||
require(
|
||||
index < authorities.length,
|
||||
"INDEX_OUT_OF_BOUNDS"
|
||||
);
|
||||
require(
|
||||
authorities[index] == target,
|
||||
"AUTHORIZED_ADDRESS_MISMATCH"
|
||||
);
|
||||
|
||||
delete authorized[target];
|
||||
authorities[index] = authorities[authorities.length - 1];
|
||||
authorities.length -= 1;
|
||||
emit AuthorizedAddressRemoved(target, msg.sender);
|
||||
}
|
||||
|
||||
/// @dev Gets all authorized addresses.
|
||||
/// @return Array of authorized addresses.
|
||||
function getAuthorizedAddresses()
|
||||
external
|
||||
view
|
||||
returns (address[] memory)
|
||||
{
|
||||
return authorities;
|
||||
}
|
||||
}
|
@@ -1,33 +0,0 @@
|
||||
pragma solidity ^0.5.9;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/interfaces/IOwnable.sol";
|
||||
|
||||
|
||||
contract Ownable is
|
||||
IOwnable
|
||||
{
|
||||
address public owner;
|
||||
|
||||
constructor ()
|
||||
public
|
||||
{
|
||||
owner = msg.sender;
|
||||
}
|
||||
|
||||
modifier onlyOwner() {
|
||||
require(
|
||||
msg.sender == owner,
|
||||
"ONLY_CONTRACT_OWNER"
|
||||
);
|
||||
_;
|
||||
}
|
||||
|
||||
function transferOwnership(address newOwner)
|
||||
public
|
||||
onlyOwner
|
||||
{
|
||||
if (newOwner != address(0)) {
|
||||
owner = newOwner;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
||||
import "@0x/contracts-erc1155/contracts/src/interfaces/IERC1155.sol";
|
||||
import "../archive/MixinAuthorizable.sol";
|
||||
import "./interfaces/IAssetProxy.sol";
|
||||
|
||||
|
||||
contract ERC1155Proxy is
|
||||
MixinAuthorizable,
|
||||
IAssetProxy
|
||||
{
|
||||
using LibBytes for bytes;
|
||||
using LibSafeMath for uint256;
|
||||
|
||||
// Id of this proxy.
|
||||
bytes4 constant internal PROXY_ID = bytes4(keccak256("ERC1155Assets(address,uint256[],uint256[],bytes)"));
|
||||
|
||||
/// @dev Transfers batch of ERC1155 assets. Either succeeds or throws.
|
||||
/// @param assetData Byte array encoded with ERC1155 token address, array of ids, array of values, and callback data.
|
||||
/// @param from Address to transfer assets from.
|
||||
/// @param to Address to transfer assets to.
|
||||
/// @param amount Amount that will be multiplied with each element of `assetData.values` to scale the
|
||||
/// values that will be transferred.
|
||||
function transferFrom(
|
||||
bytes calldata assetData,
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount
|
||||
)
|
||||
external
|
||||
onlyAuthorized
|
||||
{
|
||||
// Decode params from `assetData`
|
||||
// solhint-disable indent
|
||||
(
|
||||
address erc1155TokenAddress,
|
||||
uint256[] memory ids,
|
||||
uint256[] memory values,
|
||||
bytes memory data
|
||||
) = abi.decode(
|
||||
assetData.sliceDestructive(4, assetData.length),
|
||||
(address, uint256[], uint256[], bytes)
|
||||
);
|
||||
// solhint-enable indent
|
||||
|
||||
// Scale values up by `amount`
|
||||
uint256 length = values.length;
|
||||
uint256[] memory scaledValues = new uint256[](length);
|
||||
for (uint256 i = 0; i != length; i++) {
|
||||
// We write the scaled values to an unused location in memory in order
|
||||
// to avoid copying over `ids` or `data`. This is possible if they are
|
||||
// identical to `values` and the offsets for each are pointing to the
|
||||
// same location in the ABI encoded calldata.
|
||||
scaledValues[i] = values[i].safeMul(amount);
|
||||
}
|
||||
|
||||
// Execute `safeBatchTransferFrom` call
|
||||
// Either succeeds or throws
|
||||
IERC1155(erc1155TokenAddress).safeBatchTransferFrom(
|
||||
from,
|
||||
to,
|
||||
ids,
|
||||
scaledValues,
|
||||
data
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Gets the proxy id associated with the proxy address.
|
||||
/// @return Proxy id.
|
||||
function getProxyId()
|
||||
external
|
||||
pure
|
||||
returns (bytes4)
|
||||
{
|
||||
return PROXY_ID;
|
||||
}
|
||||
}
|
@@ -1,126 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
||||
import "@0x/contracts-utils/contracts/src/Authorizable.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||
import "./interfaces/IAssetProxy.sol";
|
||||
import "./interfaces/IERC20Bridge.sol";
|
||||
|
||||
|
||||
contract ERC20BridgeProxy is
|
||||
IAssetProxy,
|
||||
Authorizable
|
||||
{
|
||||
using LibBytes for bytes;
|
||||
using LibSafeMath for uint256;
|
||||
|
||||
// @dev Id of this proxy. Also the result of a successful bridge call.
|
||||
// bytes4(keccak256("ERC20Bridge(address,address,bytes)"))
|
||||
bytes4 constant private PROXY_ID = 0xdc1600f3;
|
||||
|
||||
/// @dev Calls a bridge contract to transfer `amount` of ERC20 from `from`
|
||||
/// to `to`. Asserts that the balance of `to` has increased by `amount`.
|
||||
/// @param assetData Abi-encoded data for this asset proxy encoded as:
|
||||
/// abi.encodeWithSelector(
|
||||
/// bytes4 PROXY_ID,
|
||||
/// address tokenAddress,
|
||||
/// address bridgeAddress,
|
||||
/// bytes bridgeData
|
||||
/// )
|
||||
/// @param from Address to transfer asset from.
|
||||
/// @param to Address to transfer asset to.
|
||||
/// @param amount Amount of asset to transfer.
|
||||
function transferFrom(
|
||||
bytes calldata assetData,
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount
|
||||
)
|
||||
external
|
||||
onlyAuthorized
|
||||
{
|
||||
// Extract asset data fields.
|
||||
(
|
||||
address tokenAddress,
|
||||
address bridgeAddress,
|
||||
bytes memory bridgeData
|
||||
) = abi.decode(
|
||||
assetData.sliceDestructive(4, assetData.length),
|
||||
(address, address, bytes)
|
||||
);
|
||||
|
||||
// Remember the balance of `to` before calling the bridge.
|
||||
uint256 balanceBefore = balanceOf(tokenAddress, to);
|
||||
// Call the bridge, who should transfer `amount` of `tokenAddress` to
|
||||
// `to`.
|
||||
bytes4 success = IERC20Bridge(bridgeAddress).bridgeTransferFrom(
|
||||
tokenAddress,
|
||||
from,
|
||||
to,
|
||||
amount,
|
||||
bridgeData
|
||||
);
|
||||
// Bridge must return the proxy ID to indicate success.
|
||||
require(success == PROXY_ID, "BRIDGE_FAILED");
|
||||
// Ensure that the balance of `to` has increased by at least `amount`.
|
||||
require(
|
||||
balanceBefore.safeAdd(amount) <= balanceOf(tokenAddress, to),
|
||||
"BRIDGE_UNDERPAY"
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Gets the proxy id associated with this asset proxy.
|
||||
/// @return proxyId The proxy id.
|
||||
function getProxyId()
|
||||
external
|
||||
pure
|
||||
returns (bytes4 proxyId)
|
||||
{
|
||||
return PROXY_ID;
|
||||
}
|
||||
|
||||
/// @dev Retrieves the balance of `owner` for this asset.
|
||||
/// @return balance The balance of the ERC20 token being transferred by this
|
||||
/// asset proxy.
|
||||
function balanceOf(bytes calldata assetData, address owner)
|
||||
external
|
||||
view
|
||||
returns (uint256 balance)
|
||||
{
|
||||
(address tokenAddress) = abi.decode(
|
||||
assetData.sliceDestructive(4, assetData.length),
|
||||
(address)
|
||||
);
|
||||
return balanceOf(tokenAddress, owner);
|
||||
}
|
||||
|
||||
/// @dev Retrieves the balance of `owner` given an ERC20 address.
|
||||
/// @return balance The balance of the ERC20 token for `owner`.
|
||||
function balanceOf(address tokenAddress, address owner)
|
||||
private
|
||||
view
|
||||
returns (uint256 balance)
|
||||
{
|
||||
return IERC20Token(tokenAddress).balanceOf(owner);
|
||||
}
|
||||
}
|
@@ -1,184 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
|
||||
import "../archive/MixinAuthorizable.sol";
|
||||
|
||||
|
||||
contract ERC20Proxy is
|
||||
MixinAuthorizable
|
||||
{
|
||||
// Id of this proxy.
|
||||
bytes4 constant internal PROXY_ID = bytes4(keccak256("ERC20Token(address)"));
|
||||
|
||||
// solhint-disable-next-line payable-fallback
|
||||
function ()
|
||||
external
|
||||
{
|
||||
assembly {
|
||||
// The first 4 bytes of calldata holds the function selector
|
||||
let selector := and(calldataload(0), 0xffffffff00000000000000000000000000000000000000000000000000000000)
|
||||
|
||||
// `transferFrom` will be called with the following parameters:
|
||||
// assetData Encoded byte array.
|
||||
// from Address to transfer asset from.
|
||||
// to Address to transfer asset to.
|
||||
// amount Amount of asset to transfer.
|
||||
// bytes4(keccak256("transferFrom(bytes,address,address,uint256)")) = 0xa85e59e4
|
||||
if eq(selector, 0xa85e59e400000000000000000000000000000000000000000000000000000000) {
|
||||
|
||||
// To lookup a value in a mapping, we load from the storage location keccak256(k, p),
|
||||
// where k is the key left padded to 32 bytes and p is the storage slot
|
||||
let start := mload(64)
|
||||
mstore(start, and(caller, 0xffffffffffffffffffffffffffffffffffffffff))
|
||||
mstore(add(start, 32), authorized_slot)
|
||||
|
||||
// Revert if authorized[msg.sender] == false
|
||||
if iszero(sload(keccak256(start, 64))) {
|
||||
// Revert with `Error("SENDER_NOT_AUTHORIZED")`
|
||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(64, 0x0000001553454e4445525f4e4f545f415554484f52495a454400000000000000)
|
||||
mstore(96, 0)
|
||||
revert(0, 100)
|
||||
}
|
||||
|
||||
// `transferFrom`.
|
||||
// The function is marked `external`, so no abi decodeding is done for
|
||||
// us. Instead, we expect the `calldata` memory to contain the
|
||||
// following:
|
||||
//
|
||||
// | Area | Offset | Length | Contents |
|
||||
// |----------|--------|---------|-------------------------------------|
|
||||
// | Header | 0 | 4 | function selector |
|
||||
// | Params | | 4 * 32 | function parameters: |
|
||||
// | | 4 | | 1. offset to assetData (*) |
|
||||
// | | 36 | | 2. from |
|
||||
// | | 68 | | 3. to |
|
||||
// | | 100 | | 4. amount |
|
||||
// | Data | | | assetData: |
|
||||
// | | 132 | 32 | assetData Length |
|
||||
// | | 164 | ** | assetData Contents |
|
||||
//
|
||||
// (*): offset is computed from start of function parameters, so offset
|
||||
// by an additional 4 bytes in the calldata.
|
||||
//
|
||||
// (**): see table below to compute length of assetData Contents
|
||||
//
|
||||
// WARNING: The ABIv2 specification allows additional padding between
|
||||
// the Params and Data section. This will result in a larger
|
||||
// offset to assetData.
|
||||
|
||||
// Asset data itself is encoded as follows:
|
||||
//
|
||||
// | Area | Offset | Length | Contents |
|
||||
// |----------|--------|---------|-------------------------------------|
|
||||
// | Header | 0 | 4 | function selector |
|
||||
// | Params | | 1 * 32 | function parameters: |
|
||||
// | | 4 | 12 + 20 | 1. token address |
|
||||
|
||||
// We construct calldata for the `token.transferFrom` ABI.
|
||||
// The layout of this calldata is in the table below.
|
||||
//
|
||||
// | Area | Offset | Length | Contents |
|
||||
// |----------|--------|---------|-------------------------------------|
|
||||
// | Header | 0 | 4 | function selector |
|
||||
// | Params | | 3 * 32 | function parameters: |
|
||||
// | | 4 | | 1. from |
|
||||
// | | 36 | | 2. to |
|
||||
// | | 68 | | 3. amount |
|
||||
|
||||
/////// Read token address from calldata ///////
|
||||
// * The token address is stored in `assetData`.
|
||||
//
|
||||
// * The "offset to assetData" is stored at offset 4 in the calldata (table 1).
|
||||
// [assetDataOffsetFromParams = calldataload(4)]
|
||||
//
|
||||
// * Notes that the "offset to assetData" is relative to the "Params" area of calldata;
|
||||
// add 4 bytes to account for the length of the "Header" area (table 1).
|
||||
// [assetDataOffsetFromHeader = assetDataOffsetFromParams + 4]
|
||||
//
|
||||
// * The "token address" is offset 32+4=36 bytes into "assetData" (tables 1 & 2).
|
||||
// [tokenOffset = assetDataOffsetFromHeader + 36 = calldataload(4) + 4 + 36]
|
||||
let token := calldataload(add(calldataload(4), 40))
|
||||
|
||||
/////// Setup Header Area ///////
|
||||
// This area holds the 4-byte `transferFrom` selector.
|
||||
// Any trailing data in transferFromSelector will be
|
||||
// overwritten in the next `mstore` call.
|
||||
mstore(0, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
|
||||
|
||||
/////// Setup Params Area ///////
|
||||
// We copy the fields `from`, `to` and `amount` in bulk
|
||||
// from our own calldata to the new calldata.
|
||||
calldatacopy(4, 36, 96)
|
||||
|
||||
/////// Call `token.transferFrom` using the calldata ///////
|
||||
let success := call(
|
||||
gas, // forward all gas
|
||||
token, // call address of token contract
|
||||
0, // don't send any ETH
|
||||
0, // pointer to start of input
|
||||
100, // length of input
|
||||
0, // write output over input
|
||||
32 // output size should be 32 bytes
|
||||
)
|
||||
|
||||
/////// Check return data. ///////
|
||||
// If there is no return data, we assume the token incorrectly
|
||||
// does not return a bool. In this case we expect it to revert
|
||||
// on failure, which was handled above.
|
||||
// If the token does return data, we require that it is a single
|
||||
// nonzero 32 bytes value.
|
||||
// So the transfer succeeded if the call succeeded and either
|
||||
// returned nothing, or returned a non-zero 32 byte value.
|
||||
success := and(success, or(
|
||||
iszero(returndatasize),
|
||||
and(
|
||||
eq(returndatasize, 32),
|
||||
gt(mload(0), 0)
|
||||
)
|
||||
))
|
||||
if success {
|
||||
return(0, 0)
|
||||
}
|
||||
|
||||
// Revert with `Error("TRANSFER_FAILED")`
|
||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(64, 0x0000000f5452414e534645525f4641494c454400000000000000000000000000)
|
||||
mstore(96, 0)
|
||||
revert(0, 100)
|
||||
}
|
||||
|
||||
// Revert if undefined function is called
|
||||
revert(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Gets the proxy id associated with the proxy address.
|
||||
/// @return Proxy id.
|
||||
function getProxyId()
|
||||
external
|
||||
pure
|
||||
returns (bytes4)
|
||||
{
|
||||
return PROXY_ID;
|
||||
}
|
||||
}
|
@@ -1,171 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
|
||||
import "../archive/MixinAuthorizable.sol";
|
||||
|
||||
|
||||
contract ERC721Proxy is
|
||||
MixinAuthorizable
|
||||
{
|
||||
// Id of this proxy.
|
||||
bytes4 constant internal PROXY_ID = bytes4(keccak256("ERC721Token(address,uint256)"));
|
||||
|
||||
// solhint-disable-next-line payable-fallback
|
||||
function ()
|
||||
external
|
||||
{
|
||||
assembly {
|
||||
// The first 4 bytes of calldata holds the function selector
|
||||
let selector := and(calldataload(0), 0xffffffff00000000000000000000000000000000000000000000000000000000)
|
||||
|
||||
// `transferFrom` will be called with the following parameters:
|
||||
// assetData Encoded byte array.
|
||||
// from Address to transfer asset from.
|
||||
// to Address to transfer asset to.
|
||||
// amount Amount of asset to transfer.
|
||||
// bytes4(keccak256("transferFrom(bytes,address,address,uint256)")) = 0xa85e59e4
|
||||
if eq(selector, 0xa85e59e400000000000000000000000000000000000000000000000000000000) {
|
||||
|
||||
// To lookup a value in a mapping, we load from the storage location keccak256(k, p),
|
||||
// where k is the key left padded to 32 bytes and p is the storage slot
|
||||
let start := mload(64)
|
||||
mstore(start, and(caller, 0xffffffffffffffffffffffffffffffffffffffff))
|
||||
mstore(add(start, 32), authorized_slot)
|
||||
|
||||
// Revert if authorized[msg.sender] == false
|
||||
if iszero(sload(keccak256(start, 64))) {
|
||||
// Revert with `Error("SENDER_NOT_AUTHORIZED")`
|
||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(64, 0x0000001553454e4445525f4e4f545f415554484f52495a454400000000000000)
|
||||
mstore(96, 0)
|
||||
revert(0, 100)
|
||||
}
|
||||
|
||||
// `transferFrom`.
|
||||
// The function is marked `external`, so no abi decodeding is done for
|
||||
// us. Instead, we expect the `calldata` memory to contain the
|
||||
// following:
|
||||
//
|
||||
// | Area | Offset | Length | Contents |
|
||||
// |----------|--------|---------|-------------------------------------|
|
||||
// | Header | 0 | 4 | function selector |
|
||||
// | Params | | 4 * 32 | function parameters: |
|
||||
// | | 4 | | 1. offset to assetData (*) |
|
||||
// | | 36 | | 2. from |
|
||||
// | | 68 | | 3. to |
|
||||
// | | 100 | | 4. amount |
|
||||
// | Data | | | assetData: |
|
||||
// | | 132 | 32 | assetData Length |
|
||||
// | | 164 | ** | assetData Contents |
|
||||
//
|
||||
// (*): offset is computed from start of function parameters, so offset
|
||||
// by an additional 4 bytes in the calldata.
|
||||
//
|
||||
// (**): see table below to compute length of assetData Contents
|
||||
//
|
||||
// WARNING: The ABIv2 specification allows additional padding between
|
||||
// the Params and Data section. This will result in a larger
|
||||
// offset to assetData.
|
||||
|
||||
// Asset data itself is encoded as follows:
|
||||
//
|
||||
// | Area | Offset | Length | Contents |
|
||||
// |----------|--------|---------|-------------------------------------|
|
||||
// | Header | 0 | 4 | function selector |
|
||||
// | Params | | 2 * 32 | function parameters: |
|
||||
// | | 4 | 12 + 20 | 1. token address |
|
||||
// | | 36 | | 2. tokenId |
|
||||
|
||||
// We construct calldata for the `token.transferFrom` ABI.
|
||||
// The layout of this calldata is in the table below.
|
||||
//
|
||||
// | Area | Offset | Length | Contents |
|
||||
// |----------|--------|---------|-------------------------------------|
|
||||
// | Header | 0 | 4 | function selector |
|
||||
// | Params | | 3 * 32 | function parameters: |
|
||||
// | | 4 | | 1. from |
|
||||
// | | 36 | | 2. to |
|
||||
// | | 68 | | 3. tokenId |
|
||||
|
||||
// There exists only 1 of each token.
|
||||
// require(amount == 1, "INVALID_AMOUNT")
|
||||
if sub(calldataload(100), 1) {
|
||||
// Revert with `Error("INVALID_AMOUNT")`
|
||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(64, 0x0000000e494e56414c49445f414d4f554e540000000000000000000000000000)
|
||||
mstore(96, 0)
|
||||
revert(0, 100)
|
||||
}
|
||||
|
||||
/////// Setup Header Area ///////
|
||||
// This area holds the 4-byte `transferFrom` selector.
|
||||
// Any trailing data in transferFromSelector will be
|
||||
// overwritten in the next `mstore` call.
|
||||
mstore(0, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
|
||||
|
||||
/////// Setup Params Area ///////
|
||||
// We copy the fields `from` and `to` in bulk
|
||||
// from our own calldata to the new calldata.
|
||||
calldatacopy(4, 36, 64)
|
||||
|
||||
// Copy `tokenId` field from our own calldata to the new calldata.
|
||||
let assetDataOffset := calldataload(4)
|
||||
calldatacopy(68, add(assetDataOffset, 72), 32)
|
||||
|
||||
/////// Call `token.transferFrom` using the calldata ///////
|
||||
let token := calldataload(add(assetDataOffset, 40))
|
||||
let success := call(
|
||||
gas, // forward all gas
|
||||
token, // call address of token contract
|
||||
0, // don't send any ETH
|
||||
0, // pointer to start of input
|
||||
100, // length of input
|
||||
0, // write output to null
|
||||
0 // output size is 0 bytes
|
||||
)
|
||||
if success {
|
||||
return(0, 0)
|
||||
}
|
||||
|
||||
// Revert with `Error("TRANSFER_FAILED")`
|
||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(64, 0x0000000f5452414e534645525f4641494c454400000000000000000000000000)
|
||||
mstore(96, 0)
|
||||
revert(0, 100)
|
||||
}
|
||||
|
||||
// Revert if undefined function is called
|
||||
revert(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Gets the proxy id associated with the proxy address.
|
||||
/// @return Proxy id.
|
||||
function getProxyId()
|
||||
external
|
||||
pure
|
||||
returns (bytes4)
|
||||
{
|
||||
return PROXY_ID;
|
||||
}
|
||||
}
|
@@ -1,335 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
|
||||
import "../archive/MixinAssetProxyDispatcher.sol";
|
||||
import "../archive/MixinAuthorizable.sol";
|
||||
|
||||
|
||||
contract MultiAssetProxy is
|
||||
MixinAssetProxyDispatcher,
|
||||
MixinAuthorizable
|
||||
{
|
||||
// Id of this proxy.
|
||||
bytes4 constant internal PROXY_ID = bytes4(keccak256("MultiAsset(uint256[],bytes[])"));
|
||||
|
||||
// solhint-disable-next-line payable-fallback
|
||||
function ()
|
||||
external
|
||||
{
|
||||
// NOTE: The below assembly assumes that clients do some input validation and that the input is properly encoded according to the AbiV2 specification.
|
||||
// It is technically possible for inputs with very large lengths and offsets to cause overflows. However, this would make the calldata prohibitively
|
||||
// expensive and we therefore do not check for overflows in these scenarios.
|
||||
assembly {
|
||||
// The first 4 bytes of calldata holds the function selector
|
||||
let selector := and(calldataload(0), 0xffffffff00000000000000000000000000000000000000000000000000000000)
|
||||
|
||||
// `transferFrom` will be called with the following parameters:
|
||||
// assetData Encoded byte array.
|
||||
// from Address to transfer asset from.
|
||||
// to Address to transfer asset to.
|
||||
// amount Amount of asset to transfer.
|
||||
// bytes4(keccak256("transferFrom(bytes,address,address,uint256)")) = 0xa85e59e4
|
||||
if eq(selector, 0xa85e59e400000000000000000000000000000000000000000000000000000000) {
|
||||
|
||||
// To lookup a value in a mapping, we load from the storage location keccak256(k, p),
|
||||
// where k is the key left padded to 32 bytes and p is the storage slot
|
||||
mstore(0, caller)
|
||||
mstore(32, authorized_slot)
|
||||
|
||||
// Revert if authorized[msg.sender] == false
|
||||
if iszero(sload(keccak256(0, 64))) {
|
||||
// Revert with `Error("SENDER_NOT_AUTHORIZED")`
|
||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(64, 0x0000001553454e4445525f4e4f545f415554484f52495a454400000000000000)
|
||||
mstore(96, 0)
|
||||
revert(0, 100)
|
||||
}
|
||||
|
||||
// `transferFrom`.
|
||||
// The function is marked `external`, so no abi decoding is done for
|
||||
// us. Instead, we expect the `calldata` memory to contain the
|
||||
// following:
|
||||
//
|
||||
// | Area | Offset | Length | Contents |
|
||||
// |----------|--------|---------|-------------------------------------|
|
||||
// | Header | 0 | 4 | function selector |
|
||||
// | Params | | 4 * 32 | function parameters: |
|
||||
// | | 4 | | 1. offset to assetData (*) |
|
||||
// | | 36 | | 2. from |
|
||||
// | | 68 | | 3. to |
|
||||
// | | 100 | | 4. amount |
|
||||
// | Data | | | assetData: |
|
||||
// | | 132 | 32 | assetData Length |
|
||||
// | | 164 | ** | assetData Contents |
|
||||
//
|
||||
// (*): offset is computed from start of function parameters, so offset
|
||||
// by an additional 4 bytes in the calldata.
|
||||
//
|
||||
// (**): see table below to compute length of assetData Contents
|
||||
//
|
||||
// WARNING: The ABIv2 specification allows additional padding between
|
||||
// the Params and Data section. This will result in a larger
|
||||
// offset to assetData.
|
||||
|
||||
// Load offset to `assetData`
|
||||
let assetDataOffset := add(calldataload(4), 4)
|
||||
|
||||
// Load length in bytes of `assetData`
|
||||
let assetDataLength := calldataload(assetDataOffset)
|
||||
|
||||
// Asset data itself is encoded as follows:
|
||||
//
|
||||
// | Area | Offset | Length | Contents |
|
||||
// |----------|-------------|---------|-------------------------------------|
|
||||
// | Header | 0 | 4 | assetProxyId |
|
||||
// | Params | | 2 * 32 | function parameters: |
|
||||
// | | 4 | | 1. offset to amounts (*) |
|
||||
// | | 36 | | 2. offset to nestedAssetData (*) |
|
||||
// | Data | | | amounts: |
|
||||
// | | 68 | 32 | amounts Length |
|
||||
// | | 100 | a | amounts Contents |
|
||||
// | | | | nestedAssetData: |
|
||||
// | | 100 + a | 32 | nestedAssetData Length |
|
||||
// | | 132 + a | b | nestedAssetData Contents (offsets) |
|
||||
// | | 132 + a + b | | nestedAssetData[0, ..., len] |
|
||||
|
||||
// Assert that the length of asset data:
|
||||
// 1. Must be at least 68 bytes (see table above)
|
||||
// 2. Must be a multiple of 32 (excluding the 4-byte selector)
|
||||
if or(lt(assetDataLength, 68), mod(sub(assetDataLength, 4), 32)) {
|
||||
// Revert with `Error("INVALID_ASSET_DATA_LENGTH")`
|
||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(64, 0x00000019494e56414c49445f41535345545f444154415f4c454e475448000000)
|
||||
mstore(96, 0)
|
||||
revert(0, 100)
|
||||
}
|
||||
|
||||
// End of asset data in calldata
|
||||
// assetDataOffset
|
||||
// + 32 (assetData len)
|
||||
let assetDataEnd := add(assetDataOffset, add(assetDataLength, 32))
|
||||
if gt(assetDataEnd, calldatasize()) {
|
||||
// Revert with `Error("INVALID_ASSET_DATA_END")`
|
||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(64, 0x00000016494e56414c49445f41535345545f444154415f454e44000000000000)
|
||||
mstore(96, 0)
|
||||
revert(0, 100)
|
||||
}
|
||||
|
||||
// In order to find the offset to `amounts`, we must add:
|
||||
// assetDataOffset
|
||||
// + 32 (assetData len)
|
||||
// + 4 (assetProxyId)
|
||||
let amountsOffset := calldataload(add(assetDataOffset, 36))
|
||||
|
||||
// In order to find the offset to `nestedAssetData`, we must add:
|
||||
// assetDataOffset
|
||||
// + 32 (assetData len)
|
||||
// + 4 (assetProxyId)
|
||||
// + 32 (amounts offset)
|
||||
let nestedAssetDataOffset := calldataload(add(assetDataOffset, 68))
|
||||
|
||||
// In order to find the start of the `amounts` contents, we must add:
|
||||
// assetDataOffset
|
||||
// + 32 (assetData len)
|
||||
// + 4 (assetProxyId)
|
||||
// + amountsOffset
|
||||
// + 32 (amounts len)
|
||||
let amountsContentsStart := add(assetDataOffset, add(amountsOffset, 68))
|
||||
|
||||
// Load number of elements in `amounts`
|
||||
let amountsLen := calldataload(sub(amountsContentsStart, 32))
|
||||
|
||||
// In order to find the start of the `nestedAssetData` contents, we must add:
|
||||
// assetDataOffset
|
||||
// + 32 (assetData len)
|
||||
// + 4 (assetProxyId)
|
||||
// + nestedAssetDataOffset
|
||||
// + 32 (nestedAssetData len)
|
||||
let nestedAssetDataContentsStart := add(assetDataOffset, add(nestedAssetDataOffset, 68))
|
||||
|
||||
// Load number of elements in `nestedAssetData`
|
||||
let nestedAssetDataLen := calldataload(sub(nestedAssetDataContentsStart, 32))
|
||||
|
||||
// Revert if number of elements in `amounts` differs from number of elements in `nestedAssetData`
|
||||
if sub(amountsLen, nestedAssetDataLen) {
|
||||
// Revert with `Error("LENGTH_MISMATCH")`
|
||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(64, 0x0000000f4c454e4754485f4d49534d4154434800000000000000000000000000)
|
||||
mstore(96, 0)
|
||||
revert(0, 100)
|
||||
}
|
||||
|
||||
// Copy `transferFrom` selector, offset to `assetData`, `from`, and `to` from calldata to memory
|
||||
calldatacopy(
|
||||
0, // memory can safely be overwritten from beginning
|
||||
0, // start of calldata
|
||||
100 // length of selector (4) and 3 params (32 * 3)
|
||||
)
|
||||
|
||||
// Overwrite existing offset to `assetData` with our own
|
||||
mstore(4, 128)
|
||||
|
||||
// Load `amount`
|
||||
let amount := calldataload(100)
|
||||
|
||||
// Calculate number of bytes in `amounts` contents
|
||||
let amountsByteLen := mul(amountsLen, 32)
|
||||
|
||||
// Initialize `assetProxyId` and `assetProxy` to 0
|
||||
let assetProxyId := 0
|
||||
let assetProxy := 0
|
||||
|
||||
// Loop through `amounts` and `nestedAssetData`, calling `transferFrom` for each respective element
|
||||
for {let i := 0} lt(i, amountsByteLen) {i := add(i, 32)} {
|
||||
|
||||
// Calculate the total amount
|
||||
let amountsElement := calldataload(add(amountsContentsStart, i))
|
||||
let totalAmount := mul(amountsElement, amount)
|
||||
|
||||
// Revert if `amount` != 0 and multiplication resulted in an overflow
|
||||
if iszero(or(
|
||||
iszero(amount),
|
||||
eq(div(totalAmount, amount), amountsElement)
|
||||
)) {
|
||||
// Revert with `Error("UINT256_OVERFLOW")`
|
||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(64, 0x0000001055494e543235365f4f564552464c4f57000000000000000000000000)
|
||||
mstore(96, 0)
|
||||
revert(0, 100)
|
||||
}
|
||||
|
||||
// Write `totalAmount` to memory
|
||||
mstore(100, totalAmount)
|
||||
|
||||
// Load offset to `nestedAssetData[i]`
|
||||
let nestedAssetDataElementOffset := calldataload(add(nestedAssetDataContentsStart, i))
|
||||
|
||||
// In order to find the start of the `nestedAssetData[i]` contents, we must add:
|
||||
// assetDataOffset
|
||||
// + 32 (assetData len)
|
||||
// + 4 (assetProxyId)
|
||||
// + nestedAssetDataOffset
|
||||
// + 32 (nestedAssetData len)
|
||||
// + nestedAssetDataElementOffset
|
||||
// + 32 (nestedAssetDataElement len)
|
||||
let nestedAssetDataElementContentsStart := add(
|
||||
assetDataOffset,
|
||||
add(
|
||||
nestedAssetDataOffset,
|
||||
add(nestedAssetDataElementOffset, 100)
|
||||
)
|
||||
)
|
||||
|
||||
// Load length of `nestedAssetData[i]`
|
||||
let nestedAssetDataElementLenStart := sub(nestedAssetDataElementContentsStart, 32)
|
||||
let nestedAssetDataElementLen := calldataload(nestedAssetDataElementLenStart)
|
||||
|
||||
// Revert if the `nestedAssetData` does not contain a 4 byte `assetProxyId`
|
||||
if lt(nestedAssetDataElementLen, 4) {
|
||||
// Revert with `Error("LENGTH_GREATER_THAN_3_REQUIRED")`
|
||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(64, 0x0000001e4c454e4754485f475245415445525f5448414e5f335f524551554952)
|
||||
mstore(96, 0x4544000000000000000000000000000000000000000000000000000000000000)
|
||||
revert(0, 100)
|
||||
}
|
||||
|
||||
// Load AssetProxy id
|
||||
let currentAssetProxyId := and(
|
||||
calldataload(nestedAssetDataElementContentsStart),
|
||||
0xffffffff00000000000000000000000000000000000000000000000000000000
|
||||
)
|
||||
|
||||
// Only load `assetProxy` if `currentAssetProxyId` does not equal `assetProxyId`
|
||||
// We do not need to check if `currentAssetProxyId` is 0 since `assetProxy` is also initialized to 0
|
||||
if sub(currentAssetProxyId, assetProxyId) {
|
||||
// Update `assetProxyId`
|
||||
assetProxyId := currentAssetProxyId
|
||||
// To lookup a value in a mapping, we load from the storage location keccak256(k, p),
|
||||
// where k is the key left padded to 32 bytes and p is the storage slot
|
||||
mstore(132, assetProxyId)
|
||||
mstore(164, assetProxies_slot)
|
||||
assetProxy := sload(keccak256(132, 64))
|
||||
}
|
||||
|
||||
// Revert if AssetProxy with given id does not exist
|
||||
if iszero(assetProxy) {
|
||||
// Revert with `Error("ASSET_PROXY_DOES_NOT_EXIST")`
|
||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(64, 0x0000001a41535345545f50524f58595f444f45535f4e4f545f45584953540000)
|
||||
mstore(96, 0)
|
||||
revert(0, 100)
|
||||
}
|
||||
|
||||
// Copy `nestedAssetData[i]` from calldata to memory
|
||||
calldatacopy(
|
||||
132, // memory slot after `amounts[i]`
|
||||
nestedAssetDataElementLenStart, // location of `nestedAssetData[i]` in calldata
|
||||
add(nestedAssetDataElementLen, 32) // `nestedAssetData[i].length` plus 32 byte length
|
||||
)
|
||||
|
||||
// call `assetProxy.transferFrom`
|
||||
let success := call(
|
||||
gas, // forward all gas
|
||||
assetProxy, // call address of asset proxy
|
||||
0, // don't send any ETH
|
||||
0, // pointer to start of input
|
||||
add(164, nestedAssetDataElementLen), // length of input
|
||||
0, // write output over memory that won't be reused
|
||||
0 // don't copy output to memory
|
||||
)
|
||||
|
||||
// Revert with reason given by AssetProxy if `transferFrom` call failed
|
||||
if iszero(success) {
|
||||
returndatacopy(
|
||||
0, // copy to memory at 0
|
||||
0, // copy from return data at 0
|
||||
returndatasize() // copy all return data
|
||||
)
|
||||
revert(0, returndatasize())
|
||||
}
|
||||
}
|
||||
|
||||
// Return if no `transferFrom` calls reverted
|
||||
return(0, 0)
|
||||
}
|
||||
|
||||
// Revert if undefined function is called
|
||||
revert(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Gets the proxy id associated with the proxy address.
|
||||
/// @return Proxy id.
|
||||
function getProxyId()
|
||||
external
|
||||
pure
|
||||
returns (bytes4)
|
||||
{
|
||||
return PROXY_ID;
|
||||
}
|
||||
}
|
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||
|
||||
|
||||
// solhint-disable no-unused-vars
|
||||
contract StaticCallProxy {
|
||||
|
||||
using LibBytes for bytes;
|
||||
|
||||
// Id of this proxy.
|
||||
bytes4 constant internal PROXY_ID = bytes4(keccak256("StaticCall(address,bytes,bytes32)"));
|
||||
|
||||
/// @dev Makes a staticcall to a target address and verifies that the data returned matches the expected return data.
|
||||
/// @param assetData Byte array encoded with staticCallTarget, staticCallData, and expectedCallResultHash
|
||||
/// @param from This value is ignored.
|
||||
/// @param to This value is ignored.
|
||||
/// @param amount This value is ignored.
|
||||
function transferFrom(
|
||||
bytes calldata assetData,
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount
|
||||
)
|
||||
external
|
||||
view
|
||||
{
|
||||
// Decode params from `assetData`
|
||||
(
|
||||
address staticCallTarget,
|
||||
bytes memory staticCallData,
|
||||
bytes32 expectedReturnDataHash
|
||||
) = abi.decode(
|
||||
assetData.sliceDestructive(4, assetData.length),
|
||||
(address, bytes, bytes32)
|
||||
);
|
||||
|
||||
// Execute staticcall
|
||||
(bool success, bytes memory returnData) = staticCallTarget.staticcall(staticCallData);
|
||||
|
||||
// Revert with returned data if staticcall is unsuccessful
|
||||
if (!success) {
|
||||
assembly {
|
||||
revert(add(returnData, 32), mload(returnData))
|
||||
}
|
||||
}
|
||||
|
||||
// Revert if hash of return data is not as expected
|
||||
bytes32 returnDataHash = keccak256(returnData);
|
||||
require(
|
||||
expectedReturnDataHash == returnDataHash,
|
||||
"UNEXPECTED_STATIC_CALL_RESULT"
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Gets the proxy id associated with the proxy address.
|
||||
/// @return Proxy id.
|
||||
function getProxyId()
|
||||
external
|
||||
pure
|
||||
returns (bytes4)
|
||||
{
|
||||
return PROXY_ID;
|
||||
}
|
||||
}
|
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
|
||||
import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol";
|
||||
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
|
||||
import "../interfaces/IERC20Bridge.sol";
|
||||
import "../interfaces/IEth2Dai.sol";
|
||||
|
||||
|
||||
// solhint-disable space-after-comma
|
||||
contract Eth2DaiBridge is
|
||||
IERC20Bridge,
|
||||
IWallet,
|
||||
DeploymentConstants
|
||||
{
|
||||
/// @dev Callback for `IERC20Bridge`. Tries to buy `amount` of
|
||||
/// `toTokenAddress` tokens by selling the entirety of the opposing asset
|
||||
/// (DAI or WETH) to the Eth2Dai contract, then transfers the bought
|
||||
/// tokens to `to`.
|
||||
/// @param toTokenAddress The token to give to `to` (either DAI or WETH).
|
||||
/// @param 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 = exchange.sellAllAmount(
|
||||
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(_getEth2DaiAddress());
|
||||
}
|
||||
}
|
@@ -1,166 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/interfaces/IEtherToken.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
|
||||
import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol";
|
||||
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
|
||||
import "../interfaces/IERC20Bridge.sol";
|
||||
import "../interfaces/IKyberNetworkProxy.sol";
|
||||
|
||||
|
||||
// solhint-disable space-after-comma
|
||||
contract KyberBridge is
|
||||
IERC20Bridge,
|
||||
IWallet,
|
||||
DeploymentConstants
|
||||
{
|
||||
// @dev Structure used internally to get around stack limits.
|
||||
struct TradeState {
|
||||
IKyberNetworkProxy kyber;
|
||||
IEtherToken weth;
|
||||
address fromTokenAddress;
|
||||
uint256 fromTokenBalance;
|
||||
uint256 payableAmount;
|
||||
}
|
||||
|
||||
/// @dev Kyber ETH pseudo-address.
|
||||
address constant public KYBER_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
|
||||
/// @dev `bridgeTransferFrom()` failure result.
|
||||
bytes4 constant private BRIDGE_FAILED = 0x0;
|
||||
/// @dev Precision of Kyber rates.
|
||||
uint256 constant private KYBER_RATE_BASE = 10 ** 18;
|
||||
|
||||
// solhint-disable no-empty-blocks
|
||||
/// @dev Payable fallback to receive ETH from Kyber.
|
||||
function ()
|
||||
external
|
||||
payable
|
||||
{}
|
||||
|
||||
/// @dev Callback for `IKyberBridge`. Tries to buy `amount` of
|
||||
/// `toTokenAddress` tokens by selling the entirety of the opposing asset
|
||||
/// to the `KyberNetworkProxy` contract, then transfers the bought
|
||||
/// tokens to `to`.
|
||||
/// @param toTokenAddress The token to give to `to`.
|
||||
/// @param to The recipient of the bought tokens.
|
||||
/// @param amount Minimum amount of `toTokenAddress` tokens to buy.
|
||||
/// @param bridgeData The abi-encoeded "from" token address.
|
||||
/// @return success The magic bytes if successful.
|
||||
function bridgeTransferFrom(
|
||||
address toTokenAddress,
|
||||
address /* from */,
|
||||
address to,
|
||||
uint256 amount,
|
||||
bytes calldata bridgeData
|
||||
)
|
||||
external
|
||||
returns (bytes4 success)
|
||||
{
|
||||
TradeState memory state;
|
||||
state.kyber = _getKyberContract();
|
||||
state.weth = _getWETHContract();
|
||||
// Decode the bridge data to get the `fromTokenAddress`.
|
||||
(state.fromTokenAddress) = abi.decode(bridgeData, (address));
|
||||
state.fromTokenBalance = IERC20Token(state.fromTokenAddress).balanceOf(address(this));
|
||||
if (state.fromTokenBalance == 0) {
|
||||
// Return failure if no input tokens.
|
||||
return BRIDGE_FAILED;
|
||||
}
|
||||
if (state.fromTokenAddress == toTokenAddress) {
|
||||
// Just transfer the tokens if they're the same.
|
||||
LibERC20Token.transfer(state.fromTokenAddress, to, state.fromTokenBalance);
|
||||
return BRIDGE_SUCCESS;
|
||||
} else if (state.fromTokenAddress != address(state.weth)) {
|
||||
// If the input token is not WETH, grant an allowance to the exchange
|
||||
// to spend them.
|
||||
LibERC20Token.approve(state.fromTokenAddress, address(state.kyber), uint256(-1));
|
||||
} else {
|
||||
// If the input token is WETH, unwrap it and attach it to the call.
|
||||
state.fromTokenAddress = KYBER_ETH_ADDRESS;
|
||||
state.payableAmount = state.fromTokenBalance;
|
||||
state.weth.withdraw(state.fromTokenBalance);
|
||||
}
|
||||
bool isToTokenWeth = toTokenAddress == address(state.weth);
|
||||
|
||||
// Try to sell all of this contract's input token balance through
|
||||
// `KyberNetworkProxy.trade()`.
|
||||
uint256 boughtAmount = state.kyber.trade.value(state.payableAmount)(
|
||||
// Input token.
|
||||
state.fromTokenAddress,
|
||||
// Sell amount.
|
||||
state.fromTokenBalance,
|
||||
// Output token.
|
||||
isToTokenWeth ? KYBER_ETH_ADDRESS : toTokenAddress,
|
||||
// Transfer to this contract if converting to ETH, otherwise
|
||||
// transfer directly to the recipient.
|
||||
isToTokenWeth ? address(uint160(address(this))) : address(uint160(to)),
|
||||
// Buy as much as possible.
|
||||
uint256(-1),
|
||||
// Compute the minimum conversion rate, which is expressed in units with
|
||||
// 18 decimal places.
|
||||
(KYBER_RATE_BASE * amount) / state.fromTokenBalance,
|
||||
// No affiliate address.
|
||||
address(0)
|
||||
);
|
||||
// Wrap ETH output and transfer to recipient.
|
||||
if (isToTokenWeth) {
|
||||
state.weth.deposit.value(boughtAmount)();
|
||||
state.weth.transfer(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 `KyberNetworkProxy` contract.
|
||||
/// @return kyber The `IKyberNetworkProxy` contract.
|
||||
function _getKyberContract()
|
||||
internal
|
||||
view
|
||||
returns (IKyberNetworkProxy kyber)
|
||||
{
|
||||
return IKyberNetworkProxy(_getKyberNetworkProxyAddress());
|
||||
}
|
||||
|
||||
/// @dev Overridable way to get the WETH contract.
|
||||
/// @return weth The WETH contract.
|
||||
function _getWETHContract()
|
||||
internal
|
||||
view
|
||||
returns (IEtherToken weth)
|
||||
{
|
||||
return IEtherToken(_getWETHAddress());
|
||||
}
|
||||
}
|
@@ -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 "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
|
||||
import "../interfaces/IUniswapExchangeFactory.sol";
|
||||
import "../interfaces/IUniswapExchange.sol";
|
||||
import "../interfaces/IERC20Bridge.sol";
|
||||
|
||||
|
||||
// solhint-disable space-after-comma
|
||||
// solhint-disable not-rely-on-time
|
||||
contract UniswapBridge is
|
||||
IERC20Bridge,
|
||||
IWallet,
|
||||
DeploymentConstants
|
||||
{
|
||||
// Struct to hold `bridgeTransferFrom()` local variables in memory and to avoid
|
||||
// stack overflows.
|
||||
struct 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(_getWETHAddress());
|
||||
}
|
||||
|
||||
/// @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(_getUniswapExchangeFactoryAddress());
|
||||
}
|
||||
|
||||
/// @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 = IUniswapExchange(
|
||||
getUniswapExchangeFactoryContract().getExchange(exchangeTokenAddress)
|
||||
);
|
||||
require(address(exchange) != address(0), "NO_UNISWAP_EXCHANGE_FOR_TOKEN");
|
||||
return exchange;
|
||||
}
|
||||
}
|
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
// solhint-disable
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
|
||||
// @dev Interface of the asset proxy's assetData.
|
||||
// The asset proxies take an ABI encoded `bytes assetData` as argument.
|
||||
// This argument is ABI encoded as one of the methods of this interface.
|
||||
interface IAssetData {
|
||||
|
||||
/// @dev Function signature for encoding ERC20 assetData.
|
||||
/// @param tokenAddress Address of ERC20Token contract.
|
||||
function ERC20Token(address tokenAddress)
|
||||
external;
|
||||
|
||||
/// @dev Function signature for encoding ERC721 assetData.
|
||||
/// @param tokenAddress Address of ERC721 token contract.
|
||||
/// @param tokenId Id of ERC721 token to be transferred.
|
||||
function ERC721Token(
|
||||
address tokenAddress,
|
||||
uint256 tokenId
|
||||
)
|
||||
external;
|
||||
|
||||
/// @dev Function signature for encoding ERC1155 assetData.
|
||||
/// @param tokenAddress Address of ERC1155 token contract.
|
||||
/// @param tokenIds Array of ids of tokens to be transferred.
|
||||
/// @param values Array of values that correspond to each token id to be transferred.
|
||||
/// Note that each value will be multiplied by the amount being filled in the order before transferring.
|
||||
/// @param callbackData Extra data to be passed to receiver's `onERC1155Received` callback function.
|
||||
function ERC1155Assets(
|
||||
address tokenAddress,
|
||||
uint256[] calldata tokenIds,
|
||||
uint256[] calldata values,
|
||||
bytes calldata callbackData
|
||||
)
|
||||
external;
|
||||
|
||||
/// @dev Function signature for encoding MultiAsset assetData.
|
||||
/// @param values Array of amounts that correspond to each asset to be transferred.
|
||||
/// Note that each value will be multiplied by the amount being filled in the order before transferring.
|
||||
/// @param nestedAssetData Array of assetData fields that will be be dispatched to their correspnding AssetProxy contract.
|
||||
function MultiAsset(
|
||||
uint256[] calldata values,
|
||||
bytes[] calldata nestedAssetData
|
||||
)
|
||||
external;
|
||||
|
||||
/// @dev Function signature for encoding StaticCall assetData.
|
||||
/// @param staticCallTargetAddress Address that will execute the staticcall.
|
||||
/// @param staticCallData Data that will be executed via staticcall on the staticCallTargetAddress.
|
||||
/// @param expectedReturnDataHash Keccak-256 hash of the expected staticcall return data.
|
||||
function StaticCall(
|
||||
address staticCallTargetAddress,
|
||||
bytes calldata staticCallData,
|
||||
bytes32 expectedReturnDataHash
|
||||
)
|
||||
external;
|
||||
|
||||
/// @dev Function signature for encoding ERC20Bridge assetData.
|
||||
/// @param tokenAddress Address of token to transfer.
|
||||
/// @param bridgeAddress Address of the bridge contract.
|
||||
/// @param bridgeData Arbitrary data to be passed to the bridge contract.
|
||||
function ERC20Bridge(
|
||||
address tokenAddress,
|
||||
address bridgeAddress,
|
||||
bytes calldata bridgeData
|
||||
)
|
||||
external;
|
||||
}
|
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
|
||||
|
||||
contract IAssetProxy {
|
||||
|
||||
/// @dev Transfers assets. Either succeeds or throws.
|
||||
/// @param assetData Byte array encoded for the respective asset proxy.
|
||||
/// @param from Address to transfer asset from.
|
||||
/// @param to Address to transfer asset to.
|
||||
/// @param amount Amount of asset to transfer.
|
||||
function transferFrom(
|
||||
bytes calldata assetData,
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount
|
||||
)
|
||||
external;
|
||||
|
||||
/// @dev Gets the proxy id associated with the proxy address.
|
||||
/// @return Proxy id.
|
||||
function getProxyId()
|
||||
external
|
||||
pure
|
||||
returns (bytes4);
|
||||
}
|
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
|
||||
|
||||
contract IAssetProxyDispatcher {
|
||||
|
||||
// Logs registration of new asset proxy
|
||||
event AssetProxyRegistered(
|
||||
bytes4 id, // Id of new registered AssetProxy.
|
||||
address assetProxy // Address of new registered AssetProxy.
|
||||
);
|
||||
|
||||
/// @dev Registers an asset proxy to its asset proxy id.
|
||||
/// Once an asset proxy is registered, it cannot be unregistered.
|
||||
/// @param assetProxy Address of new asset proxy to register.
|
||||
function registerAssetProxy(address assetProxy)
|
||||
external;
|
||||
|
||||
/// @dev Gets an asset proxy.
|
||||
/// @param assetProxyId Id of the asset proxy.
|
||||
/// @return The asset proxy registered to assetProxyId. Returns 0x0 if no proxy is registered.
|
||||
function getAssetProxy(bytes4 assetProxyId)
|
||||
external
|
||||
view
|
||||
returns (address);
|
||||
}
|
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/interfaces/IOwnable.sol";
|
||||
|
||||
|
||||
contract IAuthorizable is
|
||||
IOwnable
|
||||
{
|
||||
// Event logged when a new address is authorized.
|
||||
event AuthorizedAddressAdded(
|
||||
address indexed target,
|
||||
address indexed caller
|
||||
);
|
||||
|
||||
// Event logged when a currently authorized address is unauthorized.
|
||||
event AuthorizedAddressRemoved(
|
||||
address indexed target,
|
||||
address indexed caller
|
||||
);
|
||||
|
||||
/// @dev Authorizes an address.
|
||||
/// @param target Address to authorize.
|
||||
function addAuthorizedAddress(address target)
|
||||
external;
|
||||
|
||||
/// @dev Removes authorizion of an address.
|
||||
/// @param target Address to remove authorization from.
|
||||
function removeAuthorizedAddress(address target)
|
||||
external;
|
||||
|
||||
/// @dev Removes authorizion of an address.
|
||||
/// @param target Address to remove authorization from.
|
||||
/// @param index Index of target in authorities array.
|
||||
function removeAuthorizedAddressAtIndex(
|
||||
address target,
|
||||
uint256 index
|
||||
)
|
||||
external;
|
||||
|
||||
/// @dev Gets all authorized addresses.
|
||||
/// @return Array of authorized addresses.
|
||||
function getAuthorizedAddresses()
|
||||
external
|
||||
view
|
||||
returns (address[] memory);
|
||||
}
|
@@ -1,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);
|
||||
}
|
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
|
||||
|
||||
interface IEth2Dai {
|
||||
|
||||
/// @dev Sell `sellAmount` of `fromToken` token and receive `toToken` token.
|
||||
/// @param fromToken The token being sold.
|
||||
/// @param sellAmount The amount of `fromToken` token being sold.
|
||||
/// @param toToken The token being bought.
|
||||
/// @param minFillAmount Minimum amount of `toToken` token to buy.
|
||||
/// @return fillAmount Amount of `toToken` bought.
|
||||
function sellAllAmount(
|
||||
address fromToken,
|
||||
uint256 sellAmount,
|
||||
address toToken,
|
||||
uint256 minFillAmount
|
||||
)
|
||||
external
|
||||
returns (uint256 fillAmount);
|
||||
}
|
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
|
||||
|
||||
interface IKyberNetworkProxy {
|
||||
|
||||
/// @dev Sells `sellTokenAddress` tokens for `buyTokenAddress` tokens.
|
||||
/// @param sellTokenAddress Token to sell.
|
||||
/// @param sellAmount Amount of tokens to sell.
|
||||
/// @param buyTokenAddress Token to buy.
|
||||
/// @param recipientAddress Address to send bought tokens to.
|
||||
/// @param maxBuyTokenAmount A limit on the amount of tokens to buy.
|
||||
/// @param minConversionRate The minimal conversion rate. If actual rate
|
||||
/// is lower, trade is canceled.
|
||||
/// @param walletId The wallet ID to send part of the fees
|
||||
/// @return boughtAmount Amount of tokens bought.
|
||||
function trade(
|
||||
address sellTokenAddress,
|
||||
uint256 sellAmount,
|
||||
address buyTokenAddress,
|
||||
address payable recipientAddress,
|
||||
uint256 maxBuyTokenAmount,
|
||||
uint256 minConversionRate,
|
||||
address walletId
|
||||
)
|
||||
external
|
||||
payable
|
||||
returns(uint256 boughtAmount);
|
||||
}
|
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
|
||||
|
||||
interface IUniswapExchange {
|
||||
|
||||
/// @dev Buys at least `minTokensBought` tokens with ETH and transfer them
|
||||
/// to `recipient`.
|
||||
/// @param minTokensBought The minimum number of tokens to buy.
|
||||
/// @param deadline Time when this order expires.
|
||||
/// @param recipient Who to transfer the tokens to.
|
||||
/// @return tokensBought Amount of tokens bought.
|
||||
function ethToTokenTransferInput(
|
||||
uint256 minTokensBought,
|
||||
uint256 deadline,
|
||||
address recipient
|
||||
)
|
||||
external
|
||||
payable
|
||||
returns (uint256 tokensBought);
|
||||
|
||||
/// @dev Buys at least `minEthBought` ETH with tokens.
|
||||
/// @param tokensSold Amount of tokens to sell.
|
||||
/// @param minEthBought The minimum amount of ETH to buy.
|
||||
/// @param deadline Time when this order expires.
|
||||
/// @return ethBought Amount of tokens bought.
|
||||
function tokenToEthSwapInput(
|
||||
uint256 tokensSold,
|
||||
uint256 minEthBought,
|
||||
uint256 deadline
|
||||
)
|
||||
external
|
||||
returns (uint256 ethBought);
|
||||
|
||||
/// @dev Buys at least `minTokensBought` tokens with the exchange token
|
||||
/// and transfer them to `recipient`.
|
||||
/// @param minTokensBought The minimum number of tokens to buy.
|
||||
/// @param minEthBought The minimum amount of intermediate ETH to buy.
|
||||
/// @param deadline Time when this order expires.
|
||||
/// @param recipient Who to transfer the tokens to.
|
||||
/// @param toTokenAddress The token being bought.
|
||||
/// @return tokensBought Amount of tokens bought.
|
||||
function tokenToTokenTransferInput(
|
||||
uint256 tokensSold,
|
||||
uint256 minTokensBought,
|
||||
uint256 minEthBought,
|
||||
uint256 deadline,
|
||||
address recipient,
|
||||
address toTokenAddress
|
||||
)
|
||||
external
|
||||
returns (uint256 tokensBought);
|
||||
}
|
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
|
||||
import "./IUniswapExchange.sol";
|
||||
|
||||
|
||||
interface IUniswapExchangeFactory {
|
||||
|
||||
/// @dev Get the exchange for a token.
|
||||
/// @param tokenAddress The address of the token contract.
|
||||
function getExchange(address tokenAddress)
|
||||
external
|
||||
view
|
||||
returns (address);
|
||||
}
|
@@ -1,108 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "../src/interfaces/IERC20Bridge.sol";
|
||||
|
||||
|
||||
/// @dev Test bridge token
|
||||
contract TestERC20BridgeToken {
|
||||
mapping (address => uint256) private _balances;
|
||||
|
||||
function addBalance(address owner, int256 amount)
|
||||
external
|
||||
{
|
||||
setBalance(owner, uint256(int256(balanceOf(owner)) + amount));
|
||||
}
|
||||
|
||||
function setBalance(address owner, uint256 balance)
|
||||
public
|
||||
{
|
||||
_balances[owner] = balance;
|
||||
}
|
||||
|
||||
function balanceOf(address owner)
|
||||
public
|
||||
view
|
||||
returns (uint256)
|
||||
{
|
||||
return _balances[owner];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// @dev Test bridge contract.
|
||||
contract TestERC20Bridge is
|
||||
IERC20Bridge
|
||||
{
|
||||
TestERC20BridgeToken public testToken;
|
||||
|
||||
event BridgeWithdrawTo(
|
||||
address tokenAddress,
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount,
|
||||
bytes bridgeData
|
||||
);
|
||||
|
||||
constructor() public {
|
||||
testToken = new TestERC20BridgeToken();
|
||||
}
|
||||
|
||||
function setTestTokenBalance(address owner, uint256 balance)
|
||||
external
|
||||
{
|
||||
testToken.setBalance(owner, balance);
|
||||
}
|
||||
|
||||
function bridgeTransferFrom(
|
||||
address tokenAddress,
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount,
|
||||
bytes calldata bridgeData
|
||||
)
|
||||
external
|
||||
returns (bytes4)
|
||||
{
|
||||
emit BridgeWithdrawTo(
|
||||
tokenAddress,
|
||||
from,
|
||||
to,
|
||||
amount,
|
||||
bridgeData
|
||||
);
|
||||
// Unpack the bridgeData.
|
||||
(
|
||||
int256 transferAmount,
|
||||
bytes memory revertData,
|
||||
bytes memory returnData
|
||||
) = abi.decode(bridgeData, (int256, bytes, bytes));
|
||||
|
||||
// If `revertData` is set, revert.
|
||||
if (revertData.length != 0) {
|
||||
assembly { revert(add(revertData, 0x20), mload(revertData)) }
|
||||
}
|
||||
// Increase `to`'s balance by `transferAmount`.
|
||||
TestERC20BridgeToken(tokenAddress).addBalance(to, transferAmount);
|
||||
// Return `returnData`.
|
||||
assembly { return(add(returnData, 0x20), mload(returnData)) }
|
||||
}
|
||||
}
|
@@ -1,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));
|
||||
}
|
||||
}
|
@@ -1,322 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||
import "../src/bridges/KyberBridge.sol";
|
||||
import "../src/interfaces/IKyberNetworkProxy.sol";
|
||||
|
||||
|
||||
// solhint-disable no-simple-event-func-name
|
||||
interface ITestContract {
|
||||
|
||||
function wethWithdraw(
|
||||
address payable ownerAddress,
|
||||
uint256 amount
|
||||
)
|
||||
external;
|
||||
|
||||
function wethDeposit(
|
||||
address ownerAddress
|
||||
)
|
||||
external
|
||||
payable;
|
||||
|
||||
function tokenTransfer(
|
||||
address ownerAddress,
|
||||
address recipientAddress,
|
||||
uint256 amount
|
||||
)
|
||||
external
|
||||
returns (bool success);
|
||||
|
||||
function tokenApprove(
|
||||
address ownerAddress,
|
||||
address spenderAddress,
|
||||
uint256 allowance
|
||||
)
|
||||
external
|
||||
returns (bool success);
|
||||
|
||||
function tokenBalanceOf(
|
||||
address ownerAddress
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256 balance);
|
||||
}
|
||||
|
||||
|
||||
/// @dev A minimalist ERC20/WETH token.
|
||||
contract TestToken {
|
||||
|
||||
ITestContract private _testContract;
|
||||
|
||||
constructor() public {
|
||||
_testContract = ITestContract(msg.sender);
|
||||
}
|
||||
|
||||
function approve(address spender, uint256 allowance)
|
||||
external
|
||||
returns (bool)
|
||||
{
|
||||
return _testContract.tokenApprove(
|
||||
msg.sender,
|
||||
spender,
|
||||
allowance
|
||||
);
|
||||
}
|
||||
|
||||
function transfer(address recipient, uint256 amount)
|
||||
external
|
||||
returns (bool)
|
||||
{
|
||||
return _testContract.tokenTransfer(
|
||||
msg.sender,
|
||||
recipient,
|
||||
amount
|
||||
);
|
||||
}
|
||||
|
||||
function withdraw(uint256 amount)
|
||||
external
|
||||
{
|
||||
return _testContract.wethWithdraw(msg.sender, amount);
|
||||
}
|
||||
|
||||
function deposit()
|
||||
external
|
||||
payable
|
||||
{
|
||||
return _testContract.wethDeposit.value(msg.value)(msg.sender);
|
||||
}
|
||||
|
||||
function balanceOf(address owner)
|
||||
external
|
||||
view
|
||||
returns (uint256)
|
||||
{
|
||||
return _testContract.tokenBalanceOf(owner);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// @dev KyberBridge overridden to mock tokens and implement IKyberBridge.
|
||||
contract TestKyberBridge is
|
||||
KyberBridge,
|
||||
ITestContract,
|
||||
IKyberNetworkProxy
|
||||
{
|
||||
event KyberBridgeTrade(
|
||||
uint256 msgValue,
|
||||
address sellTokenAddress,
|
||||
uint256 sellAmount,
|
||||
address buyTokenAddress,
|
||||
address payable recipientAddress,
|
||||
uint256 maxBuyTokenAmount,
|
||||
uint256 minConversionRate,
|
||||
address walletId
|
||||
);
|
||||
|
||||
event KyberBridgeWethWithdraw(
|
||||
address ownerAddress,
|
||||
uint256 amount
|
||||
);
|
||||
|
||||
event KyberBridgeWethDeposit(
|
||||
uint256 msgValue,
|
||||
address ownerAddress,
|
||||
uint256 amount
|
||||
);
|
||||
|
||||
event KyberBridgeTokenApprove(
|
||||
address tokenAddress,
|
||||
address ownerAddress,
|
||||
address spenderAddress,
|
||||
uint256 allowance
|
||||
);
|
||||
|
||||
event KyberBridgeTokenTransfer(
|
||||
address tokenAddress,
|
||||
address ownerAddress,
|
||||
address recipientAddress,
|
||||
uint256 amount
|
||||
);
|
||||
|
||||
IEtherToken public weth;
|
||||
mapping (address => mapping (address => uint256)) private _tokenBalances;
|
||||
uint256 private _nextFillAmount;
|
||||
|
||||
constructor() public {
|
||||
weth = IEtherToken(address(new TestToken()));
|
||||
}
|
||||
|
||||
/// @dev Implementation of `IKyberNetworkProxy.trade()`
|
||||
function trade(
|
||||
address sellTokenAddress,
|
||||
uint256 sellAmount,
|
||||
address buyTokenAddress,
|
||||
address payable recipientAddress,
|
||||
uint256 maxBuyTokenAmount,
|
||||
uint256 minConversionRate,
|
||||
address walletId
|
||||
)
|
||||
external
|
||||
payable
|
||||
returns(uint256 boughtAmount)
|
||||
{
|
||||
emit KyberBridgeTrade(
|
||||
msg.value,
|
||||
sellTokenAddress,
|
||||
sellAmount,
|
||||
buyTokenAddress,
|
||||
recipientAddress,
|
||||
maxBuyTokenAmount,
|
||||
minConversionRate,
|
||||
walletId
|
||||
);
|
||||
return _nextFillAmount;
|
||||
}
|
||||
|
||||
function createToken()
|
||||
external
|
||||
returns (address tokenAddress)
|
||||
{
|
||||
return address(new TestToken());
|
||||
}
|
||||
|
||||
function setNextFillAmount(uint256 amount)
|
||||
external
|
||||
payable
|
||||
{
|
||||
if (msg.value != 0) {
|
||||
require(amount == msg.value, "VALUE_AMOUNT_MISMATCH");
|
||||
grantTokensTo(address(weth), address(this), msg.value);
|
||||
}
|
||||
_nextFillAmount = amount;
|
||||
}
|
||||
|
||||
function wethDeposit(
|
||||
address ownerAddress
|
||||
)
|
||||
external
|
||||
payable
|
||||
{
|
||||
require(msg.sender == address(weth), "ONLY_WETH");
|
||||
grantTokensTo(address(weth), ownerAddress, msg.value);
|
||||
emit KyberBridgeWethDeposit(
|
||||
msg.value,
|
||||
ownerAddress,
|
||||
msg.value
|
||||
);
|
||||
}
|
||||
|
||||
function wethWithdraw(
|
||||
address payable ownerAddress,
|
||||
uint256 amount
|
||||
)
|
||||
external
|
||||
{
|
||||
require(msg.sender == address(weth), "ONLY_WETH");
|
||||
_tokenBalances[address(weth)][ownerAddress] -= amount;
|
||||
ownerAddress.transfer(amount);
|
||||
emit KyberBridgeWethWithdraw(
|
||||
ownerAddress,
|
||||
amount
|
||||
);
|
||||
}
|
||||
|
||||
function tokenApprove(
|
||||
address ownerAddress,
|
||||
address spenderAddress,
|
||||
uint256 allowance
|
||||
)
|
||||
external
|
||||
returns (bool success)
|
||||
{
|
||||
emit KyberBridgeTokenApprove(
|
||||
msg.sender,
|
||||
ownerAddress,
|
||||
spenderAddress,
|
||||
allowance
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
function tokenTransfer(
|
||||
address ownerAddress,
|
||||
address recipientAddress,
|
||||
uint256 amount
|
||||
)
|
||||
external
|
||||
returns (bool success)
|
||||
{
|
||||
_tokenBalances[msg.sender][ownerAddress] -= amount;
|
||||
_tokenBalances[msg.sender][recipientAddress] += amount;
|
||||
emit KyberBridgeTokenTransfer(
|
||||
msg.sender,
|
||||
ownerAddress,
|
||||
recipientAddress,
|
||||
amount
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
function tokenBalanceOf(
|
||||
address ownerAddress
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256 balance)
|
||||
{
|
||||
return _tokenBalances[msg.sender][ownerAddress];
|
||||
}
|
||||
|
||||
function grantTokensTo(address tokenAddress, address ownerAddress, uint256 amount)
|
||||
public
|
||||
payable
|
||||
{
|
||||
_tokenBalances[tokenAddress][ownerAddress] += amount;
|
||||
if (tokenAddress != address(weth)) {
|
||||
// Send back ether if not WETH.
|
||||
msg.sender.transfer(msg.value);
|
||||
} else {
|
||||
require(msg.value == amount, "VALUE_AMOUNT_MISMATCH");
|
||||
}
|
||||
}
|
||||
|
||||
// @dev overridden to point to this contract.
|
||||
function _getKyberContract()
|
||||
internal
|
||||
view
|
||||
returns (IKyberNetworkProxy kyber)
|
||||
{
|
||||
return IKyberNetworkProxy(address(this));
|
||||
}
|
||||
|
||||
// @dev overridden to point to test WETH.
|
||||
function _getWETHContract()
|
||||
internal
|
||||
view
|
||||
returns (IEtherToken weth_)
|
||||
{
|
||||
return weth;
|
||||
}
|
||||
}
|
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||
|
||||
|
||||
contract TestStaticCallTarget {
|
||||
|
||||
using LibBytes for bytes;
|
||||
|
||||
uint256 internal _state;
|
||||
|
||||
function updateState()
|
||||
external
|
||||
{
|
||||
_state++;
|
||||
}
|
||||
|
||||
function assertEvenNumber(uint256 target)
|
||||
external
|
||||
pure
|
||||
{
|
||||
require(
|
||||
target % 2 == 0,
|
||||
"TARGET_NOT_EVEN"
|
||||
);
|
||||
}
|
||||
|
||||
function isOddNumber(uint256 target)
|
||||
external
|
||||
pure
|
||||
returns (bool isOdd)
|
||||
{
|
||||
isOdd = target % 2 == 1;
|
||||
return isOdd;
|
||||
}
|
||||
|
||||
function noInputFunction()
|
||||
external
|
||||
pure
|
||||
{
|
||||
assert(msg.data.length == 4 && msg.data.readBytes4(0) == bytes4(keccak256("noInputFunction()")));
|
||||
}
|
||||
|
||||
function dynamicInputFunction(bytes calldata a)
|
||||
external
|
||||
pure
|
||||
{
|
||||
bytes memory abiEncodedData = abi.encodeWithSignature("dynamicInputFunction(bytes)", a);
|
||||
assert(msg.data.equals(abiEncodedData));
|
||||
}
|
||||
|
||||
function returnComplexType(uint256 a, uint256 b)
|
||||
external
|
||||
view
|
||||
returns (bytes memory result)
|
||||
{
|
||||
result = abi.encodePacked(
|
||||
address(this),
|
||||
a,
|
||||
b
|
||||
);
|
||||
return result;
|
||||
}
|
||||
}
|
@@ -1,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 (address)
|
||||
{
|
||||
return address(_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));
|
||||
}
|
||||
}
|
@@ -1,98 +0,0 @@
|
||||
{
|
||||
"name": "@0x/contracts-asset-proxy",
|
||||
"version": "2.3.0-beta.4",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
"description": "Smart contract components of 0x protocol",
|
||||
"main": "lib/src/index.js",
|
||||
"directories": {
|
||||
"test": "test"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "yarn pre_build && tsc -b",
|
||||
"build:ci": "yarn build",
|
||||
"pre_build": "run-s compile contracts:gen generate_contract_wrappers contracts:copy",
|
||||
"test": "yarn run_mocha",
|
||||
"rebuild_and_test": "run-s build test",
|
||||
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
||||
"test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html",
|
||||
"test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha",
|
||||
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
|
||||
"compile": "sol-compiler",
|
||||
"watch": "sol-compiler -w",
|
||||
"clean": "shx rm -rf lib test/generated-artifacts test/generated-wrappers generated-artifacts generated-wrappers",
|
||||
"generate_contract_wrappers": "abi-gen --debug --abis ${npm_package_config_abis} --output test/generated-wrappers --backend ethers",
|
||||
"lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./test/generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude ./test/generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||
"fix": "tslint --fix --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./test/generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude ./test/generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||
"coverage:report:text": "istanbul report text",
|
||||
"coverage:report:html": "istanbul report html && open coverage/index.html",
|
||||
"profiler:report:html": "istanbul report html && open coverage/index.html",
|
||||
"coverage:report:lcov": "istanbul report lcov",
|
||||
"test:circleci": "yarn test",
|
||||
"contracts:gen": "contracts-gen generate",
|
||||
"contracts:copy": "contracts-gen copy",
|
||||
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol",
|
||||
"compile:truffle": "truffle compile",
|
||||
"docs:md": "ts-doc-gen --sourceDir='$PROJECT_FILES' --output=$MD_FILE_DIR --fileExtension=mdx --tsconfig=./typedoc-tsconfig.json",
|
||||
"docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES"
|
||||
},
|
||||
"config": {
|
||||
"publicInterfaceContracts": "ERC1155Proxy,ERC20Proxy,ERC721Proxy,MultiAssetProxy,StaticCallProxy,ERC20BridgeProxy,Eth2DaiBridge,IAssetData,IAssetProxy,UniswapBridge,KyberBridge,TestStaticCallTarget",
|
||||
"abis": "./test/generated-artifacts/@(ERC1155Proxy|ERC20BridgeProxy|ERC20Proxy|ERC721Proxy|Eth2DaiBridge|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|IERC20Bridge|IEth2Dai|IKyberNetworkProxy|IUniswapExchange|IUniswapExchangeFactory|KyberBridge|MixinAssetProxyDispatcher|MixinAuthorizable|MultiAssetProxy|Ownable|StaticCallProxy|TestERC20Bridge|TestEth2DaiBridge|TestKyberBridge|TestStaticCallTarget|TestUniswapBridge|UniswapBridge).json",
|
||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/0xProject/0x-monorepo.git"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/0xProject/0x-monorepo/issues"
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/protocol/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^4.4.0-beta.4",
|
||||
"@0x/contracts-gen": "^1.1.0-beta.4",
|
||||
"@0x/contracts-test-utils": "^3.2.0-beta.4",
|
||||
"@0x/contracts-utils": "^3.3.0-beta.4",
|
||||
"@0x/dev-utils": "^2.4.0-beta.4",
|
||||
"@0x/sol-compiler": "^3.2.0-beta.4",
|
||||
"@0x/ts-doc-gen": "^0.0.22",
|
||||
"@0x/tslint-config": "^3.1.0-beta.2",
|
||||
"@0x/types": "^2.5.0-beta.3",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/node": "*",
|
||||
"chai": "^4.0.1",
|
||||
"chai-as-promised": "^7.1.0",
|
||||
"chai-bignumber": "^3.0.0",
|
||||
"dirty-chai": "^2.0.1",
|
||||
"ethereumjs-util": "^5.1.1",
|
||||
"make-promises-safe": "^1.1.0",
|
||||
"mocha": "^6.2.0",
|
||||
"npm-run-all": "^4.1.2",
|
||||
"shx": "^0.2.2",
|
||||
"solhint": "^1.4.1",
|
||||
"truffle": "^5.0.32",
|
||||
"tslint": "5.11.0",
|
||||
"typedoc": "^0.15.0",
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^5.5.0-beta.4",
|
||||
"@0x/contracts-dev-utils": "^0.1.0-beta.4",
|
||||
"@0x/contracts-erc1155": "^1.2.0-beta.4",
|
||||
"@0x/contracts-erc20": "^2.3.0-beta.4",
|
||||
"@0x/contracts-erc721": "^2.2.0-beta.4",
|
||||
"@0x/order-utils": "^8.5.0-beta.4",
|
||||
"@0x/typescript-typings": "^4.4.0-beta.2",
|
||||
"@0x/utils": "^4.6.0-beta.3",
|
||||
"@0x/web3-wrapper": "^6.1.0-beta.3",
|
||||
"ethereum-types": "^2.2.0-beta.2",
|
||||
"lodash": "^4.17.11"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* -----------------------------------------------------------------------------
|
||||
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
import { ContractArtifact } from 'ethereum-types';
|
||||
|
||||
import * as ERC1155Proxy from '../generated-artifacts/ERC1155Proxy.json';
|
||||
import * as ERC20BridgeProxy from '../generated-artifacts/ERC20BridgeProxy.json';
|
||||
import * as ERC20Proxy from '../generated-artifacts/ERC20Proxy.json';
|
||||
import * as ERC721Proxy from '../generated-artifacts/ERC721Proxy.json';
|
||||
import * as Eth2DaiBridge from '../generated-artifacts/Eth2DaiBridge.json';
|
||||
import * as IAssetData from '../generated-artifacts/IAssetData.json';
|
||||
import * as IAssetProxy from '../generated-artifacts/IAssetProxy.json';
|
||||
import * as KyberBridge from '../generated-artifacts/KyberBridge.json';
|
||||
import * as MultiAssetProxy from '../generated-artifacts/MultiAssetProxy.json';
|
||||
import * as StaticCallProxy from '../generated-artifacts/StaticCallProxy.json';
|
||||
import * as TestStaticCallTarget from '../generated-artifacts/TestStaticCallTarget.json';
|
||||
import * as UniswapBridge from '../generated-artifacts/UniswapBridge.json';
|
||||
export const artifacts = {
|
||||
ERC1155Proxy: ERC1155Proxy as ContractArtifact,
|
||||
ERC20Proxy: ERC20Proxy as ContractArtifact,
|
||||
ERC721Proxy: ERC721Proxy as ContractArtifact,
|
||||
MultiAssetProxy: MultiAssetProxy as ContractArtifact,
|
||||
StaticCallProxy: StaticCallProxy as ContractArtifact,
|
||||
ERC20BridgeProxy: ERC20BridgeProxy as ContractArtifact,
|
||||
Eth2DaiBridge: Eth2DaiBridge as ContractArtifact,
|
||||
IAssetData: IAssetData as ContractArtifact,
|
||||
IAssetProxy: IAssetProxy as ContractArtifact,
|
||||
UniswapBridge: UniswapBridge as ContractArtifact,
|
||||
KyberBridge: KyberBridge as ContractArtifact,
|
||||
TestStaticCallTarget: TestStaticCallTarget as ContractArtifact,
|
||||
};
|
@@ -1,411 +0,0 @@
|
||||
import { DevUtilsContract } from '@0x/contracts-dev-utils';
|
||||
import { artifacts as erc1155Artifacts, ERC1155MintableContract, Erc1155Wrapper } from '@0x/contracts-erc1155';
|
||||
import {
|
||||
constants,
|
||||
ERC1155FungibleHoldingsByOwner,
|
||||
ERC1155HoldingsByOwner,
|
||||
ERC1155NonFungibleHoldingsByOwner,
|
||||
LogDecoder,
|
||||
txDefaults,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import { Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { artifacts } from './artifacts';
|
||||
|
||||
import { ERC1155ProxyContract, IAssetProxyContract } from './wrappers';
|
||||
|
||||
export class ERC1155ProxyWrapper {
|
||||
private readonly _tokenOwnerAddresses: string[];
|
||||
private readonly _fungibleTokenIds: string[];
|
||||
private readonly _nonFungibleTokenIds: string[];
|
||||
private readonly _nfts: Array<{ id: BigNumber; tokenId: BigNumber }>;
|
||||
private readonly _contractOwnerAddress: string;
|
||||
private readonly _web3Wrapper: Web3Wrapper;
|
||||
private readonly _provider: Provider;
|
||||
private readonly _logDecoder: LogDecoder;
|
||||
private readonly _dummyTokenWrappers: Erc1155Wrapper[];
|
||||
private readonly _assetProxyInterface: IAssetProxyContract;
|
||||
private readonly _devUtils: DevUtilsContract;
|
||||
private _proxyContract?: ERC1155ProxyContract;
|
||||
private _proxyIdIfExists?: string;
|
||||
private _initialTokenIdsByOwner: ERC1155HoldingsByOwner = { fungible: {}, nonFungible: {} };
|
||||
|
||||
constructor(provider: Provider, tokenOwnerAddresses: string[], contractOwnerAddress: string) {
|
||||
this._web3Wrapper = new Web3Wrapper(provider);
|
||||
this._provider = provider;
|
||||
const allArtifacts = _.merge(artifacts, erc1155Artifacts);
|
||||
this._logDecoder = new LogDecoder(this._web3Wrapper, allArtifacts);
|
||||
this._dummyTokenWrappers = [];
|
||||
this._assetProxyInterface = new IAssetProxyContract(constants.NULL_ADDRESS, provider);
|
||||
this._devUtils = new DevUtilsContract(constants.NULL_ADDRESS, provider);
|
||||
this._tokenOwnerAddresses = tokenOwnerAddresses;
|
||||
this._contractOwnerAddress = contractOwnerAddress;
|
||||
this._fungibleTokenIds = [];
|
||||
this._nonFungibleTokenIds = [];
|
||||
this._nfts = [];
|
||||
}
|
||||
/**
|
||||
* @dev Deploys dummy ERC1155 contracts
|
||||
* @return An array of ERC1155 wrappers; one for each deployed contract.
|
||||
*/
|
||||
public async deployDummyContractsAsync(): Promise<Erc1155Wrapper[]> {
|
||||
// tslint:disable-next-line:no-unused-variable
|
||||
for (const i of _.times(constants.NUM_DUMMY_ERC1155_CONTRACTS_TO_DEPLOY)) {
|
||||
const erc1155Contract = await ERC1155MintableContract.deployFrom0xArtifactAsync(
|
||||
erc1155Artifacts.ERC1155Mintable,
|
||||
this._provider,
|
||||
txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
const erc1155Wrapper = new Erc1155Wrapper(erc1155Contract, this._provider, this._contractOwnerAddress);
|
||||
this._dummyTokenWrappers.push(erc1155Wrapper);
|
||||
}
|
||||
return this._dummyTokenWrappers;
|
||||
}
|
||||
/**
|
||||
* @dev Deploys the ERC1155 proxy
|
||||
* @return Deployed ERC1155 proxy contract instance
|
||||
*/
|
||||
public async deployProxyAsync(): Promise<ERC1155ProxyContract> {
|
||||
this._proxyContract = await ERC1155ProxyContract.deployFrom0xArtifactAsync(
|
||||
artifacts.ERC1155Proxy,
|
||||
this._provider,
|
||||
txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
this._proxyIdIfExists = await this._proxyContract.getProxyId().callAsync();
|
||||
return this._proxyContract;
|
||||
}
|
||||
/**
|
||||
* @dev Gets the ERC1155 proxy id
|
||||
*/
|
||||
public getProxyId(): string {
|
||||
this._validateProxyContractExistsOrThrow();
|
||||
return this._proxyIdIfExists as string;
|
||||
}
|
||||
/**
|
||||
* @dev generates abi-encoded tx data for transferring erc1155 fungible/non-fungible tokens.
|
||||
* @param from source address
|
||||
* @param to destination address
|
||||
* @param contractAddress address of erc155 contract
|
||||
* @param tokensToTransfer array of erc1155 tokens to transfer
|
||||
* @param valuesToTransfer array of corresponding values for each erc1155 token to transfer
|
||||
* @param valueMultiplier each value in `valuesToTransfer` is multiplied by this
|
||||
* @param receiverCallbackData callback data if `to` is a contract
|
||||
* @param authorizedSender sender of `transferFrom` transaction
|
||||
* @param extraData extra data to append to `transferFrom` transaction. Optional.
|
||||
* @return abi encoded tx data.
|
||||
*/
|
||||
public async getTransferFromAbiEncodedTxDataAsync(
|
||||
from: string,
|
||||
to: string,
|
||||
contractAddress: string,
|
||||
tokensToTransfer: BigNumber[],
|
||||
valuesToTransfer: BigNumber[],
|
||||
valueMultiplier: BigNumber,
|
||||
receiverCallbackData: string,
|
||||
authorizedSender: string,
|
||||
assetData_?: string,
|
||||
): Promise<string> {
|
||||
this._validateProxyContractExistsOrThrow();
|
||||
const assetData =
|
||||
assetData_ === undefined
|
||||
? await this._devUtils
|
||||
.encodeERC1155AssetData(contractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
|
||||
.callAsync()
|
||||
: assetData_;
|
||||
const data = this._assetProxyInterface
|
||||
.transferFrom(assetData, from, to, valueMultiplier)
|
||||
.getABIEncodedTransactionData();
|
||||
return data;
|
||||
}
|
||||
/**
|
||||
* @dev transfers erc1155 fungible/non-fungible tokens.
|
||||
* @param txData: abi-encoded tx data
|
||||
* @param authorizedSender sender of `transferFrom` transaction
|
||||
*/
|
||||
public async transferFromRawAsync(
|
||||
txData: string,
|
||||
authorizedSender: string,
|
||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||
const txHash = await this._web3Wrapper.sendTransactionAsync({
|
||||
to: (this._proxyContract as ERC1155ProxyContract).address,
|
||||
data: txData,
|
||||
from: authorizedSender,
|
||||
gas: 300000,
|
||||
});
|
||||
const txReceipt = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
|
||||
return txReceipt;
|
||||
}
|
||||
/**
|
||||
* @dev transfers erc1155 fungible/non-fungible tokens.
|
||||
* @param from source address
|
||||
* @param to destination address
|
||||
* @param contractAddress address of erc155 contract
|
||||
* @param tokensToTransfer array of erc1155 tokens to transfer
|
||||
* @param valuesToTransfer array of corresponding values for each erc1155 token to transfer
|
||||
* @param valueMultiplier each value in `valuesToTransfer` is multiplied by this
|
||||
* @param receiverCallbackData callback data if `to` is a contract
|
||||
* @param authorizedSender sender of `transferFrom` transaction
|
||||
* @param extraData extra data to append to `transferFrom` transaction. Optional.
|
||||
* @return tranasction hash.
|
||||
*/
|
||||
public async transferFromAsync(
|
||||
from: string,
|
||||
to: string,
|
||||
contractAddress: string,
|
||||
tokensToTransfer: BigNumber[],
|
||||
valuesToTransfer: BigNumber[],
|
||||
valueMultiplier: BigNumber,
|
||||
receiverCallbackData: string,
|
||||
authorizedSender: string,
|
||||
assetData_?: string,
|
||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||
this._validateProxyContractExistsOrThrow();
|
||||
const assetData =
|
||||
assetData_ === undefined
|
||||
? await this._devUtils
|
||||
.encodeERC1155AssetData(contractAddress, tokensToTransfer, valuesToTransfer, receiverCallbackData)
|
||||
.callAsync()
|
||||
: assetData_;
|
||||
const data = this._assetProxyInterface
|
||||
.transferFrom(assetData, from, to, valueMultiplier)
|
||||
.getABIEncodedTransactionData();
|
||||
const txHash = await this._web3Wrapper.sendTransactionAsync({
|
||||
to: (this._proxyContract as ERC1155ProxyContract).address,
|
||||
data,
|
||||
from: authorizedSender,
|
||||
gas: 300000,
|
||||
});
|
||||
const txReceipt = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
|
||||
return txReceipt;
|
||||
}
|
||||
/**
|
||||
* @dev For each deployed ERC1155 contract, this function mints a set of fungible/non-fungible
|
||||
* tokens for each token owner address (`_tokenOwnerAddresses`).
|
||||
* @return Balances of each token owner, across all ERC1155 contracts and tokens.
|
||||
*/
|
||||
public async setBalancesAndAllowancesAsync(): Promise<ERC1155HoldingsByOwner> {
|
||||
this._validateDummyTokenContractsExistOrThrow();
|
||||
this._validateProxyContractExistsOrThrow();
|
||||
this._initialTokenIdsByOwner = {
|
||||
fungible: {},
|
||||
nonFungible: {},
|
||||
};
|
||||
const fungibleHoldingsByOwner: ERC1155FungibleHoldingsByOwner = {};
|
||||
const nonFungibleHoldingsByOwner: ERC1155NonFungibleHoldingsByOwner = {};
|
||||
// Set balances accordingly
|
||||
for (const dummyWrapper of this._dummyTokenWrappers) {
|
||||
const dummyAddress = dummyWrapper.getContract().address;
|
||||
// tslint:disable-next-line:no-unused-variable
|
||||
for (const i of _.times(constants.NUM_ERC1155_FUNGIBLE_TOKENS_MINT)) {
|
||||
// Create a fungible token
|
||||
const tokenId = await dummyWrapper.mintFungibleTokensAsync(
|
||||
this._tokenOwnerAddresses,
|
||||
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||
);
|
||||
const tokenIdAsString = tokenId.toString();
|
||||
this._fungibleTokenIds.push(tokenIdAsString);
|
||||
// Mint tokens for each owner for this token
|
||||
for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
|
||||
// tslint:disable-next-line:no-unused-variable
|
||||
if (fungibleHoldingsByOwner[tokenOwnerAddress] === undefined) {
|
||||
fungibleHoldingsByOwner[tokenOwnerAddress] = {};
|
||||
}
|
||||
if (fungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress] === undefined) {
|
||||
fungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress] = {};
|
||||
}
|
||||
fungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress][tokenIdAsString] =
|
||||
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE;
|
||||
await dummyWrapper.setApprovalForAllAsync(
|
||||
tokenOwnerAddress,
|
||||
(this._proxyContract as ERC1155ProxyContract).address,
|
||||
true,
|
||||
);
|
||||
}
|
||||
}
|
||||
// Non-fungible tokens
|
||||
// tslint:disable-next-line:no-unused-variable
|
||||
for (const j of _.times(constants.NUM_ERC1155_NONFUNGIBLE_TOKENS_MINT)) {
|
||||
const [tokenId, nftIds] = await dummyWrapper.mintNonFungibleTokensAsync(this._tokenOwnerAddresses);
|
||||
const tokenIdAsString = tokenId.toString();
|
||||
this._nonFungibleTokenIds.push(tokenIdAsString);
|
||||
_.each(this._tokenOwnerAddresses, async (tokenOwnerAddress: string, i: number) => {
|
||||
if (nonFungibleHoldingsByOwner[tokenOwnerAddress] === undefined) {
|
||||
nonFungibleHoldingsByOwner[tokenOwnerAddress] = {};
|
||||
}
|
||||
if (nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress] === undefined) {
|
||||
nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress] = {};
|
||||
}
|
||||
if (nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress][tokenIdAsString] === undefined) {
|
||||
nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress][tokenIdAsString] = [];
|
||||
}
|
||||
this._nfts.push({ id: nftIds[i], tokenId });
|
||||
nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress][tokenIdAsString].push(nftIds[i]);
|
||||
await dummyWrapper.setApprovalForAllAsync(
|
||||
tokenOwnerAddress,
|
||||
(this._proxyContract as ERC1155ProxyContract).address,
|
||||
true,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
this._initialTokenIdsByOwner = {
|
||||
fungible: fungibleHoldingsByOwner,
|
||||
nonFungible: nonFungibleHoldingsByOwner,
|
||||
};
|
||||
return this._initialTokenIdsByOwner;
|
||||
}
|
||||
/**
|
||||
* @dev For each deployed ERC1155 contract, this function quieries the set of fungible/non-fungible
|
||||
* tokens for each token owner address (`_tokenOwnerAddresses`).
|
||||
* @return Balances of each token owner, across all ERC1155 contracts and tokens.
|
||||
*/
|
||||
public async getBalancesAsync(): Promise<ERC1155HoldingsByOwner> {
|
||||
this._validateDummyTokenContractsExistOrThrow();
|
||||
this._validateBalancesAndAllowancesSetOrThrow();
|
||||
const tokenHoldingsByOwner: ERC1155FungibleHoldingsByOwner = {};
|
||||
const nonFungibleHoldingsByOwner: ERC1155NonFungibleHoldingsByOwner = {};
|
||||
for (const dummyTokenWrapper of this._dummyTokenWrappers) {
|
||||
const tokenContract = dummyTokenWrapper.getContract();
|
||||
const tokenAddress = tokenContract.address;
|
||||
// Construct batch balance call
|
||||
const tokenOwners: string[] = [];
|
||||
const tokenIds: BigNumber[] = [];
|
||||
for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
|
||||
for (const tokenId of this._fungibleTokenIds) {
|
||||
tokenOwners.push(tokenOwnerAddress);
|
||||
tokenIds.push(new BigNumber(tokenId));
|
||||
}
|
||||
for (const nft of this._nfts) {
|
||||
tokenOwners.push(tokenOwnerAddress);
|
||||
tokenIds.push(nft.id);
|
||||
}
|
||||
}
|
||||
const balances = await dummyTokenWrapper.getBalancesAsync(tokenOwners, tokenIds);
|
||||
// Parse out balances into fungible / non-fungible token holdings
|
||||
let i = 0;
|
||||
for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
|
||||
// Fungible tokens
|
||||
for (const tokenId of this._fungibleTokenIds) {
|
||||
if (tokenHoldingsByOwner[tokenOwnerAddress] === undefined) {
|
||||
tokenHoldingsByOwner[tokenOwnerAddress] = {};
|
||||
}
|
||||
if (tokenHoldingsByOwner[tokenOwnerAddress][tokenAddress] === undefined) {
|
||||
tokenHoldingsByOwner[tokenOwnerAddress][tokenAddress] = {};
|
||||
}
|
||||
tokenHoldingsByOwner[tokenOwnerAddress][tokenAddress][tokenId] = balances[i++];
|
||||
}
|
||||
// Non-fungible tokens
|
||||
for (const nft of this._nfts) {
|
||||
if (nonFungibleHoldingsByOwner[tokenOwnerAddress] === undefined) {
|
||||
nonFungibleHoldingsByOwner[tokenOwnerAddress] = {};
|
||||
}
|
||||
if (nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress] === undefined) {
|
||||
nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress] = {};
|
||||
}
|
||||
if (
|
||||
nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress][nft.tokenId.toString()] ===
|
||||
undefined
|
||||
) {
|
||||
nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress][nft.tokenId.toString()] = [];
|
||||
}
|
||||
const isOwner = balances[i++];
|
||||
if (isOwner.isEqualTo(1)) {
|
||||
nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress][nft.tokenId.toString()].push(
|
||||
nft.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const holdingsByOwner = {
|
||||
fungible: tokenHoldingsByOwner,
|
||||
nonFungible: nonFungibleHoldingsByOwner,
|
||||
};
|
||||
return holdingsByOwner;
|
||||
}
|
||||
/**
|
||||
* @dev Set the approval for the proxy on behalf of `userAddress` .
|
||||
* @param userAddress owner of ERC1155 tokens.
|
||||
* @param contractAddress address of ERC1155 contract.
|
||||
* @param isApproved Whether to approve the proxy for all or not.
|
||||
*/
|
||||
public async setProxyAllowanceForAllAsync(
|
||||
userAddress: string,
|
||||
contractAddress: string,
|
||||
isApproved: boolean,
|
||||
): Promise<void> {
|
||||
this._validateProxyContractExistsOrThrow();
|
||||
const tokenWrapper = this.getContractWrapper(contractAddress);
|
||||
const operator = (this._proxyContract as ERC1155ProxyContract).address;
|
||||
await tokenWrapper.setApprovalForAllAsync(userAddress, operator, isApproved);
|
||||
}
|
||||
/**
|
||||
* @dev Checks if proxy is approved to transfer tokens on behalf of `userAddress`.
|
||||
* @param userAddress owner of ERC1155 tokens.
|
||||
* @param contractAddress address of ERC1155 contract.
|
||||
* @return True iff the proxy is approved for all. False otherwise.
|
||||
*/
|
||||
public async isProxyApprovedForAllAsync(userAddress: string, contractAddress: string): Promise<boolean> {
|
||||
this._validateProxyContractExistsOrThrow();
|
||||
const tokenContract = this._getContractFromAddress(contractAddress);
|
||||
const operator = (this._proxyContract as ERC1155ProxyContract).address;
|
||||
const didApproveAll = await tokenContract.isApprovedForAll(userAddress, operator).callAsync();
|
||||
return didApproveAll;
|
||||
}
|
||||
public getFungibleTokenIds(): BigNumber[] {
|
||||
const fungibleTokenIds = _.map(this._fungibleTokenIds, (tokenIdAsString: string) => {
|
||||
return new BigNumber(tokenIdAsString);
|
||||
});
|
||||
return fungibleTokenIds;
|
||||
}
|
||||
public getNonFungibleTokenIds(): BigNumber[] {
|
||||
const nonFungibleTokenIds = _.map(this._nonFungibleTokenIds, (tokenIdAsString: string) => {
|
||||
return new BigNumber(tokenIdAsString);
|
||||
});
|
||||
return nonFungibleTokenIds;
|
||||
}
|
||||
public getTokenOwnerAddresses(): string[] {
|
||||
return this._tokenOwnerAddresses;
|
||||
}
|
||||
public getContractWrapper(contractAddress: string): Erc1155Wrapper {
|
||||
const tokenWrapper = _.find(this._dummyTokenWrappers, (wrapper: Erc1155Wrapper) => {
|
||||
return wrapper.getContract().address === contractAddress;
|
||||
});
|
||||
if (tokenWrapper === undefined) {
|
||||
throw new Error(`Contract: ${contractAddress} was not deployed through ERC1155ProxyWrapper`);
|
||||
}
|
||||
return tokenWrapper;
|
||||
}
|
||||
private _getContractFromAddress(tokenAddress: string): ERC1155MintableContract {
|
||||
const tokenContractIfExists = _.find(this._dummyTokenWrappers, c => c.getContract().address === tokenAddress);
|
||||
if (tokenContractIfExists === undefined) {
|
||||
throw new Error(`Token: ${tokenAddress} was not deployed through ERC1155ProxyWrapper`);
|
||||
}
|
||||
return tokenContractIfExists.getContract();
|
||||
}
|
||||
private _validateDummyTokenContractsExistOrThrow(): void {
|
||||
if (this._dummyTokenWrappers === undefined) {
|
||||
throw new Error('Dummy ERC1155 tokens not yet deployed, please call "deployDummyTokensAsync"');
|
||||
}
|
||||
}
|
||||
private _validateProxyContractExistsOrThrow(): void {
|
||||
if (this._proxyContract === undefined) {
|
||||
throw new Error('ERC1155 proxy contract not yet deployed, please call "deployProxyAsync"');
|
||||
}
|
||||
}
|
||||
private _validateBalancesAndAllowancesSetOrThrow(): void {
|
||||
if (
|
||||
_.keys(this._initialTokenIdsByOwner.fungible).length === 0 ||
|
||||
_.keys(this._initialTokenIdsByOwner.nonFungible).length === 0
|
||||
) {
|
||||
throw new Error(
|
||||
'Dummy ERC1155 balances and allowances not yet set, please call "setBalancesAndAllowancesAsync"',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,165 +0,0 @@
|
||||
import { DevUtilsContract } from '@0x/contracts-dev-utils';
|
||||
import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contracts-erc20';
|
||||
import { constants, ERC20BalancesByOwner, txDefaults } from '@0x/contracts-test-utils';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import { ZeroExProvider } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { artifacts } from './artifacts';
|
||||
|
||||
import { ERC20ProxyContract } from './wrappers';
|
||||
|
||||
export class ERC20Wrapper {
|
||||
private readonly _tokenOwnerAddresses: string[];
|
||||
private readonly _contractOwnerAddress: string;
|
||||
private readonly _provider: ZeroExProvider;
|
||||
private readonly _dummyTokenContracts: DummyERC20TokenContract[];
|
||||
private readonly _devUtils: DevUtilsContract;
|
||||
private _proxyContract?: ERC20ProxyContract;
|
||||
private _proxyIdIfExists?: string;
|
||||
/**
|
||||
* Instanitates an ERC20Wrapper
|
||||
* @param provider Web3 provider to use for all JSON RPC requests
|
||||
* @param tokenOwnerAddresses Addresses that we want to endow as owners for dummy ERC20 tokens
|
||||
* @param contractOwnerAddress Desired owner of the contract
|
||||
* Instance of ERC20Wrapper
|
||||
*/
|
||||
constructor(provider: ZeroExProvider, tokenOwnerAddresses: string[], contractOwnerAddress: string) {
|
||||
this._dummyTokenContracts = [];
|
||||
this._provider = provider;
|
||||
this._tokenOwnerAddresses = tokenOwnerAddresses;
|
||||
this._contractOwnerAddress = contractOwnerAddress;
|
||||
this._devUtils = new DevUtilsContract(constants.NULL_ADDRESS, provider);
|
||||
}
|
||||
public async deployDummyTokensAsync(
|
||||
numberToDeploy: number,
|
||||
decimals: BigNumber,
|
||||
): Promise<DummyERC20TokenContract[]> {
|
||||
for (let i = 0; i < numberToDeploy; i++) {
|
||||
this._dummyTokenContracts.push(
|
||||
await DummyERC20TokenContract.deployFrom0xArtifactAsync(
|
||||
erc20Artifacts.DummyERC20Token,
|
||||
this._provider,
|
||||
txDefaults,
|
||||
artifacts,
|
||||
constants.DUMMY_TOKEN_NAME,
|
||||
constants.DUMMY_TOKEN_SYMBOL,
|
||||
decimals,
|
||||
constants.DUMMY_TOKEN_TOTAL_SUPPLY,
|
||||
),
|
||||
);
|
||||
}
|
||||
return this._dummyTokenContracts;
|
||||
}
|
||||
public async deployProxyAsync(): Promise<ERC20ProxyContract> {
|
||||
this._proxyContract = await ERC20ProxyContract.deployFrom0xArtifactAsync(
|
||||
artifacts.ERC20Proxy,
|
||||
this._provider,
|
||||
txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
this._proxyIdIfExists = await this._proxyContract.getProxyId().callAsync();
|
||||
return this._proxyContract;
|
||||
}
|
||||
public getProxyId(): string {
|
||||
this._validateProxyContractExistsOrThrow();
|
||||
return this._proxyIdIfExists as string;
|
||||
}
|
||||
public async setBalancesAndAllowancesAsync(): Promise<void> {
|
||||
this._validateDummyTokenContractsExistOrThrow();
|
||||
this._validateProxyContractExistsOrThrow();
|
||||
for (const dummyTokenContract of this._dummyTokenContracts) {
|
||||
for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
|
||||
await dummyTokenContract
|
||||
.setBalance(tokenOwnerAddress, constants.INITIAL_ERC20_BALANCE)
|
||||
.awaitTransactionSuccessAsync({ from: this._contractOwnerAddress });
|
||||
await dummyTokenContract
|
||||
.approve((this._proxyContract as ERC20ProxyContract).address, constants.INITIAL_ERC20_ALLOWANCE)
|
||||
.awaitTransactionSuccessAsync({ from: tokenOwnerAddress });
|
||||
}
|
||||
}
|
||||
}
|
||||
public async getBalanceAsync(userAddress: string, assetData: string): Promise<BigNumber> {
|
||||
const tokenContract = await this._getTokenContractFromAssetDataAsync(assetData);
|
||||
const balance = new BigNumber(await tokenContract.balanceOf(userAddress).callAsync());
|
||||
return balance;
|
||||
}
|
||||
public async setBalanceAsync(userAddress: string, assetData: string, amount: BigNumber): Promise<void> {
|
||||
const tokenContract = await this._getTokenContractFromAssetDataAsync(assetData);
|
||||
await tokenContract
|
||||
.setBalance(userAddress, amount)
|
||||
.awaitTransactionSuccessAsync(
|
||||
{ from: this._contractOwnerAddress },
|
||||
{ pollingIntervalMs: constants.AWAIT_TRANSACTION_MINED_MS },
|
||||
);
|
||||
}
|
||||
public async getProxyAllowanceAsync(userAddress: string, assetData: string): Promise<BigNumber> {
|
||||
const tokenContract = await this._getTokenContractFromAssetDataAsync(assetData);
|
||||
const proxyAddress = (this._proxyContract as ERC20ProxyContract).address;
|
||||
const allowance = new BigNumber(await tokenContract.allowance(userAddress, proxyAddress).callAsync());
|
||||
return allowance;
|
||||
}
|
||||
public async setAllowanceAsync(userAddress: string, assetData: string, amount: BigNumber): Promise<void> {
|
||||
const tokenContract = await this._getTokenContractFromAssetDataAsync(assetData);
|
||||
const proxyAddress = (this._proxyContract as ERC20ProxyContract).address;
|
||||
await tokenContract.approve(proxyAddress, amount).awaitTransactionSuccessAsync({ from: userAddress });
|
||||
}
|
||||
public async getBalancesAsync(): Promise<ERC20BalancesByOwner> {
|
||||
this._validateDummyTokenContractsExistOrThrow();
|
||||
const balancesByOwner: ERC20BalancesByOwner = {};
|
||||
const balances: BigNumber[] = [];
|
||||
const balanceInfo: Array<{ tokenOwnerAddress: string; tokenAddress: string }> = [];
|
||||
for (const dummyTokenContract of this._dummyTokenContracts) {
|
||||
for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
|
||||
balances.push(await dummyTokenContract.balanceOf(tokenOwnerAddress).callAsync());
|
||||
balanceInfo.push({
|
||||
tokenOwnerAddress,
|
||||
tokenAddress: dummyTokenContract.address,
|
||||
});
|
||||
}
|
||||
}
|
||||
_.forEach(balances, (balance, balanceIndex) => {
|
||||
const tokenAddress = balanceInfo[balanceIndex].tokenAddress;
|
||||
const tokenOwnerAddress = balanceInfo[balanceIndex].tokenOwnerAddress;
|
||||
if (balancesByOwner[tokenOwnerAddress] === undefined) {
|
||||
balancesByOwner[tokenOwnerAddress] = {};
|
||||
}
|
||||
const wrappedBalance = new BigNumber(balance);
|
||||
balancesByOwner[tokenOwnerAddress][tokenAddress] = wrappedBalance;
|
||||
});
|
||||
return balancesByOwner;
|
||||
}
|
||||
public addDummyTokenContract(dummy: DummyERC20TokenContract): void {
|
||||
if (this._dummyTokenContracts !== undefined) {
|
||||
this._dummyTokenContracts.push(dummy);
|
||||
}
|
||||
}
|
||||
public addTokenOwnerAddress(address: string): void {
|
||||
this._tokenOwnerAddresses.push(address);
|
||||
}
|
||||
public getTokenOwnerAddresses(): string[] {
|
||||
return this._tokenOwnerAddresses;
|
||||
}
|
||||
public getTokenAddresses(): string[] {
|
||||
const tokenAddresses = _.map(this._dummyTokenContracts, dummyTokenContract => dummyTokenContract.address);
|
||||
return tokenAddresses;
|
||||
}
|
||||
private async _getTokenContractFromAssetDataAsync(assetData: string): Promise<DummyERC20TokenContract> {
|
||||
const [proxyId, tokenAddress] = await this._devUtils.decodeERC20AssetData(assetData).callAsync(); // tslint:disable-line:no-unused-variable
|
||||
const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === tokenAddress);
|
||||
if (tokenContractIfExists === undefined) {
|
||||
throw new Error(`Token: ${tokenAddress} was not deployed through ERC20Wrapper`);
|
||||
}
|
||||
return tokenContractIfExists;
|
||||
}
|
||||
private _validateDummyTokenContractsExistOrThrow(): void {
|
||||
if (this._dummyTokenContracts === undefined) {
|
||||
throw new Error('Dummy ERC20 tokens not yet deployed, please call "deployDummyTokensAsync"');
|
||||
}
|
||||
}
|
||||
private _validateProxyContractExistsOrThrow(): void {
|
||||
if (this._proxyContract === undefined) {
|
||||
throw new Error('ERC20 proxy contract not yet deployed, please call "deployProxyAsync"');
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,220 +0,0 @@
|
||||
import { artifacts as erc721Artifacts, DummyERC721TokenContract } from '@0x/contracts-erc721';
|
||||
import { constants, ERC721TokenIdsByOwner, txDefaults } from '@0x/contracts-test-utils';
|
||||
import { generatePseudoRandomSalt } from '@0x/order-utils';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import { ZeroExProvider } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { artifacts } from './artifacts';
|
||||
|
||||
import { ERC721ProxyContract } from './wrappers';
|
||||
|
||||
export class ERC721Wrapper {
|
||||
private readonly _tokenOwnerAddresses: string[];
|
||||
private readonly _contractOwnerAddress: string;
|
||||
private readonly _provider: ZeroExProvider;
|
||||
private readonly _dummyTokenContracts: DummyERC721TokenContract[];
|
||||
private _proxyContract?: ERC721ProxyContract;
|
||||
private _proxyIdIfExists?: string;
|
||||
private _initialTokenIdsByOwner: ERC721TokenIdsByOwner = {};
|
||||
constructor(provider: ZeroExProvider, tokenOwnerAddresses: string[], contractOwnerAddress: string) {
|
||||
this._provider = provider;
|
||||
this._dummyTokenContracts = [];
|
||||
this._tokenOwnerAddresses = tokenOwnerAddresses;
|
||||
this._contractOwnerAddress = contractOwnerAddress;
|
||||
}
|
||||
public async deployDummyTokensAsync(): Promise<DummyERC721TokenContract[]> {
|
||||
// tslint:disable-next-line:no-unused-variable
|
||||
for (const i of _.times(constants.NUM_DUMMY_ERC721_TO_DEPLOY)) {
|
||||
this._dummyTokenContracts.push(
|
||||
await DummyERC721TokenContract.deployFrom0xArtifactAsync(
|
||||
erc721Artifacts.DummyERC721Token,
|
||||
this._provider,
|
||||
txDefaults,
|
||||
artifacts,
|
||||
constants.DUMMY_TOKEN_NAME,
|
||||
constants.DUMMY_TOKEN_SYMBOL,
|
||||
),
|
||||
);
|
||||
}
|
||||
return this._dummyTokenContracts;
|
||||
}
|
||||
public async deployProxyAsync(): Promise<ERC721ProxyContract> {
|
||||
this._proxyContract = await ERC721ProxyContract.deployFrom0xArtifactAsync(
|
||||
artifacts.ERC721Proxy,
|
||||
this._provider,
|
||||
txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
this._proxyIdIfExists = await this._proxyContract.getProxyId().callAsync();
|
||||
return this._proxyContract;
|
||||
}
|
||||
public getProxyId(): string {
|
||||
this._validateProxyContractExistsOrThrow();
|
||||
return this._proxyIdIfExists as string;
|
||||
}
|
||||
public async setBalancesAndAllowancesAsync(): Promise<void> {
|
||||
this._validateDummyTokenContractsExistOrThrow();
|
||||
this._validateProxyContractExistsOrThrow();
|
||||
this._initialTokenIdsByOwner = {};
|
||||
for (const dummyTokenContract of this._dummyTokenContracts) {
|
||||
for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
|
||||
// tslint:disable-next-line:no-unused-variable
|
||||
for (const i of _.times(constants.NUM_ERC721_TOKENS_TO_MINT)) {
|
||||
const tokenId = generatePseudoRandomSalt();
|
||||
await this.mintAsync(dummyTokenContract.address, tokenId, tokenOwnerAddress);
|
||||
if (this._initialTokenIdsByOwner[tokenOwnerAddress] === undefined) {
|
||||
this._initialTokenIdsByOwner[tokenOwnerAddress] = {
|
||||
[dummyTokenContract.address]: [],
|
||||
};
|
||||
}
|
||||
if (this._initialTokenIdsByOwner[tokenOwnerAddress][dummyTokenContract.address] === undefined) {
|
||||
this._initialTokenIdsByOwner[tokenOwnerAddress][dummyTokenContract.address] = [];
|
||||
}
|
||||
this._initialTokenIdsByOwner[tokenOwnerAddress][dummyTokenContract.address].push(tokenId);
|
||||
|
||||
await this.approveProxyForAllAsync(dummyTokenContract.address, tokenOwnerAddress, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public async doesTokenExistAsync(tokenAddress: string, tokenId: BigNumber): Promise<boolean> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||
const owner = await tokenContract.ownerOf(tokenId).callAsync();
|
||||
const doesExist = owner !== constants.NULL_ADDRESS;
|
||||
return doesExist;
|
||||
}
|
||||
public async approveProxyAsync(tokenAddress: string, tokenId: BigNumber): Promise<void> {
|
||||
const proxyAddress = (this._proxyContract as ERC721ProxyContract).address;
|
||||
await this.approveAsync(proxyAddress, tokenAddress, tokenId);
|
||||
}
|
||||
public async approveProxyForAllAsync(
|
||||
tokenAddress: string,
|
||||
ownerAddress: string,
|
||||
isApproved: boolean,
|
||||
): Promise<void> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||
const proxyAddress = (this._proxyContract as ERC721ProxyContract).address;
|
||||
await tokenContract.setApprovalForAll(proxyAddress, isApproved).awaitTransactionSuccessAsync({
|
||||
from: ownerAddress,
|
||||
});
|
||||
}
|
||||
public async approveAsync(to: string, tokenAddress: string, tokenId: BigNumber): Promise<void> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||
const tokenOwner = await this.ownerOfAsync(tokenAddress, tokenId);
|
||||
await tokenContract.approve(to, tokenId).awaitTransactionSuccessAsync({ from: tokenOwner });
|
||||
}
|
||||
public async transferFromAsync(
|
||||
tokenAddress: string,
|
||||
tokenId: BigNumber,
|
||||
currentOwner: string,
|
||||
userAddress: string,
|
||||
): Promise<void> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||
await tokenContract.transferFrom(currentOwner, userAddress, tokenId).awaitTransactionSuccessAsync({
|
||||
from: currentOwner,
|
||||
});
|
||||
}
|
||||
public async mintAsync(tokenAddress: string, tokenId: BigNumber, userAddress: string): Promise<void> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||
await tokenContract.mint(userAddress, tokenId).awaitTransactionSuccessAsync({
|
||||
from: this._contractOwnerAddress,
|
||||
});
|
||||
}
|
||||
public async burnAsync(tokenAddress: string, tokenId: BigNumber, owner: string): Promise<void> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||
await tokenContract.burn(owner, tokenId).awaitTransactionSuccessAsync({ from: this._contractOwnerAddress });
|
||||
}
|
||||
public async ownerOfAsync(tokenAddress: string, tokenId: BigNumber): Promise<string> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||
const owner = await tokenContract.ownerOf(tokenId).callAsync();
|
||||
return owner;
|
||||
}
|
||||
public async isOwnerAsync(userAddress: string, tokenAddress: string, tokenId: BigNumber): Promise<boolean> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||
const tokenOwner = await tokenContract.ownerOf(tokenId).callAsync();
|
||||
const isOwner = tokenOwner === userAddress;
|
||||
return isOwner;
|
||||
}
|
||||
public async isProxyApprovedForAllAsync(userAddress: string, tokenAddress: string): Promise<boolean> {
|
||||
this._validateProxyContractExistsOrThrow();
|
||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||
const operator = (this._proxyContract as ERC721ProxyContract).address;
|
||||
const didApproveAll = await tokenContract.isApprovedForAll(userAddress, operator).callAsync();
|
||||
return didApproveAll;
|
||||
}
|
||||
public async isProxyApprovedAsync(tokenAddress: string, tokenId: BigNumber): Promise<boolean> {
|
||||
this._validateProxyContractExistsOrThrow();
|
||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||
const approvedAddress = await tokenContract.getApproved(tokenId).callAsync();
|
||||
const proxyAddress = (this._proxyContract as ERC721ProxyContract).address;
|
||||
const isProxyAnApprovedOperator = approvedAddress === proxyAddress;
|
||||
return isProxyAnApprovedOperator;
|
||||
}
|
||||
public async getBalancesAsync(): Promise<ERC721TokenIdsByOwner> {
|
||||
this._validateDummyTokenContractsExistOrThrow();
|
||||
this._validateBalancesAndAllowancesSetOrThrow();
|
||||
const tokenIdsByOwner: ERC721TokenIdsByOwner = {};
|
||||
const tokenOwnerAddresses: string[] = [];
|
||||
const tokenInfo: Array<{ tokenId: BigNumber; tokenAddress: string }> = [];
|
||||
for (const dummyTokenContract of this._dummyTokenContracts) {
|
||||
for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
|
||||
const initialTokenOwnerIds = this._initialTokenIdsByOwner[tokenOwnerAddress][
|
||||
dummyTokenContract.address
|
||||
];
|
||||
for (const tokenId of initialTokenOwnerIds) {
|
||||
tokenOwnerAddresses.push(await dummyTokenContract.ownerOf(tokenId).callAsync());
|
||||
tokenInfo.push({
|
||||
tokenId,
|
||||
tokenAddress: dummyTokenContract.address,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
_.forEach(tokenOwnerAddresses, (tokenOwnerAddress, ownerIndex) => {
|
||||
const tokenAddress = tokenInfo[ownerIndex].tokenAddress;
|
||||
const tokenId = tokenInfo[ownerIndex].tokenId;
|
||||
if (tokenIdsByOwner[tokenOwnerAddress] === undefined) {
|
||||
tokenIdsByOwner[tokenOwnerAddress] = {
|
||||
[tokenAddress]: [],
|
||||
};
|
||||
}
|
||||
if (tokenIdsByOwner[tokenOwnerAddress][tokenAddress] === undefined) {
|
||||
tokenIdsByOwner[tokenOwnerAddress][tokenAddress] = [];
|
||||
}
|
||||
tokenIdsByOwner[tokenOwnerAddress][tokenAddress].push(tokenId);
|
||||
});
|
||||
return tokenIdsByOwner;
|
||||
}
|
||||
public getTokenOwnerAddresses(): string[] {
|
||||
return this._tokenOwnerAddresses;
|
||||
}
|
||||
public getTokenAddresses(): string[] {
|
||||
const tokenAddresses = _.map(this._dummyTokenContracts, dummyTokenContract => dummyTokenContract.address);
|
||||
return tokenAddresses;
|
||||
}
|
||||
private _getTokenContractFromAssetData(tokenAddress: string): DummyERC721TokenContract {
|
||||
const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === tokenAddress);
|
||||
if (tokenContractIfExists === undefined) {
|
||||
throw new Error(`Token: ${tokenAddress} was not deployed through ERC20Wrapper`);
|
||||
}
|
||||
return tokenContractIfExists;
|
||||
}
|
||||
private _validateDummyTokenContractsExistOrThrow(): void {
|
||||
if (this._dummyTokenContracts === undefined) {
|
||||
throw new Error('Dummy ERC721 tokens not yet deployed, please call "deployDummyTokensAsync"');
|
||||
}
|
||||
}
|
||||
private _validateProxyContractExistsOrThrow(): void {
|
||||
if (this._proxyContract === undefined) {
|
||||
throw new Error('ERC721 proxy contract not yet deployed, please call "deployProxyAsync"');
|
||||
}
|
||||
}
|
||||
private _validateBalancesAndAllowancesSetOrThrow(): void {
|
||||
if (_.keys(this._initialTokenIdsByOwner).length === 0) {
|
||||
throw new Error(
|
||||
'Dummy ERC721 balances and allowances not yet set, please call "setBalancesAndAllowancesAsync"',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,64 +0,0 @@
|
||||
export { artifacts } from './artifacts';
|
||||
export {
|
||||
ERC1155ProxyContract,
|
||||
ERC20BridgeProxyContract,
|
||||
ERC20ProxyContract,
|
||||
ERC721ProxyContract,
|
||||
Eth2DaiBridgeContract,
|
||||
IAssetDataContract,
|
||||
IAssetProxyContract,
|
||||
MultiAssetProxyContract,
|
||||
StaticCallProxyContract,
|
||||
TestStaticCallTargetContract,
|
||||
UniswapBridgeContract,
|
||||
} from './wrappers';
|
||||
|
||||
export { ERC20Wrapper } from './erc20_wrapper';
|
||||
export { ERC721Wrapper } from './erc721_wrapper';
|
||||
export { ERC1155ProxyWrapper } from './erc1155_proxy_wrapper';
|
||||
export { ERC1155MintableContract, Erc1155Wrapper } from '@0x/contracts-erc1155';
|
||||
export { DummyERC20TokenContract } from '@0x/contracts-erc20';
|
||||
export { DummyERC721TokenContract } from '@0x/contracts-erc721';
|
||||
export {
|
||||
ERC1155HoldingsByOwner,
|
||||
ERC20BalancesByOwner,
|
||||
ERC721TokenIdsByOwner,
|
||||
ERC1155FungibleHoldingsByOwner,
|
||||
ERC1155NonFungibleHoldingsByOwner,
|
||||
} from '@0x/contracts-test-utils';
|
||||
export {
|
||||
TransactionReceiptWithDecodedLogs,
|
||||
Provider,
|
||||
ZeroExProvider,
|
||||
JSONRPCRequestPayload,
|
||||
JSONRPCErrorCallback,
|
||||
TransactionReceiptStatus,
|
||||
JSONRPCResponsePayload,
|
||||
JSONRPCResponseError,
|
||||
ContractArtifact,
|
||||
ContractChains,
|
||||
CompilerOpts,
|
||||
StandardContractOutput,
|
||||
CompilerSettings,
|
||||
ContractChainData,
|
||||
ContractAbi,
|
||||
DevdocOutput,
|
||||
EvmOutput,
|
||||
CompilerSettingsMetadata,
|
||||
OptimizerSettings,
|
||||
OutputField,
|
||||
ParamDescription,
|
||||
EvmBytecodeOutput,
|
||||
AbiDefinition,
|
||||
FunctionAbi,
|
||||
EventAbi,
|
||||
RevertErrorAbi,
|
||||
EventParameter,
|
||||
DataItem,
|
||||
MethodAbi,
|
||||
ConstructorAbi,
|
||||
FallbackAbi,
|
||||
ConstructorStateMutability,
|
||||
TupleDataItem,
|
||||
StateMutability,
|
||||
} from 'ethereum-types';
|
@@ -1,17 +0,0 @@
|
||||
/*
|
||||
* -----------------------------------------------------------------------------
|
||||
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
export * from '../generated-wrappers/erc1155_proxy';
|
||||
export * from '../generated-wrappers/erc20_bridge_proxy';
|
||||
export * from '../generated-wrappers/erc20_proxy';
|
||||
export * from '../generated-wrappers/erc721_proxy';
|
||||
export * from '../generated-wrappers/eth2_dai_bridge';
|
||||
export * from '../generated-wrappers/i_asset_data';
|
||||
export * from '../generated-wrappers/i_asset_proxy';
|
||||
export * from '../generated-wrappers/kyber_bridge';
|
||||
export * from '../generated-wrappers/multi_asset_proxy';
|
||||
export * from '../generated-wrappers/static_call_proxy';
|
||||
export * from '../generated-wrappers/test_static_call_target';
|
||||
export * from '../generated-wrappers/uniswap_bridge';
|
@@ -1,61 +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 IKyberNetworkProxy from '../test/generated-artifacts/IKyberNetworkProxy.json';
|
||||
import * as IUniswapExchange from '../test/generated-artifacts/IUniswapExchange.json';
|
||||
import * as IUniswapExchangeFactory from '../test/generated-artifacts/IUniswapExchangeFactory.json';
|
||||
import * as KyberBridge from '../test/generated-artifacts/KyberBridge.json';
|
||||
import * as MixinAssetProxyDispatcher from '../test/generated-artifacts/MixinAssetProxyDispatcher.json';
|
||||
import * as MixinAuthorizable from '../test/generated-artifacts/MixinAuthorizable.json';
|
||||
import * as 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 TestKyberBridge from '../test/generated-artifacts/TestKyberBridge.json';
|
||||
import * as TestStaticCallTarget from '../test/generated-artifacts/TestStaticCallTarget.json';
|
||||
import * as TestUniswapBridge from '../test/generated-artifacts/TestUniswapBridge.json';
|
||||
import * as 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,
|
||||
KyberBridge: KyberBridge 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,
|
||||
IKyberNetworkProxy: IKyberNetworkProxy as ContractArtifact,
|
||||
IUniswapExchange: IUniswapExchange as ContractArtifact,
|
||||
IUniswapExchangeFactory: IUniswapExchangeFactory as ContractArtifact,
|
||||
TestERC20Bridge: TestERC20Bridge as ContractArtifact,
|
||||
TestEth2DaiBridge: TestEth2DaiBridge as ContractArtifact,
|
||||
TestKyberBridge: TestKyberBridge as ContractArtifact,
|
||||
TestStaticCallTarget: TestStaticCallTarget as ContractArtifact,
|
||||
TestUniswapBridge: TestUniswapBridge as ContractArtifact,
|
||||
};
|
@@ -1,169 +0,0 @@
|
||||
import { chaiSetup, expectTransactionFailedAsync, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils';
|
||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||
import { RevertReason } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as chai from 'chai';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { artifacts } from './artifacts';
|
||||
|
||||
import { MixinAuthorizableContract } from './wrappers';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
|
||||
describe('Authorizable', () => {
|
||||
let owner: string;
|
||||
let notOwner: string;
|
||||
let address: string;
|
||||
let authorizable: MixinAuthorizableContract;
|
||||
|
||||
before(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
|
||||
before(async () => {
|
||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
||||
[owner, address, notOwner] = _.slice(accounts, 0, 3);
|
||||
authorizable = await MixinAuthorizableContract.deployFrom0xArtifactAsync(
|
||||
artifacts.MixinAuthorizable,
|
||||
provider,
|
||||
txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
|
||||
describe('addAuthorizedAddress', () => {
|
||||
it('should revert if not called by owner', async () => {
|
||||
await expectTransactionFailedAsync(
|
||||
authorizable.addAuthorizedAddress(notOwner).sendTransactionAsync({ from: notOwner }),
|
||||
RevertReason.OnlyContractOwner,
|
||||
);
|
||||
});
|
||||
|
||||
it('should allow owner to add an authorized address', async () => {
|
||||
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
||||
const isAuthorized = await authorizable.authorized(address).callAsync();
|
||||
expect(isAuthorized).to.be.true();
|
||||
});
|
||||
|
||||
it('should revert if owner attempts to authorize a duplicate address', async () => {
|
||||
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
||||
return expectTransactionFailedAsync(
|
||||
authorizable.addAuthorizedAddress(address).sendTransactionAsync({ from: owner }),
|
||||
RevertReason.TargetAlreadyAuthorized,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeAuthorizedAddress', () => {
|
||||
it('should revert if not called by owner', async () => {
|
||||
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
||||
await expectTransactionFailedAsync(
|
||||
authorizable.removeAuthorizedAddress(address).sendTransactionAsync({ from: notOwner }),
|
||||
RevertReason.OnlyContractOwner,
|
||||
);
|
||||
});
|
||||
|
||||
it('should allow owner to remove an authorized address', async () => {
|
||||
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
||||
await authorizable.removeAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
||||
const isAuthorized = await authorizable.authorized(address).callAsync();
|
||||
expect(isAuthorized).to.be.false();
|
||||
});
|
||||
|
||||
it('should revert if owner attempts to remove an address that is not authorized', async () => {
|
||||
return expectTransactionFailedAsync(
|
||||
authorizable.removeAuthorizedAddress(address).sendTransactionAsync({
|
||||
from: owner,
|
||||
}),
|
||||
RevertReason.TargetNotAuthorized,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeAuthorizedAddressAtIndex', () => {
|
||||
it('should revert if not called by owner', async () => {
|
||||
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
||||
const index = new BigNumber(0);
|
||||
await expectTransactionFailedAsync(
|
||||
authorizable.removeAuthorizedAddressAtIndex(address, index).sendTransactionAsync({
|
||||
from: notOwner,
|
||||
}),
|
||||
RevertReason.OnlyContractOwner,
|
||||
);
|
||||
});
|
||||
|
||||
it('should revert if index is >= authorities.length', async () => {
|
||||
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
||||
const index = new BigNumber(1);
|
||||
return expectTransactionFailedAsync(
|
||||
authorizable.removeAuthorizedAddressAtIndex(address, index).sendTransactionAsync({
|
||||
from: owner,
|
||||
}),
|
||||
RevertReason.IndexOutOfBounds,
|
||||
);
|
||||
});
|
||||
|
||||
it('should revert if owner attempts to remove an address that is not authorized', async () => {
|
||||
const index = new BigNumber(0);
|
||||
return expectTransactionFailedAsync(
|
||||
authorizable.removeAuthorizedAddressAtIndex(address, index).sendTransactionAsync({
|
||||
from: owner,
|
||||
}),
|
||||
RevertReason.TargetNotAuthorized,
|
||||
);
|
||||
});
|
||||
|
||||
it('should revert if address at index does not match target', async () => {
|
||||
const address1 = address;
|
||||
const address2 = notOwner;
|
||||
await authorizable.addAuthorizedAddress(address1).awaitTransactionSuccessAsync({ from: owner });
|
||||
await authorizable.addAuthorizedAddress(address2).awaitTransactionSuccessAsync({ from: owner });
|
||||
const address1Index = new BigNumber(0);
|
||||
return expectTransactionFailedAsync(
|
||||
authorizable.removeAuthorizedAddressAtIndex(address2, address1Index).sendTransactionAsync({
|
||||
from: owner,
|
||||
}),
|
||||
RevertReason.AuthorizedAddressMismatch,
|
||||
);
|
||||
});
|
||||
|
||||
it('should allow owner to remove an authorized address', async () => {
|
||||
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
||||
const index = new BigNumber(0);
|
||||
await authorizable.removeAuthorizedAddressAtIndex(address, index).awaitTransactionSuccessAsync({
|
||||
from: owner,
|
||||
});
|
||||
const isAuthorized = await authorizable.authorized(address).callAsync();
|
||||
expect(isAuthorized).to.be.false();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAuthorizedAddresses', () => {
|
||||
it('should return all authorized addresses', async () => {
|
||||
const initial = await authorizable.getAuthorizedAddresses().callAsync();
|
||||
expect(initial).to.have.length(0);
|
||||
await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
||||
const afterAdd = await authorizable.getAuthorizedAddresses().callAsync();
|
||||
expect(afterAdd).to.have.length(1);
|
||||
expect(afterAdd).to.include(address);
|
||||
await authorizable.removeAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner });
|
||||
const afterRemove = await authorizable.getAuthorizedAddresses().callAsync();
|
||||
expect(afterRemove).to.have.length(0);
|
||||
});
|
||||
});
|
||||
});
|
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
@@ -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) });
|
||||
});
|
||||
});
|
||||
});
|
@@ -1,19 +0,0 @@
|
||||
import { env, EnvVars } from '@0x/dev-utils';
|
||||
|
||||
import { coverage, profiler, provider } from '@0x/contracts-test-utils';
|
||||
import { providerUtils } from '@0x/utils';
|
||||
|
||||
before('start web3 provider', () => {
|
||||
providerUtils.startProviderEngine(provider);
|
||||
});
|
||||
after('generate coverage report', async () => {
|
||||
if (env.parseBoolean(EnvVars.SolidityCoverage)) {
|
||||
const coverageSubprovider = coverage.getCoverageSubproviderSingleton();
|
||||
await coverageSubprovider.writeCoverageAsync();
|
||||
}
|
||||
if (env.parseBoolean(EnvVars.SolidityProfiler)) {
|
||||
const profilerSubprovider = profiler.getProfilerSubproviderSingleton();
|
||||
await profilerSubprovider.writeProfilerOutputAsync();
|
||||
}
|
||||
provider.stop();
|
||||
});
|
@@ -1,272 +0,0 @@
|
||||
import {
|
||||
blockchainTests,
|
||||
constants,
|
||||
expect,
|
||||
getRandomInteger,
|
||||
hexLeftPad,
|
||||
hexRandom,
|
||||
randomAddress,
|
||||
verifyEventsFromLogs,
|
||||
} 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 { TestKyberBridgeContract, TestKyberBridgeEvents } from './wrappers';
|
||||
|
||||
blockchainTests.resets('KyberBridge unit tests', env => {
|
||||
const KYBER_ETH_ADDRESS = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee';
|
||||
let testContract: TestKyberBridgeContract;
|
||||
|
||||
before(async () => {
|
||||
testContract = await TestKyberBridgeContract.deployFrom0xArtifactAsync(
|
||||
artifacts.TestKyberBridge,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
});
|
||||
|
||||
describe('isValidSignature()', () => {
|
||||
it('returns success bytes', async () => {
|
||||
const LEGACY_WALLET_MAGIC_VALUE = '0xb0671381';
|
||||
const result = await testContract.isValidSignature(hexRandom(), hexRandom(_.random(0, 32))).callAsync();
|
||||
expect(result).to.eq(LEGACY_WALLET_MAGIC_VALUE);
|
||||
});
|
||||
});
|
||||
|
||||
describe('bridgeTransferFrom()', () => {
|
||||
let fromTokenAddress: string;
|
||||
let toTokenAddress: string;
|
||||
let wethAddress: string;
|
||||
|
||||
before(async () => {
|
||||
wethAddress = await testContract.weth().callAsync();
|
||||
fromTokenAddress = await testContract.createToken().callAsync();
|
||||
await testContract.createToken().awaitTransactionSuccessAsync();
|
||||
toTokenAddress = await testContract.createToken().callAsync();
|
||||
await testContract.createToken().awaitTransactionSuccessAsync();
|
||||
});
|
||||
|
||||
const STATIC_KYBER_TRADE_ARGS = {
|
||||
maxBuyTokenAmount: constants.MAX_UINT256,
|
||||
walletId: constants.NULL_ADDRESS,
|
||||
};
|
||||
|
||||
interface TransferFromOpts {
|
||||
toTokenAddress: string;
|
||||
fromTokenAddress: string;
|
||||
toAddress: string;
|
||||
// Amount to pass into `bridgeTransferFrom()`
|
||||
amount: BigNumber;
|
||||
// Amount to convert in `trade()`.
|
||||
fillAmount: BigNumber;
|
||||
// Token balance of the bridge.
|
||||
fromTokenBalance: BigNumber;
|
||||
}
|
||||
|
||||
interface TransferFromResult {
|
||||
opts: TransferFromOpts;
|
||||
result: string;
|
||||
logs: DecodedLogs;
|
||||
}
|
||||
|
||||
function createTransferFromOpts(opts?: Partial<TransferFromOpts>): TransferFromOpts {
|
||||
return {
|
||||
fromTokenAddress,
|
||||
toTokenAddress,
|
||||
toAddress: randomAddress(),
|
||||
amount: getRandomInteger(1, 10e18),
|
||||
fillAmount: getRandomInteger(1, 10e18),
|
||||
fromTokenBalance: getRandomInteger(1, 10e18),
|
||||
...opts,
|
||||
};
|
||||
}
|
||||
|
||||
async function withdrawToAsync(opts?: Partial<TransferFromOpts>): Promise<TransferFromResult> {
|
||||
const _opts = createTransferFromOpts(opts);
|
||||
// Fund the contract with input tokens.
|
||||
await testContract
|
||||
.grantTokensTo(_opts.fromTokenAddress, testContract.address, _opts.fromTokenBalance)
|
||||
.awaitTransactionSuccessAsync({ value: _opts.fromTokenBalance });
|
||||
// Fund the contract with output tokens.
|
||||
await testContract.setNextFillAmount(_opts.fillAmount).awaitTransactionSuccessAsync({
|
||||
value: _opts.toTokenAddress === wethAddress ? _opts.fillAmount : constants.ZERO_AMOUNT,
|
||||
});
|
||||
// Call bridgeTransferFrom().
|
||||
const bridgeTransferFromFn = testContract.bridgeTransferFrom(
|
||||
// Output token
|
||||
_opts.toTokenAddress,
|
||||
// Random maker address.
|
||||
randomAddress(),
|
||||
// Recipient address.
|
||||
_opts.toAddress,
|
||||
// Transfer amount.
|
||||
_opts.amount,
|
||||
// ABI-encode the input token address as the bridge data.
|
||||
hexLeftPad(_opts.fromTokenAddress),
|
||||
);
|
||||
const result = await bridgeTransferFromFn.callAsync();
|
||||
const { logs } = await bridgeTransferFromFn.awaitTransactionSuccessAsync();
|
||||
return {
|
||||
opts: _opts,
|
||||
result,
|
||||
logs: (logs as any) as DecodedLogs,
|
||||
};
|
||||
}
|
||||
|
||||
function getMinimumConversionRate(opts: TransferFromOpts): BigNumber {
|
||||
return opts.amount
|
||||
.times(constants.ONE_ETHER)
|
||||
.div(opts.fromTokenBalance)
|
||||
.integerValue(BigNumber.ROUND_DOWN);
|
||||
}
|
||||
|
||||
it('returns magic bytes on success', async () => {
|
||||
const BRIDGE_SUCCESS_RETURN_DATA = AssetProxyId.ERC20Bridge;
|
||||
const { result } = await withdrawToAsync();
|
||||
expect(result).to.eq(BRIDGE_SUCCESS_RETURN_DATA);
|
||||
});
|
||||
|
||||
it('can trade token -> token', async () => {
|
||||
const { opts, logs } = await withdrawToAsync();
|
||||
verifyEventsFromLogs(
|
||||
logs,
|
||||
[
|
||||
{
|
||||
sellTokenAddress: opts.fromTokenAddress,
|
||||
buyTokenAddress: opts.toTokenAddress,
|
||||
sellAmount: opts.fromTokenBalance,
|
||||
recipientAddress: opts.toAddress,
|
||||
minConversionRate: getMinimumConversionRate(opts),
|
||||
msgValue: constants.ZERO_AMOUNT,
|
||||
...STATIC_KYBER_TRADE_ARGS,
|
||||
},
|
||||
],
|
||||
TestKyberBridgeEvents.KyberBridgeTrade,
|
||||
);
|
||||
});
|
||||
|
||||
it('can trade token -> ETH', async () => {
|
||||
const { opts, logs } = await withdrawToAsync({
|
||||
toTokenAddress: wethAddress,
|
||||
});
|
||||
verifyEventsFromLogs(
|
||||
logs,
|
||||
[
|
||||
{
|
||||
sellTokenAddress: opts.fromTokenAddress,
|
||||
buyTokenAddress: KYBER_ETH_ADDRESS,
|
||||
sellAmount: opts.fromTokenBalance,
|
||||
recipientAddress: testContract.address,
|
||||
minConversionRate: getMinimumConversionRate(opts),
|
||||
msgValue: constants.ZERO_AMOUNT,
|
||||
...STATIC_KYBER_TRADE_ARGS,
|
||||
},
|
||||
],
|
||||
TestKyberBridgeEvents.KyberBridgeTrade,
|
||||
);
|
||||
});
|
||||
|
||||
it('can trade ETH -> token', async () => {
|
||||
const { opts, logs } = await withdrawToAsync({
|
||||
fromTokenAddress: wethAddress,
|
||||
});
|
||||
verifyEventsFromLogs(
|
||||
logs,
|
||||
[
|
||||
{
|
||||
sellTokenAddress: KYBER_ETH_ADDRESS,
|
||||
buyTokenAddress: opts.toTokenAddress,
|
||||
sellAmount: opts.fromTokenBalance,
|
||||
recipientAddress: opts.toAddress,
|
||||
minConversionRate: getMinimumConversionRate(opts),
|
||||
msgValue: opts.fromTokenBalance,
|
||||
...STATIC_KYBER_TRADE_ARGS,
|
||||
},
|
||||
],
|
||||
TestKyberBridgeEvents.KyberBridgeTrade,
|
||||
);
|
||||
});
|
||||
|
||||
it('does nothing if bridge has no token balance', async () => {
|
||||
const { logs } = await withdrawToAsync({
|
||||
fromTokenBalance: constants.ZERO_AMOUNT,
|
||||
});
|
||||
expect(logs).to.be.length(0);
|
||||
});
|
||||
|
||||
it('only transfers the token if trading the same token', async () => {
|
||||
const { opts, logs } = await withdrawToAsync({
|
||||
toTokenAddress: fromTokenAddress,
|
||||
});
|
||||
verifyEventsFromLogs(
|
||||
logs,
|
||||
[
|
||||
{
|
||||
tokenAddress: fromTokenAddress,
|
||||
ownerAddress: testContract.address,
|
||||
recipientAddress: opts.toAddress,
|
||||
amount: opts.fromTokenBalance,
|
||||
},
|
||||
],
|
||||
TestKyberBridgeEvents.KyberBridgeTokenTransfer,
|
||||
);
|
||||
});
|
||||
|
||||
it('grants Kyber an allowance when selling non-WETH', async () => {
|
||||
const { opts, logs } = await withdrawToAsync();
|
||||
verifyEventsFromLogs(
|
||||
logs,
|
||||
[
|
||||
{
|
||||
tokenAddress: opts.fromTokenAddress,
|
||||
ownerAddress: testContract.address,
|
||||
spenderAddress: testContract.address,
|
||||
allowance: constants.MAX_UINT256,
|
||||
},
|
||||
],
|
||||
TestKyberBridgeEvents.KyberBridgeTokenApprove,
|
||||
);
|
||||
});
|
||||
|
||||
it('does not grant Kyber an allowance when selling WETH', async () => {
|
||||
const { logs } = await withdrawToAsync({
|
||||
fromTokenAddress: wethAddress,
|
||||
});
|
||||
verifyEventsFromLogs(logs, [], TestKyberBridgeEvents.KyberBridgeTokenApprove);
|
||||
});
|
||||
|
||||
it('withdraws WETH and passes it to Kyber when selling WETH', async () => {
|
||||
const { opts, logs } = await withdrawToAsync({
|
||||
fromTokenAddress: wethAddress,
|
||||
});
|
||||
expect(logs[0].event).to.eq(TestKyberBridgeEvents.KyberBridgeWethWithdraw);
|
||||
expect(logs[0].args).to.deep.eq({
|
||||
ownerAddress: testContract.address,
|
||||
amount: opts.fromTokenBalance,
|
||||
});
|
||||
expect(logs[1].event).to.eq(TestKyberBridgeEvents.KyberBridgeTrade);
|
||||
expect(logs[1].args.msgValue).to.bignumber.eq(opts.fromTokenBalance);
|
||||
});
|
||||
|
||||
it('wraps WETH and transfers it to the recipient when buyng WETH', async () => {
|
||||
const { opts, logs } = await withdrawToAsync({
|
||||
toTokenAddress: wethAddress,
|
||||
});
|
||||
expect(logs[0].event).to.eq(TestKyberBridgeEvents.KyberBridgeTokenApprove);
|
||||
expect(logs[0].args.tokenAddress).to.eq(opts.fromTokenAddress);
|
||||
expect(logs[1].event).to.eq(TestKyberBridgeEvents.KyberBridgeTrade);
|
||||
expect(logs[1].args.recipientAddress).to.eq(testContract.address);
|
||||
expect(logs[2].event).to.eq(TestKyberBridgeEvents.KyberBridgeWethDeposit);
|
||||
expect(logs[2].args).to.deep.eq({
|
||||
msgValue: opts.fillAmount,
|
||||
ownerAddress: testContract.address,
|
||||
amount: opts.fillAmount,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
File diff suppressed because it is too large
Load Diff
@@ -1,243 +0,0 @@
|
||||
import { DevUtilsContract } from '@0x/contracts-dev-utils';
|
||||
import {
|
||||
chaiSetup,
|
||||
constants,
|
||||
expectTransactionFailedAsync,
|
||||
expectTransactionFailedWithoutReasonAsync,
|
||||
provider,
|
||||
txDefaults,
|
||||
web3Wrapper,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||
import { AssetProxyId, RevertReason } from '@0x/types';
|
||||
import { AbiEncoder, BigNumber } from '@0x/utils';
|
||||
import * as chai from 'chai';
|
||||
import * as ethUtil from 'ethereumjs-util';
|
||||
|
||||
import { artifacts } from './artifacts';
|
||||
|
||||
import { IAssetProxyContract, StaticCallProxyContract, TestStaticCallTargetContract } from './wrappers';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
|
||||
describe('StaticCallProxy', () => {
|
||||
const amount = constants.ZERO_AMOUNT;
|
||||
let fromAddress: string;
|
||||
let toAddress: string;
|
||||
|
||||
let devUtils: DevUtilsContract;
|
||||
let staticCallProxy: IAssetProxyContract;
|
||||
let staticCallTarget: TestStaticCallTargetContract;
|
||||
|
||||
before(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
after(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
before(async () => {
|
||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
||||
[fromAddress, toAddress] = accounts.slice(0, 2);
|
||||
const staticCallProxyWithoutTransferFrom = await StaticCallProxyContract.deployFrom0xArtifactAsync(
|
||||
artifacts.StaticCallProxy,
|
||||
provider,
|
||||
txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
devUtils = new DevUtilsContract(constants.NULL_ADDRESS, provider);
|
||||
staticCallProxy = new IAssetProxyContract(
|
||||
staticCallProxyWithoutTransferFrom.address,
|
||||
provider,
|
||||
txDefaults,
|
||||
{},
|
||||
StaticCallProxyContract.deployedBytecode,
|
||||
);
|
||||
staticCallTarget = await TestStaticCallTargetContract.deployFrom0xArtifactAsync(
|
||||
artifacts.TestStaticCallTarget,
|
||||
provider,
|
||||
txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
afterEach(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
|
||||
describe('general', () => {
|
||||
it('should revert if undefined function is called', async () => {
|
||||
const undefinedSelector = '0x01020304';
|
||||
await expectTransactionFailedWithoutReasonAsync(
|
||||
web3Wrapper.sendTransactionAsync({
|
||||
from: fromAddress,
|
||||
to: staticCallProxy.address,
|
||||
value: constants.ZERO_AMOUNT,
|
||||
data: undefinedSelector,
|
||||
}),
|
||||
);
|
||||
});
|
||||
it('should have an id of 0xc339d10a', async () => {
|
||||
const proxyId = await staticCallProxy.getProxyId().callAsync();
|
||||
const expectedProxyId = AssetProxyId.StaticCall;
|
||||
expect(proxyId).to.equal(expectedProxyId);
|
||||
});
|
||||
});
|
||||
describe('transferFrom', () => {
|
||||
it('should revert if assetData lies outside the bounds of calldata', async () => {
|
||||
const staticCallData = staticCallTarget.noInputFunction().getABIEncodedTransactionData();
|
||||
const expectedResultHash = constants.KECCAK256_NULL;
|
||||
const assetData = await devUtils
|
||||
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||
.callAsync();
|
||||
const txData = staticCallProxy
|
||||
.transferFrom(assetData, fromAddress, toAddress, amount)
|
||||
.getABIEncodedTransactionData();
|
||||
const offsetToAssetData = '0000000000000000000000000000000000000000000000000000000000000080';
|
||||
const txDataEndBuffer = ethUtil.toBuffer((txData.length - 2) / 2 - 4);
|
||||
const paddedTxDataEndBuffer = ethUtil.setLengthLeft(txDataEndBuffer, 32);
|
||||
const invalidOffsetToAssetData = ethUtil.bufferToHex(paddedTxDataEndBuffer).slice(2);
|
||||
const newAssetData = '0000000000000000000000000000000000000000000000000000000000000304';
|
||||
const badTxData = `${txData.replace(offsetToAssetData, invalidOffsetToAssetData)}${newAssetData}`;
|
||||
await expectTransactionFailedWithoutReasonAsync(
|
||||
web3Wrapper.sendTransactionAsync({
|
||||
to: staticCallProxy.address,
|
||||
from: fromAddress,
|
||||
data: badTxData,
|
||||
}),
|
||||
);
|
||||
});
|
||||
it('should revert if the length of assetData is less than 100 bytes', async () => {
|
||||
const staticCallData = constants.NULL_BYTES;
|
||||
const expectedResultHash = constants.KECCAK256_NULL;
|
||||
const assetData = (await devUtils
|
||||
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||
.callAsync()).slice(0, -128);
|
||||
const assetDataByteLen = (assetData.length - 2) / 2;
|
||||
expect((assetDataByteLen - 4) % 32).to.equal(0);
|
||||
await expectTransactionFailedWithoutReasonAsync(
|
||||
staticCallProxy.transferFrom(assetData, fromAddress, toAddress, amount).sendTransactionAsync(),
|
||||
);
|
||||
});
|
||||
it('should revert if the offset to `staticCallData` points to outside of assetData', async () => {
|
||||
const staticCallData = staticCallTarget.noInputFunction().getABIEncodedTransactionData();
|
||||
const expectedResultHash = constants.KECCAK256_NULL;
|
||||
const assetData = await devUtils
|
||||
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||
.callAsync();
|
||||
const offsetToStaticCallData = '0000000000000000000000000000000000000000000000000000000000000060';
|
||||
const assetDataEndBuffer = ethUtil.toBuffer((assetData.length - 2) / 2 - 4);
|
||||
const paddedAssetDataEndBuffer = ethUtil.setLengthLeft(assetDataEndBuffer, 32);
|
||||
const invalidOffsetToStaticCallData = ethUtil.bufferToHex(paddedAssetDataEndBuffer).slice(2);
|
||||
const newStaticCallData = '0000000000000000000000000000000000000000000000000000000000000304';
|
||||
const badAssetData = `${assetData.replace(
|
||||
offsetToStaticCallData,
|
||||
invalidOffsetToStaticCallData,
|
||||
)}${newStaticCallData}`;
|
||||
await expectTransactionFailedWithoutReasonAsync(
|
||||
staticCallProxy.transferFrom(badAssetData, fromAddress, toAddress, amount).sendTransactionAsync(),
|
||||
);
|
||||
});
|
||||
it('should revert if the callTarget attempts to write to state', async () => {
|
||||
const staticCallData = staticCallTarget.updateState().getABIEncodedTransactionData();
|
||||
const expectedResultHash = constants.KECCAK256_NULL;
|
||||
const assetData = await devUtils
|
||||
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||
.callAsync();
|
||||
await expectTransactionFailedWithoutReasonAsync(
|
||||
staticCallProxy.transferFrom(assetData, fromAddress, toAddress, amount).sendTransactionAsync(),
|
||||
);
|
||||
});
|
||||
it('should revert with data provided by the callTarget if the staticcall reverts', async () => {
|
||||
const staticCallData = staticCallTarget.assertEvenNumber(new BigNumber(1)).getABIEncodedTransactionData();
|
||||
const expectedResultHash = constants.KECCAK256_NULL;
|
||||
const assetData = await devUtils
|
||||
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||
.callAsync();
|
||||
await expectTransactionFailedAsync(
|
||||
staticCallProxy.transferFrom(assetData, fromAddress, toAddress, amount).sendTransactionAsync(),
|
||||
RevertReason.TargetNotEven,
|
||||
);
|
||||
});
|
||||
it('should revert if the hash of the output is different than expected expected', async () => {
|
||||
const staticCallData = staticCallTarget.isOddNumber(new BigNumber(0)).getABIEncodedTransactionData();
|
||||
const trueAsBuffer = ethUtil.toBuffer('0x0000000000000000000000000000000000000000000000000000000000000001');
|
||||
const expectedResultHash = ethUtil.bufferToHex(ethUtil.sha3(trueAsBuffer));
|
||||
const assetData = await devUtils
|
||||
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||
.callAsync();
|
||||
await expectTransactionFailedAsync(
|
||||
staticCallProxy.transferFrom(assetData, fromAddress, toAddress, amount).sendTransactionAsync(),
|
||||
RevertReason.UnexpectedStaticCallResult,
|
||||
);
|
||||
});
|
||||
it('should be successful if a function call with no inputs and no outputs is successful', async () => {
|
||||
const staticCallData = staticCallTarget.noInputFunction().getABIEncodedTransactionData();
|
||||
const expectedResultHash = constants.KECCAK256_NULL;
|
||||
const assetData = await devUtils
|
||||
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||
.callAsync();
|
||||
await staticCallProxy
|
||||
.transferFrom(assetData, fromAddress, toAddress, amount)
|
||||
.awaitTransactionSuccessAsync();
|
||||
});
|
||||
it('should be successful if the staticCallTarget is not a contract and no return value is expected', async () => {
|
||||
const staticCallData = '0x0102030405060708';
|
||||
const expectedResultHash = constants.KECCAK256_NULL;
|
||||
const assetData = await devUtils
|
||||
.encodeStaticCallAssetData(toAddress, staticCallData, expectedResultHash)
|
||||
.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 () => {
|
||||
const staticCallData = staticCallTarget.isOddNumber(new BigNumber(1)).getABIEncodedTransactionData();
|
||||
const trueAsBuffer = ethUtil.toBuffer('0x0000000000000000000000000000000000000000000000000000000000000001');
|
||||
const expectedResultHash = ethUtil.bufferToHex(ethUtil.sha3(trueAsBuffer));
|
||||
const assetData = await devUtils
|
||||
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||
.callAsync();
|
||||
await staticCallProxy
|
||||
.transferFrom(assetData, fromAddress, toAddress, amount)
|
||||
.awaitTransactionSuccessAsync();
|
||||
});
|
||||
it('should be successful if a function with one dynamic input is successful', async () => {
|
||||
const dynamicInput = '0x0102030405060708';
|
||||
const staticCallData = staticCallTarget.dynamicInputFunction(dynamicInput).getABIEncodedTransactionData();
|
||||
const expectedResultHash = constants.KECCAK256_NULL;
|
||||
const assetData = await devUtils
|
||||
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||
.callAsync();
|
||||
await staticCallProxy
|
||||
.transferFrom(assetData, fromAddress, toAddress, amount)
|
||||
.awaitTransactionSuccessAsync();
|
||||
});
|
||||
it('should be successful if a function call returns a complex type', async () => {
|
||||
const a = new BigNumber(1);
|
||||
const b = new BigNumber(2);
|
||||
const staticCallData = staticCallTarget.returnComplexType(a, b).getABIEncodedTransactionData();
|
||||
const abiEncoder = new AbiEncoder.DynamicBytes({
|
||||
name: '',
|
||||
type: 'bytes',
|
||||
});
|
||||
const aHex = '0000000000000000000000000000000000000000000000000000000000000001';
|
||||
const bHex = '0000000000000000000000000000000000000000000000000000000000000002';
|
||||
const expectedResults = `${staticCallTarget.address}${aHex}${bHex}`;
|
||||
const offset = '0000000000000000000000000000000000000000000000000000000000000020';
|
||||
const encodedExpectedResultWithOffset = `0x${offset}${abiEncoder.encode(expectedResults).slice(2)}`;
|
||||
const expectedResultHash = ethUtil.bufferToHex(
|
||||
ethUtil.sha3(ethUtil.toBuffer(encodedExpectedResultWithOffset)),
|
||||
);
|
||||
const assetData = await devUtils
|
||||
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||
.callAsync();
|
||||
await staticCallProxy
|
||||
.transferFrom(assetData, fromAddress, toAddress, amount)
|
||||
.awaitTransactionSuccessAsync();
|
||||
});
|
||||
});
|
||||
});
|
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@@ -1,31 +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_kyber_network_proxy';
|
||||
export * from '../test/generated-wrappers/i_uniswap_exchange';
|
||||
export * from '../test/generated-wrappers/i_uniswap_exchange_factory';
|
||||
export * from '../test/generated-wrappers/kyber_bridge';
|
||||
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_kyber_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';
|
@@ -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 },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
@@ -1,46 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig",
|
||||
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
|
||||
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
|
||||
"files": [
|
||||
"generated-artifacts/ERC1155Proxy.json",
|
||||
"generated-artifacts/ERC20BridgeProxy.json",
|
||||
"generated-artifacts/ERC20Proxy.json",
|
||||
"generated-artifacts/ERC721Proxy.json",
|
||||
"generated-artifacts/Eth2DaiBridge.json",
|
||||
"generated-artifacts/IAssetData.json",
|
||||
"generated-artifacts/IAssetProxy.json",
|
||||
"generated-artifacts/KyberBridge.json",
|
||||
"generated-artifacts/MultiAssetProxy.json",
|
||||
"generated-artifacts/StaticCallProxy.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/IKyberNetworkProxy.json",
|
||||
"test/generated-artifacts/IUniswapExchange.json",
|
||||
"test/generated-artifacts/IUniswapExchangeFactory.json",
|
||||
"test/generated-artifacts/KyberBridge.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/TestKyberBridge.json",
|
||||
"test/generated-artifacts/TestStaticCallTarget.json",
|
||||
"test/generated-artifacts/TestUniswapBridge.json",
|
||||
"test/generated-artifacts/UniswapBridge.json"
|
||||
],
|
||||
"exclude": ["./deploy/solc/solc_bin"]
|
||||
}
|
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"extends": ["@0x/tslint-config"],
|
||||
"rules": {
|
||||
"custom-no-magic-numbers": false
|
||||
}
|
||||
}
|
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"extends": "../../typedoc-tsconfig",
|
||||
"compilerOptions": {
|
||||
"outDir": "lib"
|
||||
},
|
||||
"include": ["./src/**/*", "./test/**/*"]
|
||||
}
|
@@ -1,10 +0,0 @@
|
||||
# Blacklist all files
|
||||
.*
|
||||
*
|
||||
# Whitelist lib
|
||||
!lib/**/*
|
||||
# Whitelist Solidity contracts
|
||||
!contracts/src/**/*
|
||||
# Blacklist tests in lib
|
||||
/lib/test/*
|
||||
# Package specific ignore
|
@@ -1,2 +0,0 @@
|
||||
# solhint can't parse `abi.decode` syntax.
|
||||
contracts/src/MixinCoordinatorApprovalVerifier.sol
|
@@ -1,256 +0,0 @@
|
||||
[
|
||||
{
|
||||
"version": "2.1.0-beta.4",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
],
|
||||
"timestamp": 1575290197
|
||||
},
|
||||
{
|
||||
"version": "2.1.0-beta.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
],
|
||||
"timestamp": 1574238768
|
||||
},
|
||||
{
|
||||
"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,
|
||||
"version": "2.0.11",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1565296576,
|
||||
"version": "2.0.10",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "2.0.9",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Updated calls to <contract wrapper>.deployFrom0xArtifactAsync to include artifact dependencies.",
|
||||
"pr": 1995
|
||||
}
|
||||
],
|
||||
"timestamp": 1564607468
|
||||
},
|
||||
{
|
||||
"timestamp": 1563957393,
|
||||
"version": "2.0.8",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1563193019,
|
||||
"version": "2.0.7",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1563047529,
|
||||
"version": "2.0.6",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1563006338,
|
||||
"version": "2.0.5",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1558712885,
|
||||
"version": "2.0.4",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1557961111,
|
||||
"version": "2.0.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1557799313,
|
||||
"version": "2.0.2",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1557507213,
|
||||
"version": "2.0.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Make `decodeOrdersFromFillData`, `getCoordinatorApprovalHash`, and `getTransactionHash` public",
|
||||
"pr": 1729
|
||||
},
|
||||
{
|
||||
"note": "Make `assertValidTransactionOrdersApproval` internal",
|
||||
"pr": 1729
|
||||
}
|
||||
],
|
||||
"timestamp": 1554997931
|
||||
},
|
||||
{
|
||||
"version": "1.1.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Run Web3ProviderEngine without excess block polling",
|
||||
"pr": 1695
|
||||
}
|
||||
],
|
||||
"timestamp": 1553183790
|
||||
},
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Created Coordinator package"
|
||||
},
|
||||
{
|
||||
"note": "Use separate EIP712 domains for transactions and approvals",
|
||||
"pr": 1705
|
||||
},
|
||||
{
|
||||
"note": "Add `SignatureType.Invalid`",
|
||||
"pr": 1705
|
||||
},
|
||||
{
|
||||
"note": "Set `evmVersion` to `constantinople`",
|
||||
"pr": 1707
|
||||
}
|
||||
],
|
||||
"timestamp": 1553091633
|
||||
}
|
||||
]
|
@@ -1,105 +0,0 @@
|
||||
<!--
|
||||
changelogUtils.file is auto-generated using the monorepo-scripts package. Don't edit directly.
|
||||
Edit the package's CHANGELOG.json file only.
|
||||
-->
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v2.1.0-beta.4 - _December 2, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.1.0-beta.3 - _November 20, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## 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_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.0.10 - _August 8, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.0.9 - _July 31, 2019_
|
||||
|
||||
* Updated calls to <contract wrapper>.deployFrom0xArtifactAsync to include artifact dependencies. (#1995)
|
||||
|
||||
## v2.0.8 - _July 24, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.0.7 - _July 15, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.0.6 - _July 13, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.0.5 - _July 13, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.0.4 - _May 24, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.0.3 - _May 15, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.0.2 - _May 14, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.0.1 - _May 10, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.0.0 - _April 11, 2019_
|
||||
|
||||
* Make `decodeOrdersFromFillData`, `getCoordinatorApprovalHash`, and `getTransactionHash` public (#1729)
|
||||
* Make `assertValidTransactionOrdersApproval` internal (#1729)
|
||||
|
||||
## v1.1.0 - _March 21, 2019_
|
||||
|
||||
* Run Web3ProviderEngine without excess block polling (#1695)
|
||||
|
||||
## v1.0.0 - _March 20, 2019_
|
||||
|
||||
* Created Coordinator package
|
||||
* Use separate EIP712 domains for transactions and approvals (#1705)
|
||||
* Add `SignatureType.Invalid` (#1705)
|
||||
* Set `evmVersion` to `constantinople` (#1707)
|
@@ -1 +0,0 @@
|
||||
[]
|
@@ -1,73 +0,0 @@
|
||||
## 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.
|
||||
|
||||
## Installation
|
||||
|
||||
**Install**
|
||||
|
||||
```bash
|
||||
npm install @0x/contracts-coordinator --save
|
||||
```
|
||||
|
||||
## Bug bounty
|
||||
|
||||
A bug bounty for the 2.0.0 contracts is ongoing! Instructions can be found [here](https://0x.org/docs/guides/bug-bounty-program).
|
||||
|
||||
## Contributing
|
||||
|
||||
We strongly recommend that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository.
|
||||
|
||||
For proposals regarding the 0x protocol's smart contract architecture, message format, or additional functionality, go to the [0x Improvement Proposals (ZEIPs)](https://github.com/0xProject/ZEIPs) repository and follow the contribution guidelines provided therein.
|
||||
|
||||
Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started.
|
||||
|
||||
### Install Dependencies
|
||||
|
||||
If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them:
|
||||
|
||||
```bash
|
||||
yarn config set workspaces-experimental true
|
||||
```
|
||||
|
||||
Then install dependencies
|
||||
|
||||
```bash
|
||||
yarn install
|
||||
```
|
||||
|
||||
### Build
|
||||
|
||||
To build this package and all other monorepo packages that it depends on, run the following from the monorepo root directory:
|
||||
|
||||
```bash
|
||||
PKG=@0x/contracts-coordinator yarn build
|
||||
```
|
||||
|
||||
Or continuously rebuild on change:
|
||||
|
||||
```bash
|
||||
PKG=@0x/contracts-coordinator yarn watch
|
||||
```
|
||||
|
||||
### Clean
|
||||
|
||||
```bash
|
||||
yarn clean
|
||||
```
|
||||
|
||||
### Lint
|
||||
|
||||
```bash
|
||||
yarn lint
|
||||
```
|
||||
|
||||
### Run Tests
|
||||
|
||||
```bash
|
||||
yarn test
|
||||
```
|
||||
|
||||
#### Testing options
|
||||
|
||||
Contracts testing options like coverage, profiling, revert traces or backing node choosing - are described [here](../TESTING.md).
|
@@ -1,25 +0,0 @@
|
||||
{
|
||||
"artifactsDir": "./test/generated-artifacts",
|
||||
"contractsDir": "./contracts",
|
||||
"useDockerisedSolc": false,
|
||||
"compilerSettings": {
|
||||
"evmVersion": "constantinople",
|
||||
"optimizer": {
|
||||
"enabled": true,
|
||||
"runs": 1000000,
|
||||
"details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
|
||||
},
|
||||
"outputSelection": {
|
||||
"*": {
|
||||
"*": [
|
||||
"abi",
|
||||
"devdoc",
|
||||
"evm.bytecode.object",
|
||||
"evm.bytecode.sourceMap",
|
||||
"evm.deployedBytecode.object",
|
||||
"evm.deployedBytecode.sourceMap"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -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;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibEIP712ExchangeDomain.sol";
|
||||
import "./libs/LibConstants.sol";
|
||||
import "./libs/LibEIP712CoordinatorDomain.sol";
|
||||
import "./MixinSignatureValidator.sol";
|
||||
import "./MixinCoordinatorApprovalVerifier.sol";
|
||||
import "./MixinCoordinatorCore.sol";
|
||||
|
||||
|
||||
// solhint-disable no-empty-blocks
|
||||
contract Coordinator is
|
||||
LibConstants,
|
||||
MixinSignatureValidator,
|
||||
MixinCoordinatorApprovalVerifier,
|
||||
MixinCoordinatorCore
|
||||
{
|
||||
/// @param exchange Address of the 0x Exchange contract.
|
||||
/// @param chainId Chain ID of the network this contract is deployed on.
|
||||
constructor (address exchange, uint256 chainId)
|
||||
public
|
||||
LibConstants(exchange)
|
||||
LibEIP712CoordinatorDomain(chainId, address(0))
|
||||
LibEIP712ExchangeDomain(chainId, exchange)
|
||||
{}
|
||||
}
|
@@ -1,195 +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-exchange-libs/contracts/src/LibEIP712ExchangeDomain.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/LibRichErrors.sol";
|
||||
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
||||
import "./libs/LibCoordinatorApproval.sol";
|
||||
import "./libs/LibCoordinatorRichErrors.sol";
|
||||
import "./interfaces/ICoordinatorSignatureValidator.sol";
|
||||
import "./interfaces/ICoordinatorApprovalVerifier.sol";
|
||||
|
||||
|
||||
// solhint-disable avoid-tx-origin
|
||||
contract MixinCoordinatorApprovalVerifier is
|
||||
LibCoordinatorApproval,
|
||||
LibEIP712ExchangeDomain,
|
||||
ICoordinatorSignatureValidator,
|
||||
ICoordinatorApprovalVerifier
|
||||
{
|
||||
using LibBytes for bytes;
|
||||
using LibAddressArray for address[];
|
||||
|
||||
/// @dev Validates that the 0x transaction has been approved by all of the feeRecipients
|
||||
/// that correspond to each order in the transaction's Exchange calldata.
|
||||
/// @param transaction 0x transaction containing salt, signerAddress, and data.
|
||||
/// @param txOrigin Required signer of Ethereum transaction calling this function.
|
||||
/// @param transactionSignature Proof that the transaction has been signed by the signer.
|
||||
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each
|
||||
/// order in the transaction's Exchange calldata.
|
||||
function assertValidCoordinatorApprovals(
|
||||
LibZeroExTransaction.ZeroExTransaction memory transaction,
|
||||
address txOrigin,
|
||||
bytes memory transactionSignature,
|
||||
bytes[] memory approvalSignatures
|
||||
)
|
||||
public
|
||||
view
|
||||
{
|
||||
// Get the orders from the the Exchange calldata in the 0x transaction
|
||||
LibOrder.Order[] memory orders = decodeOrdersFromFillData(transaction.data);
|
||||
|
||||
// No approval is required for non-fill methods
|
||||
if (orders.length > 0) {
|
||||
// Revert if approval is invalid for transaction orders
|
||||
_assertValidTransactionOrdersApproval(
|
||||
transaction,
|
||||
orders,
|
||||
txOrigin,
|
||||
transactionSignature,
|
||||
approvalSignatures
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Decodes the orders from Exchange calldata representing any fill method.
|
||||
/// @param data Exchange calldata representing a fill method.
|
||||
/// @return orders The orders from the Exchange calldata.
|
||||
function decodeOrdersFromFillData(bytes memory data)
|
||||
public
|
||||
pure
|
||||
returns (LibOrder.Order[] memory orders)
|
||||
{
|
||||
bytes4 selector = data.readBytes4(0);
|
||||
if (
|
||||
selector == IExchange(address(0)).fillOrder.selector ||
|
||||
selector == IExchange(address(0)).fillOrKillOrder.selector
|
||||
) {
|
||||
// Decode single order
|
||||
(LibOrder.Order memory order) = abi.decode(
|
||||
data.slice(4, data.length),
|
||||
(LibOrder.Order)
|
||||
);
|
||||
orders = new LibOrder.Order[](1);
|
||||
orders[0] = order;
|
||||
} else if (
|
||||
selector == IExchange(address(0)).batchFillOrders.selector ||
|
||||
selector == IExchange(address(0)).batchFillOrdersNoThrow.selector ||
|
||||
selector == IExchange(address(0)).batchFillOrKillOrders.selector ||
|
||||
selector == IExchange(address(0)).marketBuyOrdersNoThrow.selector ||
|
||||
selector == IExchange(address(0)).marketBuyOrdersFillOrKill.selector ||
|
||||
selector == IExchange(address(0)).marketSellOrdersNoThrow.selector ||
|
||||
selector == IExchange(address(0)).marketSellOrdersFillOrKill.selector
|
||||
) {
|
||||
// Decode all orders
|
||||
// solhint-disable indent
|
||||
(orders) = abi.decode(
|
||||
data.slice(4, data.length),
|
||||
(LibOrder.Order[])
|
||||
);
|
||||
} else if (
|
||||
selector == IExchange(address(0)).matchOrders.selector ||
|
||||
selector == IExchange(address(0)).matchOrdersWithMaximalFill.selector
|
||||
) {
|
||||
// Decode left and right orders
|
||||
(LibOrder.Order memory leftOrder, LibOrder.Order memory rightOrder) = abi.decode(
|
||||
data.slice(4, data.length),
|
||||
(LibOrder.Order, LibOrder.Order)
|
||||
);
|
||||
|
||||
// Create array of orders
|
||||
orders = new LibOrder.Order[](2);
|
||||
orders[0] = leftOrder;
|
||||
orders[1] = rightOrder;
|
||||
}
|
||||
return orders;
|
||||
}
|
||||
|
||||
/// @dev Validates that the feeRecipients of a batch of order have approved a 0x transaction.
|
||||
/// @param transaction 0x transaction containing salt, signerAddress, and data.
|
||||
/// @param orders Array of order structs containing order specifications.
|
||||
/// @param txOrigin Required signer of Ethereum transaction calling this function.
|
||||
/// @param transactionSignature Proof that the transaction has been signed by the signer.
|
||||
/// @param 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,
|
||||
bytes[] memory approvalSignatures
|
||||
)
|
||||
internal
|
||||
view
|
||||
{
|
||||
// Verify that Ethereum tx signer is the same as the approved txOrigin
|
||||
if (tx.origin != txOrigin) {
|
||||
LibRichErrors.rrevert(LibCoordinatorRichErrors.InvalidOriginError(txOrigin));
|
||||
}
|
||||
|
||||
// Hash 0x transaction
|
||||
bytes32 transactionHash = LibZeroExTransaction.getTypedDataHash(transaction, EIP712_EXCHANGE_DOMAIN_HASH);
|
||||
|
||||
// Create empty list of approval signers
|
||||
address[] memory approvalSignerAddresses = new address[](0);
|
||||
|
||||
uint256 signaturesLength = approvalSignatures.length;
|
||||
for (uint256 i = 0; i != signaturesLength; i++) {
|
||||
// Create approval message
|
||||
CoordinatorApproval memory approval = CoordinatorApproval({
|
||||
txOrigin: txOrigin,
|
||||
transactionHash: transactionHash,
|
||||
transactionSignature: transactionSignature
|
||||
});
|
||||
|
||||
// Hash approval message and recover signer address
|
||||
bytes32 approvalHash = getCoordinatorApprovalHash(approval);
|
||||
address approvalSignerAddress = getSignerAddress(approvalHash, approvalSignatures[i]);
|
||||
|
||||
// Add approval signer to list of signers
|
||||
approvalSignerAddresses = approvalSignerAddresses.append(approvalSignerAddress);
|
||||
}
|
||||
|
||||
// Ethereum transaction signer gives implicit signature of approval
|
||||
approvalSignerAddresses = approvalSignerAddresses.append(tx.origin);
|
||||
|
||||
uint256 ordersLength = orders.length;
|
||||
for (uint256 i = 0; i != ordersLength; i++) {
|
||||
// Do not check approval if the order's senderAddress is null
|
||||
if (orders[i].senderAddress == address(0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ensure feeRecipient of order has approved this 0x transaction
|
||||
address approverAddress = orders[i].feeRecipientAddress;
|
||||
bool isOrderApproved = approvalSignerAddresses.contains(approverAddress);
|
||||
if (!isOrderApproved) {
|
||||
LibRichErrors.rrevert(LibCoordinatorRichErrors.InvalidApprovalSignatureError(
|
||||
transactionHash,
|
||||
approverAddress
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol";
|
||||
import "@0x/contracts-utils/contracts/src/Refundable.sol";
|
||||
import "./libs/LibConstants.sol";
|
||||
import "./interfaces/ICoordinatorCore.sol";
|
||||
import "./interfaces/ICoordinatorApprovalVerifier.sol";
|
||||
|
||||
|
||||
// solhint-disable no-empty-blocks
|
||||
contract MixinCoordinatorCore is
|
||||
Refundable,
|
||||
LibConstants,
|
||||
ICoordinatorApprovalVerifier,
|
||||
ICoordinatorCore
|
||||
{
|
||||
|
||||
/// @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 txOrigin Required signer of Ethereum transaction calling this function.
|
||||
/// @param transactionSignature Proof that the transaction has been signed by the signer.
|
||||
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each
|
||||
/// order in the transaction's Exchange calldata.
|
||||
function executeTransaction(
|
||||
LibZeroExTransaction.ZeroExTransaction memory transaction,
|
||||
address txOrigin,
|
||||
bytes memory transactionSignature,
|
||||
bytes[] memory approvalSignatures
|
||||
)
|
||||
public
|
||||
payable
|
||||
refundFinalBalance
|
||||
{
|
||||
// Validate that the 0x transaction has been approves by each feeRecipient
|
||||
assertValidCoordinatorApprovals(
|
||||
transaction,
|
||||
txOrigin,
|
||||
transactionSignature,
|
||||
approvalSignatures
|
||||
);
|
||||
|
||||
// Execute the transaction
|
||||
EXCHANGE.executeTransaction.value(msg.value)(transaction, transactionSignature);
|
||||
}
|
||||
}
|
@@ -1,142 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
|
||||
import "./interfaces/ICoordinatorSignatureValidator.sol";
|
||||
import "./libs/LibCoordinatorRichErrors.sol";
|
||||
|
||||
|
||||
contract MixinSignatureValidator is
|
||||
ICoordinatorSignatureValidator
|
||||
{
|
||||
using LibBytes for bytes;
|
||||
|
||||
/// @dev Recovers the address of a signer given a hash and signature.
|
||||
/// @param hash Any 32 byte hash.
|
||||
/// @param signature Proof that the hash has been signed by signer.
|
||||
/// @return signerAddress Address of the signer.
|
||||
function getSignerAddress(bytes32 hash, bytes memory signature)
|
||||
public
|
||||
pure
|
||||
returns (address signerAddress)
|
||||
{
|
||||
uint256 signatureLength = signature.length;
|
||||
if (signatureLength == 0) {
|
||||
LibRichErrors.rrevert(LibCoordinatorRichErrors.SignatureError(
|
||||
LibCoordinatorRichErrors.SignatureErrorCodes.INVALID_LENGTH,
|
||||
hash,
|
||||
signature
|
||||
));
|
||||
}
|
||||
|
||||
// Pop last byte off of signature byte array.
|
||||
uint8 signatureTypeRaw = uint8(signature[signature.length - 1]);
|
||||
|
||||
// Ensure signature is supported
|
||||
if (signatureTypeRaw >= uint8(SignatureType.NSignatureTypes)) {
|
||||
LibRichErrors.rrevert(LibCoordinatorRichErrors.SignatureError(
|
||||
LibCoordinatorRichErrors.SignatureErrorCodes.UNSUPPORTED,
|
||||
hash,
|
||||
signature
|
||||
));
|
||||
}
|
||||
|
||||
SignatureType signatureType = SignatureType(signatureTypeRaw);
|
||||
|
||||
// Always illegal signature.
|
||||
// This is always an implicit option since a signer can create a
|
||||
// signature array with invalid type or length. We may as well make
|
||||
// it an explicit option. This aids testing and analysis. It is
|
||||
// also the initialization value for the enum type.
|
||||
if (signatureType == SignatureType.Illegal) {
|
||||
LibRichErrors.rrevert(LibCoordinatorRichErrors.SignatureError(
|
||||
LibCoordinatorRichErrors.SignatureErrorCodes.ILLEGAL,
|
||||
hash,
|
||||
signature
|
||||
));
|
||||
|
||||
// Always invalid signature.
|
||||
// Like Illegal, this is always implicitly available and therefore
|
||||
// offered explicitly. It can be implicitly created by providing
|
||||
// a correctly formatted but incorrect signature.
|
||||
} else if (signatureType == SignatureType.Invalid) {
|
||||
LibRichErrors.rrevert(LibCoordinatorRichErrors.SignatureError(
|
||||
LibCoordinatorRichErrors.SignatureErrorCodes.INVALID,
|
||||
hash,
|
||||
signature
|
||||
));
|
||||
|
||||
// Signature using EIP712
|
||||
} else if (signatureType == SignatureType.EIP712) {
|
||||
if (signatureLength != 66) {
|
||||
LibRichErrors.rrevert(LibCoordinatorRichErrors.SignatureError(
|
||||
LibCoordinatorRichErrors.SignatureErrorCodes.INVALID_LENGTH,
|
||||
hash,
|
||||
signature
|
||||
));
|
||||
}
|
||||
uint8 v = uint8(signature[0]);
|
||||
bytes32 r = signature.readBytes32(1);
|
||||
bytes32 s = signature.readBytes32(33);
|
||||
signerAddress = ecrecover(
|
||||
hash,
|
||||
v,
|
||||
r,
|
||||
s
|
||||
);
|
||||
return signerAddress;
|
||||
|
||||
// Signed using web3.eth_sign
|
||||
} else if (signatureType == SignatureType.EthSign) {
|
||||
if (signatureLength != 66) {
|
||||
LibRichErrors.rrevert(LibCoordinatorRichErrors.SignatureError(
|
||||
LibCoordinatorRichErrors.SignatureErrorCodes.INVALID_LENGTH,
|
||||
hash,
|
||||
signature
|
||||
));
|
||||
}
|
||||
uint8 v = uint8(signature[0]);
|
||||
bytes32 r = signature.readBytes32(1);
|
||||
bytes32 s = signature.readBytes32(33);
|
||||
signerAddress = ecrecover(
|
||||
keccak256(abi.encodePacked(
|
||||
"\x19Ethereum Signed Message:\n32",
|
||||
hash
|
||||
)),
|
||||
v,
|
||||
r,
|
||||
s
|
||||
);
|
||||
return signerAddress;
|
||||
}
|
||||
|
||||
// Anything else is illegal (We do not return false because
|
||||
// the signature may actually be valid, just not in a format
|
||||
// that we currently support. In this case returning false
|
||||
// may lead the caller to incorrectly believe that the
|
||||
// signature was invalid.)
|
||||
LibRichErrors.rrevert(LibCoordinatorRichErrors.SignatureError(
|
||||
LibCoordinatorRichErrors.SignatureErrorCodes.UNSUPPORTED,
|
||||
hash,
|
||||
signature
|
||||
));
|
||||
}
|
||||
}
|
@@ -1,51 +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-exchange-libs/contracts/src/LibOrder.sol";
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol";
|
||||
|
||||
|
||||
contract ICoordinatorApprovalVerifier {
|
||||
|
||||
/// @dev Validates that the 0x transaction has been approved by all of the feeRecipients
|
||||
/// that correspond to each order in the transaction's Exchange calldata.
|
||||
/// @param transaction 0x transaction containing salt, signerAddress, and data.
|
||||
/// @param txOrigin Required signer of Ethereum transaction calling this function.
|
||||
/// @param transactionSignature Proof that the transaction has been signed by the signer.
|
||||
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each
|
||||
/// order in the transaction's Exchange calldata.
|
||||
function assertValidCoordinatorApprovals(
|
||||
LibZeroExTransaction.ZeroExTransaction memory transaction,
|
||||
address txOrigin,
|
||||
bytes memory transactionSignature,
|
||||
bytes[] memory approvalSignatures
|
||||
)
|
||||
public
|
||||
view;
|
||||
|
||||
/// @dev Decodes the orders from Exchange calldata representing any fill method.
|
||||
/// @param data Exchange calldata representing a fill method.
|
||||
/// @return orders The orders from the Exchange calldata.
|
||||
function decodeOrdersFromFillData(bytes memory data)
|
||||
public
|
||||
pure
|
||||
returns (LibOrder.Order[] memory orders);
|
||||
}
|
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol";
|
||||
|
||||
|
||||
contract ICoordinatorCore {
|
||||
|
||||
/// @dev Executes a 0x transaction that has been signed by the feeRecipients that correspond to
|
||||
/// each order in the transaction's Exchange calldata.
|
||||
/// @param transaction 0x transaction containing salt, signerAddress, and data.
|
||||
/// @param txOrigin Required signer of Ethereum transaction calling this function.
|
||||
/// @param transactionSignature Proof that the transaction has been signed by the signer.
|
||||
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each
|
||||
/// order in the transaction's Exchange calldata.
|
||||
function executeTransaction(
|
||||
LibZeroExTransaction.ZeroExTransaction memory transaction,
|
||||
address txOrigin,
|
||||
bytes memory transactionSignature,
|
||||
bytes[] memory approvalSignatures
|
||||
)
|
||||
public
|
||||
payable;
|
||||
}
|
@@ -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);
|
||||
}
|
@@ -1,36 +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-exchange/contracts/src/interfaces/ITransactions.sol";
|
||||
|
||||
|
||||
// solhint-disable var-name-mixedcase
|
||||
contract LibConstants {
|
||||
|
||||
// The 0x Exchange contract.
|
||||
ITransactions internal EXCHANGE;
|
||||
|
||||
/// @param exchange Address of the 0x Exchange contract.
|
||||
constructor (address exchange)
|
||||
public
|
||||
{
|
||||
EXCHANGE = ITransactions(exchange);
|
||||
}
|
||||
}
|
@@ -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 "./LibEIP712CoordinatorDomain.sol";
|
||||
|
||||
|
||||
contract LibCoordinatorApproval is
|
||||
LibEIP712CoordinatorDomain
|
||||
{
|
||||
// Hash for the EIP712 Coordinator approval message
|
||||
// keccak256(abi.encodePacked(
|
||||
// "CoordinatorApproval(",
|
||||
// "address txOrigin,",
|
||||
// "bytes32 transactionHash,",
|
||||
// "bytes transactionSignature",
|
||||
// ")"
|
||||
// ));
|
||||
bytes32 constant public EIP712_COORDINATOR_APPROVAL_SCHEMA_HASH =
|
||||
0xa6511c04ca44625d50986f8c36bedc09366207a17b96e347094053a9f8507168;
|
||||
|
||||
struct CoordinatorApproval {
|
||||
address txOrigin; // Required signer of Ethereum transaction that is submitting approval.
|
||||
bytes32 transactionHash; // EIP712 hash of the transaction.
|
||||
bytes transactionSignature; // Signature of the 0x transaction.
|
||||
}
|
||||
|
||||
/// @dev Calculates the EIP712 hash of the Coordinator approval mesasage using the domain
|
||||
/// separator of this contract.
|
||||
/// @param approval Coordinator approval message containing the transaction hash, and transaction
|
||||
/// signature.
|
||||
/// @return approvalHash EIP712 hash of the Coordinator approval message with the domain
|
||||
/// separator of this contract.
|
||||
function getCoordinatorApprovalHash(CoordinatorApproval memory approval)
|
||||
public
|
||||
view
|
||||
returns (bytes32 approvalHash)
|
||||
{
|
||||
approvalHash = _hashEIP712CoordinatorMessage(_hashCoordinatorApproval(approval));
|
||||
return approvalHash;
|
||||
}
|
||||
|
||||
/// @dev Calculates the EIP712 hash of the Coordinator approval mesasage with no domain separator.
|
||||
/// @param approval Coordinator approval message containing the transaction hash, and transaction
|
||||
// signature.
|
||||
/// @return result EIP712 hash of the Coordinator approval message with no domain separator.
|
||||
function _hashCoordinatorApproval(CoordinatorApproval memory approval)
|
||||
internal
|
||||
pure
|
||||
returns (bytes32 result)
|
||||
{
|
||||
bytes32 schemaHash = EIP712_COORDINATOR_APPROVAL_SCHEMA_HASH;
|
||||
bytes memory transactionSignature = approval.transactionSignature;
|
||||
address txOrigin = approval.txOrigin;
|
||||
bytes32 transactionHash = approval.transactionHash;
|
||||
|
||||
// Assembly for more efficiently computing:
|
||||
// keccak256(abi.encodePacked(
|
||||
// EIP712_COORDINATOR_APPROVAL_SCHEMA_HASH,
|
||||
// approval.txOrigin,
|
||||
// approval.transactionHash,
|
||||
// keccak256(approval.transactionSignature)
|
||||
// ));
|
||||
|
||||
assembly {
|
||||
// Compute hash of transaction signature
|
||||
let transactionSignatureHash := keccak256(add(transactionSignature, 32), mload(transactionSignature))
|
||||
|
||||
// Load free memory pointer
|
||||
let memPtr := mload(64)
|
||||
|
||||
mstore(memPtr, schemaHash) // hash of schema
|
||||
mstore(add(memPtr, 32), txOrigin) // txOrigin
|
||||
mstore(add(memPtr, 64), transactionHash) // transactionHash
|
||||
mstore(add(memPtr, 96), transactionSignatureHash) // transactionSignatureHash
|
||||
// Compute hash
|
||||
result := keccak256(memPtr, 128)
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@@ -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
|
||||
);
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -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 "./MixinCoordinatorRegistryCore.sol";
|
||||
|
||||
|
||||
// solhint-disable no-empty-blocks
|
||||
contract CoordinatorRegistry is
|
||||
MixinCoordinatorRegistryCore
|
||||
{
|
||||
constructor ()
|
||||
public
|
||||
MixinCoordinatorRegistryCore()
|
||||
{}
|
||||
}
|
@@ -1,49 +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 "./interfaces/ICoordinatorRegistryCore.sol";
|
||||
|
||||
|
||||
// solhint-disable no-empty-blocks
|
||||
contract MixinCoordinatorRegistryCore is
|
||||
ICoordinatorRegistryCore
|
||||
{
|
||||
// mapping from `coordinatorOperator` -> `coordinatorEndpoint`
|
||||
mapping (address => string) internal coordinatorEndpoints;
|
||||
|
||||
/// @dev Called by a Coordinator operator to set the endpoint of their Coordinator.
|
||||
/// @param coordinatorEndpoint Endpoint of the Coordinator as a string.
|
||||
function setCoordinatorEndpoint(string calldata coordinatorEndpoint) external {
|
||||
address coordinatorOperator = msg.sender;
|
||||
coordinatorEndpoints[coordinatorOperator] = coordinatorEndpoint;
|
||||
emit CoordinatorEndpointSet(coordinatorOperator, coordinatorEndpoint);
|
||||
}
|
||||
|
||||
/// @dev Gets the endpoint for a Coordinator.
|
||||
/// @param coordinatorOperator Operator of the Coordinator endpoint.
|
||||
/// @return coordinatorEndpoint Endpoint of the Coordinator as a string.
|
||||
function getCoordinatorEndpoint(address coordinatorOperator)
|
||||
external
|
||||
view
|
||||
returns (string memory coordinatorEndpoint)
|
||||
{
|
||||
return coordinatorEndpoints[coordinatorOperator];
|
||||
}
|
||||
}
|
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
|
||||
|
||||
// solhint-disable no-empty-blocks
|
||||
contract ICoordinatorRegistryCore
|
||||
{
|
||||
/// @dev Emitted when a Coordinator endpoint is set.
|
||||
event CoordinatorEndpointSet(
|
||||
address coordinatorOperator,
|
||||
string coordinatorEndpoint
|
||||
);
|
||||
|
||||
/// @dev Called by a Coordinator operator to set the endpoint of their Coordinator.
|
||||
/// @param coordinatorEndpoint Endpoint of the Coordinator as a string.
|
||||
function setCoordinatorEndpoint(string calldata coordinatorEndpoint) external;
|
||||
|
||||
/// @dev Gets the endpoint for a Coordinator.
|
||||
/// @param coordinatorOperator Operator of the Coordinator endpoint.
|
||||
/// @return coordinatorEndpoint Endpoint of the Coordinator as a string.
|
||||
function getCoordinatorEndpoint(address coordinatorOperator)
|
||||
external
|
||||
view
|
||||
returns (string memory coordinatorEndpoint);
|
||||
}
|
@@ -1,101 +0,0 @@
|
||||
{
|
||||
"name": "@0x/contracts-coordinator",
|
||||
"version": "2.1.0-beta.4",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
"description": "Smart contract extensions of 0x protocol",
|
||||
"main": "lib/src/index.js",
|
||||
"directories": {
|
||||
"test": "test"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "yarn pre_build && tsc -b",
|
||||
"build:ci": "yarn build",
|
||||
"pre_build": "run-s compile contracts:gen generate_contract_wrappers contracts:copy",
|
||||
"test": "yarn run_mocha",
|
||||
"rebuild_and_test": "run-s build test",
|
||||
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
||||
"test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html",
|
||||
"test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha",
|
||||
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
|
||||
"compile": "sol-compiler",
|
||||
"watch": "sol-compiler -w",
|
||||
"clean": "shx rm -rf lib test/generated-artifacts test/generated-wrappers generated-artifacts generated-wrappers",
|
||||
"generate_contract_wrappers": "abi-gen --debug --abis ${npm_package_config_abis} --output test/generated-wrappers --backend ethers",
|
||||
"lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./test/generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude ./test/generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||
"fix": "tslint --fix --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./test/generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude ./test/generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||
"coverage:report:text": "istanbul report text",
|
||||
"coverage:report:html": "istanbul report html && open coverage/index.html",
|
||||
"profiler:report:html": "istanbul report html && open coverage/index.html",
|
||||
"coverage:report:lcov": "istanbul report lcov",
|
||||
"test:circleci": "yarn test",
|
||||
"contracts:gen": "contracts-gen generate",
|
||||
"contracts:copy": "contracts-gen copy",
|
||||
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol",
|
||||
"compile:truffle": "truffle compile",
|
||||
"docs:md": "ts-doc-gen --sourceDir='$PROJECT_FILES' --output=$MD_FILE_DIR --fileExtension=mdx --tsconfig=./typedoc-tsconfig.json",
|
||||
"docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES"
|
||||
},
|
||||
"config": {
|
||||
"publicInterfaceContracts": "Coordinator,CoordinatorRegistry,LibCoordinatorApproval,LibCoordinatorRichErrors,LibEIP712CoordinatorDomain,LibConstants",
|
||||
"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."
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/0xProject/0x-monorepo.git"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/0xProject/0x-monorepo/issues"
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^4.4.0-beta.4",
|
||||
"@0x/contracts-asset-proxy": "^2.3.0-beta.4",
|
||||
"@0x/contracts-dev-utils": "^0.1.0-beta.4",
|
||||
"@0x/contracts-erc20": "^2.3.0-beta.4",
|
||||
"@0x/contracts-exchange": "^2.2.0-beta.4",
|
||||
"@0x/contracts-gen": "^1.1.0-beta.4",
|
||||
"@0x/contracts-test-utils": "^3.2.0-beta.4",
|
||||
"@0x/dev-utils": "^2.4.0-beta.4",
|
||||
"@0x/order-utils": "^8.5.0-beta.4",
|
||||
"@0x/sol-compiler": "^3.2.0-beta.4",
|
||||
"@0x/ts-doc-gen": "^0.0.22",
|
||||
"@0x/tslint-config": "^3.1.0-beta.2",
|
||||
"@0x/web3-wrapper": "^6.1.0-beta.3",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/node": "*",
|
||||
"chai": "^4.0.1",
|
||||
"chai-as-promised": "^7.1.0",
|
||||
"chai-bignumber": "^3.0.0",
|
||||
"dirty-chai": "^2.0.1",
|
||||
"lodash": "^4.17.11",
|
||||
"make-promises-safe": "^1.1.0",
|
||||
"mocha": "^6.2.0",
|
||||
"npm-run-all": "^4.1.2",
|
||||
"shx": "^0.2.2",
|
||||
"solhint": "^1.4.1",
|
||||
"truffle": "^5.0.32",
|
||||
"tslint": "5.11.0",
|
||||
"typedoc": "^0.15.0",
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/assert": "^2.2.0-beta.3",
|
||||
"@0x/base-contract": "^5.5.0-beta.4",
|
||||
"@0x/contract-addresses": "^3.3.0-beta.5",
|
||||
"@0x/contracts-utils": "^3.3.0-beta.4",
|
||||
"@0x/json-schemas": "^4.1.0-beta.3",
|
||||
"@0x/types": "^2.5.0-beta.3",
|
||||
"@0x/typescript-typings": "^4.4.0-beta.2",
|
||||
"@0x/utils": "^4.6.0-beta.3",
|
||||
"ethereum-types": "^2.2.0-beta.2",
|
||||
"http-status-codes": "^1.3.2"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
@@ -1,34 +0,0 @@
|
||||
import { hexConcat, signingUtils } from '@0x/contracts-test-utils';
|
||||
import { SignatureType, SignedZeroExTransaction } from '@0x/types';
|
||||
|
||||
import { hashUtils } from './hash_utils';
|
||||
import { SignedCoordinatorApproval } from './types';
|
||||
|
||||
export class ApprovalFactory {
|
||||
private readonly _privateKey: Buffer;
|
||||
private readonly _verifyingContractAddress: string;
|
||||
|
||||
constructor(privateKey: Buffer, verifyingContract: string) {
|
||||
this._privateKey = privateKey;
|
||||
this._verifyingContractAddress = verifyingContract;
|
||||
}
|
||||
|
||||
public async newSignedApprovalAsync(
|
||||
transaction: SignedZeroExTransaction,
|
||||
txOrigin: string,
|
||||
signatureType: SignatureType = SignatureType.EthSign,
|
||||
): Promise<SignedCoordinatorApproval> {
|
||||
const approvalHashBuff = await hashUtils.getApprovalHashBufferAsync(
|
||||
transaction,
|
||||
this._verifyingContractAddress,
|
||||
txOrigin,
|
||||
);
|
||||
const signatureBuff = signingUtils.signMessage(approvalHashBuff, this._privateKey, signatureType);
|
||||
const signedApproval = {
|
||||
txOrigin,
|
||||
transaction,
|
||||
signature: hexConcat(signatureBuff),
|
||||
};
|
||||
return signedApproval;
|
||||
}
|
||||
}
|
@@ -1,21 +0,0 @@
|
||||
/*
|
||||
* -----------------------------------------------------------------------------
|
||||
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
import { ContractArtifact } from 'ethereum-types';
|
||||
|
||||
import * as Coordinator from '../generated-artifacts/Coordinator.json';
|
||||
import * as CoordinatorRegistry from '../generated-artifacts/CoordinatorRegistry.json';
|
||||
import * as 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 = {
|
||||
Coordinator: Coordinator as ContractArtifact,
|
||||
CoordinatorRegistry: CoordinatorRegistry as ContractArtifact,
|
||||
LibCoordinatorApproval: LibCoordinatorApproval as ContractArtifact,
|
||||
LibCoordinatorRichErrors: LibCoordinatorRichErrors as ContractArtifact,
|
||||
LibEIP712CoordinatorDomain: LibEIP712CoordinatorDomain as ContractArtifact,
|
||||
LibConstants: LibConstants as ContractArtifact,
|
||||
};
|
@@ -1,820 +0,0 @@
|
||||
import { SendTransactionOpts } from '@0x/base-contract';
|
||||
import { getContractAddressesForChainOrThrow } from '@0x/contract-addresses';
|
||||
import { ExchangeContract } from '@0x/contracts-exchange';
|
||||
import { ExchangeFunctionName } from '@0x/contracts-test-utils';
|
||||
import { devConstants } from '@0x/dev-utils';
|
||||
import { schemas } from '@0x/json-schemas';
|
||||
import { generatePseudoRandomSalt, signatureUtils } from '@0x/order-utils';
|
||||
import { Order, SignedOrder, SignedZeroExTransaction, ZeroExTransaction } from '@0x/types';
|
||||
import { BigNumber, fetchAsync } from '@0x/utils';
|
||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import { CallData, ContractAbi, SupportedProvider, TxData } from 'ethereum-types';
|
||||
import * as HttpStatus from 'http-status-codes';
|
||||
import { flatten } from 'lodash';
|
||||
|
||||
import { artifacts } from '../artifacts';
|
||||
import { CoordinatorContract, CoordinatorRegistryContract } from '../wrappers';
|
||||
|
||||
import { assert } from './utils/assert';
|
||||
import {
|
||||
CoordinatorServerApprovalResponse,
|
||||
CoordinatorServerCancellationResponse,
|
||||
CoordinatorServerError,
|
||||
CoordinatorServerErrorMsg,
|
||||
CoordinatorServerResponse,
|
||||
} from './utils/coordinator_server_types';
|
||||
|
||||
import { decorators } from './utils/decorators';
|
||||
|
||||
export { CoordinatorServerErrorMsg, CoordinatorServerCancellationResponse };
|
||||
|
||||
const DEFAULT_TX_DATA = {
|
||||
gas: devConstants.GAS_LIMIT,
|
||||
gasPrice: new BigNumber(1),
|
||||
value: new BigNumber(150000), // DEFAULT_PROTOCOL_FEE_MULTIPLIER
|
||||
};
|
||||
|
||||
// tx expiration time will be set to (now + default_approval - time_buffer)
|
||||
const DEFAULT_APPROVAL_EXPIRATION_TIME_SECONDS = 90;
|
||||
const DEFAULT_EXPIRATION_TIME_BUFFER_SECONDS = 30;
|
||||
|
||||
/**
|
||||
* This class includes all the functionality related to filling or cancelling orders through
|
||||
* the 0x V2 Coordinator extension contract.
|
||||
*/
|
||||
export class CoordinatorClient {
|
||||
public abi: ContractAbi = artifacts.Coordinator.compilerOutput.abi;
|
||||
public chainId: number;
|
||||
public address: string;
|
||||
public exchangeAddress: string;
|
||||
public registryAddress: string;
|
||||
|
||||
private readonly _web3Wrapper: Web3Wrapper;
|
||||
private readonly _contractInstance: CoordinatorContract;
|
||||
private readonly _registryInstance: CoordinatorRegistryContract;
|
||||
private readonly _exchangeInstance: ExchangeContract;
|
||||
private readonly _feeRecipientToEndpoint: { [feeRecipient: string]: string } = {};
|
||||
private readonly _txDefaults: CallData = DEFAULT_TX_DATA;
|
||||
|
||||
/**
|
||||
* Validates that the 0x transaction has been approved by all of the feeRecipients that correspond to each order in the transaction's Exchange calldata.
|
||||
* Throws an error if the transaction approvals are not valid. Will not detect failures that would occur when the transaction is executed on the Exchange contract.
|
||||
* @param transaction 0x transaction containing salt, signerAddress, and data.
|
||||
* @param txOrigin Required signer of Ethereum transaction calling this function.
|
||||
* @param transactionSignature Proof that the transaction has been signed by the signer.
|
||||
* @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order in the transaction's Exchange calldata.
|
||||
*/
|
||||
@decorators.asyncZeroExErrorHandler
|
||||
public async assertValidCoordinatorApprovalsOrThrowAsync(
|
||||
transaction: ZeroExTransaction,
|
||||
txOrigin: string,
|
||||
transactionSignature: string,
|
||||
approvalSignatures: string[],
|
||||
): Promise<void> {
|
||||
assert.doesConformToSchema('transaction', transaction, schemas.zeroExTransactionSchema);
|
||||
assert.isETHAddressHex('txOrigin', txOrigin);
|
||||
assert.isHexString('transactionSignature', transactionSignature);
|
||||
for (const approvalSignature of approvalSignatures) {
|
||||
assert.isHexString('approvalSignature', approvalSignature);
|
||||
}
|
||||
return this._contractInstance
|
||||
.assertValidCoordinatorApprovals(transaction, txOrigin, transactionSignature, approvalSignatures)
|
||||
.callAsync();
|
||||
}
|
||||
/**
|
||||
* Instantiate CoordinatorClient
|
||||
* @param web3Wrapper Web3Wrapper instance to use.
|
||||
* @param chainId Desired chainId.
|
||||
* @param address The address of the Coordinator contract. If undefined, will
|
||||
* default to the known address corresponding to the chainId.
|
||||
* @param exchangeAddress The address of the Exchange contract. If undefined, will
|
||||
* default to the known address corresponding to the chainId.
|
||||
* @param registryAddress The address of the CoordinatorRegistry contract. If undefined, will
|
||||
* default to the known address corresponding to the chainId.
|
||||
*/
|
||||
constructor(
|
||||
address: string,
|
||||
provider: SupportedProvider,
|
||||
chainId: number,
|
||||
txDefaults?: Partial<TxData>,
|
||||
exchangeAddress?: string,
|
||||
registryAddress?: string,
|
||||
) {
|
||||
this.chainId = chainId;
|
||||
const contractAddresses = getContractAddressesForChainOrThrow(this.chainId);
|
||||
this.address = address === undefined ? contractAddresses.coordinator : address;
|
||||
this.exchangeAddress = exchangeAddress === undefined ? contractAddresses.exchange : exchangeAddress;
|
||||
this.registryAddress = registryAddress === undefined ? contractAddresses.coordinatorRegistry : registryAddress;
|
||||
this._web3Wrapper = new Web3Wrapper(provider);
|
||||
this._txDefaults = { ...txDefaults, ...DEFAULT_TX_DATA };
|
||||
this._contractInstance = new CoordinatorContract(
|
||||
this.address,
|
||||
this._web3Wrapper.getProvider(),
|
||||
this._web3Wrapper.getContractDefaults(),
|
||||
);
|
||||
this._registryInstance = new CoordinatorRegistryContract(
|
||||
this.registryAddress,
|
||||
this._web3Wrapper.getProvider(),
|
||||
this._web3Wrapper.getContractDefaults(),
|
||||
);
|
||||
this._exchangeInstance = new ExchangeContract(
|
||||
this.exchangeAddress,
|
||||
this._web3Wrapper.getProvider(),
|
||||
this._web3Wrapper.getContractDefaults(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills a signed order with an amount denominated in baseUnits of the taker asset. Under-the-hood, this
|
||||
* method uses the `feeRecipientAddress` of the order to look up the coordinator server endpoint registered in the
|
||||
* coordinator registry contract. It requests an approval from that coordinator server before
|
||||
* submitting the order and approval as a 0x transaction to the coordinator extension contract. The coordinator extension
|
||||
* contract validates approvals and then fills the order via the Exchange contract.
|
||||
* @param order An object that conforms to the Order interface.
|
||||
* @param takerAssetFillAmount The amount of the order (in taker asset baseUnits) that you wish to fill.
|
||||
* @param signature Signature corresponding to the order.
|
||||
* @param txData Transaction data. The `from` field should be the user Ethereum address who would like
|
||||
* to fill these orders. Must be available via the Provider supplied at instantiation.
|
||||
* @param sendTxOpts Optional arguments for sending the transaction.
|
||||
* @return Transaction hash.
|
||||
*/
|
||||
@decorators.asyncZeroExErrorHandler
|
||||
public async fillOrderAsync(
|
||||
order: Order,
|
||||
takerAssetFillAmount: BigNumber,
|
||||
signature: string,
|
||||
txData: TxData,
|
||||
sendTxOpts: Partial<SendTransactionOpts> = { shouldValidate: true },
|
||||
): Promise<string> {
|
||||
assert.doesConformToSchema('order', order, schemas.orderSchema);
|
||||
assert.isValidBaseUnitAmount('takerAssetFillAmount', takerAssetFillAmount);
|
||||
return this._executeTxThroughCoordinatorAsync(
|
||||
ExchangeFunctionName.FillOrder,
|
||||
txData,
|
||||
sendTxOpts,
|
||||
[order],
|
||||
order,
|
||||
takerAssetFillAmount,
|
||||
signature,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to fill a specific amount of an order. If the entire amount specified cannot be filled,
|
||||
* the fill order is abandoned.
|
||||
* @param order An object that conforms to the Order interface.
|
||||
* @param takerAssetFillAmount The amount of the order (in taker asset baseUnits) that you wish to fill.
|
||||
* @param signature Signature corresponding to the order.
|
||||
* @param txData Transaction data. The `from` field should be the user Ethereum address who would like
|
||||
* to fill these orders. Must be available via the Provider supplied at instantiation.
|
||||
* @param sendTxOpts Optional arguments for sending the transaction.
|
||||
* @return Transaction hash.
|
||||
*/
|
||||
@decorators.asyncZeroExErrorHandler
|
||||
public async fillOrKillOrderAsync(
|
||||
order: Order,
|
||||
takerAssetFillAmount: BigNumber,
|
||||
signature: string,
|
||||
txData: TxData,
|
||||
sendTxOpts: Partial<SendTransactionOpts> = { shouldValidate: true },
|
||||
): Promise<string> {
|
||||
assert.doesConformToSchema('order', order, schemas.orderSchema);
|
||||
assert.isValidBaseUnitAmount('takerAssetFillAmount', takerAssetFillAmount);
|
||||
return this._executeTxThroughCoordinatorAsync(
|
||||
ExchangeFunctionName.FillOrKillOrder,
|
||||
txData,
|
||||
sendTxOpts,
|
||||
[order],
|
||||
order,
|
||||
takerAssetFillAmount,
|
||||
signature,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch version of fillOrderAsync. Executes multiple fills atomically in a single transaction.
|
||||
* If any `feeRecipientAddress` in the batch is not registered to a coordinator server through the CoordinatorRegistryContract, the whole batch fails.
|
||||
* @param orders An array of orders to fill.
|
||||
* @param takerAssetFillAmounts The amounts of the orders (in taker asset baseUnits) that you wish to fill.
|
||||
* @param signatures Signatures corresponding to the orders.
|
||||
* @param txData Transaction data. The `from` field should be the user Ethereum address who would like
|
||||
* to fill these orders. Must be available via the Provider supplied at instantiation.
|
||||
* @param sendTxOpts Optional arguments for sending the transaction.
|
||||
* @return Transaction hash.
|
||||
*/
|
||||
@decorators.asyncZeroExErrorHandler
|
||||
public async batchFillOrdersAsync(
|
||||
orders: Order[],
|
||||
takerAssetFillAmounts: BigNumber[],
|
||||
signatures: string[],
|
||||
txData: TxData,
|
||||
sendTxOpts?: Partial<SendTransactionOpts>,
|
||||
): Promise<string> {
|
||||
return this._batchFillAsync(
|
||||
ExchangeFunctionName.BatchFillOrders,
|
||||
orders,
|
||||
takerAssetFillAmounts,
|
||||
signatures,
|
||||
txData,
|
||||
sendTxOpts,
|
||||
);
|
||||
}
|
||||
/**
|
||||
* No throw version of batchFillOrdersAsync
|
||||
* @param orders An array of orders to fill.
|
||||
* @param takerAssetFillAmounts The amounts of the orders (in taker asset baseUnits) that you wish to fill.
|
||||
* @param signatures Signatures corresponding to the orders.
|
||||
* @param txData Transaction data. The `from` field should be the user Ethereum address who would like
|
||||
* to fill these orders. Must be available via the Provider supplied at instantiation.
|
||||
* @param sendTxOpts Optional arguments for sending the transaction.
|
||||
* @return Transaction hash.
|
||||
*/
|
||||
|
||||
public async batchFillOrdersNoThrowAsync(
|
||||
orders: Order[],
|
||||
takerAssetFillAmounts: BigNumber[],
|
||||
signatures: string[],
|
||||
txData: TxData,
|
||||
sendTxOpts?: Partial<SendTransactionOpts>,
|
||||
): Promise<string> {
|
||||
return this._batchFillAsync(
|
||||
ExchangeFunctionName.BatchFillOrdersNoThrow,
|
||||
orders,
|
||||
takerAssetFillAmounts,
|
||||
signatures,
|
||||
txData,
|
||||
sendTxOpts,
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Batch version of fillOrKillOrderAsync. Executes multiple fills atomically in a single transaction.
|
||||
* @param orders An array of orders to fill.
|
||||
* @param takerAssetFillAmounts The amounts of the orders (in taker asset baseUnits) that you wish to fill.
|
||||
* @param signatures Signatures corresponding to the orders.
|
||||
* @param txData Transaction data. The `from` field should be the user Ethereum address who would like
|
||||
* to fill these orders. Must be available via the Provider supplied at instantiation.
|
||||
* @param sendTxOpts Optional arguments for sending the transaction.
|
||||
* @return Transaction hash.
|
||||
*/
|
||||
@decorators.asyncZeroExErrorHandler
|
||||
public async batchFillOrKillOrdersAsync(
|
||||
orders: Order[],
|
||||
takerAssetFillAmounts: BigNumber[],
|
||||
signatures: string[],
|
||||
txData: TxData,
|
||||
sendTxOpts?: Partial<SendTransactionOpts>,
|
||||
): Promise<string> {
|
||||
return this._batchFillAsync(
|
||||
ExchangeFunctionName.BatchFillOrKillOrders,
|
||||
orders,
|
||||
takerAssetFillAmounts,
|
||||
signatures,
|
||||
txData,
|
||||
sendTxOpts,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes multiple calls of fillOrder until total amount of makerAsset is bought by taker.
|
||||
* If any fill reverts, the error is caught and ignored. Finally, reverts if < makerAssetFillAmount has been bought.
|
||||
* NOTE: This function does not enforce that the makerAsset is the same for each order.
|
||||
* @param orders Array of order specifications.
|
||||
* @param makerAssetFillAmount Desired amount of makerAsset to buy.
|
||||
* @param signatures Proofs that orders have been signed by makers.
|
||||
* @param txData Transaction data. The `from` field should be the user Ethereum address who would like
|
||||
* to fill these orders. Must be available via the Provider supplied at instantiation.
|
||||
* @param sendTxOpts Optional arguments for sending the transaction.
|
||||
* @return Transaction hash.
|
||||
*/
|
||||
@decorators.asyncZeroExErrorHandler
|
||||
public async marketBuyOrdersFillOrKillAsync(
|
||||
orders: Order[],
|
||||
makerAssetFillAmount: BigNumber,
|
||||
signatures: string[],
|
||||
txData: TxData,
|
||||
sendTxOpts: SendTransactionOpts = { shouldValidate: true },
|
||||
): Promise<string> {
|
||||
return this._marketBuySellOrdersAsync(
|
||||
ExchangeFunctionName.MarketBuyOrdersFillOrKill,
|
||||
orders,
|
||||
makerAssetFillAmount,
|
||||
signatures,
|
||||
txData,
|
||||
sendTxOpts,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* No throw version of marketBuyOrdersFillOrKillAsync
|
||||
* @param orders An array of orders to fill.
|
||||
* @param makerAssetFillAmount Maker asset fill amount.
|
||||
* @param signatures Signatures corresponding to the orders.
|
||||
* @param txData Transaction data. The `from` field should be the user Ethereum address who would like
|
||||
* to fill these orders. Must be available via the Provider supplied at instantiation.
|
||||
* @param sendTxOpts Optional arguments for sending the transaction.
|
||||
* @return Transaction hash.
|
||||
*/
|
||||
@decorators.asyncZeroExErrorHandler
|
||||
public async marketBuyOrdersNoThrowAsync(
|
||||
orders: Order[],
|
||||
makerAssetFillAmount: BigNumber,
|
||||
signatures: string[],
|
||||
txData: TxData,
|
||||
sendTxOpts: SendTransactionOpts = { shouldValidate: true },
|
||||
): Promise<string> {
|
||||
return this._marketBuySellOrdersAsync(
|
||||
ExchangeFunctionName.MarketBuyOrdersNoThrow,
|
||||
orders,
|
||||
makerAssetFillAmount,
|
||||
signatures,
|
||||
txData,
|
||||
sendTxOpts,
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Executes multiple calls of fillOrder until total amount of takerAsset is sold by taker.
|
||||
* If any fill reverts, the error is caught and ignored. Finally, reverts if < takerAssetFillAmount has been sold.
|
||||
* NOTE: This function does not enforce that the takerAsset is the same for each order.
|
||||
* @param orders Array of order specifications.
|
||||
* @param takerAssetFillAmount Desired amount of takerAsset to sell.
|
||||
* @param signatures Proofs that orders have been signed by makers.
|
||||
* @param txData Transaction data. The `from` field should be the user Ethereum address who would like
|
||||
* to fill these orders. Must be available via the Provider supplied at instantiation.
|
||||
* @param sendTxOpts Optional arguments for sending the transaction.
|
||||
* @return Transaction hash.
|
||||
*/
|
||||
@decorators.asyncZeroExErrorHandler
|
||||
public async marketSellOrdersFillOrKillAsync(
|
||||
orders: Order[],
|
||||
takerAssetFillAmount: BigNumber,
|
||||
signatures: string[],
|
||||
txData: TxData,
|
||||
sendTxOpts: SendTransactionOpts = { shouldValidate: true },
|
||||
): Promise<string> {
|
||||
return this._marketBuySellOrdersAsync(
|
||||
ExchangeFunctionName.MarketSellOrdersFillOrKill,
|
||||
orders,
|
||||
takerAssetFillAmount,
|
||||
signatures,
|
||||
txData,
|
||||
sendTxOpts,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* No throw version of marketSellOrdersAsync
|
||||
* @param orders An array of orders to fill.
|
||||
* @param takerAssetFillAmount Taker asset fill amount.
|
||||
* @param signatures Signatures corresponding to the orders.
|
||||
* @param txData Transaction data. The `from` field should be the user Ethereum address who would like
|
||||
* to fill these orders. Must be available via the Provider supplied at instantiation.
|
||||
* @param sendTxOpts Optional arguments for sending the transaction.
|
||||
* @return Transaction hash.
|
||||
*/
|
||||
@decorators.asyncZeroExErrorHandler
|
||||
public async marketSellOrdersNoThrowAsync(
|
||||
orders: Order[],
|
||||
takerAssetFillAmount: BigNumber,
|
||||
signatures: string[],
|
||||
txData: TxData,
|
||||
sendTxOpts: SendTransactionOpts = { shouldValidate: true },
|
||||
): Promise<string> {
|
||||
return this._marketBuySellOrdersAsync(
|
||||
ExchangeFunctionName.MarketSellOrdersNoThrow,
|
||||
orders,
|
||||
takerAssetFillAmount,
|
||||
signatures,
|
||||
txData,
|
||||
sendTxOpts,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels an order on-chain by submitting an Ethereum transaction.
|
||||
* @param order An object that conforms to the Order interface. The order you would like to cancel.
|
||||
* @param txData Transaction data. The `from` field should be the maker's Ethereum address. Must be available
|
||||
* via the Provider supplied at instantiation.
|
||||
* @param sendTxOpts Optional arguments for sending the transaction.
|
||||
* @return Transaction hash.
|
||||
*/
|
||||
@decorators.asyncZeroExErrorHandler
|
||||
public async hardCancelOrderAsync(
|
||||
order: Order,
|
||||
txData: TxData,
|
||||
sendTxOpts: SendTransactionOpts = { shouldValidate: true },
|
||||
): Promise<string> {
|
||||
assert.doesConformToSchema('order', order, schemas.orderSchema);
|
||||
return this._executeTxThroughCoordinatorAsync(
|
||||
ExchangeFunctionName.CancelOrder,
|
||||
txData,
|
||||
sendTxOpts,
|
||||
[order],
|
||||
order,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch version of hardCancelOrderAsync. Cancels orders on-chain by submitting an Ethereum transaction.
|
||||
* Executes multiple cancels atomically in a single transaction.
|
||||
* @param orders An array of orders to cancel.
|
||||
* @param txData Transaction data. The `from` field should be the maker's Ethereum address. Must be available
|
||||
* via the Provider supplied at instantiation.
|
||||
* @param sendTxOpts Optional arguments for sending the transaction.
|
||||
* @return Transaction hash.
|
||||
*/
|
||||
@decorators.asyncZeroExErrorHandler
|
||||
public async batchHardCancelOrdersAsync(
|
||||
orders: Order[],
|
||||
txData: TxData,
|
||||
sendTxOpts: SendTransactionOpts = { shouldValidate: true },
|
||||
): Promise<string> {
|
||||
assert.doesConformToSchema('orders', orders, schemas.ordersSchema);
|
||||
return this._executeTxThroughCoordinatorAsync(
|
||||
ExchangeFunctionName.BatchCancelOrders,
|
||||
txData,
|
||||
sendTxOpts,
|
||||
orders,
|
||||
orders,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels orders on-chain by submitting an Ethereum transaction.
|
||||
* Cancels all orders created by makerAddress with a salt less than or equal to the targetOrderEpoch
|
||||
* and senderAddress equal to coordinator extension contract address.
|
||||
* @param targetOrderEpoch Target order epoch.
|
||||
* @param txData Transaction data. The `from` field should be the maker's Ethereum address. Must be available
|
||||
* via the Provider supplied at instantiation.
|
||||
* @param sendTxOpts Optional arguments for sending the transaction.
|
||||
* @return Transaction hash.
|
||||
*/
|
||||
@decorators.asyncZeroExErrorHandler
|
||||
public async hardCancelOrdersUpToAsync(
|
||||
targetOrderEpoch: BigNumber,
|
||||
txData: TxData,
|
||||
sendTxOpts: SendTransactionOpts = { shouldValidate: true },
|
||||
): Promise<string> {
|
||||
assert.isBigNumber('targetOrderEpoch', targetOrderEpoch);
|
||||
return this._executeTxThroughCoordinatorAsync(
|
||||
ExchangeFunctionName.CancelOrdersUpTo,
|
||||
txData,
|
||||
sendTxOpts,
|
||||
[],
|
||||
targetOrderEpoch,
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Soft cancel a given order.
|
||||
* Soft cancels are recorded only on coordinator operator servers and do not involve an Ethereum transaction.
|
||||
* See [soft cancels](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/coordinator-specification.md#soft-cancels).
|
||||
* @param order An object that conforms to the Order or SignedOrder interface. The order you would like to cancel.
|
||||
* @return CoordinatorServerCancellationResponse. See [Cancellation Response](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/coordinator-specification.md#response).
|
||||
*/
|
||||
@decorators.asyncZeroExErrorHandler
|
||||
public async softCancelAsync(order: Order): Promise<CoordinatorServerCancellationResponse> {
|
||||
assert.doesConformToSchema('order', order, schemas.orderSchema);
|
||||
assert.isETHAddressHex('feeRecipientAddress', order.feeRecipientAddress);
|
||||
assert.isSenderAddressAsync('makerAddress', order.makerAddress, this._web3Wrapper);
|
||||
|
||||
const data = this._exchangeInstance.cancelOrder(order).getABIEncodedTransactionData();
|
||||
const transaction = await this._generateSignedZeroExTransactionAsync(data, order.makerAddress);
|
||||
const endpoint = await this._getServerEndpointOrThrowAsync(order);
|
||||
|
||||
const response = await this._executeServerRequestAsync(transaction, order.makerAddress, endpoint);
|
||||
if (response.isError) {
|
||||
const approvedOrders = new Array();
|
||||
const cancellations = new Array();
|
||||
const errors = [
|
||||
{
|
||||
...response,
|
||||
orders: [order],
|
||||
},
|
||||
];
|
||||
throw new CoordinatorServerError(
|
||||
CoordinatorServerErrorMsg.CancellationFailed,
|
||||
approvedOrders,
|
||||
cancellations,
|
||||
errors,
|
||||
);
|
||||
} else {
|
||||
return response.body as CoordinatorServerCancellationResponse;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Batch version of softCancelOrderAsync. Requests multiple soft cancels
|
||||
* @param orders An array of orders to cancel.
|
||||
* @return CoordinatorServerCancellationResponse. See [Cancellation Response](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/coordinator-specification.md#response).
|
||||
*/
|
||||
@decorators.asyncZeroExErrorHandler
|
||||
public async batchSoftCancelAsync(orders: SignedOrder[]): Promise<CoordinatorServerCancellationResponse[]> {
|
||||
assert.doesConformToSchema('orders', orders, schemas.ordersSchema);
|
||||
const makerAddress = getMakerAddressOrThrow(orders);
|
||||
assert.isSenderAddressAsync('makerAddress', makerAddress, this._web3Wrapper);
|
||||
const data = this._exchangeInstance.batchCancelOrders(orders).getABIEncodedTransactionData();
|
||||
const transaction = await this._generateSignedZeroExTransactionAsync(data, makerAddress);
|
||||
|
||||
// make server requests
|
||||
const errorResponses: CoordinatorServerResponse[] = [];
|
||||
const successResponses: CoordinatorServerCancellationResponse[] = [];
|
||||
const serverEndpointsToOrders = await this._mapServerEndpointsToOrdersAsync(orders);
|
||||
for (const endpoint of Object.keys(serverEndpointsToOrders)) {
|
||||
const response = await this._executeServerRequestAsync(transaction, makerAddress, endpoint);
|
||||
if (response.isError) {
|
||||
errorResponses.push(response);
|
||||
} else {
|
||||
successResponses.push(response.body as CoordinatorServerCancellationResponse);
|
||||
}
|
||||
}
|
||||
|
||||
// if no errors
|
||||
if (errorResponses.length === 0) {
|
||||
return successResponses;
|
||||
} else {
|
||||
// lookup orders with errors
|
||||
const errorsWithOrders = errorResponses.map(resp => {
|
||||
const endpoint = resp.coordinatorOperator;
|
||||
const _orders = serverEndpointsToOrders[endpoint];
|
||||
return {
|
||||
...resp,
|
||||
orders: _orders,
|
||||
};
|
||||
});
|
||||
|
||||
const approvedOrders = new Array();
|
||||
const cancellations = successResponses;
|
||||
// return errors and approvals
|
||||
throw new CoordinatorServerError(
|
||||
CoordinatorServerErrorMsg.CancellationFailed,
|
||||
approvedOrders,
|
||||
cancellations,
|
||||
errorsWithOrders,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @returns Signer address.
|
||||
*/
|
||||
@decorators.asyncZeroExErrorHandler
|
||||
public async getSignerAddressAsync(hash: string, signature: string): Promise<string> {
|
||||
assert.isHexString('hash', hash);
|
||||
assert.isHexString('signature', signature);
|
||||
const signerAddress = await this._contractInstance.getSignerAddress(hash, signature).callAsync();
|
||||
return signerAddress;
|
||||
}
|
||||
|
||||
private async _marketBuySellOrdersAsync(
|
||||
exchangeFn: ExchangeFunctionName,
|
||||
orders: Order[],
|
||||
assetFillAmount: BigNumber,
|
||||
signatures: string[],
|
||||
txData: TxData,
|
||||
sendTxOpts: SendTransactionOpts = { shouldValidate: true },
|
||||
): Promise<string> {
|
||||
assert.doesConformToSchema('orders', orders, schemas.ordersSchema);
|
||||
assert.isBigNumber('assetFillAmount', assetFillAmount);
|
||||
return this._executeTxThroughCoordinatorAsync(
|
||||
exchangeFn,
|
||||
txData,
|
||||
sendTxOpts,
|
||||
orders,
|
||||
orders,
|
||||
assetFillAmount,
|
||||
signatures,
|
||||
);
|
||||
}
|
||||
|
||||
private async _batchFillAsync(
|
||||
exchangeFn: ExchangeFunctionName,
|
||||
orders: Order[],
|
||||
takerAssetFillAmounts: BigNumber[],
|
||||
signatures: string[],
|
||||
txData: TxData,
|
||||
sendTxOpts: SendTransactionOpts = { shouldValidate: true },
|
||||
): Promise<string> {
|
||||
assert.doesConformToSchema('orders', orders, schemas.ordersSchema);
|
||||
takerAssetFillAmounts.forEach(takerAssetFillAmount =>
|
||||
assert.isValidBaseUnitAmount('takerAssetFillAmount', takerAssetFillAmount),
|
||||
);
|
||||
return this._executeTxThroughCoordinatorAsync(
|
||||
exchangeFn,
|
||||
txData,
|
||||
sendTxOpts,
|
||||
orders,
|
||||
orders,
|
||||
takerAssetFillAmounts,
|
||||
signatures,
|
||||
);
|
||||
}
|
||||
|
||||
private async _executeTxThroughCoordinatorAsync(
|
||||
exchangeFn: ExchangeFunctionName,
|
||||
txData: TxData,
|
||||
sendTxOpts: Partial<SendTransactionOpts>,
|
||||
ordersNeedingApprovals: Order[],
|
||||
...args: any[] // tslint:disable-line:trailing-comma
|
||||
): Promise<string> {
|
||||
assert.isETHAddressHex('takerAddress', txData.from);
|
||||
await assert.isSenderAddressAsync('takerAddress', txData.from, this._web3Wrapper);
|
||||
|
||||
// get ABI encoded transaction data for the desired exchange method
|
||||
const data = (this._exchangeInstance as any)[exchangeFn](...args).getABIEncodedTransactionData();
|
||||
|
||||
// generate and sign a ZeroExTransaction
|
||||
const signedZrxTx = await this._generateSignedZeroExTransactionAsync(data, txData.from, txData.gasPrice);
|
||||
|
||||
// get approval signatures from registered coordinator operators
|
||||
const approvalSignatures = await this._getApprovalsAsync(signedZrxTx, ordersNeedingApprovals, txData.from);
|
||||
|
||||
// execute the transaction through the Coordinator Contract
|
||||
const txDataWithDefaults = {
|
||||
...this._txDefaults,
|
||||
...txData, // override defaults
|
||||
};
|
||||
const txHash = this._contractInstance
|
||||
.executeTransaction(signedZrxTx, txData.from, signedZrxTx.signature, approvalSignatures)
|
||||
.sendTransactionAsync(txDataWithDefaults, sendTxOpts);
|
||||
return txHash;
|
||||
}
|
||||
|
||||
private async _generateSignedZeroExTransactionAsync(
|
||||
data: string,
|
||||
signerAddress: string,
|
||||
gasPrice?: BigNumber | string | number,
|
||||
): Promise<SignedZeroExTransaction> {
|
||||
const transaction: ZeroExTransaction = {
|
||||
salt: generatePseudoRandomSalt(),
|
||||
signerAddress,
|
||||
data,
|
||||
domain: {
|
||||
verifyingContract: this.exchangeAddress,
|
||||
chainId: await this._web3Wrapper.getChainIdAsync(),
|
||||
},
|
||||
expirationTimeSeconds: new BigNumber(
|
||||
Math.floor(Date.now() / 1000) +
|
||||
DEFAULT_APPROVAL_EXPIRATION_TIME_SECONDS -
|
||||
DEFAULT_EXPIRATION_TIME_BUFFER_SECONDS,
|
||||
),
|
||||
gasPrice: gasPrice ? new BigNumber(gasPrice) : new BigNumber(1),
|
||||
};
|
||||
const signedZrxTx = await signatureUtils.ecSignTransactionAsync(
|
||||
this._web3Wrapper.getProvider(),
|
||||
transaction,
|
||||
transaction.signerAddress,
|
||||
);
|
||||
return signedZrxTx;
|
||||
}
|
||||
|
||||
private async _getApprovalsAsync(
|
||||
transaction: SignedZeroExTransaction,
|
||||
orders: Order[],
|
||||
txOrigin: string,
|
||||
): Promise<string[]> {
|
||||
const coordinatorOrders = orders.filter(o => o.senderAddress === this.address);
|
||||
if (coordinatorOrders.length === 0) {
|
||||
return [];
|
||||
}
|
||||
const serverEndpointsToOrders = await this._mapServerEndpointsToOrdersAsync(coordinatorOrders);
|
||||
|
||||
// make server requests
|
||||
const errorResponses: CoordinatorServerResponse[] = [];
|
||||
const approvalResponses: CoordinatorServerResponse[] = [];
|
||||
for (const endpoint of Object.keys(serverEndpointsToOrders)) {
|
||||
const response = await this._executeServerRequestAsync(transaction, txOrigin, endpoint);
|
||||
if (response.isError) {
|
||||
errorResponses.push(response);
|
||||
} else {
|
||||
approvalResponses.push(response);
|
||||
}
|
||||
}
|
||||
|
||||
// if no errors
|
||||
if (errorResponses.length === 0) {
|
||||
// concatenate all approval responses
|
||||
return approvalResponses.reduce(
|
||||
(accumulator, response) =>
|
||||
accumulator.concat((response.body as CoordinatorServerApprovalResponse).signatures),
|
||||
[] as string[],
|
||||
);
|
||||
} else {
|
||||
// format errors and approvals
|
||||
// concatenate approvals
|
||||
const notCoordinatorOrders = orders.filter(o => o.senderAddress !== this.address);
|
||||
const approvedOrdersNested = approvalResponses.map(resp => {
|
||||
const endpoint = resp.coordinatorOperator;
|
||||
return serverEndpointsToOrders[endpoint];
|
||||
});
|
||||
const approvedOrders = flatten(approvedOrdersNested.concat(notCoordinatorOrders));
|
||||
|
||||
// lookup orders with errors
|
||||
const errorsWithOrders = errorResponses.map(resp => {
|
||||
const endpoint = resp.coordinatorOperator;
|
||||
return {
|
||||
...resp,
|
||||
orders: serverEndpointsToOrders[endpoint],
|
||||
};
|
||||
});
|
||||
|
||||
// throw informative error
|
||||
const cancellations = new Array();
|
||||
throw new CoordinatorServerError(
|
||||
CoordinatorServerErrorMsg.FillFailed,
|
||||
approvedOrders,
|
||||
cancellations,
|
||||
errorsWithOrders,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async _getServerEndpointOrThrowAsync(order: Order): Promise<string> {
|
||||
const cached = this._feeRecipientToEndpoint[order.feeRecipientAddress];
|
||||
const endpoint =
|
||||
cached !== undefined
|
||||
? cached
|
||||
: await _fetchServerEndpointOrThrowAsync(order.feeRecipientAddress, this._registryInstance);
|
||||
return endpoint;
|
||||
|
||||
async function _fetchServerEndpointOrThrowAsync(
|
||||
feeRecipient: string,
|
||||
registryInstance: CoordinatorRegistryContract,
|
||||
): Promise<string> {
|
||||
const coordinatorOperatorEndpoint = await registryInstance.getCoordinatorEndpoint(feeRecipient).callAsync();
|
||||
if (coordinatorOperatorEndpoint === '' || coordinatorOperatorEndpoint === undefined) {
|
||||
throw new Error(
|
||||
`No Coordinator server endpoint found in Coordinator Registry for feeRecipientAddress: ${feeRecipient}. Registry contract address: [${
|
||||
registryInstance.address
|
||||
}] Order: [${JSON.stringify(order)}]`,
|
||||
);
|
||||
}
|
||||
return coordinatorOperatorEndpoint;
|
||||
}
|
||||
}
|
||||
|
||||
private async _executeServerRequestAsync(
|
||||
signedTransaction: SignedZeroExTransaction,
|
||||
txOrigin: string,
|
||||
endpoint: string,
|
||||
): Promise<CoordinatorServerResponse> {
|
||||
const requestPayload = {
|
||||
signedTransaction,
|
||||
txOrigin,
|
||||
};
|
||||
const response = await fetchAsync(`${endpoint}/v2/request_transaction?chainId=${this.chainId}`, {
|
||||
body: JSON.stringify(requestPayload),
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8',
|
||||
},
|
||||
});
|
||||
|
||||
const isError = response.status !== HttpStatus.OK;
|
||||
const isValidationError = response.status === HttpStatus.BAD_REQUEST;
|
||||
const json = isError && !isValidationError ? undefined : await response.json();
|
||||
|
||||
const result = {
|
||||
isError,
|
||||
status: response.status,
|
||||
body: isError ? undefined : json,
|
||||
error: isError ? json : undefined,
|
||||
request: requestPayload,
|
||||
coordinatorOperator: endpoint,
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private async _mapServerEndpointsToOrdersAsync(
|
||||
coordinatorOrders: Order[],
|
||||
): Promise<{ [endpoint: string]: Order[] }> {
|
||||
const groupByFeeRecipient: { [feeRecipient: string]: Order[] } = {};
|
||||
for (const order of coordinatorOrders) {
|
||||
const feeRecipient = order.feeRecipientAddress;
|
||||
if (groupByFeeRecipient[feeRecipient] === undefined) {
|
||||
groupByFeeRecipient[feeRecipient] = [] as Order[];
|
||||
}
|
||||
groupByFeeRecipient[feeRecipient].push(order);
|
||||
}
|
||||
const serverEndpointsToOrders: { [endpoint: string]: Order[] } = {};
|
||||
for (const orders of Object.values(groupByFeeRecipient)) {
|
||||
const endpoint = await this._getServerEndpointOrThrowAsync(orders[0]);
|
||||
if (serverEndpointsToOrders[endpoint] === undefined) {
|
||||
serverEndpointsToOrders[endpoint] = [];
|
||||
}
|
||||
serverEndpointsToOrders[endpoint] = serverEndpointsToOrders[endpoint].concat(orders);
|
||||
}
|
||||
return serverEndpointsToOrders;
|
||||
}
|
||||
}
|
||||
|
||||
function getMakerAddressOrThrow(orders: Array<Order | SignedOrder>): string {
|
||||
const uniqueMakerAddresses = new Set(orders.map(o => o.makerAddress));
|
||||
if (uniqueMakerAddresses.size > 1) {
|
||||
throw new Error(`All orders in a batch must have the same makerAddress`);
|
||||
}
|
||||
return orders[0].makerAddress;
|
||||
}
|
||||
|
||||
// tslint:disable:max-file-line-count
|
@@ -1,22 +0,0 @@
|
||||
import { assert as sharedAssert } from '@0x/assert';
|
||||
// HACK: We need those two unused imports because they're actually used by sharedAssert which gets injected here
|
||||
import { Schema } from '@0x/json-schemas'; // tslint:disable-line:no-unused-variable
|
||||
import { Order } from '@0x/types'; // tslint:disable-line:no-unused-variable
|
||||
import { BigNumber } from '@0x/utils'; // tslint:disable-line:no-unused-variable
|
||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
|
||||
export const assert = {
|
||||
...sharedAssert,
|
||||
async isSenderAddressAsync(
|
||||
variableName: string,
|
||||
senderAddressHex: string,
|
||||
web3Wrapper: Web3Wrapper,
|
||||
): Promise<void> {
|
||||
sharedAssert.isETHAddressHex(variableName, senderAddressHex);
|
||||
const isSenderAddressAvailable = await web3Wrapper.isSenderAddressAvailableAsync(senderAddressHex);
|
||||
sharedAssert.assert(
|
||||
isSenderAddressAvailable,
|
||||
`Specified ${variableName} ${senderAddressHex} isn't available through the supplied web3 provider`,
|
||||
);
|
||||
},
|
||||
};
|
@@ -1,57 +0,0 @@
|
||||
import { Order, SignedOrder, SignedZeroExTransaction } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
|
||||
export interface CoordinatorServerApprovalResponse {
|
||||
signatures: string[];
|
||||
expirationTimeSeconds: BigNumber;
|
||||
}
|
||||
|
||||
export interface CoordinatorServerCancellationResponse {
|
||||
outstandingFillSignatures: CoordinatorOutstandingFillSignatures[];
|
||||
cancellationSignatures: string[];
|
||||
}
|
||||
export interface CoordinatorOutstandingFillSignatures {
|
||||
orderHash: string;
|
||||
approvalSignatures: string[];
|
||||
expirationTimeSeconds: BigNumber;
|
||||
takerAssetFillAmount: BigNumber;
|
||||
}
|
||||
|
||||
export interface CoordinatorServerResponse {
|
||||
isError: boolean;
|
||||
status: number;
|
||||
body?: CoordinatorServerCancellationResponse | CoordinatorServerApprovalResponse;
|
||||
error?: any;
|
||||
request: CoordinatorServerRequest;
|
||||
coordinatorOperator: string;
|
||||
orders?: Array<SignedOrder | Order>;
|
||||
}
|
||||
|
||||
export interface CoordinatorServerRequest {
|
||||
signedTransaction: SignedZeroExTransaction;
|
||||
txOrigin: string;
|
||||
}
|
||||
|
||||
export class CoordinatorServerError extends Error {
|
||||
public message: CoordinatorServerErrorMsg;
|
||||
public approvedOrders?: Order[] = [];
|
||||
public cancellations?: CoordinatorServerCancellationResponse[] = [];
|
||||
public errors: CoordinatorServerResponse[];
|
||||
constructor(
|
||||
message: CoordinatorServerErrorMsg,
|
||||
approvedOrders: Order[],
|
||||
cancellations: CoordinatorServerCancellationResponse[],
|
||||
errors: CoordinatorServerResponse[],
|
||||
) {
|
||||
super();
|
||||
this.message = message;
|
||||
this.approvedOrders = approvedOrders;
|
||||
this.cancellations = cancellations;
|
||||
this.errors = errors;
|
||||
}
|
||||
}
|
||||
|
||||
export enum CoordinatorServerErrorMsg {
|
||||
CancellationFailed = 'Failed to cancel with some coordinator server(s). See errors for more info. See cancellations for successful cancellations.',
|
||||
FillFailed = 'Failed to obtain approval signatures from some coordinator server(s). See errors for more info. Current transaction has been abandoned but you may resubmit with only approvedOrders (a new ZeroEx transaction will have to be signed).',
|
||||
}
|
@@ -1,133 +0,0 @@
|
||||
import * as _ from 'lodash';
|
||||
|
||||
export enum ContractError {
|
||||
ContractNotDeployedOnChain = 'CONTRACT_NOT_DEPLOYED_ON_CHAIN',
|
||||
InsufficientAllowanceForTransfer = 'INSUFFICIENT_ALLOWANCE_FOR_TRANSFER',
|
||||
InsufficientBalanceForTransfer = 'INSUFFICIENT_BALANCE_FOR_TRANSFER',
|
||||
InsufficientEthBalanceForDeposit = 'INSUFFICIENT_ETH_BALANCE_FOR_DEPOSIT',
|
||||
InsufficientWEthBalanceForWithdrawal = 'INSUFFICIENT_WETH_BALANCE_FOR_WITHDRAWAL',
|
||||
InvalidJump = 'INVALID_JUMP',
|
||||
OutOfGas = 'OUT_OF_GAS',
|
||||
SubscriptionNotFound = 'SUBSCRIPTION_NOT_FOUND',
|
||||
SubscriptionAlreadyPresent = 'SUBSCRIPTION_ALREADY_PRESENT',
|
||||
ERC721OwnerNotFound = 'ERC_721_OWNER_NOT_FOUND',
|
||||
ERC721NoApproval = 'ERC_721_NO_APPROVAL',
|
||||
SignatureRequestDenied = 'SIGNATURE_REQUEST_DENIED',
|
||||
}
|
||||
|
||||
export type AsyncMethod = (...args: any[]) => Promise<any>;
|
||||
export type SyncMethod = (...args: any[]) => any;
|
||||
|
||||
const constants = {
|
||||
INVALID_JUMP_PATTERN: 'invalid JUMP at',
|
||||
REVERT: 'revert',
|
||||
OUT_OF_GAS_PATTERN: 'out of gas',
|
||||
INVALID_TAKER_FORMAT: 'instance.taker is not of a type(s) string',
|
||||
METAMASK_USER_DENIED_SIGNATURE_PATTERN: 'User denied transaction signature',
|
||||
TRUST_WALLET_USER_DENIED_SIGNATURE_PATTERN: 'cancelled',
|
||||
};
|
||||
|
||||
type ErrorTransformer = (err: Error) => Error;
|
||||
|
||||
const contractCallErrorTransformer = (error: Error) => {
|
||||
if (_.includes(error.message, constants.INVALID_JUMP_PATTERN)) {
|
||||
return new Error(ContractError.InvalidJump);
|
||||
}
|
||||
if (_.includes(error.message, constants.OUT_OF_GAS_PATTERN)) {
|
||||
return new Error(ContractError.OutOfGas);
|
||||
}
|
||||
if (_.includes(error.message, constants.REVERT)) {
|
||||
const revertReason = error.message.split(constants.REVERT)[1].trim();
|
||||
return new Error(revertReason);
|
||||
}
|
||||
return error;
|
||||
};
|
||||
|
||||
const schemaErrorTransformer = (error: Error) => {
|
||||
if (_.includes(error.message, constants.INVALID_TAKER_FORMAT)) {
|
||||
const errMsg =
|
||||
'Order taker must be of type string. If you want anyone to be able to fill an order - pass NULL_ADDRESS';
|
||||
return new Error(errMsg);
|
||||
}
|
||||
return error;
|
||||
};
|
||||
|
||||
const signatureRequestErrorTransformer = (error: Error) => {
|
||||
if (
|
||||
_.includes(error.message, constants.METAMASK_USER_DENIED_SIGNATURE_PATTERN) ||
|
||||
_.includes(error.message, constants.TRUST_WALLET_USER_DENIED_SIGNATURE_PATTERN)
|
||||
) {
|
||||
const errMsg = ContractError.SignatureRequestDenied;
|
||||
return new Error(errMsg);
|
||||
}
|
||||
return error;
|
||||
};
|
||||
|
||||
/**
|
||||
* Source: https://stackoverflow.com/a/29837695/3546986
|
||||
*/
|
||||
const asyncErrorHandlerFactory = (errorTransformer: ErrorTransformer) => {
|
||||
const asyncErrorHandlingDecorator = (
|
||||
_target: object,
|
||||
_key: string | symbol,
|
||||
descriptor: TypedPropertyDescriptor<AsyncMethod>,
|
||||
) => {
|
||||
const originalMethod = descriptor.value as AsyncMethod;
|
||||
|
||||
// Do not use arrow syntax here. Use a function expression in
|
||||
// order to use the correct value of `this` in this method
|
||||
// tslint:disable-next-line:only-arrow-functions
|
||||
descriptor.value = async function(...args: any[]): Promise<any> {
|
||||
try {
|
||||
const result = await originalMethod.apply(this, args); // tslint:disable-line:no-invalid-this
|
||||
return result;
|
||||
} catch (error) {
|
||||
const transformedError = errorTransformer(error);
|
||||
throw transformedError;
|
||||
}
|
||||
};
|
||||
|
||||
return descriptor;
|
||||
};
|
||||
|
||||
return asyncErrorHandlingDecorator;
|
||||
};
|
||||
|
||||
const syncErrorHandlerFactory = (errorTransformer: ErrorTransformer) => {
|
||||
const syncErrorHandlingDecorator = (
|
||||
_target: object,
|
||||
_key: string | symbol,
|
||||
descriptor: TypedPropertyDescriptor<SyncMethod>,
|
||||
) => {
|
||||
const originalMethod = descriptor.value as SyncMethod;
|
||||
|
||||
// Do not use arrow syntax here. Use a function expression in
|
||||
// order to use the correct value of `this` in this method
|
||||
// tslint:disable-next-line:only-arrow-functions
|
||||
descriptor.value = function(...args: any[]): any {
|
||||
try {
|
||||
const result = originalMethod.apply(this, args); // tslint:disable-line:no-invalid-this
|
||||
return result;
|
||||
} catch (error) {
|
||||
const transformedError = errorTransformer(error);
|
||||
throw transformedError;
|
||||
}
|
||||
};
|
||||
|
||||
return descriptor;
|
||||
};
|
||||
|
||||
return syncErrorHandlingDecorator;
|
||||
};
|
||||
|
||||
// _.flow(f, g) = f ∘ g
|
||||
const zeroExErrorTransformer = _.flow(
|
||||
schemaErrorTransformer,
|
||||
contractCallErrorTransformer,
|
||||
signatureRequestErrorTransformer,
|
||||
);
|
||||
|
||||
export const decorators = {
|
||||
asyncZeroExErrorHandler: asyncErrorHandlerFactory(zeroExErrorTransformer),
|
||||
syncZeroExErrorHandler: syncErrorHandlerFactory(zeroExErrorTransformer),
|
||||
};
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user