Compare commits
46 Commits
@0x/migrat
...
@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,420 +1,219 @@
|
||||
version: 2
|
||||
|
||||
jobs:
|
||||
build:
|
||||
resource_class: medium+
|
||||
docker:
|
||||
- image: circleci/node:9-browsers
|
||||
environment:
|
||||
CONTRACTS_COMMIT_HASH: '9ed05f5'
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- checkout
|
||||
- run: echo 'export PATH=$HOME/CIRCLE_PROJECT_REPONAME/node_modules/.bin:$PATH' >> $BASH_ENV
|
||||
- run:
|
||||
name: install-yarn
|
||||
command: sudo npm install --global yarn@1.9.4
|
||||
- run:
|
||||
name: yarn
|
||||
command: yarn --frozen-lockfile --ignore-engines install || yarn --frozen-lockfile --ignore-engines install
|
||||
- setup_remote_docker
|
||||
- run: yarn build:ci:no_website
|
||||
- run: yarn build:ts
|
||||
- save_cache:
|
||||
key: repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo
|
||||
build-website:
|
||||
resource_class: medium+
|
||||
docker:
|
||||
- image: circleci/node:9-browsers
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: cd packages/website && yarn build:prod
|
||||
test-contracts-ganache:
|
||||
docker:
|
||||
- image: circleci/node:9-browsers
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: yarn wsrun test:circleci @0x/contracts-multisig
|
||||
- run: yarn wsrun test:circleci @0x/contracts-utils
|
||||
- run: yarn wsrun test:circleci @0x/contracts-exchange-libs
|
||||
- run: yarn wsrun test:circleci @0x/contracts-erc20
|
||||
- run: yarn wsrun test:circleci @0x/contracts-erc721
|
||||
- run: yarn wsrun test:circleci @0x/contracts-erc1155
|
||||
- run: yarn wsrun test:circleci @0x/contracts-extensions
|
||||
- run: yarn wsrun test:circleci @0x/contracts-asset-proxy
|
||||
- run: yarn wsrun test:circleci @0x/contracts-exchange
|
||||
- run: yarn wsrun test:circleci @0x/contracts-exchange-forwarder
|
||||
- run: yarn wsrun test:circleci @0x/contracts-coordinator
|
||||
test-contracts-geth:
|
||||
docker:
|
||||
- image: circleci/node:9-browsers
|
||||
- image: 0xorg/devnet
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
# HACK(albrow): we need to sleep 10 seconds to ensure the devnet is
|
||||
# initialized
|
||||
- run: sleep 10 && TEST_PROVIDER=geth yarn wsrun test @0x/contracts-multisig
|
||||
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-utils
|
||||
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-exchange-libs
|
||||
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-erc20
|
||||
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-erc721
|
||||
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-erc1155
|
||||
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-extensions
|
||||
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-asset-proxy
|
||||
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-exchange
|
||||
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-exchange-forwarder
|
||||
- run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-coordinator
|
||||
test-publish:
|
||||
resource_class: medium+
|
||||
docker:
|
||||
- image: circleci/node:9-browsers
|
||||
- image: 0xorg/verdaccio
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: yarn test:publish:circleci
|
||||
test-doc-generation:
|
||||
docker:
|
||||
- image: circleci/node:9-browsers
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: yarn test:generate_docs:circleci
|
||||
test-pipeline:
|
||||
docker:
|
||||
- image: circleci/node:9
|
||||
- image: postgres:11-alpine
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: ZEROEX_DATA_PIPELINE_TEST_DB_URL='postgresql://postgres@localhost/postgres' yarn wsrun test:circleci @0x/pipeline
|
||||
- save_cache:
|
||||
key: coverage-pipeline-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/pipeline/coverage/lcov.info
|
||||
test-rest:
|
||||
docker:
|
||||
- image: circleci/node:9-browsers
|
||||
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/assert
|
||||
- run: yarn wsrun test:circleci @0x/base-contract
|
||||
- run: yarn wsrun test:circleci @0x/connect
|
||||
- run: yarn wsrun test:circleci @0x/contract-wrappers
|
||||
- run: yarn wsrun test:circleci @0x/dev-utils
|
||||
- run: yarn wsrun test:circleci @0x/json-schemas
|
||||
- run: yarn wsrun test:circleci @0x/metacoin
|
||||
- run: yarn wsrun test:circleci @0x/order-utils
|
||||
- run: yarn wsrun test:circleci @0x/order-watcher
|
||||
- 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-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-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/contract-wrappers/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-metacoin-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/metacoin/coverage/lcov.info
|
||||
- save_cache:
|
||||
key: coverage-order-utils-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/order-utils/coverage/lcov.info
|
||||
- save_cache:
|
||||
key: coverage-order-watcher-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/order-watcher/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: circleci/python
|
||||
- image: 0xorg/ganache-cli:2.2.2
|
||||
- image: 0xorg/launch-kit-ci
|
||||
command: |
|
||||
yarn start:ts -p 3000:3000
|
||||
steps:
|
||||
- checkout
|
||||
- run: sudo chown -R circleci:circleci /usr/local/bin
|
||||
- run: sudo chown -R circleci:circleci /usr/local/lib/python3.7
|
||||
- restore_cache:
|
||||
key: deps9-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run:
|
||||
command: |
|
||||
cd python-packages
|
||||
python -m ensurepip
|
||||
./install
|
||||
- save_cache:
|
||||
key: deps9-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- '/usr/local/bin'
|
||||
- '/usr/local/lib/python3.7/site-packages'
|
||||
- run:
|
||||
command: |
|
||||
cd python-packages
|
||||
./cmd_pkgs_in_dep_order.py coverage run setup.py test
|
||||
- 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
|
||||
test-rest-python:
|
||||
working_directory: ~/repo
|
||||
docker:
|
||||
- image: circleci/python
|
||||
steps:
|
||||
- checkout
|
||||
- run: sudo chown -R circleci:circleci /usr/local/bin
|
||||
- run: sudo chown -R circleci:circleci /usr/local/lib/python3.7
|
||||
- restore_cache:
|
||||
key: deps9-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run:
|
||||
command: |
|
||||
cd python-packages/order_utils
|
||||
python -m ensurepip
|
||||
python -m pip install .
|
||||
- save_cache:
|
||||
key: deps9-{{ .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: circleci/python
|
||||
steps:
|
||||
- checkout
|
||||
- run: sudo chown -R circleci:circleci /usr/local/bin
|
||||
- run: sudo chown -R circleci:circleci /usr/local/lib/python3.7
|
||||
- restore_cache:
|
||||
key: deps9-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run:
|
||||
command: |
|
||||
python -m ensurepip
|
||||
cd python-packages
|
||||
./install
|
||||
./lint
|
||||
static-tests:
|
||||
working_directory: ~/repo
|
||||
docker:
|
||||
- image: circleci/node:9-browsers
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: yarn lerna run lint
|
||||
- run: yarn prettier:ci
|
||||
- run: yarn deps_versions:ci
|
||||
- run: cd packages/0x.js && yarn build:umd:prod
|
||||
- run: yarn bundlewatch
|
||||
submit-coverage:
|
||||
docker:
|
||||
- image: circleci/node:9-browsers
|
||||
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-base-contract-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-connect-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-contract-wrappers-{{ .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-metacoin-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-order-utils-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-order-watcher-{{ .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
|
||||
- build-website:
|
||||
requires:
|
||||
- build
|
||||
- test-contracts-ganache:
|
||||
requires:
|
||||
- build
|
||||
- test-contracts-geth:
|
||||
requires:
|
||||
- build
|
||||
- test-pipeline:
|
||||
requires:
|
||||
- build
|
||||
- test-rest:
|
||||
requires:
|
||||
- build
|
||||
- static-tests:
|
||||
requires:
|
||||
- build
|
||||
- test-publish:
|
||||
requires:
|
||||
- build
|
||||
- test-doc-generation:
|
||||
requires:
|
||||
- build
|
||||
- submit-coverage:
|
||||
requires:
|
||||
- test-rest
|
||||
- test-python
|
||||
- static-tests-python:
|
||||
requires:
|
||||
- test-python
|
||||
- test-python
|
||||
# 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
|
||||
|
6
.gitattributes
vendored
6
.gitattributes
vendored
@@ -1,7 +1 @@
|
||||
*.sol linguist-language=Solidity
|
||||
|
||||
# Automatically collapse generated files in GitHub.
|
||||
*.svg linguist-generated
|
||||
packages/contract-artifacts/artifacts/*json linguist-generated
|
||||
packages/abi-gen-wrappers/wrappers/*.ts liguist-generated
|
||||
|
||||
|
43
.github/autolabeler.yml
vendored
43
.github/autolabeler.yml
vendored
@@ -1,43 +0,0 @@
|
||||
python: ['python-packages']
|
||||
contracts: ['contracts']
|
||||
sol-doc: ['packages/sol-doc']
|
||||
sol-resolver: ['packages/sol-resolver']
|
||||
contracts-gen: ['packages/contracts-gen']
|
||||
sra-spec: ['packages/sra-spec']
|
||||
subproviders: ['packages/subproviders']
|
||||
contract-addresses: ['packages/contract-addresses']
|
||||
migrations: ['packages/migrations']
|
||||
web3-wrapper: ['packages/web3-wrapper']
|
||||
sol-compiler: ['packages/sol-compiler']
|
||||
types: ['packages/types']
|
||||
instant: ['packages/instant']
|
||||
abi-gen-templates: ['packages/abi-gen-templates']
|
||||
abi-gen: ['packages/abi-gen']
|
||||
website: ['packages/website']
|
||||
sol-coverage: ['packages/sol-coverage']
|
||||
sol-profiler: ['packages/sol-profiler']
|
||||
sol-trace: ['packages/sol-trace']
|
||||
sol-tracing-utils: ['packages/sol-tracing-utils']
|
||||
utils: ['packages/utils']
|
||||
tslint-config: ['packages/tslint-config']
|
||||
asset-buyer: ['packages/asset-buyer']
|
||||
order-watcher: ['packages/order-watcher']
|
||||
react-docs: ['packages/react-docs']
|
||||
order-utils: ['packages/order-utils']
|
||||
react-shared: ['packages/react-shared']
|
||||
assert: ['packages/assert']
|
||||
base-contract: ['packages/base-contract']
|
||||
typescript-typings: ['packages/typescript-typings']
|
||||
0x.js: ['packages/0x.js']
|
||||
abi-gen-wrappers: ['packages/abi-gen-wrappers']
|
||||
metacoin: ['packages/metacoin']
|
||||
contract-artifacts: ['packages/contract-artifacts']
|
||||
dev-utils: ['packages/dev-utils']
|
||||
contract-wrappers: ['packages/contract-wrappers']
|
||||
json-schemas: ['packages/json-schemas']
|
||||
ethereum-types: ['ethereum-types']
|
||||
connect: ['packages/connect']
|
||||
fill-scenarios: ['packages/fill-scenarios']
|
||||
dev-tools-pages: ['packages/dev-tools-pages']
|
||||
testnet-faucets: ['packages/testnet-faucets']
|
||||
monorepo-scripts: ['packages/monorepo-scripts']
|
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.
|
52
.gitignore
vendored
52
.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
|
||||
|
||||
@@ -45,7 +41,6 @@ typings/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
.npmrc
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
@@ -72,45 +67,26 @@ generated_docs/
|
||||
|
||||
TODO.md
|
||||
|
||||
# VSCode file
|
||||
.vscode
|
||||
|
||||
packages/website/public/bundle*
|
||||
packages/dev-tools-pages/public/bundle*
|
||||
packages/react-docs/example/public/bundle*
|
||||
|
||||
# server cli
|
||||
packages/testnet-faucets/server/
|
||||
|
||||
# generated contract artifacts/
|
||||
contracts/coordinator/generated-artifacts/
|
||||
contracts/exchange/generated-artifacts/
|
||||
contracts/asset-proxy/generated-artifacts/
|
||||
contracts/multisig/generated-artifacts/
|
||||
contracts/utils/generated-artifacts/
|
||||
contracts/exchange-libs/generated-artifacts/
|
||||
contracts/erc20/generated-artifacts/
|
||||
contracts/erc721/generated-artifacts/
|
||||
contracts/erc1155/generated-artifacts/
|
||||
contracts/extensions/generated-artifacts/
|
||||
contracts/exchange-forwarder/generated-artifacts/
|
||||
packages/sol-tracing-utils/test/fixtures/artifacts/
|
||||
packages/sol-cov/test/fixtures/artifacts/
|
||||
packages/metacoin/artifacts/
|
||||
packages/order-watcher/test/artifacts/
|
||||
packages/contract-wrappers/test/artifacts/
|
||||
|
||||
# generated contract wrappers
|
||||
packages/abi-gen-wrappers/wrappers
|
||||
contracts/coordinator/generated-wrappers/
|
||||
contracts/exchange/generated-wrappers/
|
||||
contracts/asset-proxy/generated-wrappers/
|
||||
contracts/multisig/generated-wrappers/
|
||||
contracts/utils/generated-wrappers/
|
||||
contracts/exchange-libs/generated-wrappers/
|
||||
contracts/erc20/generated-wrappers/
|
||||
contracts/erc721/generated-wrappers/
|
||||
contracts/erc1155/generated-wrappers/
|
||||
contracts/extensions/generated-wrappers/
|
||||
contracts/exchange-forwarder/generated-wrappers/
|
||||
packages/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/
|
||||
@@ -118,12 +94,4 @@ packages/sol-compiler/solc_bin/
|
||||
# Monorepo scripts
|
||||
packages/*/scripts/
|
||||
|
||||
# python stuff
|
||||
.eggs
|
||||
.mypy_cache
|
||||
.tox
|
||||
python-packages/*/build
|
||||
python-packages/*/dist
|
||||
__pycache__
|
||||
python-packages/*/src/*.egg-info
|
||||
python-packages/*/.coverage
|
||||
ganache.log
|
||||
|
@@ -1,39 +1,10 @@
|
||||
lib
|
||||
.nyc_output
|
||||
/contracts/coordinator/generated-wrappers
|
||||
/contracts/coordinator/generated-artifacts
|
||||
/contracts/exchange/generated-wrappers
|
||||
/contracts/exchange/generated-artifacts
|
||||
/contracts/asset-proxy/generated-wrappers
|
||||
/contracts/asset-proxy/generated-artifacts
|
||||
/contracts/multisig/generated-wrappers
|
||||
/contracts/multisig/generated-artifacts
|
||||
/contracts/utils/generated-wrappers
|
||||
/contracts/utils/generated-artifacts
|
||||
/contracts/exchange-libs/generated-wrappers
|
||||
/contracts/exchange-libs/generated-artifacts
|
||||
/contracts/erc20/generated-wrappers
|
||||
/contracts/erc20/generated-artifacts
|
||||
/contracts/erc721/generated-wrappers
|
||||
/contracts/erc721/generated-artifacts
|
||||
/contracts/erc1155/generated-wrappers
|
||||
/contracts/erc1155/generated-artifacts
|
||||
/contracts/extensions/generated-wrappers
|
||||
/contracts/extensions/generated-artifacts
|
||||
/contracts/exchange-forwarder/generated-wrappers
|
||||
/contracts/exchange-forwarder/generated-artifacts
|
||||
/packages/abi-gen-wrappers/src/generated-wrappers
|
||||
/packages/contract-artifacts/artifacts
|
||||
/python-packages/contract_artifacts/src/zero_ex/contract_artifacts/artifacts
|
||||
/packages/json-schemas/schemas
|
||||
/python-packages/json_schemas/src/zero_ex/json_schemas/schemas
|
||||
/packages/metacoin/src/contract_wrappers
|
||||
/packages/0x.js/test/artifacts
|
||||
/packages/contracts/src/artifacts
|
||||
/packages/metacoin/artifacts
|
||||
/packages/sra-spec/public/
|
||||
/packages/dev-tools-pages/ts/**/data.json
|
||||
/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
|
||||
|
40
CODEOWNERS
40
CODEOWNERS
@@ -1,40 +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-buyer/ @BMillman19 @fragosti @steveklebanoff
|
||||
packages/instant/ @BMillman19 @fragosti @steveklebanoff
|
||||
packages/website/ @BMillman19 @fragosti @fabioberger @steveklebanoff
|
||||
|
||||
# Dev tools & setup
|
||||
.circleci/ @LogvinovLeon
|
||||
packages/abi-gen/ @LogvinovLeon
|
||||
packages/base-contract/ @LogvinovLeon
|
||||
packages/connect/ @fragosti
|
||||
packages/abi-gen-templates/ @LogvinovLeon
|
||||
packages/contract-addresses/ @albrow
|
||||
packages/contract-artifacts/ @albrow
|
||||
packages/dev-utils/ @LogvinovLeon @fabioberger
|
||||
packages/devnet/ @albrow
|
||||
packages/ethereum-types/ @LogvinovLeon
|
||||
packages/metacoin/ @LogvinovLeon
|
||||
packages/monorepo-scripts/ @fabioberger
|
||||
packages/order-utils/ @fabioberger @LogvinovLeon
|
||||
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/core/test/ @albrow
|
||||
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/broken-wiki-link`
|
||||
- If the PR only edits a single package, add it's name too
|
||||
- e.g `fix/website/broken-wiki-link`
|
||||
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`.
|
||||
|
@@ -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.
|
||||
|
161
README.md
161
README.md
@@ -1,16 +1,13 @@
|
||||
<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.
|
||||
|
||||
If you're developing on 0x now or are interested in using 0x infrastructure in the future, please join our [developer mailing list][dev-mailing-list-url] for updates.
|
||||
|
||||
[website-url]: https://0xproject.com
|
||||
[website-url]: https://0xproject.com/
|
||||
[whitepaper-url]: https://0xproject.com/pdfs/0x_white_paper.pdf
|
||||
[dev-mailing-list-url]: http://eepurl.com/dx4cPf
|
||||
|
||||
[](https://circleci.com/gh/0xProject/0x-monorepo)
|
||||
[](https://coveralls.io/github/0xProject/0x-monorepo?branch=development)
|
||||
@@ -18,104 +15,60 @@ If you're developing on 0x now or are interested in using 0x infrastructure in t
|
||||
[](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://0xproject.com/docs/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-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 |
|
||||
|
||||
### 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/order-watcher`](/packages/order-watcher) | [](https://www.npmjs.com/package/@0x/order-watcher) | An order watcher daemon that watches for order validity |
|
||||
| [`@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/abi-gen-wrappers`](/packages/abi-gen-wrappers) | [](https://www.npmjs.com/package/@0x/abi-gen-wrappers) | Low-level 0x smart contract wrappers generated using `@0x/abi-gen` |
|
||||
| [`@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-buyer`](/packages/asset-buyer) | [](https://www.npmjs.com/package/@0x/asset-buyer) | Convenience package for discovering and buying assets with Ether |
|
||||
|
||||
#### 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/react-docs`](/packages/react-docs) | [](https://www.npmjs.com/package/@0x/react-docs) | React documentation component for rendering TypeDoc & sol-doc generated JSON |
|
||||
| [`@0x/react-shared`](/packages/react-shared) | [](https://www.npmjs.com/package/@0x/react-shared) | 0x shared react components |
|
||||
| [`@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 |
|
||||
| [`@0x/fill-scenarios`](/packages/fill-scenarios) | [](https://www.npmjs.com/package/@0x/fill-scenarios) | 0x order fill scenario generator |
|
||||
|
||||
#### Private Packages
|
||||
|
||||
| Package | Description |
|
||||
| -------------------------------------------------- | -------------------------------------------------------------------------------- |
|
||||
| [`@0x/instant`](/packages/instant) | A free and flexible way to offer simple crypto purchasing in any app or website. |
|
||||
| [`@0x/testnet-faucets`](/packages/testnet-faucets) | A faucet micro-service that dispenses test ERC20 tokens or Ether |
|
||||
| [`@0x/website`](/packages/website) | 0x website |
|
||||
| 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
|
||||
|
||||
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
|
||||
@@ -126,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
|
||||
@@ -149,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
|
||||
@@ -172,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
|
||||
@@ -228,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,12 +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) |
|
@@ -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,124 +0,0 @@
|
||||
[
|
||||
{
|
||||
"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,57 +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 - _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 the 0x [wiki](https://0xproject.com/wiki#Deployed-Addresses) or the [DEPLOYS](./DEPLOYS.json) file within this package.
|
||||
|
||||
## Installation
|
||||
|
||||
**Install**
|
||||
|
||||
```bash
|
||||
npm install @0x/contracts-asset-proxy --save
|
||||
```
|
||||
|
||||
## Bug bounty
|
||||
|
||||
A bug bounty for the 2.0.0 contracts is ongoing! Instructions can be found [here](https://0xproject.com/wiki#Bug-Bounty).
|
||||
|
||||
## Contributing
|
||||
|
||||
We strongly recommend that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository.
|
||||
|
||||
For proposals regarding the 0x protocol's smart contract architecture, message format, or additional functionality, go to the [0x Improvement Proposals (ZEIPs)](https://github.com/0xProject/ZEIPs) repository and follow the contribution guidelines provided therein.
|
||||
|
||||
Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started.
|
||||
|
||||
### Install Dependencies
|
||||
|
||||
If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them:
|
||||
|
||||
```bash
|
||||
yarn config set workspaces-experimental true
|
||||
```
|
||||
|
||||
Then install dependencies
|
||||
|
||||
```bash
|
||||
yarn install
|
||||
```
|
||||
|
||||
### Build
|
||||
|
||||
To build this package and all other monorepo packages that it depends on, run the following from the monorepo root directory:
|
||||
|
||||
```bash
|
||||
PKG=@0x/contracts-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,35 +0,0 @@
|
||||
{
|
||||
"artifactsDir": "./generated-artifacts",
|
||||
"contractsDir": "./contracts",
|
||||
"useDockerisedSolc": true,
|
||||
"isOfflineMode": false,
|
||||
"compilerSettings": {
|
||||
"evmVersion": "constantinople",
|
||||
"optimizer": {
|
||||
"enabled": true,
|
||||
"runs": 1000000,
|
||||
"details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
|
||||
},
|
||||
"outputSelection": {
|
||||
"*": {
|
||||
"*": [
|
||||
"abi",
|
||||
"evm.bytecode.object",
|
||||
"evm.bytecode.sourceMap",
|
||||
"evm.deployedBytecode.object",
|
||||
"evm.deployedBytecode.sourceMap"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"contracts": [
|
||||
"src/ERC1155Proxy.sol",
|
||||
"src/ERC20Proxy.sol",
|
||||
"src/ERC721Proxy.sol",
|
||||
"src/MixinAuthorizable.sol",
|
||||
"src/MultiAssetProxy.sol",
|
||||
"src/interfaces/IAssetData.sol",
|
||||
"src/interfaces/IAssetProxy.sol",
|
||||
"src/interfaces/IAuthorizable.sol"
|
||||
]
|
||||
}
|
@@ -1,267 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "./MixinAuthorizable.sol";
|
||||
|
||||
|
||||
contract ERC1155Proxy is
|
||||
MixinAuthorizable
|
||||
{
|
||||
|
||||
// Id of this proxy.
|
||||
bytes4 constant internal PROXY_ID = bytes4(keccak256("ERC1155Assets(address,uint256[],uint256[],bytes)"));
|
||||
|
||||
function ()
|
||||
external
|
||||
{
|
||||
// Input calldata to this function is encoded as follows:
|
||||
// -- TABLE #1 --
|
||||
// | Area | Offset (**) | Length | Contents |
|
||||
// |----------|-------------|-------------|---------------------------------|
|
||||
// | Header | 0 | 4 | function selector |
|
||||
// | Params | | 4 * 32 | function parameters: |
|
||||
// | | 4 | | 1. offset to assetData (*) |
|
||||
// | | 36 | | 2. from |
|
||||
// | | 68 | | 3. to |
|
||||
// | | 100 | | 4. amount |
|
||||
// | Data | | | assetData: |
|
||||
// | | 132 | 32 | assetData Length |
|
||||
// | | 164 | (see below) | assetData Contents |
|
||||
//
|
||||
//
|
||||
// Asset data is encoded as follows:
|
||||
// -- TABLE #2 --
|
||||
// | Area | Offset | Length | Contents |
|
||||
// |----------|-------------|---------|-------------------------------------|
|
||||
// | Header | 0 | 4 | assetProxyId |
|
||||
// | Params | | 4 * 32 | function parameters: |
|
||||
// | | 4 | | 1. address of ERC1155 contract |
|
||||
// | | 36 | | 2. offset to ids (*) |
|
||||
// | | 68 | | 3. offset to values (*) |
|
||||
// | | 100 | | 4. offset to data (*) |
|
||||
// | Data | | | ids: |
|
||||
// | | 132 | 32 | 1. ids Length |
|
||||
// | | 164 | a | 2. ids Contents |
|
||||
// | | | | values: |
|
||||
// | | 164 + a | 32 | 1. values Length |
|
||||
// | | 196 + a | b | 2. values Contents |
|
||||
// | | | | data |
|
||||
// | | 196 + a + b | 32 | 1. data Length |
|
||||
// | | 228 + a + b | c | 2. data Contents |
|
||||
//
|
||||
//
|
||||
// Calldata for target ERC155 asset is encoded for safeBatchTransferFrom:
|
||||
// -- TABLE #3 --
|
||||
// | Area | Offset (**) | Length | Contents |
|
||||
// |----------|-------------|---------|-------------------------------------|
|
||||
// | Header | 0 | 4 | safeBatchTransferFrom selector |
|
||||
// | Params | | 5 * 32 | function parameters: |
|
||||
// | | 4 | | 1. from address |
|
||||
// | | 36 | | 2. to address |
|
||||
// | | 68 | | 3. offset to ids (*) |
|
||||
// | | 100 | | 4. offset to values (*) |
|
||||
// | | 132 | | 5. offset to data (*) |
|
||||
// | Data | | | ids: |
|
||||
// | | 164 | 32 | 1. ids Length |
|
||||
// | | 196 | a | 2. ids Contents |
|
||||
// | | | | values: |
|
||||
// | | 196 + a | 32 | 1. values Length |
|
||||
// | | 228 + a | b | 2. values Contents |
|
||||
// | | | | data |
|
||||
// | | 228 + a + b | 32 | 1. data Length |
|
||||
// | | 260 + a + b | c | 2. data Contents |
|
||||
//
|
||||
//
|
||||
// (*): offset is computed from start of function parameters, so offset
|
||||
// by an additional 4 bytes in the calldata.
|
||||
//
|
||||
// (**): the `Offset` column is computed assuming no calldata compression;
|
||||
// offsets in the Data Area are dynamic and should be evaluated in
|
||||
// real-time.
|
||||
//
|
||||
// WARNING: The ABIv2 specification allows additional padding between
|
||||
// the Params and Data section. This will result in a larger
|
||||
// offset to assetData.
|
||||
//
|
||||
// Note: Table #1 and Table #2 exists in Calldata. We construct Table #3 in memory.
|
||||
//
|
||||
//
|
||||
assembly {
|
||||
// The first 4 bytes of calldata holds the function selector
|
||||
let selector := and(calldataload(0), 0xffffffff00000000000000000000000000000000000000000000000000000000)
|
||||
|
||||
// `transferFrom` will be called with the following parameters:
|
||||
// assetData Encoded byte array.
|
||||
// from Address to transfer asset from.
|
||||
// to Address to transfer asset to.
|
||||
// amount Amount of asset to transfer.
|
||||
// bytes4(keccak256("transferFrom(bytes,address,address,uint256)")) = 0xa85e59e4
|
||||
if eq(selector, 0xa85e59e400000000000000000000000000000000000000000000000000000000) {
|
||||
|
||||
// To lookup a value in a mapping, we load from the storage location keccak256(k, p),
|
||||
// where k is the key left padded to 32 bytes and p is the storage slot
|
||||
mstore(0, caller)
|
||||
mstore(32, authorized_slot)
|
||||
|
||||
// Revert if authorized[msg.sender] == false
|
||||
if iszero(sload(keccak256(0, 64))) {
|
||||
// Revert with `Error("SENDER_NOT_AUTHORIZED")`
|
||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(64, 0x0000001553454e4445525f4e4f545f415554484f52495a454400000000000000)
|
||||
mstore(96, 0)
|
||||
revert(0, 100)
|
||||
}
|
||||
|
||||
// Construct Table #3 in memory, starting at memory offset 0.
|
||||
// The algorithm below maps asset data from Table #1 and Table #2 to Table #3, while
|
||||
// scaling the `values` (Table #2) by `amount` (Table #1). Once Table #3 has
|
||||
// been constructed in memory, the destination erc1155 contract is called using this
|
||||
// as its calldata. This process is divided into four steps, below.
|
||||
|
||||
////////// STEP 1/4 //////////
|
||||
// Map relevant fields from assetData (Table #2) into memory (Table #3)
|
||||
// The Contents column of Table #2 is the same as Table #3,
|
||||
// beginning from parameter 3 - `offset to ids (*)`
|
||||
// The offsets in these rows are offset by 32 bytes in Table #3.
|
||||
// Strategy:
|
||||
// 1. Copy the assetData into memory at offset 32
|
||||
// 2. Increment by 32 the offsets to `ids`, `values`, and `data`
|
||||
|
||||
// Load offset to `assetData`
|
||||
let assetDataOffset := calldataload(4)
|
||||
|
||||
// Load length in bytes of `assetData`, computed by:
|
||||
// 4 (function selector)
|
||||
// + assetDataOffset
|
||||
let assetDataLength := calldataload(add(4, assetDataOffset))
|
||||
|
||||
// This corresponds to the beginning of the Data Area for Table #3.
|
||||
// Computed by:
|
||||
// 4 (function selector)
|
||||
// + assetDataOffset
|
||||
// + 32 (length of assetData)
|
||||
calldatacopy(
|
||||
32, // aligned such that "offset to ids" is at the correct location for Table #3
|
||||
add(36, assetDataOffset), // beginning of asset data contents
|
||||
assetDataLength // length of asset data
|
||||
)
|
||||
|
||||
// Increment by 32 the offsets to `ids`, `values`, and `data`
|
||||
mstore(68, add(mload(68), 32))
|
||||
mstore(100, add(mload(100), 32))
|
||||
mstore(132, add(mload(132), 32))
|
||||
|
||||
// Record the address of the destination erc1155 asset for later.
|
||||
let assetAddress := and(
|
||||
mload(36),
|
||||
0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff
|
||||
)
|
||||
|
||||
////////// STEP 2/4 //////////
|
||||
let amount := calldataload(100)
|
||||
let valuesOffset := add(mload(100), 4) // add 4 for calldata offset
|
||||
let valuesLengthInBytes := mul(
|
||||
mload(valuesOffset),
|
||||
32
|
||||
)
|
||||
let valuesBegin := add(valuesOffset, 32)
|
||||
let valuesEnd := add(valuesBegin, valuesLengthInBytes)
|
||||
for { let tokenValueOffset := valuesBegin }
|
||||
lt(tokenValueOffset, valuesEnd)
|
||||
{ tokenValueOffset := add(tokenValueOffset, 32) }
|
||||
{
|
||||
// Load token value and generate scaled value
|
||||
let tokenValue := mload(tokenValueOffset)
|
||||
let scaledTokenValue := mul(tokenValue, amount)
|
||||
|
||||
// Revert if `amount` != 0 and multiplication resulted in an overflow
|
||||
if iszero(or(
|
||||
iszero(amount),
|
||||
eq(div(scaledTokenValue, amount), tokenValue)
|
||||
)) {
|
||||
// Revert with `Error("UINT256_OVERFLOW")`
|
||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(64, 0x0000001055494e543235365f4f564552464c4f57000000000000000000000000)
|
||||
mstore(96, 0)
|
||||
revert(0, 100)
|
||||
}
|
||||
|
||||
// There was no overflow, update `tokenValue` with its scaled counterpart
|
||||
mstore(tokenValueOffset, scaledTokenValue)
|
||||
}
|
||||
|
||||
////////// STEP 3/4 //////////
|
||||
// Store the safeBatchTransferFrom function selector,
|
||||
// and copy `from`/`to` fields from Table #1 to Table #3.
|
||||
|
||||
// The function selector is computed using:
|
||||
// bytes4(keccak256("safeBatchTransferFrom(address,address,uint256[],uint256[],bytes)"))
|
||||
mstore(0, 0x2eb2c2d600000000000000000000000000000000000000000000000000000000)
|
||||
|
||||
// Copy `from` and `to` fields from Table #1 to Table #3
|
||||
calldatacopy(
|
||||
4, // aligned such that `from` and `to` are at the correct location for Table #3
|
||||
36, // beginning of `from` field from Table #1
|
||||
64 // 32 bytes for `from` + 32 bytes for `to` field
|
||||
)
|
||||
|
||||
////////// STEP 4/4 //////////
|
||||
// Call into the destination erc1155 contract using as calldata Table #3 (constructed in-memory above)
|
||||
let success := call(
|
||||
gas, // forward all gas
|
||||
assetAddress, // call address of erc1155 asset
|
||||
0, // don't send any ETH
|
||||
0, // pointer to start of input
|
||||
add(assetDataLength, 32), // length of input (Table #3) is 32 bytes longer than `assetData` (Table #2)
|
||||
0, // write output over memory that won't be reused
|
||||
0 // don't copy output to memory
|
||||
)
|
||||
|
||||
// Revert with reason given by AssetProxy if `transferFrom` call failed
|
||||
if iszero(success) {
|
||||
returndatacopy(
|
||||
0, // copy to memory at 0
|
||||
0, // copy from return data at 0
|
||||
returndatasize() // copy all return data
|
||||
)
|
||||
revert(0, returndatasize())
|
||||
}
|
||||
|
||||
// Return if call was successful
|
||||
return(0, 0)
|
||||
}
|
||||
|
||||
// Revert if undefined function is called
|
||||
revert(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Gets the proxy id associated with the proxy address.
|
||||
/// @return Proxy id.
|
||||
function getProxyId()
|
||||
external
|
||||
pure
|
||||
returns (bytes4)
|
||||
{
|
||||
return PROXY_ID;
|
||||
}
|
||||
}
|
@@ -1,184 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "./MixinAuthorizable.sol";
|
||||
|
||||
|
||||
contract 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 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "./MixinAuthorizable.sol";
|
||||
|
||||
|
||||
contract 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,172 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/Ownable.sol";
|
||||
import "./mixins/MAssetProxyDispatcher.sol";
|
||||
import "./interfaces/IAssetProxy.sol";
|
||||
|
||||
|
||||
contract MixinAssetProxyDispatcher is
|
||||
Ownable,
|
||||
MAssetProxyDispatcher
|
||||
{
|
||||
// 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 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/Ownable.sol";
|
||||
import "./mixins/MAuthorizable.sol";
|
||||
|
||||
|
||||
contract MixinAuthorizable is
|
||||
Ownable,
|
||||
MAuthorizable
|
||||
{
|
||||
/// @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,306 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "./MixinAssetProxyDispatcher.sol";
|
||||
import "./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 := calldataload(4)
|
||||
|
||||
// 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] |
|
||||
|
||||
// In order to find the offset to `amounts`, we must add:
|
||||
// 4 (function selector)
|
||||
// + assetDataOffset
|
||||
// + 32 (assetData len)
|
||||
// + 4 (assetProxyId)
|
||||
let amountsOffset := calldataload(add(assetDataOffset, 40))
|
||||
|
||||
// In order to find the offset to `nestedAssetData`, we must add:
|
||||
// 4 (function selector)
|
||||
// + assetDataOffset
|
||||
// + 32 (assetData len)
|
||||
// + 4 (assetProxyId)
|
||||
// + 32 (amounts offset)
|
||||
let nestedAssetDataOffset := calldataload(add(assetDataOffset, 72))
|
||||
|
||||
// In order to find the start of the `amounts` contents, we must add:
|
||||
// 4 (function selector)
|
||||
// + assetDataOffset
|
||||
// + 32 (assetData len)
|
||||
// + 4 (assetProxyId)
|
||||
// + amountsOffset
|
||||
// + 32 (amounts len)
|
||||
let amountsContentsStart := add(assetDataOffset, add(amountsOffset, 72))
|
||||
|
||||
// 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:
|
||||
// 4 (function selector)
|
||||
// + assetDataOffset
|
||||
// + 32 (assetData len)
|
||||
// + 4 (assetProxyId)
|
||||
// + nestedAssetDataOffset
|
||||
// + 32 (nestedAssetData len)
|
||||
let nestedAssetDataContentsStart := add(assetDataOffset, add(nestedAssetDataOffset, 72))
|
||||
|
||||
// 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:
|
||||
// 4 (function selector)
|
||||
// + assetDataOffset
|
||||
// + 32 (assetData len)
|
||||
// + 4 (assetProxyId)
|
||||
// + nestedAssetDataOffset
|
||||
// + 32 (nestedAssetData len)
|
||||
// + nestedAssetDataElementOffset
|
||||
// + 32 (nestedAssetDataElement len)
|
||||
let nestedAssetDataElementContentsStart := add(assetDataOffset, add(nestedAssetDataOffset, add(nestedAssetDataElementOffset, 104)))
|
||||
|
||||
// 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,44 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
// solhint-disable
|
||||
pragma solidity ^0.5.5;
|
||||
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 {
|
||||
|
||||
function ERC20Token(address tokenContract)
|
||||
external;
|
||||
|
||||
function ERC721Token(
|
||||
address tokenContract,
|
||||
uint256 tokenId
|
||||
)
|
||||
external;
|
||||
|
||||
function MultiAsset(
|
||||
uint256[] calldata amounts,
|
||||
bytes[] calldata nestedAssetData
|
||||
)
|
||||
external;
|
||||
|
||||
}
|
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "./IAuthorizable.sol";
|
||||
|
||||
|
||||
contract IAssetProxy is
|
||||
IAuthorizable
|
||||
{
|
||||
/// @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,37 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
|
||||
contract IAssetProxyDispatcher {
|
||||
|
||||
/// @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,52 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/interfaces/IOwnable.sol";
|
||||
|
||||
|
||||
contract IAuthorizable is
|
||||
IOwnable
|
||||
{
|
||||
/// @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,45 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "../interfaces/IAssetProxyDispatcher.sol";
|
||||
|
||||
|
||||
contract MAssetProxyDispatcher is
|
||||
IAssetProxyDispatcher
|
||||
{
|
||||
// Logs registration of new asset proxy
|
||||
event AssetProxyRegistered(
|
||||
bytes4 id, // Id of new registered AssetProxy.
|
||||
address assetProxy // Address of new registered AssetProxy.
|
||||
);
|
||||
|
||||
/// @dev Forwards arguments to assetProxy and calls `transferFrom`. Either succeeds or throws.
|
||||
/// @param assetData Byte array encoded for the asset.
|
||||
/// @param from Address to transfer token from.
|
||||
/// @param to Address to transfer token to.
|
||||
/// @param amount Amount of token to transfer.
|
||||
function dispatchTransferFrom(
|
||||
bytes memory assetData,
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount
|
||||
)
|
||||
internal;
|
||||
}
|
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "../interfaces/IAuthorizable.sol";
|
||||
|
||||
|
||||
contract MAuthorizable is
|
||||
IAuthorizable
|
||||
{
|
||||
// Event logged when a new address is authorized.
|
||||
event AuthorizedAddressAdded(
|
||||
address indexed target,
|
||||
address indexed caller
|
||||
);
|
||||
|
||||
// Event logged when a currently authorized address is unauthorized.
|
||||
event AuthorizedAddressRemoved(
|
||||
address indexed target,
|
||||
address indexed caller
|
||||
);
|
||||
|
||||
/// @dev Only authorized addresses can invoke functions with this modifier.
|
||||
modifier onlyAuthorized { revert(); _; }
|
||||
}
|
@@ -1,86 +0,0 @@
|
||||
{
|
||||
"name": "@0x/contracts-asset-proxy",
|
||||
"version": "2.1.0",
|
||||
"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 generate_contract_wrappers",
|
||||
"test": "yarn run_mocha",
|
||||
"rebuild_and_test": "run-s build test",
|
||||
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
||||
"test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html",
|
||||
"test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha",
|
||||
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
|
||||
"compile": "sol-compiler",
|
||||
"watch": "sol-compiler -w",
|
||||
"clean": "shx rm -rf lib generated-artifacts generated-wrappers",
|
||||
"generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../../node_modules/@0x/abi-gen-templates/contract.handlebars --partials '../../node_modules/@0x/abi-gen-templates/partials/**/*.handlebars' --output generated-wrappers --backend ethers",
|
||||
"lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||
"coverage:report:text": "istanbul report text",
|
||||
"coverage:report:html": "istanbul report html && open coverage/index.html",
|
||||
"profiler:report:html": "istanbul report html && open coverage/index.html",
|
||||
"coverage:report:lcov": "istanbul report lcov",
|
||||
"test:circleci": "yarn test",
|
||||
"contracts:gen": "contracts-gen",
|
||||
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol"
|
||||
},
|
||||
"config": {
|
||||
"abis": "./generated-artifacts/@(ERC1155Proxy|ERC20Proxy|ERC721Proxy|IAssetData|IAssetProxy|IAuthorizable|MixinAuthorizable|MultiAssetProxy).json",
|
||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"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": "^2.0.8",
|
||||
"@0x/contracts-gen": "^1.0.7",
|
||||
"@0x/contracts-test-utils": "^3.1.1",
|
||||
"@0x/dev-utils": "^2.2.0",
|
||||
"@0x/sol-compiler": "^3.1.5",
|
||||
"@0x/tslint-config": "^3.0.0",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/node": "*",
|
||||
"chai": "^4.0.1",
|
||||
"chai-as-promised": "^7.1.0",
|
||||
"chai-bignumber": "^3.0.0",
|
||||
"dirty-chai": "^2.0.1",
|
||||
"make-promises-safe": "^1.1.0",
|
||||
"mocha": "^4.1.0",
|
||||
"npm-run-all": "^4.1.2",
|
||||
"shx": "^0.2.2",
|
||||
"solhint": "^1.4.1",
|
||||
"tslint": "5.11.0",
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^5.0.4",
|
||||
"@0x/contracts-erc1155": "^1.1.0",
|
||||
"@0x/contracts-erc20": "^2.1.0",
|
||||
"@0x/contracts-erc721": "^2.1.0",
|
||||
"@0x/contracts-utils": "^3.1.0",
|
||||
"@0x/order-utils": "^7.1.1",
|
||||
"@0x/types": "^2.2.1",
|
||||
"@0x/typescript-typings": "^4.2.1",
|
||||
"@0x/utils": "^4.3.0",
|
||||
"@0x/web3-wrapper": "^6.0.4",
|
||||
"ethereum-types": "^2.1.1",
|
||||
"lodash": "^4.17.11"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
@@ -1,25 +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 ERC20Proxy from '../generated-artifacts/ERC20Proxy.json';
|
||||
import * as ERC721Proxy from '../generated-artifacts/ERC721Proxy.json';
|
||||
import * as IAssetData from '../generated-artifacts/IAssetData.json';
|
||||
import * as IAssetProxy from '../generated-artifacts/IAssetProxy.json';
|
||||
import * as IAuthorizable from '../generated-artifacts/IAuthorizable.json';
|
||||
import * as MixinAuthorizable from '../generated-artifacts/MixinAuthorizable.json';
|
||||
import * as MultiAssetProxy from '../generated-artifacts/MultiAssetProxy.json';
|
||||
export const artifacts = {
|
||||
ERC20Proxy: ERC20Proxy as ContractArtifact,
|
||||
ERC721Proxy: ERC721Proxy as ContractArtifact,
|
||||
ERC1155Proxy: ERC1155Proxy as ContractArtifact,
|
||||
MixinAuthorizable: MixinAuthorizable as ContractArtifact,
|
||||
MultiAssetProxy: MultiAssetProxy as ContractArtifact,
|
||||
IAssetData: IAssetData as ContractArtifact,
|
||||
IAssetProxy: IAssetProxy as ContractArtifact,
|
||||
IAuthorizable: IAuthorizable as ContractArtifact,
|
||||
};
|
@@ -1,3 +0,0 @@
|
||||
export * from './artifacts';
|
||||
export * from './wrappers';
|
||||
export * from '../test/utils';
|
@@ -1,13 +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_proxy';
|
||||
export * from '../generated-wrappers/erc721_proxy';
|
||||
export * from '../generated-wrappers/i_asset_data';
|
||||
export * from '../generated-wrappers/i_asset_proxy';
|
||||
export * from '../generated-wrappers/i_authorizable';
|
||||
export * from '../generated-wrappers/mixin_authorizable';
|
||||
export * from '../generated-wrappers/multi_asset_proxy';
|
@@ -1,210 +0,0 @@
|
||||
import {
|
||||
chaiSetup,
|
||||
constants,
|
||||
expectTransactionFailedAsync,
|
||||
provider,
|
||||
txDefaults,
|
||||
web3Wrapper,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||
import { RevertReason } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as chai from 'chai';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { artifacts, MixinAuthorizableContract } from '../src';
|
||||
|
||||
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,
|
||||
);
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
afterEach(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
describe('addAuthorizedAddress', () => {
|
||||
it('should throw if not called by owner', async () => {
|
||||
return expectTransactionFailedAsync(
|
||||
authorizable.addAuthorizedAddress.sendTransactionAsync(notOwner, { from: notOwner }),
|
||||
RevertReason.OnlyContractOwner,
|
||||
);
|
||||
});
|
||||
it('should allow owner to add an authorized address', async () => {
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const isAuthorized = await authorizable.authorized.callAsync(address);
|
||||
expect(isAuthorized).to.be.true();
|
||||
});
|
||||
it('should throw if owner attempts to authorize a duplicate address', async () => {
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
return expectTransactionFailedAsync(
|
||||
authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }),
|
||||
RevertReason.TargetAlreadyAuthorized,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeAuthorizedAddress', () => {
|
||||
it('should throw if not called by owner', async () => {
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
return expectTransactionFailedAsync(
|
||||
authorizable.removeAuthorizedAddress.sendTransactionAsync(address, {
|
||||
from: notOwner,
|
||||
}),
|
||||
RevertReason.OnlyContractOwner,
|
||||
);
|
||||
});
|
||||
|
||||
it('should allow owner to remove an authorized address', async () => {
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await authorizable.removeAuthorizedAddress.sendTransactionAsync(address, {
|
||||
from: owner,
|
||||
}),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const isAuthorized = await authorizable.authorized.callAsync(address);
|
||||
expect(isAuthorized).to.be.false();
|
||||
});
|
||||
|
||||
it('should throw if owner attempts to remove an address that is not authorized', async () => {
|
||||
return expectTransactionFailedAsync(
|
||||
authorizable.removeAuthorizedAddress.sendTransactionAsync(address, {
|
||||
from: owner,
|
||||
}),
|
||||
RevertReason.TargetNotAuthorized,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeAuthorizedAddressAtIndex', () => {
|
||||
it('should throw if not called by owner', async () => {
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const index = new BigNumber(0);
|
||||
return expectTransactionFailedAsync(
|
||||
authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, {
|
||||
from: notOwner,
|
||||
}),
|
||||
RevertReason.OnlyContractOwner,
|
||||
);
|
||||
});
|
||||
it('should throw if index is >= authorities.length', async () => {
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const index = new BigNumber(1);
|
||||
return expectTransactionFailedAsync(
|
||||
authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, {
|
||||
from: owner,
|
||||
}),
|
||||
RevertReason.IndexOutOfBounds,
|
||||
);
|
||||
});
|
||||
it('should throw if owner attempts to remove an address that is not authorized', async () => {
|
||||
const index = new BigNumber(0);
|
||||
return expectTransactionFailedAsync(
|
||||
authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, {
|
||||
from: owner,
|
||||
}),
|
||||
RevertReason.TargetNotAuthorized,
|
||||
);
|
||||
});
|
||||
it('should throw if address at index does not match target', async () => {
|
||||
const address1 = address;
|
||||
const address2 = notOwner;
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await authorizable.addAuthorizedAddress.sendTransactionAsync(address1, { from: owner }),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await authorizable.addAuthorizedAddress.sendTransactionAsync(address2, { from: owner }),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const address1Index = new BigNumber(0);
|
||||
return expectTransactionFailedAsync(
|
||||
authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address2, address1Index, {
|
||||
from: owner,
|
||||
}),
|
||||
RevertReason.AuthorizedAddressMismatch,
|
||||
);
|
||||
});
|
||||
it('should allow owner to remove an authorized address', async () => {
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const index = new BigNumber(0);
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, {
|
||||
from: owner,
|
||||
}),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const isAuthorized = await authorizable.authorized.callAsync(address);
|
||||
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 web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await authorizable.addAuthorizedAddress.sendTransactionAsync(address, {
|
||||
from: owner,
|
||||
}),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const afterAdd = await authorizable.getAuthorizedAddresses.callAsync();
|
||||
expect(afterAdd).to.have.length(1);
|
||||
expect(afterAdd).to.include(address);
|
||||
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await authorizable.removeAuthorizedAddress.sendTransactionAsync(address, {
|
||||
from: owner,
|
||||
}),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const afterRemove = await authorizable.getAuthorizedAddresses.callAsync();
|
||||
expect(afterRemove).to.have.length(0);
|
||||
});
|
||||
});
|
||||
});
|
@@ -1,833 +0,0 @@
|
||||
import {
|
||||
artifacts as erc1155Artifacts,
|
||||
DummyERC1155ReceiverBatchTokenReceivedEventArgs,
|
||||
DummyERC1155ReceiverContract,
|
||||
ERC1155MintableContract,
|
||||
Erc1155Wrapper,
|
||||
} from '@0x/contracts-erc1155';
|
||||
import {
|
||||
chaiSetup,
|
||||
constants,
|
||||
expectTransactionFailedAsync,
|
||||
expectTransactionFailedWithoutReasonAsync,
|
||||
provider,
|
||||
txDefaults,
|
||||
web3Wrapper,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||
import { AssetProxyId, RevertReason } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as chai from 'chai';
|
||||
import { LogWithDecodedArgs } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { ERC1155ProxyWrapper, ERC721ProxyContract } from '../src';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
|
||||
// tslint:disable:no-unnecessary-type-assertion
|
||||
describe('ERC1155Proxy', () => {
|
||||
// constant values used in transfer tests
|
||||
const nftOwnerBalance = new BigNumber(1);
|
||||
const nftNotOwnerBalance = new BigNumber(0);
|
||||
const spenderInitialFungibleBalance = constants.INITIAL_ERC1155_FUNGIBLE_BALANCE;
|
||||
const receiverInitialFungibleBalance = constants.INITIAL_ERC1155_FUNGIBLE_BALANCE;
|
||||
const receiverContractInitialFungibleBalance = new BigNumber(0);
|
||||
const fungibleValueToTransferSmall = spenderInitialFungibleBalance.div(100);
|
||||
const fungibleValueToTransferLarge = spenderInitialFungibleBalance.div(4);
|
||||
const valueMultiplierSmall = new BigNumber(2);
|
||||
const valueMultiplierNft = new BigNumber(1);
|
||||
const nonFungibleValueToTransfer = nftOwnerBalance;
|
||||
const receiverCallbackData = '0x01020304';
|
||||
// addresses
|
||||
let owner: string;
|
||||
let notAuthorized: string;
|
||||
let authorized: string;
|
||||
let spender: string;
|
||||
let receiver: string;
|
||||
let receiverContract: string;
|
||||
// contracts & wrappers
|
||||
let erc1155Proxy: ERC721ProxyContract;
|
||||
let erc1155Receiver: DummyERC1155ReceiverContract;
|
||||
let erc1155ProxyWrapper: ERC1155ProxyWrapper;
|
||||
let erc1155Contract: ERC1155MintableContract;
|
||||
let erc1155Wrapper: Erc1155Wrapper;
|
||||
// tokens
|
||||
let fungibleTokens: BigNumber[];
|
||||
let nonFungibleTokensOwnedBySpender: BigNumber[];
|
||||
// tests
|
||||
before(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
after(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
before(async () => {
|
||||
/// deploy & configure ERC1155Proxy
|
||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
||||
const usedAddresses = ([owner, notAuthorized, authorized, spender, receiver] = _.slice(accounts, 0, 5));
|
||||
erc1155ProxyWrapper = new ERC1155ProxyWrapper(provider, usedAddresses, owner);
|
||||
erc1155Proxy = await erc1155ProxyWrapper.deployProxyAsync();
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await erc1155Proxy.addAuthorizedAddress.sendTransactionAsync(authorized, {
|
||||
from: owner,
|
||||
}),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await erc1155Proxy.addAuthorizedAddress.sendTransactionAsync(erc1155Proxy.address, {
|
||||
from: owner,
|
||||
}),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
// deploy & configure ERC1155 tokens and receiver
|
||||
[erc1155Wrapper] = await erc1155ProxyWrapper.deployDummyContractsAsync();
|
||||
erc1155Contract = erc1155Wrapper.getContract();
|
||||
erc1155Receiver = await DummyERC1155ReceiverContract.deployFrom0xArtifactAsync(
|
||||
erc1155Artifacts.DummyERC1155Receiver,
|
||||
provider,
|
||||
txDefaults,
|
||||
);
|
||||
receiverContract = erc1155Receiver.address;
|
||||
await erc1155ProxyWrapper.setBalancesAndAllowancesAsync();
|
||||
fungibleTokens = erc1155ProxyWrapper.getFungibleTokenIds();
|
||||
const nonFungibleTokens = erc1155ProxyWrapper.getNonFungibleTokenIds();
|
||||
const tokenBalances = await erc1155ProxyWrapper.getBalancesAsync();
|
||||
nonFungibleTokensOwnedBySpender = [];
|
||||
_.each(nonFungibleTokens, (nonFungibleToken: BigNumber) => {
|
||||
const nonFungibleTokenAsString = nonFungibleToken.toString();
|
||||
const nonFungibleTokenHeldBySpender =
|
||||
tokenBalances.nonFungible[spender][erc1155Contract.address][nonFungibleTokenAsString][0];
|
||||
nonFungibleTokensOwnedBySpender.push(nonFungibleTokenHeldBySpender);
|
||||
});
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
afterEach(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
describe('general', () => {
|
||||
it('should revert if undefined function is called', async () => {
|
||||
const undefinedSelector = '0x01020304';
|
||||
await expectTransactionFailedWithoutReasonAsync(
|
||||
web3Wrapper.sendTransactionAsync({
|
||||
from: owner,
|
||||
to: erc1155Proxy.address,
|
||||
value: constants.ZERO_AMOUNT,
|
||||
data: undefinedSelector,
|
||||
}),
|
||||
);
|
||||
});
|
||||
it('should have an id of 0x9645780d', async () => {
|
||||
const proxyId = await erc1155Proxy.getProxyId.callAsync();
|
||||
const expectedProxyId = AssetProxyId.ERC1155;
|
||||
expect(proxyId).to.equal(expectedProxyId);
|
||||
});
|
||||
});
|
||||
describe('transferFrom', () => {
|
||||
it('should successfully transfer value for a single, fungible token', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokensToTransfer = fungibleTokens.slice(0, 1);
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
await erc1155ProxyWrapper.transferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
receiverCallbackData,
|
||||
authorized,
|
||||
);
|
||||
// check balances after transfer
|
||||
const totalValueTransferred = valuesToTransfer[0].times(valueMultiplier);
|
||||
const expectedFinalBalances = [
|
||||
spenderInitialFungibleBalance.minus(totalValueTransferred),
|
||||
receiverInitialFungibleBalance.plus(totalValueTransferred),
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||
});
|
||||
it('should successfully transfer value for the same fungible token several times', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokenToTransfer = fungibleTokens[0];
|
||||
const tokensToTransfer = [tokenToTransfer, tokenToTransfer, tokenToTransfer];
|
||||
const valuesToTransfer = [
|
||||
fungibleValueToTransferSmall.plus(10),
|
||||
fungibleValueToTransferSmall.plus(20),
|
||||
fungibleValueToTransferSmall.plus(30),
|
||||
];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [
|
||||
// spender
|
||||
spenderInitialFungibleBalance,
|
||||
// receiver
|
||||
receiverInitialFungibleBalance,
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedInitialBalances);
|
||||
// execute transfer
|
||||
await erc1155ProxyWrapper.transferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
receiverCallbackData,
|
||||
authorized,
|
||||
);
|
||||
// check balances after transfer
|
||||
let totalValueTransferred = _.reduce(valuesToTransfer, (sum: BigNumber, value: BigNumber) => {
|
||||
return sum.plus(value);
|
||||
}) as BigNumber;
|
||||
totalValueTransferred = totalValueTransferred.times(valueMultiplier);
|
||||
const expectedFinalBalances = [
|
||||
// spender
|
||||
spenderInitialFungibleBalance.minus(totalValueTransferred),
|
||||
// receiver
|
||||
receiverInitialFungibleBalance.plus(totalValueTransferred),
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedFinalBalances);
|
||||
});
|
||||
it('should successfully transfer value for several fungible tokens', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokensToTransfer = fungibleTokens.slice(0, 3);
|
||||
const valuesToTransfer = [
|
||||
fungibleValueToTransferSmall.plus(10),
|
||||
fungibleValueToTransferSmall.plus(20),
|
||||
fungibleValueToTransferSmall.plus(30),
|
||||
];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [
|
||||
// spender
|
||||
spenderInitialFungibleBalance,
|
||||
spenderInitialFungibleBalance,
|
||||
spenderInitialFungibleBalance,
|
||||
// receiver
|
||||
receiverInitialFungibleBalance,
|
||||
receiverInitialFungibleBalance,
|
||||
receiverInitialFungibleBalance,
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
await erc1155ProxyWrapper.transferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
receiverCallbackData,
|
||||
authorized,
|
||||
);
|
||||
// check balances after transfer
|
||||
const totalValuesTransferred = _.map(valuesToTransfer, (value: BigNumber) => {
|
||||
return value.times(valueMultiplier);
|
||||
});
|
||||
const expectedFinalBalances = [
|
||||
// spender
|
||||
spenderInitialFungibleBalance.minus(totalValuesTransferred[0]),
|
||||
spenderInitialFungibleBalance.minus(totalValuesTransferred[1]),
|
||||
spenderInitialFungibleBalance.minus(totalValuesTransferred[2]),
|
||||
// receiver
|
||||
receiverInitialFungibleBalance.plus(totalValuesTransferred[0]),
|
||||
receiverInitialFungibleBalance.plus(totalValuesTransferred[1]),
|
||||
receiverInitialFungibleBalance.plus(totalValuesTransferred[2]),
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||
});
|
||||
it('should successfully transfer a non-fungible token', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokensToTransfer = nonFungibleTokensOwnedBySpender.slice(0, 1);
|
||||
const valuesToTransfer = [nonFungibleValueToTransfer];
|
||||
const valueMultiplier = valueMultiplierNft;
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [
|
||||
// spender
|
||||
nftOwnerBalance,
|
||||
// receiver
|
||||
nftNotOwnerBalance,
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
await erc1155ProxyWrapper.transferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
receiverCallbackData,
|
||||
authorized,
|
||||
);
|
||||
// check balances after transfer
|
||||
const expectedFinalBalances = [
|
||||
// spender
|
||||
nftNotOwnerBalance,
|
||||
// receiver
|
||||
nftOwnerBalance,
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||
});
|
||||
it('should successfully transfer multiple non-fungible tokens', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokensToTransfer = nonFungibleTokensOwnedBySpender.slice(0, 3);
|
||||
const valuesToTransfer = [
|
||||
nonFungibleValueToTransfer,
|
||||
nonFungibleValueToTransfer,
|
||||
nonFungibleValueToTransfer,
|
||||
];
|
||||
const valueMultiplier = valueMultiplierNft;
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [
|
||||
// spender
|
||||
nftOwnerBalance,
|
||||
nftOwnerBalance,
|
||||
nftOwnerBalance,
|
||||
// receiver
|
||||
nftNotOwnerBalance,
|
||||
nftNotOwnerBalance,
|
||||
nftNotOwnerBalance,
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
await erc1155ProxyWrapper.transferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
receiverCallbackData,
|
||||
authorized,
|
||||
);
|
||||
// check balances after transfer
|
||||
const expectedFinalBalances = [
|
||||
// spender
|
||||
nftNotOwnerBalance,
|
||||
nftNotOwnerBalance,
|
||||
nftNotOwnerBalance,
|
||||
// receiver
|
||||
nftOwnerBalance,
|
||||
nftOwnerBalance,
|
||||
nftOwnerBalance,
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||
});
|
||||
it('should successfully transfer value for a combination of several fungible/non-fungible tokens', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const fungibleTokensToTransfer = fungibleTokens.slice(0, 3);
|
||||
const nonFungibleTokensToTransfer = nonFungibleTokensOwnedBySpender.slice(0, 2);
|
||||
const tokensToTransfer = fungibleTokensToTransfer.concat(nonFungibleTokensToTransfer);
|
||||
const valuesToTransfer = [
|
||||
fungibleValueToTransferLarge,
|
||||
fungibleValueToTransferSmall,
|
||||
fungibleValueToTransferSmall,
|
||||
nonFungibleValueToTransfer,
|
||||
nonFungibleValueToTransfer,
|
||||
];
|
||||
const valueMultiplier = valueMultiplierNft;
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [
|
||||
// spender
|
||||
spenderInitialFungibleBalance,
|
||||
spenderInitialFungibleBalance,
|
||||
spenderInitialFungibleBalance,
|
||||
nftOwnerBalance,
|
||||
nftOwnerBalance,
|
||||
// receiver
|
||||
receiverInitialFungibleBalance,
|
||||
receiverInitialFungibleBalance,
|
||||
receiverInitialFungibleBalance,
|
||||
nftNotOwnerBalance,
|
||||
nftNotOwnerBalance,
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
await erc1155ProxyWrapper.transferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
receiverCallbackData,
|
||||
authorized,
|
||||
);
|
||||
// check balances after transfer
|
||||
const totalValuesTransferred = _.map(valuesToTransfer, (value: BigNumber) => {
|
||||
return value.times(valueMultiplier);
|
||||
});
|
||||
const expectedFinalBalances = [
|
||||
// spender
|
||||
expectedInitialBalances[0].minus(totalValuesTransferred[0]),
|
||||
expectedInitialBalances[1].minus(totalValuesTransferred[1]),
|
||||
expectedInitialBalances[2].minus(totalValuesTransferred[2]),
|
||||
expectedInitialBalances[3].minus(totalValuesTransferred[3]),
|
||||
expectedInitialBalances[4].minus(totalValuesTransferred[4]),
|
||||
// receiver
|
||||
expectedInitialBalances[5].plus(totalValuesTransferred[0]),
|
||||
expectedInitialBalances[6].plus(totalValuesTransferred[1]),
|
||||
expectedInitialBalances[7].plus(totalValuesTransferred[2]),
|
||||
expectedInitialBalances[8].plus(totalValuesTransferred[3]),
|
||||
expectedInitialBalances[9].plus(totalValuesTransferred[4]),
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||
});
|
||||
it('should successfully transfer value to a smart contract and trigger its callback', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiverContract];
|
||||
const tokensToTransfer = fungibleTokens.slice(0, 1);
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
const totalValuesTransferred = _.map(valuesToTransfer, (value: BigNumber) => {
|
||||
return value.times(valueMultiplier);
|
||||
});
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverContractInitialFungibleBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
const txReceipt = await erc1155ProxyWrapper.transferFromWithLogsAsync(
|
||||
spender,
|
||||
receiverContract,
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
receiverCallbackData,
|
||||
authorized,
|
||||
);
|
||||
// check receiver log ignored extra asset data
|
||||
expect(txReceipt.logs.length).to.be.equal(2);
|
||||
const receiverLog = txReceipt.logs[1] as LogWithDecodedArgs<
|
||||
DummyERC1155ReceiverBatchTokenReceivedEventArgs
|
||||
>;
|
||||
expect(receiverLog.args.operator).to.be.equal(erc1155Proxy.address);
|
||||
expect(receiverLog.args.from).to.be.equal(spender);
|
||||
expect(receiverLog.args.tokenIds.length).to.be.deep.equal(1);
|
||||
expect(receiverLog.args.tokenIds[0]).to.be.bignumber.equal(tokensToTransfer[0]);
|
||||
expect(receiverLog.args.tokenValues.length).to.be.deep.equal(1);
|
||||
expect(receiverLog.args.tokenValues[0]).to.be.bignumber.equal(totalValuesTransferred[0]);
|
||||
// note - if the `extraData` is ignored then the receiver log should ignore it as well.
|
||||
expect(receiverLog.args.data).to.be.deep.equal(receiverCallbackData);
|
||||
// check balances after transfer
|
||||
const expectedFinalBalances = [
|
||||
expectedInitialBalances[0].minus(totalValuesTransferred[0]),
|
||||
expectedInitialBalances[1].plus(totalValuesTransferred[0]),
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||
});
|
||||
it('should successfully transfer value to a smart contract and trigger its callback, when callback `data` is NULL', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiverContract];
|
||||
const tokensToTransfer = fungibleTokens.slice(0, 1);
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
const totalValuesTransferred = _.map(valuesToTransfer, (value: BigNumber) => {
|
||||
return value.times(valueMultiplier);
|
||||
});
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverContractInitialFungibleBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
const nullReceiverCallbackData = '0x';
|
||||
const txReceipt = await erc1155ProxyWrapper.transferFromWithLogsAsync(
|
||||
spender,
|
||||
receiverContract,
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
nullReceiverCallbackData,
|
||||
authorized,
|
||||
);
|
||||
// check receiver log ignored extra asset data
|
||||
expect(txReceipt.logs.length).to.be.equal(2);
|
||||
const receiverLog = txReceipt.logs[1] as LogWithDecodedArgs<
|
||||
DummyERC1155ReceiverBatchTokenReceivedEventArgs
|
||||
>;
|
||||
expect(receiverLog.args.operator).to.be.equal(erc1155Proxy.address);
|
||||
expect(receiverLog.args.from).to.be.equal(spender);
|
||||
expect(receiverLog.args.tokenIds.length).to.be.deep.equal(1);
|
||||
expect(receiverLog.args.tokenIds[0]).to.be.bignumber.equal(tokensToTransfer[0]);
|
||||
expect(receiverLog.args.tokenValues.length).to.be.deep.equal(1);
|
||||
expect(receiverLog.args.tokenValues[0]).to.be.bignumber.equal(totalValuesTransferred[0]);
|
||||
// note - if the `extraData` is ignored then the receiver log should ignore it as well.
|
||||
expect(receiverLog.args.data).to.be.deep.equal(nullReceiverCallbackData);
|
||||
// check balances after transfer
|
||||
const expectedFinalBalances = [
|
||||
expectedInitialBalances[0].minus(totalValuesTransferred[0]),
|
||||
expectedInitialBalances[1].plus(totalValuesTransferred[0]),
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||
});
|
||||
it('should successfully transfer value and ignore extra assetData', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiverContract];
|
||||
const tokensToTransfer = fungibleTokens.slice(0, 1);
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
const totalValuesTransferred = _.map(valuesToTransfer, (value: BigNumber) => {
|
||||
return value.times(valueMultiplier);
|
||||
});
|
||||
const extraData = '0102030405060708';
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverContractInitialFungibleBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
const txReceipt = await erc1155ProxyWrapper.transferFromWithLogsAsync(
|
||||
spender,
|
||||
receiverContract,
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
receiverCallbackData,
|
||||
authorized,
|
||||
extraData,
|
||||
);
|
||||
// check receiver log ignored extra asset data
|
||||
expect(txReceipt.logs.length).to.be.equal(2);
|
||||
const receiverLog = txReceipt.logs[1] as LogWithDecodedArgs<
|
||||
DummyERC1155ReceiverBatchTokenReceivedEventArgs
|
||||
>;
|
||||
expect(receiverLog.args.operator).to.be.equal(erc1155Proxy.address);
|
||||
expect(receiverLog.args.from).to.be.equal(spender);
|
||||
expect(receiverLog.args.tokenIds.length).to.be.deep.equal(1);
|
||||
expect(receiverLog.args.tokenIds[0]).to.be.bignumber.equal(tokensToTransfer[0]);
|
||||
expect(receiverLog.args.tokenValues.length).to.be.deep.equal(1);
|
||||
expect(receiverLog.args.tokenValues[0]).to.be.bignumber.equal(totalValuesTransferred[0]);
|
||||
// note - if the `extraData` is ignored then the receiver log should ignore it as well.
|
||||
expect(receiverLog.args.data).to.be.deep.equal(receiverCallbackData);
|
||||
// check balances after transfer
|
||||
const expectedFinalBalances = [
|
||||
expectedInitialBalances[0].minus(totalValuesTransferred[0]),
|
||||
expectedInitialBalances[1].plus(totalValuesTransferred[0]),
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||
});
|
||||
it('should transfer nothing if value is zero', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokensToTransfer = fungibleTokens.slice(0, 1);
|
||||
const valuesToTransfer = [new BigNumber(0)];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
await erc1155ProxyWrapper.transferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
receiverCallbackData,
|
||||
authorized,
|
||||
);
|
||||
// check balances after transfer
|
||||
const expectedFinalBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||
});
|
||||
it('should transfer nothing if value multiplier is zero', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokensToTransfer = fungibleTokens.slice(0, 1);
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = new BigNumber(0);
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
await erc1155ProxyWrapper.transferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
receiverCallbackData,
|
||||
authorized,
|
||||
);
|
||||
// check balances after transfer
|
||||
const expectedFinalBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||
});
|
||||
it('should transfer nothing if there are no tokens in asset data', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokensToTransfer: BigNumber[] = [];
|
||||
const valuesToTransfer: BigNumber[] = [];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
await erc1155ProxyWrapper.transferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
receiverCallbackData,
|
||||
authorized,
|
||||
);
|
||||
// check balances after transfer
|
||||
const expectedFinalBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
||||
});
|
||||
it('should propagate revert reason from erc1155 contract failure', async () => {
|
||||
// disable transfers
|
||||
const shouldRejectTransfer = true;
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await erc1155Receiver.setRejectTransferFlag.sendTransactionAsync(shouldRejectTransfer),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiverContract];
|
||||
const tokensToTransfer = fungibleTokens.slice(0, 1);
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverContractInitialFungibleBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
await expectTransactionFailedAsync(
|
||||
erc1155ProxyWrapper.transferFromAsync(
|
||||
spender,
|
||||
receiverContract,
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
receiverCallbackData,
|
||||
authorized,
|
||||
),
|
||||
RevertReason.TransferRejected,
|
||||
);
|
||||
});
|
||||
it('should revert if transferring the same non-fungible token more than once', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const nftToTransfer = nonFungibleTokensOwnedBySpender[0];
|
||||
const tokensToTransfer = [nftToTransfer, nftToTransfer];
|
||||
const valuesToTransfer = [nonFungibleValueToTransfer, nonFungibleValueToTransfer];
|
||||
const valueMultiplier = valueMultiplierNft;
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [
|
||||
// spender
|
||||
nftOwnerBalance,
|
||||
nftOwnerBalance,
|
||||
// receiver
|
||||
nftNotOwnerBalance,
|
||||
nftNotOwnerBalance,
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
await expectTransactionFailedAsync(
|
||||
erc1155ProxyWrapper.transferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
receiverCallbackData,
|
||||
authorized,
|
||||
),
|
||||
RevertReason.NFTNotOwnedByFromAddress,
|
||||
);
|
||||
});
|
||||
it('should revert if there is a multiplication overflow', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokensToTransfer = nonFungibleTokensOwnedBySpender.slice(0, 3);
|
||||
const maxUintValue = new BigNumber(2).pow(256).minus(1);
|
||||
const valuesToTransfer = [nonFungibleValueToTransfer, maxUintValue, nonFungibleValueToTransfer];
|
||||
const valueMultiplier = new BigNumber(2);
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [
|
||||
// spender
|
||||
nftOwnerBalance,
|
||||
nftOwnerBalance,
|
||||
nftOwnerBalance,
|
||||
// receiver
|
||||
nftNotOwnerBalance,
|
||||
nftNotOwnerBalance,
|
||||
nftNotOwnerBalance,
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
// note - this will overflow because we are trying to transfer `maxUintValue * 2` of the 2nd token
|
||||
await expectTransactionFailedAsync(
|
||||
erc1155ProxyWrapper.transferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
receiverCallbackData,
|
||||
authorized,
|
||||
),
|
||||
RevertReason.Uint256Overflow,
|
||||
);
|
||||
});
|
||||
it('should revert if transferring > 1 instances of a non-fungible token (valueMultiplier field >1)', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokensToTransfer = nonFungibleTokensOwnedBySpender.slice(0, 1);
|
||||
const valuesToTransfer = [nonFungibleValueToTransfer];
|
||||
const valueMultiplier = new BigNumber(2);
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [
|
||||
// spender
|
||||
nftOwnerBalance,
|
||||
// receiver
|
||||
nftNotOwnerBalance,
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
await expectTransactionFailedAsync(
|
||||
erc1155ProxyWrapper.transferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
receiverCallbackData,
|
||||
authorized,
|
||||
),
|
||||
RevertReason.AmountEqualToOneRequired,
|
||||
);
|
||||
});
|
||||
it('should revert if transferring > 1 instances of a non-fungible token (`valuesToTransfer` field >1)', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokensToTransfer = nonFungibleTokensOwnedBySpender.slice(0, 1);
|
||||
const valuesToTransfer = [new BigNumber(2)];
|
||||
const valueMultiplier = valueMultiplierNft;
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [
|
||||
// spender
|
||||
nftOwnerBalance,
|
||||
// receiver
|
||||
nftNotOwnerBalance,
|
||||
];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
await expectTransactionFailedAsync(
|
||||
erc1155ProxyWrapper.transferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
receiverCallbackData,
|
||||
authorized,
|
||||
),
|
||||
RevertReason.AmountEqualToOneRequired,
|
||||
);
|
||||
});
|
||||
it('should revert if sender balance is insufficient', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokensToTransfer = fungibleTokens.slice(0, 1);
|
||||
const valueGreaterThanSpenderBalance = spenderInitialFungibleBalance.plus(1);
|
||||
const valuesToTransfer = [valueGreaterThanSpenderBalance];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
await expectTransactionFailedAsync(
|
||||
erc1155ProxyWrapper.transferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
receiverCallbackData,
|
||||
authorized,
|
||||
),
|
||||
RevertReason.Uint256Underflow,
|
||||
);
|
||||
});
|
||||
it('should revert if sender allowance is insufficient', async () => {
|
||||
// dremove allowance for ERC1155 proxy
|
||||
const wrapper = erc1155ProxyWrapper.getContractWrapper(erc1155Contract.address);
|
||||
const isApproved = false;
|
||||
await wrapper.setApprovalForAllAsync(spender, erc1155Proxy.address, isApproved);
|
||||
const isApprovedActualValue = await wrapper.isApprovedForAllAsync(spender, erc1155Proxy.address);
|
||||
expect(isApprovedActualValue).to.be.equal(isApproved);
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokensToTransfer = fungibleTokens.slice(0, 1);
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
await expectTransactionFailedAsync(
|
||||
erc1155ProxyWrapper.transferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
receiverCallbackData,
|
||||
authorized,
|
||||
),
|
||||
RevertReason.InsufficientAllowance,
|
||||
);
|
||||
});
|
||||
it('should revert if caller is not authorized', async () => {
|
||||
// setup test parameters
|
||||
const tokenHolders = [spender, receiver];
|
||||
const tokensToTransfer = fungibleTokens.slice(0, 1);
|
||||
const valuesToTransfer = [fungibleValueToTransferLarge];
|
||||
const valueMultiplier = valueMultiplierSmall;
|
||||
// check balances before transfer
|
||||
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
|
||||
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
||||
// execute transfer
|
||||
await expectTransactionFailedAsync(
|
||||
erc1155ProxyWrapper.transferFromAsync(
|
||||
spender,
|
||||
receiver,
|
||||
erc1155Contract.address,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
receiverCallbackData,
|
||||
notAuthorized,
|
||||
),
|
||||
RevertReason.SenderNotAuthorized,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
// tslint:enable:no-unnecessary-type-assertion
|
||||
// tslint:disable:max-file-line-count
|
@@ -1,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();
|
||||
});
|
File diff suppressed because it is too large
Load Diff
@@ -1,383 +0,0 @@
|
||||
import { artifacts as erc1155Artifacts, ERC1155MintableContract, Erc1155Wrapper } from '@0x/contracts-erc1155';
|
||||
import {
|
||||
constants,
|
||||
ERC1155FungibleHoldingsByOwner,
|
||||
ERC1155HoldingsByOwner,
|
||||
ERC1155NonFungibleHoldingsByOwner,
|
||||
LogDecoder,
|
||||
txDefaults,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { assetDataUtils } from '@0x/order-utils';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import { Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { artifacts, ERC1155ProxyContract, IAssetProxyContract } from '../../src';
|
||||
|
||||
export class ERC1155ProxyWrapper {
|
||||
private readonly _tokenOwnerAddresses: string[];
|
||||
private readonly _fungibleTokenIds: string[];
|
||||
private readonly _nonFungibleTokenIds: string[];
|
||||
private readonly _nfts: Array<{ id: BigNumber; tokenId: BigNumber }>;
|
||||
private readonly _contractOwnerAddress: string;
|
||||
private readonly _web3Wrapper: Web3Wrapper;
|
||||
private readonly _provider: Provider;
|
||||
private readonly _logDecoder: LogDecoder;
|
||||
private readonly _dummyTokenWrappers: Erc1155Wrapper[];
|
||||
private readonly _assetProxyInterface: IAssetProxyContract;
|
||||
private _proxyContract?: ERC1155ProxyContract;
|
||||
private _proxyIdIfExists?: string;
|
||||
private _initialTokenIdsByOwner: ERC1155HoldingsByOwner = { fungible: {}, nonFungible: {} };
|
||||
|
||||
constructor(provider: Provider, tokenOwnerAddresses: string[], contractOwnerAddress: string) {
|
||||
this._web3Wrapper = new Web3Wrapper(provider);
|
||||
this._provider = provider;
|
||||
const allArtifacts = _.merge(artifacts, erc1155Artifacts);
|
||||
this._logDecoder = new LogDecoder(this._web3Wrapper, allArtifacts);
|
||||
this._dummyTokenWrappers = [];
|
||||
this._assetProxyInterface = new IAssetProxyContract(
|
||||
artifacts.IAssetProxy.compilerOutput.abi,
|
||||
constants.NULL_ADDRESS,
|
||||
provider,
|
||||
);
|
||||
this._tokenOwnerAddresses = tokenOwnerAddresses;
|
||||
this._contractOwnerAddress = contractOwnerAddress;
|
||||
this._fungibleTokenIds = [];
|
||||
this._nonFungibleTokenIds = [];
|
||||
this._nfts = [];
|
||||
}
|
||||
/**
|
||||
* @dev Deploys dummy ERC1155 contracts
|
||||
* @return An array of ERC1155 wrappers; one for each deployed contract.
|
||||
*/
|
||||
public async deployDummyContractsAsync(): Promise<Erc1155Wrapper[]> {
|
||||
// tslint:disable-next-line:no-unused-variable
|
||||
for (const i of _.times(constants.NUM_DUMMY_ERC1155_CONTRACTS_TO_DEPLOY)) {
|
||||
const erc1155Contract = await ERC1155MintableContract.deployFrom0xArtifactAsync(
|
||||
erc1155Artifacts.ERC1155Mintable,
|
||||
this._provider,
|
||||
txDefaults,
|
||||
);
|
||||
const erc1155Wrapper = new Erc1155Wrapper(erc1155Contract, this._provider, this._contractOwnerAddress);
|
||||
this._dummyTokenWrappers.push(erc1155Wrapper);
|
||||
}
|
||||
return this._dummyTokenWrappers;
|
||||
}
|
||||
/**
|
||||
* @dev Deploys the ERC1155 proxy
|
||||
* @return Deployed ERC1155 proxy contract instance
|
||||
*/
|
||||
public async deployProxyAsync(): Promise<ERC1155ProxyContract> {
|
||||
this._proxyContract = await ERC1155ProxyContract.deployFrom0xArtifactAsync(
|
||||
artifacts.ERC1155Proxy,
|
||||
this._provider,
|
||||
txDefaults,
|
||||
);
|
||||
this._proxyIdIfExists = await this._proxyContract.getProxyId.callAsync();
|
||||
return this._proxyContract;
|
||||
}
|
||||
/**
|
||||
* @dev Gets the ERC1155 proxy id
|
||||
*/
|
||||
public getProxyId(): string {
|
||||
this._validateProxyContractExistsOrThrow();
|
||||
return this._proxyIdIfExists as string;
|
||||
}
|
||||
/**
|
||||
* @dev transfers erc1155 fungible/non-fungible tokens.
|
||||
* @param from source address
|
||||
* @param to destination address
|
||||
* @param contractAddress address of erc155 contract
|
||||
* @param tokensToTransfer array of erc1155 tokens to transfer
|
||||
* @param valuesToTransfer array of corresponding values for each erc1155 token to transfer
|
||||
* @param valueMultiplier each value in `valuesToTransfer` is multiplied by this
|
||||
* @param receiverCallbackData callback data if `to` is a contract
|
||||
* @param authorizedSender sender of `transferFrom` transaction
|
||||
* @param extraData extra data to append to `transferFrom` transaction. Optional.
|
||||
* @return tranasction hash.
|
||||
*/
|
||||
public async transferFromAsync(
|
||||
from: string,
|
||||
to: string,
|
||||
contractAddress: string,
|
||||
tokensToTransfer: BigNumber[],
|
||||
valuesToTransfer: BigNumber[],
|
||||
valueMultiplier: BigNumber,
|
||||
receiverCallbackData: string,
|
||||
authorizedSender: string,
|
||||
extraData?: string,
|
||||
): Promise<string> {
|
||||
this._validateProxyContractExistsOrThrow();
|
||||
let encodedAssetData = assetDataUtils.encodeERC1155AssetData(
|
||||
contractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
receiverCallbackData,
|
||||
);
|
||||
if (!_.isUndefined(extraData)) {
|
||||
encodedAssetData = `${encodedAssetData}${extraData}`;
|
||||
}
|
||||
const data = this._assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
encodedAssetData,
|
||||
from,
|
||||
to,
|
||||
valueMultiplier,
|
||||
);
|
||||
const txHash = await this._web3Wrapper.sendTransactionAsync({
|
||||
to: (this._proxyContract as ERC1155ProxyContract).address,
|
||||
data,
|
||||
from: authorizedSender,
|
||||
});
|
||||
return txHash;
|
||||
}
|
||||
/**
|
||||
* @dev transfers erc1155 fungible/non-fungible tokens.
|
||||
* @param from source address
|
||||
* @param to destination address
|
||||
* @param contractAddress address of erc155 contract
|
||||
* @param tokensToTransfer array of erc1155 tokens to transfer
|
||||
* @param valuesToTransfer array of corresponding values for each erc1155 token to transfer
|
||||
* @param valueMultiplier each value in `valuesToTransfer` is multiplied by this
|
||||
* @param receiverCallbackData callback data if `to` is a contract
|
||||
* @param authorizedSender sender of `transferFrom` transaction
|
||||
* @param extraData extra data to append to `transferFrom` transaction. Optional.
|
||||
* @return tranasction receipt with decoded logs.
|
||||
*/
|
||||
public async transferFromWithLogsAsync(
|
||||
from: string,
|
||||
to: string,
|
||||
contractAddress: string,
|
||||
tokensToTransfer: BigNumber[],
|
||||
valuesToTransfer: BigNumber[],
|
||||
valueMultiplier: BigNumber,
|
||||
receiverCallbackData: string,
|
||||
authorizedSender: string,
|
||||
extraData?: string,
|
||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||
const txReceipt = await this._logDecoder.getTxWithDecodedLogsAsync(
|
||||
await this.transferFromAsync(
|
||||
from,
|
||||
to,
|
||||
contractAddress,
|
||||
tokensToTransfer,
|
||||
valuesToTransfer,
|
||||
valueMultiplier,
|
||||
receiverCallbackData,
|
||||
authorizedSender,
|
||||
extraData,
|
||||
),
|
||||
);
|
||||
return txReceipt;
|
||||
}
|
||||
/**
|
||||
* @dev For each deployed ERC1155 contract, this function mints a set of fungible/non-fungible
|
||||
* tokens for each token owner address (`_tokenOwnerAddresses`).
|
||||
* @return Balances of each token owner, across all ERC1155 contracts and tokens.
|
||||
*/
|
||||
public async setBalancesAndAllowancesAsync(): Promise<ERC1155HoldingsByOwner> {
|
||||
this._validateDummyTokenContractsExistOrThrow();
|
||||
this._validateProxyContractExistsOrThrow();
|
||||
this._initialTokenIdsByOwner = {
|
||||
fungible: {},
|
||||
nonFungible: {},
|
||||
};
|
||||
const fungibleHoldingsByOwner: ERC1155FungibleHoldingsByOwner = {};
|
||||
const nonFungibleHoldingsByOwner: ERC1155NonFungibleHoldingsByOwner = {};
|
||||
// Set balances accordingly
|
||||
for (const dummyWrapper of this._dummyTokenWrappers) {
|
||||
const dummyAddress = dummyWrapper.getContract().address;
|
||||
// tslint:disable-next-line:no-unused-variable
|
||||
for (const i of _.times(constants.NUM_ERC1155_FUNGIBLE_TOKENS_MINT)) {
|
||||
// Create a fungible token
|
||||
const tokenId = await dummyWrapper.mintFungibleTokensAsync(
|
||||
this._tokenOwnerAddresses,
|
||||
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
|
||||
);
|
||||
const tokenIdAsString = tokenId.toString();
|
||||
this._fungibleTokenIds.push(tokenIdAsString);
|
||||
// Mint tokens for each owner for this token
|
||||
for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
|
||||
// tslint:disable-next-line:no-unused-variable
|
||||
if (_.isUndefined(fungibleHoldingsByOwner[tokenOwnerAddress])) {
|
||||
fungibleHoldingsByOwner[tokenOwnerAddress] = {};
|
||||
}
|
||||
if (_.isUndefined(fungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress])) {
|
||||
fungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress] = {};
|
||||
}
|
||||
fungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress][tokenIdAsString] =
|
||||
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE;
|
||||
await dummyWrapper.setApprovalForAllAsync(
|
||||
tokenOwnerAddress,
|
||||
(this._proxyContract as ERC1155ProxyContract).address,
|
||||
true,
|
||||
);
|
||||
}
|
||||
}
|
||||
// Non-fungible tokens
|
||||
// tslint:disable-next-line:no-unused-variable
|
||||
for (const j of _.times(constants.NUM_ERC1155_NONFUNGIBLE_TOKENS_MINT)) {
|
||||
const [tokenId, nftIds] = await dummyWrapper.mintNonFungibleTokensAsync(this._tokenOwnerAddresses);
|
||||
const tokenIdAsString = tokenId.toString();
|
||||
this._nonFungibleTokenIds.push(tokenIdAsString);
|
||||
_.each(this._tokenOwnerAddresses, async (tokenOwnerAddress: string, i: number) => {
|
||||
if (_.isUndefined(nonFungibleHoldingsByOwner[tokenOwnerAddress])) {
|
||||
nonFungibleHoldingsByOwner[tokenOwnerAddress] = {};
|
||||
}
|
||||
if (_.isUndefined(nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress])) {
|
||||
nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress] = {};
|
||||
}
|
||||
if (_.isUndefined(nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress][tokenIdAsString])) {
|
||||
nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress][tokenIdAsString] = [];
|
||||
}
|
||||
this._nfts.push({ id: nftIds[i], tokenId });
|
||||
nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress][tokenIdAsString].push(nftIds[i]);
|
||||
await dummyWrapper.setApprovalForAllAsync(
|
||||
tokenOwnerAddress,
|
||||
(this._proxyContract as ERC1155ProxyContract).address,
|
||||
true,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
this._initialTokenIdsByOwner = {
|
||||
fungible: fungibleHoldingsByOwner,
|
||||
nonFungible: nonFungibleHoldingsByOwner,
|
||||
};
|
||||
return this._initialTokenIdsByOwner;
|
||||
}
|
||||
/**
|
||||
* @dev For each deployed ERC1155 contract, this function quieries the set of fungible/non-fungible
|
||||
* tokens for each token owner address (`_tokenOwnerAddresses`).
|
||||
* @return Balances of each token owner, across all ERC1155 contracts and tokens.
|
||||
*/
|
||||
public async getBalancesAsync(): Promise<ERC1155HoldingsByOwner> {
|
||||
this._validateDummyTokenContractsExistOrThrow();
|
||||
this._validateBalancesAndAllowancesSetOrThrow();
|
||||
const tokenHoldingsByOwner: ERC1155FungibleHoldingsByOwner = {};
|
||||
const nonFungibleHoldingsByOwner: ERC1155NonFungibleHoldingsByOwner = {};
|
||||
for (const dummyTokenWrapper of this._dummyTokenWrappers) {
|
||||
const tokenContract = dummyTokenWrapper.getContract();
|
||||
const tokenAddress = tokenContract.address;
|
||||
// Construct batch balance call
|
||||
const tokenOwners: string[] = [];
|
||||
const tokenIds: BigNumber[] = [];
|
||||
for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
|
||||
for (const tokenId of this._fungibleTokenIds) {
|
||||
tokenOwners.push(tokenOwnerAddress);
|
||||
tokenIds.push(new BigNumber(tokenId));
|
||||
}
|
||||
for (const nft of this._nfts) {
|
||||
tokenOwners.push(tokenOwnerAddress);
|
||||
tokenIds.push(nft.id);
|
||||
}
|
||||
}
|
||||
const balances = await dummyTokenWrapper.getBalancesAsync(tokenOwners, tokenIds);
|
||||
// Parse out balances into fungible / non-fungible token holdings
|
||||
let i = 0;
|
||||
for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
|
||||
// Fungible tokens
|
||||
for (const tokenId of this._fungibleTokenIds) {
|
||||
if (_.isUndefined(tokenHoldingsByOwner[tokenOwnerAddress])) {
|
||||
tokenHoldingsByOwner[tokenOwnerAddress] = {};
|
||||
}
|
||||
if (_.isUndefined(tokenHoldingsByOwner[tokenOwnerAddress][tokenAddress])) {
|
||||
tokenHoldingsByOwner[tokenOwnerAddress][tokenAddress] = {};
|
||||
}
|
||||
tokenHoldingsByOwner[tokenOwnerAddress][tokenAddress][tokenId] = balances[i++];
|
||||
}
|
||||
// Non-fungible tokens
|
||||
for (const nft of this._nfts) {
|
||||
if (_.isUndefined(nonFungibleHoldingsByOwner[tokenOwnerAddress])) {
|
||||
nonFungibleHoldingsByOwner[tokenOwnerAddress] = {};
|
||||
}
|
||||
if (_.isUndefined(nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress])) {
|
||||
nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress] = {};
|
||||
}
|
||||
if (
|
||||
_.isUndefined(
|
||||
nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress][nft.tokenId.toString()],
|
||||
)
|
||||
) {
|
||||
nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress][nft.tokenId.toString()] = [];
|
||||
}
|
||||
const isOwner = balances[i++];
|
||||
if (isOwner.isEqualTo(1)) {
|
||||
nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress][nft.tokenId.toString()].push(
|
||||
nft.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const holdingsByOwner = {
|
||||
fungible: tokenHoldingsByOwner,
|
||||
nonFungible: nonFungibleHoldingsByOwner,
|
||||
};
|
||||
return holdingsByOwner;
|
||||
}
|
||||
/**
|
||||
* @dev Checks if proxy is approved to transfer tokens on behalf of `userAddress`.
|
||||
* @param userAddress owner of ERC1155 tokens.
|
||||
* @param contractAddress address of ERC1155 contract.
|
||||
* @return True iff the proxy is approved for all. False otherwise.
|
||||
*/
|
||||
public async isProxyApprovedForAllAsync(userAddress: string, contractAddress: string): Promise<boolean> {
|
||||
this._validateProxyContractExistsOrThrow();
|
||||
const tokenContract = this._getContractFromAddress(contractAddress);
|
||||
const operator = (this._proxyContract as ERC1155ProxyContract).address;
|
||||
const didApproveAll = await tokenContract.isApprovedForAll.callAsync(userAddress, operator);
|
||||
return didApproveAll;
|
||||
}
|
||||
public getFungibleTokenIds(): BigNumber[] {
|
||||
const fungibleTokenIds = _.map(this._fungibleTokenIds, (tokenIdAsString: string) => {
|
||||
return new BigNumber(tokenIdAsString);
|
||||
});
|
||||
return fungibleTokenIds;
|
||||
}
|
||||
public getNonFungibleTokenIds(): BigNumber[] {
|
||||
const nonFungibleTokenIds = _.map(this._nonFungibleTokenIds, (tokenIdAsString: string) => {
|
||||
return new BigNumber(tokenIdAsString);
|
||||
});
|
||||
return nonFungibleTokenIds;
|
||||
}
|
||||
public getTokenOwnerAddresses(): string[] {
|
||||
return this._tokenOwnerAddresses;
|
||||
}
|
||||
public getContractWrapper(contractAddress: string): Erc1155Wrapper {
|
||||
const tokenWrapper = _.find(this._dummyTokenWrappers, (wrapper: Erc1155Wrapper) => {
|
||||
return wrapper.getContract().address === contractAddress;
|
||||
});
|
||||
if (_.isUndefined(tokenWrapper)) {
|
||||
throw new Error(`Contract: ${contractAddress} was not deployed through ERC1155ProxyWrapper`);
|
||||
}
|
||||
return tokenWrapper;
|
||||
}
|
||||
private _getContractFromAddress(tokenAddress: string): ERC1155MintableContract {
|
||||
const tokenContractIfExists = _.find(this._dummyTokenWrappers, c => c.getContract().address === tokenAddress);
|
||||
if (_.isUndefined(tokenContractIfExists)) {
|
||||
throw new Error(`Token: ${tokenAddress} was not deployed through ERC1155ProxyWrapper`);
|
||||
}
|
||||
return tokenContractIfExists.getContract();
|
||||
}
|
||||
private _validateDummyTokenContractsExistOrThrow(): void {
|
||||
if (_.isUndefined(this._dummyTokenWrappers)) {
|
||||
throw new Error('Dummy ERC1155 tokens not yet deployed, please call "deployDummyTokensAsync"');
|
||||
}
|
||||
}
|
||||
private _validateProxyContractExistsOrThrow(): void {
|
||||
if (_.isUndefined(this._proxyContract)) {
|
||||
throw new Error('ERC1155 proxy contract not yet deployed, please call "deployProxyAsync"');
|
||||
}
|
||||
}
|
||||
private _validateBalancesAndAllowancesSetOrThrow(): void {
|
||||
if (
|
||||
_.keys(this._initialTokenIdsByOwner.fungible).length === 0 ||
|
||||
_.keys(this._initialTokenIdsByOwner.nonFungible).length === 0
|
||||
) {
|
||||
throw new Error(
|
||||
'Dummy ERC1155 balances and allowances not yet set, please call "setBalancesAndAllowancesAsync"',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,178 +0,0 @@
|
||||
import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contracts-erc20';
|
||||
import { constants, ERC20BalancesByOwner, txDefaults } from '@0x/contracts-test-utils';
|
||||
import { assetDataUtils } from '@0x/order-utils';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import { ZeroExProvider } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { artifacts, ERC20ProxyContract } from '../../src';
|
||||
|
||||
export class ERC20Wrapper {
|
||||
private readonly _tokenOwnerAddresses: string[];
|
||||
private readonly _contractOwnerAddress: string;
|
||||
private readonly _web3Wrapper: Web3Wrapper;
|
||||
private readonly _provider: ZeroExProvider;
|
||||
private readonly _dummyTokenContracts: DummyERC20TokenContract[];
|
||||
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._web3Wrapper = new Web3Wrapper(provider);
|
||||
this._provider = provider;
|
||||
this._tokenOwnerAddresses = tokenOwnerAddresses;
|
||||
this._contractOwnerAddress = contractOwnerAddress;
|
||||
}
|
||||
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,
|
||||
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,
|
||||
);
|
||||
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 this._web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await dummyTokenContract.setBalance.sendTransactionAsync(
|
||||
tokenOwnerAddress,
|
||||
constants.INITIAL_ERC20_BALANCE,
|
||||
{ from: this._contractOwnerAddress },
|
||||
),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await this._web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await dummyTokenContract.approve.sendTransactionAsync(
|
||||
(this._proxyContract as ERC20ProxyContract).address,
|
||||
constants.INITIAL_ERC20_ALLOWANCE,
|
||||
{ from: tokenOwnerAddress },
|
||||
),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
public async getBalanceAsync(userAddress: string, assetData: string): Promise<BigNumber> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(assetData);
|
||||
const balance = new BigNumber(await tokenContract.balanceOf.callAsync(userAddress));
|
||||
return balance;
|
||||
}
|
||||
public async setBalanceAsync(userAddress: string, assetData: string, amount: BigNumber): Promise<void> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(assetData);
|
||||
await this._web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await tokenContract.setBalance.sendTransactionAsync(userAddress, amount, {
|
||||
from: this._contractOwnerAddress,
|
||||
}),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
}
|
||||
public async getProxyAllowanceAsync(userAddress: string, assetData: string): Promise<BigNumber> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(assetData);
|
||||
const proxyAddress = (this._proxyContract as ERC20ProxyContract).address;
|
||||
const allowance = new BigNumber(await tokenContract.allowance.callAsync(userAddress, proxyAddress));
|
||||
return allowance;
|
||||
}
|
||||
public async setAllowanceAsync(userAddress: string, assetData: string, amount: BigNumber): Promise<void> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(assetData);
|
||||
const proxyAddress = (this._proxyContract as ERC20ProxyContract).address;
|
||||
await this._web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await tokenContract.approve.sendTransactionAsync(proxyAddress, amount, {
|
||||
from: userAddress,
|
||||
}),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
}
|
||||
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.callAsync(tokenOwnerAddress));
|
||||
balanceInfo.push({
|
||||
tokenOwnerAddress,
|
||||
tokenAddress: dummyTokenContract.address,
|
||||
});
|
||||
}
|
||||
}
|
||||
_.forEach(balances, (balance, balanceIndex) => {
|
||||
const tokenAddress = balanceInfo[balanceIndex].tokenAddress;
|
||||
const tokenOwnerAddress = balanceInfo[balanceIndex].tokenOwnerAddress;
|
||||
if (_.isUndefined(balancesByOwner[tokenOwnerAddress])) {
|
||||
balancesByOwner[tokenOwnerAddress] = {};
|
||||
}
|
||||
const wrappedBalance = new BigNumber(balance);
|
||||
balancesByOwner[tokenOwnerAddress][tokenAddress] = wrappedBalance;
|
||||
});
|
||||
return balancesByOwner;
|
||||
}
|
||||
public addDummyTokenContract(dummy: DummyERC20TokenContract): void {
|
||||
if (!_.isUndefined(this._dummyTokenContracts)) {
|
||||
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 _getTokenContractFromAssetData(assetData: string): DummyERC20TokenContract {
|
||||
const erc20ProxyData = assetDataUtils.decodeERC20AssetData(assetData);
|
||||
const tokenAddress = erc20ProxyData.tokenAddress;
|
||||
const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === tokenAddress);
|
||||
if (_.isUndefined(tokenContractIfExists)) {
|
||||
throw new Error(`Token: ${tokenAddress} was not deployed through ERC20Wrapper`);
|
||||
}
|
||||
return tokenContractIfExists;
|
||||
}
|
||||
private _validateDummyTokenContractsExistOrThrow(): void {
|
||||
if (_.isUndefined(this._dummyTokenContracts)) {
|
||||
throw new Error('Dummy ERC20 tokens not yet deployed, please call "deployDummyTokensAsync"');
|
||||
}
|
||||
}
|
||||
private _validateProxyContractExistsOrThrow(): void {
|
||||
if (_.isUndefined(this._proxyContract)) {
|
||||
throw new Error('ERC20 proxy contract not yet deployed, please call "deployProxyAsync"');
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,235 +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 { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import { ZeroExProvider } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { artifacts, ERC721ProxyContract } from '../../src';
|
||||
|
||||
export class ERC721Wrapper {
|
||||
private readonly _tokenOwnerAddresses: string[];
|
||||
private readonly _contractOwnerAddress: string;
|
||||
private readonly _web3Wrapper: Web3Wrapper;
|
||||
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._web3Wrapper = new Web3Wrapper(provider);
|
||||
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,
|
||||
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,
|
||||
);
|
||||
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 (_.isUndefined(this._initialTokenIdsByOwner[tokenOwnerAddress])) {
|
||||
this._initialTokenIdsByOwner[tokenOwnerAddress] = {
|
||||
[dummyTokenContract.address]: [],
|
||||
};
|
||||
}
|
||||
if (_.isUndefined(this._initialTokenIdsByOwner[tokenOwnerAddress][dummyTokenContract.address])) {
|
||||
this._initialTokenIdsByOwner[tokenOwnerAddress][dummyTokenContract.address] = [];
|
||||
}
|
||||
this._initialTokenIdsByOwner[tokenOwnerAddress][dummyTokenContract.address].push(tokenId);
|
||||
|
||||
await this.approveProxyAsync(dummyTokenContract.address, tokenId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public async doesTokenExistAsync(tokenAddress: string, tokenId: BigNumber): Promise<boolean> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||
const owner = await tokenContract.ownerOf.callAsync(tokenId);
|
||||
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, tokenId: BigNumber, isApproved: boolean): Promise<void> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||
const tokenOwner = await this.ownerOfAsync(tokenAddress, tokenId);
|
||||
const proxyAddress = (this._proxyContract as ERC721ProxyContract).address;
|
||||
await this._web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await tokenContract.setApprovalForAll.sendTransactionAsync(proxyAddress, isApproved, {
|
||||
from: tokenOwner,
|
||||
}),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
}
|
||||
public async approveAsync(to: string, tokenAddress: string, tokenId: BigNumber): Promise<void> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||
const tokenOwner = await this.ownerOfAsync(tokenAddress, tokenId);
|
||||
await this._web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await tokenContract.approve.sendTransactionAsync(to, tokenId, {
|
||||
from: tokenOwner,
|
||||
}),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
}
|
||||
public async transferFromAsync(
|
||||
tokenAddress: string,
|
||||
tokenId: BigNumber,
|
||||
currentOwner: string,
|
||||
userAddress: string,
|
||||
): Promise<void> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||
await this._web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await tokenContract.transferFrom.sendTransactionAsync(currentOwner, userAddress, tokenId, {
|
||||
from: currentOwner,
|
||||
}),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
}
|
||||
public async mintAsync(tokenAddress: string, tokenId: BigNumber, userAddress: string): Promise<void> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||
await this._web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await tokenContract.mint.sendTransactionAsync(userAddress, tokenId, {
|
||||
from: this._contractOwnerAddress,
|
||||
}),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
}
|
||||
public async burnAsync(tokenAddress: string, tokenId: BigNumber, owner: string): Promise<void> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||
await this._web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await tokenContract.burn.sendTransactionAsync(owner, tokenId, {
|
||||
from: this._contractOwnerAddress,
|
||||
}),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
}
|
||||
public async ownerOfAsync(tokenAddress: string, tokenId: BigNumber): Promise<string> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||
const owner = await tokenContract.ownerOf.callAsync(tokenId);
|
||||
return owner;
|
||||
}
|
||||
public async isOwnerAsync(userAddress: string, tokenAddress: string, tokenId: BigNumber): Promise<boolean> {
|
||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||
const tokenOwner = await tokenContract.ownerOf.callAsync(tokenId);
|
||||
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.callAsync(userAddress, operator);
|
||||
return didApproveAll;
|
||||
}
|
||||
public async isProxyApprovedAsync(tokenAddress: string, tokenId: BigNumber): Promise<boolean> {
|
||||
this._validateProxyContractExistsOrThrow();
|
||||
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
|
||||
const approvedAddress = await tokenContract.getApproved.callAsync(tokenId);
|
||||
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.callAsync(tokenId));
|
||||
tokenInfo.push({
|
||||
tokenId,
|
||||
tokenAddress: dummyTokenContract.address,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
_.forEach(tokenOwnerAddresses, (tokenOwnerAddress, ownerIndex) => {
|
||||
const tokenAddress = tokenInfo[ownerIndex].tokenAddress;
|
||||
const tokenId = tokenInfo[ownerIndex].tokenId;
|
||||
if (_.isUndefined(tokenIdsByOwner[tokenOwnerAddress])) {
|
||||
tokenIdsByOwner[tokenOwnerAddress] = {
|
||||
[tokenAddress]: [],
|
||||
};
|
||||
}
|
||||
if (_.isUndefined(tokenIdsByOwner[tokenOwnerAddress][tokenAddress])) {
|
||||
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 (_.isUndefined(tokenContractIfExists)) {
|
||||
throw new Error(`Token: ${tokenAddress} was not deployed through ERC20Wrapper`);
|
||||
}
|
||||
return tokenContractIfExists;
|
||||
}
|
||||
private _validateDummyTokenContractsExistOrThrow(): void {
|
||||
if (_.isUndefined(this._dummyTokenContracts)) {
|
||||
throw new Error('Dummy ERC721 tokens not yet deployed, please call "deployDummyTokensAsync"');
|
||||
}
|
||||
}
|
||||
private _validateProxyContractExistsOrThrow(): void {
|
||||
if (_.isUndefined(this._proxyContract)) {
|
||||
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,3 +0,0 @@
|
||||
export * from './erc20_wrapper';
|
||||
export * from './erc721_wrapper';
|
||||
export * from './erc1155_proxy_wrapper';
|
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig",
|
||||
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
|
||||
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
|
||||
"files": [
|
||||
"generated-artifacts/ERC1155Proxy.json",
|
||||
"generated-artifacts/ERC20Proxy.json",
|
||||
"generated-artifacts/ERC721Proxy.json",
|
||||
"generated-artifacts/IAssetData.json",
|
||||
"generated-artifacts/IAssetProxy.json",
|
||||
"generated-artifacts/IAuthorizable.json",
|
||||
"generated-artifacts/MixinAuthorizable.json",
|
||||
"generated-artifacts/MultiAssetProxy.json"
|
||||
],
|
||||
"exclude": ["./deploy/solc/solc_bin"]
|
||||
}
|
@@ -1,33 +0,0 @@
|
||||
[
|
||||
{
|
||||
"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,17 +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
|
||||
|
||||
## 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 the 0x [wiki](https://0xproject.com/wiki#Deployed-Addresses) or the [DEPLOYS](./DEPLOYS.json) file within this package.
|
||||
|
||||
## Installation
|
||||
|
||||
**Install**
|
||||
|
||||
```bash
|
||||
npm install @0x/contracts-coordinator --save
|
||||
```
|
||||
|
||||
## Bug bounty
|
||||
|
||||
A bug bounty for the 2.0.0 contracts is ongoing! Instructions can be found [here](https://0xproject.com/wiki#Bug-Bounty).
|
||||
|
||||
## Contributing
|
||||
|
||||
We strongly recommend that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository.
|
||||
|
||||
For proposals regarding the 0x protocol's smart contract architecture, message format, or additional functionality, go to the [0x Improvement Proposals (ZEIPs)](https://github.com/0xProject/ZEIPs) repository and follow the contribution guidelines provided therein.
|
||||
|
||||
Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started.
|
||||
|
||||
### Install Dependencies
|
||||
|
||||
If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them:
|
||||
|
||||
```bash
|
||||
yarn config set workspaces-experimental true
|
||||
```
|
||||
|
||||
Then install dependencies
|
||||
|
||||
```bash
|
||||
yarn install
|
||||
```
|
||||
|
||||
### Build
|
||||
|
||||
To build this package and all other monorepo packages that it depends on, run the following from the monorepo root directory:
|
||||
|
||||
```bash
|
||||
PKG=@0x/contracts-coordinator yarn build
|
||||
```
|
||||
|
||||
Or continuously rebuild on change:
|
||||
|
||||
```bash
|
||||
PKG=@0x/contracts-coordinator yarn watch
|
||||
```
|
||||
|
||||
### Clean
|
||||
|
||||
```bash
|
||||
yarn clean
|
||||
```
|
||||
|
||||
### Lint
|
||||
|
||||
```bash
|
||||
yarn lint
|
||||
```
|
||||
|
||||
### Run Tests
|
||||
|
||||
```bash
|
||||
yarn test
|
||||
```
|
||||
|
||||
#### Testing options
|
||||
|
||||
Contracts testing options like coverage, profiling, revert traces or backing node choosing - are described [here](../TESTING.md).
|
@@ -1,30 +0,0 @@
|
||||
{
|
||||
"artifactsDir": "./generated-artifacts",
|
||||
"contractsDir": "./contracts",
|
||||
"useDockerisedSolc": true,
|
||||
"compilerSettings": {
|
||||
"evmVersion": "constantinople",
|
||||
"optimizer": {
|
||||
"enabled": true,
|
||||
"runs": 1000000,
|
||||
"details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
|
||||
},
|
||||
"outputSelection": {
|
||||
"*": {
|
||||
"*": [
|
||||
"abi",
|
||||
"evm.bytecode.object",
|
||||
"evm.bytecode.sourceMap",
|
||||
"evm.deployedBytecode.object",
|
||||
"evm.deployedBytecode.sourceMap"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"contracts": [
|
||||
"src/Coordinator.sol",
|
||||
"src/registry/CoordinatorRegistry.sol",
|
||||
"test/TestLibs.sol",
|
||||
"test/TestMixins.sol"
|
||||
]
|
||||
}
|
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental "ABIEncoderV2";
|
||||
|
||||
import "./libs/LibConstants.sol";
|
||||
import "./MixinSignatureValidator.sol";
|
||||
import "./MixinCoordinatorApprovalVerifier.sol";
|
||||
import "./MixinCoordinatorCore.sol";
|
||||
|
||||
|
||||
// solhint-disable no-empty-blocks
|
||||
contract Coordinator is
|
||||
LibConstants,
|
||||
MixinSignatureValidator,
|
||||
MixinCoordinatorApprovalVerifier,
|
||||
MixinCoordinatorCore
|
||||
{
|
||||
constructor (address _exchange)
|
||||
public
|
||||
LibConstants(_exchange)
|
||||
{}
|
||||
}
|
@@ -1,203 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental "ABIEncoderV2";
|
||||
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibExchangeSelectors.sol";
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibAddressArray.sol";
|
||||
import "./libs/LibCoordinatorApproval.sol";
|
||||
import "./libs/LibZeroExTransaction.sol";
|
||||
import "./mixins/MSignatureValidator.sol";
|
||||
import "./mixins/MCoordinatorApprovalVerifier.sol";
|
||||
|
||||
|
||||
// solhint-disable avoid-tx-origin
|
||||
contract MixinCoordinatorApprovalVerifier is
|
||||
LibExchangeSelectors,
|
||||
LibCoordinatorApproval,
|
||||
LibZeroExTransaction,
|
||||
MSignatureValidator,
|
||||
MCoordinatorApprovalVerifier
|
||||
{
|
||||
using LibBytes for bytes;
|
||||
using LibAddressArray for address[];
|
||||
|
||||
/// @dev Validates that the 0x transaction has been approved by all of the feeRecipients
|
||||
/// that correspond to each order in the transaction's Exchange calldata.
|
||||
/// @param transaction 0x transaction containing salt, signerAddress, and data.
|
||||
/// @param txOrigin Required signer of Ethereum transaction calling this function.
|
||||
/// @param transactionSignature Proof that the transaction has been signed by the signer.
|
||||
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
|
||||
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order in the transaction's Exchange calldata.
|
||||
function assertValidCoordinatorApprovals(
|
||||
LibZeroExTransaction.ZeroExTransaction memory transaction,
|
||||
address txOrigin,
|
||||
bytes memory transactionSignature,
|
||||
uint256[] memory approvalExpirationTimeSeconds,
|
||||
bytes[] memory approvalSignatures
|
||||
)
|
||||
public
|
||||
view
|
||||
{
|
||||
// Get the orders from the the Exchange calldata in the 0x transaction
|
||||
LibOrder.Order[] memory orders = decodeFillDataOrders(transaction.data);
|
||||
|
||||
// No approval is required for non-fill methods
|
||||
if (orders.length > 0) {
|
||||
// Revert if approval is invalid for transaction orders
|
||||
assertValidTransactionOrdersApproval(
|
||||
transaction,
|
||||
orders,
|
||||
txOrigin,
|
||||
transactionSignature,
|
||||
approvalExpirationTimeSeconds,
|
||||
approvalSignatures
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Validates that the feeRecipients of a batch of order have approved a 0x transaction.
|
||||
/// @param transaction 0x transaction containing salt, signerAddress, and data.
|
||||
/// @param orders Array of order structs containing order specifications.
|
||||
/// @param txOrigin Required signer of Ethereum transaction calling this function.
|
||||
/// @param transactionSignature Proof that the transaction has been signed by the signer.
|
||||
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
|
||||
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order.
|
||||
function assertValidTransactionOrdersApproval(
|
||||
LibZeroExTransaction.ZeroExTransaction memory transaction,
|
||||
LibOrder.Order[] memory orders,
|
||||
address txOrigin,
|
||||
bytes memory transactionSignature,
|
||||
uint256[] memory approvalExpirationTimeSeconds,
|
||||
bytes[] memory approvalSignatures
|
||||
)
|
||||
public
|
||||
view
|
||||
{
|
||||
// Verify that Ethereum tx signer is the same as the approved txOrigin
|
||||
require(
|
||||
tx.origin == txOrigin,
|
||||
"INVALID_ORIGIN"
|
||||
);
|
||||
|
||||
// Hash 0x transaction
|
||||
bytes32 transactionHash = getTransactionHash(transaction);
|
||||
|
||||
// Create empty list of approval signers
|
||||
address[] memory approvalSignerAddresses = new address[](0);
|
||||
|
||||
uint256 signaturesLength = approvalSignatures.length;
|
||||
for (uint256 i = 0; i != signaturesLength; i++) {
|
||||
// Create approval message
|
||||
uint256 currentApprovalExpirationTimeSeconds = approvalExpirationTimeSeconds[i];
|
||||
CoordinatorApproval memory approval = CoordinatorApproval({
|
||||
txOrigin: txOrigin,
|
||||
transactionHash: transactionHash,
|
||||
transactionSignature: transactionSignature,
|
||||
approvalExpirationTimeSeconds: currentApprovalExpirationTimeSeconds
|
||||
});
|
||||
|
||||
// Ensure approval has not expired
|
||||
require(
|
||||
// solhint-disable-next-line not-rely-on-time
|
||||
currentApprovalExpirationTimeSeconds > block.timestamp,
|
||||
"APPROVAL_EXPIRED"
|
||||
);
|
||||
|
||||
// Hash approval message and recover signer address
|
||||
bytes32 approvalHash = getCoordinatorApprovalHash(approval);
|
||||
address approvalSignerAddress = getSignerAddress(approvalHash, approvalSignatures[i]);
|
||||
|
||||
// Add approval signer to list of signers
|
||||
approvalSignerAddresses = approvalSignerAddresses.append(approvalSignerAddress);
|
||||
}
|
||||
|
||||
// Ethereum transaction signer gives implicit signature of approval
|
||||
approvalSignerAddresses = approvalSignerAddresses.append(tx.origin);
|
||||
|
||||
uint256 ordersLength = orders.length;
|
||||
for (uint256 i = 0; i != ordersLength; i++) {
|
||||
// Do not check approval if the order's senderAddress is null
|
||||
if (orders[i].senderAddress == address(0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ensure feeRecipient of order has approved this 0x transaction
|
||||
address approverAddress = orders[i].feeRecipientAddress;
|
||||
bool isOrderApproved = approvalSignerAddresses.contains(approverAddress);
|
||||
require(
|
||||
isOrderApproved,
|
||||
"INVALID_APPROVAL_SIGNATURE"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Decodes the orders from Exchange calldata representing any fill method.
|
||||
/// @param data Exchange calldata representing a fill method.
|
||||
/// @return The orders from the Exchange calldata.
|
||||
function decodeFillDataOrders(bytes memory data)
|
||||
internal
|
||||
pure
|
||||
returns (LibOrder.Order[] memory orders)
|
||||
{
|
||||
bytes4 selector = data.readBytes4(0);
|
||||
if (
|
||||
selector == FILL_ORDER_SELECTOR ||
|
||||
selector == FILL_ORDER_NO_THROW_SELECTOR ||
|
||||
selector == FILL_OR_KILL_ORDER_SELECTOR
|
||||
) {
|
||||
// Decode single order
|
||||
(LibOrder.Order memory order) = abi.decode(
|
||||
data.slice(4, data.length),
|
||||
(LibOrder.Order)
|
||||
);
|
||||
orders = new LibOrder.Order[](1);
|
||||
orders[0] = order;
|
||||
} else if (
|
||||
selector == BATCH_FILL_ORDERS_SELECTOR ||
|
||||
selector == BATCH_FILL_ORDERS_NO_THROW_SELECTOR ||
|
||||
selector == BATCH_FILL_OR_KILL_ORDERS_SELECTOR ||
|
||||
selector == MARKET_BUY_ORDERS_SELECTOR ||
|
||||
selector == MARKET_BUY_ORDERS_NO_THROW_SELECTOR ||
|
||||
selector == MARKET_SELL_ORDERS_SELECTOR ||
|
||||
selector == MARKET_SELL_ORDERS_NO_THROW_SELECTOR
|
||||
) {
|
||||
// Decode all orders
|
||||
// solhint-disable indent
|
||||
(orders) = abi.decode(
|
||||
data.slice(4, data.length),
|
||||
(LibOrder.Order[])
|
||||
);
|
||||
} else if (selector == MATCH_ORDERS_SELECTOR) {
|
||||
// Decode left and right orders
|
||||
(LibOrder.Order memory leftOrder, LibOrder.Order memory rightOrder) = abi.decode(
|
||||
data.slice(4, data.length),
|
||||
(LibOrder.Order, LibOrder.Order)
|
||||
);
|
||||
|
||||
// Create array of orders
|
||||
orders = new LibOrder.Order[](2);
|
||||
orders[0] = leftOrder;
|
||||
orders[1] = rightOrder;
|
||||
}
|
||||
return orders;
|
||||
}
|
||||
}
|
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental "ABIEncoderV2";
|
||||
|
||||
import "./libs/LibZeroExTransaction.sol";
|
||||
import "./libs/LibConstants.sol";
|
||||
import "./mixins/MCoordinatorApprovalVerifier.sol";
|
||||
import "./interfaces/ICoordinatorCore.sol";
|
||||
|
||||
|
||||
contract MixinCoordinatorCore is
|
||||
LibConstants,
|
||||
MCoordinatorApprovalVerifier,
|
||||
ICoordinatorCore
|
||||
{
|
||||
/// @dev Executes a 0x transaction that has been signed by the feeRecipients that correspond to each order in the transaction's Exchange calldata.
|
||||
/// @param transaction 0x transaction containing salt, signerAddress, and data.
|
||||
/// @param txOrigin Required signer of Ethereum transaction calling this function.
|
||||
/// @param transactionSignature Proof that the transaction has been signed by the signer.
|
||||
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
|
||||
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order in the transaction's Exchange calldata.
|
||||
function executeTransaction(
|
||||
LibZeroExTransaction.ZeroExTransaction memory transaction,
|
||||
address txOrigin,
|
||||
bytes memory transactionSignature,
|
||||
uint256[] memory approvalExpirationTimeSeconds,
|
||||
bytes[] memory approvalSignatures
|
||||
)
|
||||
public
|
||||
{
|
||||
// Validate that the 0x transaction has been approves by each feeRecipient
|
||||
assertValidCoordinatorApprovals(
|
||||
transaction,
|
||||
txOrigin,
|
||||
transactionSignature,
|
||||
approvalExpirationTimeSeconds,
|
||||
approvalSignatures
|
||||
);
|
||||
|
||||
// Execute the transaction
|
||||
EXCHANGE.executeTransaction(
|
||||
transaction.salt,
|
||||
transaction.signerAddress,
|
||||
transaction.data,
|
||||
transactionSignature
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,118 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||
import "./mixins/MSignatureValidator.sol";
|
||||
|
||||
|
||||
contract MixinSignatureValidator is
|
||||
MSignatureValidator
|
||||
{
|
||||
using LibBytes for bytes;
|
||||
|
||||
/// @dev Recovers the address of a signer given a hash and signature.
|
||||
/// @param hash Any 32 byte hash.
|
||||
/// @param signature Proof that the hash has been signed by signer.
|
||||
function getSignerAddress(bytes32 hash, bytes memory signature)
|
||||
public
|
||||
pure
|
||||
returns (address signerAddress)
|
||||
{
|
||||
require(
|
||||
signature.length > 0,
|
||||
"LENGTH_GREATER_THAN_0_REQUIRED"
|
||||
);
|
||||
|
||||
// Pop last byte off of signature byte array.
|
||||
uint8 signatureTypeRaw = uint8(signature.popLastByte());
|
||||
|
||||
// Ensure signature is supported
|
||||
require(
|
||||
signatureTypeRaw < uint8(SignatureType.NSignatureTypes),
|
||||
"SIGNATURE_UNSUPPORTED"
|
||||
);
|
||||
|
||||
SignatureType signatureType = SignatureType(signatureTypeRaw);
|
||||
|
||||
// Always illegal signature.
|
||||
// This is always an implicit option since a signer can create a
|
||||
// signature array with invalid type or length. We may as well make
|
||||
// it an explicit option. This aids testing and analysis. It is
|
||||
// also the initialization value for the enum type.
|
||||
if (signatureType == SignatureType.Illegal) {
|
||||
revert("SIGNATURE_ILLEGAL");
|
||||
|
||||
// Always invalid signature.
|
||||
// Like Illegal, this is always implicitly available and therefore
|
||||
// offered explicitly. It can be implicitly created by providing
|
||||
// a correctly formatted but incorrect signature.
|
||||
} else if (signatureType == SignatureType.Invalid) {
|
||||
require(
|
||||
signature.length == 0,
|
||||
"LENGTH_0_REQUIRED"
|
||||
);
|
||||
revert("SIGNATURE_INVALID");
|
||||
|
||||
// Signature using EIP712
|
||||
} else if (signatureType == SignatureType.EIP712) {
|
||||
require(
|
||||
signature.length == 65,
|
||||
"LENGTH_65_REQUIRED"
|
||||
);
|
||||
uint8 v = uint8(signature[0]);
|
||||
bytes32 r = signature.readBytes32(1);
|
||||
bytes32 s = signature.readBytes32(33);
|
||||
signerAddress = ecrecover(
|
||||
hash,
|
||||
v,
|
||||
r,
|
||||
s
|
||||
);
|
||||
return signerAddress;
|
||||
|
||||
// Signed using web3.eth_sign
|
||||
} else if (signatureType == SignatureType.EthSign) {
|
||||
require(
|
||||
signature.length == 65,
|
||||
"LENGTH_65_REQUIRED"
|
||||
);
|
||||
uint8 v = uint8(signature[0]);
|
||||
bytes32 r = signature.readBytes32(1);
|
||||
bytes32 s = signature.readBytes32(33);
|
||||
signerAddress = ecrecover(
|
||||
keccak256(abi.encodePacked(
|
||||
"\x19Ethereum Signed Message:\n32",
|
||||
hash
|
||||
)),
|
||||
v,
|
||||
r,
|
||||
s
|
||||
);
|
||||
return signerAddress;
|
||||
}
|
||||
|
||||
// Anything else is illegal (We do not return false because
|
||||
// the signature may actually be valid, just not in a format
|
||||
// that we currently support. In this case returning false
|
||||
// may lead the caller to incorrectly believe that the
|
||||
// signature was invalid.)
|
||||
revert("SIGNATURE_UNSUPPORTED");
|
||||
}
|
||||
}
|
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental "ABIEncoderV2";
|
||||
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||
import "../libs/LibZeroExTransaction.sol";
|
||||
|
||||
|
||||
contract ICoordinatorApprovalVerifier {
|
||||
|
||||
/// @dev Validates that the 0x transaction has been approved by all of the feeRecipients
|
||||
/// that correspond to each order in the transaction's Exchange calldata.
|
||||
/// @param transaction 0x transaction containing salt, signerAddress, and data.
|
||||
/// @param txOrigin Required signer of Ethereum transaction calling this function.
|
||||
/// @param transactionSignature Proof that the transaction has been signed by the signer.
|
||||
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
|
||||
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order in the transaction's Exchange calldata.
|
||||
function assertValidCoordinatorApprovals(
|
||||
LibZeroExTransaction.ZeroExTransaction memory transaction,
|
||||
address txOrigin,
|
||||
bytes memory transactionSignature,
|
||||
uint256[] memory approvalExpirationTimeSeconds,
|
||||
bytes[] memory approvalSignatures
|
||||
)
|
||||
public
|
||||
view;
|
||||
|
||||
/// @dev Validates that the feeRecipients of a batch of order have approved a 0x transaction.
|
||||
/// @param transaction 0x transaction containing salt, signerAddress, and data.
|
||||
/// @param orders Array of order structs containing order specifications.
|
||||
/// @param txOrigin Required signer of Ethereum transaction calling this function.
|
||||
/// @param transactionSignature Proof that the transaction has been signed by the signer.
|
||||
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
|
||||
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order.
|
||||
function assertValidTransactionOrdersApproval(
|
||||
LibZeroExTransaction.ZeroExTransaction memory transaction,
|
||||
LibOrder.Order[] memory orders,
|
||||
address txOrigin,
|
||||
bytes memory transactionSignature,
|
||||
uint256[] memory approvalExpirationTimeSeconds,
|
||||
bytes[] memory approvalSignatures
|
||||
)
|
||||
public
|
||||
view;
|
||||
}
|
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental "ABIEncoderV2";
|
||||
|
||||
import "../libs/LibZeroExTransaction.sol";
|
||||
|
||||
|
||||
contract ICoordinatorCore {
|
||||
|
||||
/// @dev Executes a 0x transaction that has been signed by the feeRecipients that correspond to each order in the transaction's Exchange calldata.
|
||||
/// @param transaction 0x transaction containing salt, signerAddress, and data.
|
||||
/// @param txOrigin Required signer of Ethereum transaction calling this function.
|
||||
/// @param transactionSignature Proof that the transaction has been signed by the signer.
|
||||
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
|
||||
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order in the transaction's Exchange calldata.
|
||||
function executeTransaction(
|
||||
LibZeroExTransaction.ZeroExTransaction memory transaction,
|
||||
address txOrigin,
|
||||
bytes memory transactionSignature,
|
||||
uint256[] memory approvalExpirationTimeSeconds,
|
||||
bytes[] memory approvalSignatures
|
||||
)
|
||||
public;
|
||||
}
|
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
|
||||
contract ISignatureValidator {
|
||||
|
||||
/// @dev Recovers the address of a signer given a hash and signature.
|
||||
/// @param hash Any 32 byte hash.
|
||||
/// @param signature Proof that the hash has been signed by signer.
|
||||
function getSignerAddress(bytes32 hash, bytes memory signature)
|
||||
public
|
||||
pure
|
||||
returns (address signerAddress);
|
||||
}
|
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
|
||||
contract ITransactions {
|
||||
|
||||
/// @dev Executes an exchange method call in the context of signer.
|
||||
/// @param salt Arbitrary number to ensure uniqueness of transaction hash.
|
||||
/// @param signerAddress Address of transaction signer.
|
||||
/// @param data AbiV2 encoded calldata.
|
||||
/// @param signature Proof of signer transaction by signer.
|
||||
function executeTransaction(
|
||||
uint256 salt,
|
||||
address signerAddress,
|
||||
bytes calldata data,
|
||||
bytes calldata signature
|
||||
)
|
||||
external;
|
||||
}
|
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "./LibEIP712Domain.sol";
|
||||
|
||||
|
||||
contract LibCoordinatorApproval is
|
||||
LibEIP712Domain
|
||||
{
|
||||
// Hash for the EIP712 Coordinator approval message
|
||||
// keccak256(abi.encodePacked(
|
||||
// "CoordinatorApproval(",
|
||||
// "address txOrigin,",
|
||||
// "bytes32 transactionHash,",
|
||||
// "bytes transactionSignature,",
|
||||
// "uint256 approvalExpirationTimeSeconds",
|
||||
// ")"
|
||||
// ));
|
||||
bytes32 constant internal EIP712_COORDINATOR_APPROVAL_SCHEMA_HASH = 0x2fbcdbaa76bc7589916958ae919dfbef04d23f6bbf26de6ff317b32c6cc01e05;
|
||||
|
||||
struct CoordinatorApproval {
|
||||
address txOrigin; // Required signer of Ethereum transaction that is submitting approval.
|
||||
bytes32 transactionHash; // EIP712 hash of the transaction, using the domain separator of this contract.
|
||||
bytes transactionSignature; // Signature of the 0x transaction.
|
||||
uint256 approvalExpirationTimeSeconds; // Timestamp in seconds for which the signature expires.
|
||||
}
|
||||
|
||||
/// @dev Calculated the EIP712 hash of the Coordinator approval mesasage using the domain separator of this contract.
|
||||
/// @param approval Coordinator approval message containing the transaction hash, transaction signature, and expiration of the approval.
|
||||
/// @return EIP712 hash of the Coordinator approval message with the domain separator of this contract.
|
||||
function getCoordinatorApprovalHash(CoordinatorApproval memory approval)
|
||||
internal
|
||||
view
|
||||
returns (bytes32 approvalHash)
|
||||
{
|
||||
approvalHash = hashEIP712CoordinatorMessage(hashCoordinatorApproval(approval));
|
||||
return approvalHash;
|
||||
}
|
||||
|
||||
/// @dev Calculated the EIP712 hash of the Coordinator approval mesasage with no domain separator.
|
||||
/// @param approval Coordinator approval message containing the transaction hash, transaction signature, and expiration of the approval.
|
||||
/// @return EIP712 hash of the Coordinator approval message with no domain separator.
|
||||
function hashCoordinatorApproval(CoordinatorApproval memory approval)
|
||||
internal
|
||||
pure
|
||||
returns (bytes32 result)
|
||||
{
|
||||
bytes32 schemaHash = EIP712_COORDINATOR_APPROVAL_SCHEMA_HASH;
|
||||
bytes memory transactionSignature = approval.transactionSignature;
|
||||
address txOrigin = approval.txOrigin;
|
||||
bytes32 transactionHash = approval.transactionHash;
|
||||
uint256 approvalExpirationTimeSeconds = approval.approvalExpirationTimeSeconds;
|
||||
|
||||
// Assembly for more efficiently computing:
|
||||
// keccak256(abi.encodePacked(
|
||||
// EIP712_COORDINATOR_APPROVAL_SCHEMA_HASH,
|
||||
// approval.transactionHash,
|
||||
// keccak256(approval.transactionSignature)
|
||||
// approval.expiration,
|
||||
// ));
|
||||
|
||||
assembly {
|
||||
// Compute hash of transaction signature
|
||||
let transactionSignatureHash := keccak256(add(transactionSignature, 32), mload(transactionSignature))
|
||||
|
||||
// Load free memory pointer
|
||||
let memPtr := mload(64)
|
||||
|
||||
mstore(memPtr, schemaHash) // hash of schema
|
||||
mstore(add(memPtr, 32), txOrigin) // txOrigin
|
||||
mstore(add(memPtr, 64), transactionHash) // transactionHash
|
||||
mstore(add(memPtr, 96), transactionSignatureHash) // transactionSignatureHash
|
||||
mstore(add(memPtr, 128), approvalExpirationTimeSeconds) // approvalExpirationTimeSeconds
|
||||
// Compute hash
|
||||
result := keccak256(memPtr, 160)
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@@ -1,131 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "./LibConstants.sol";
|
||||
|
||||
|
||||
contract LibEIP712Domain is
|
||||
LibConstants
|
||||
{
|
||||
|
||||
// EIP191 header for EIP712 prefix
|
||||
string constant internal EIP191_HEADER = "\x19\x01";
|
||||
|
||||
// EIP712 Domain Name value for the Coordinator
|
||||
string constant internal EIP712_COORDINATOR_DOMAIN_NAME = "0x Protocol Coordinator";
|
||||
|
||||
// EIP712 Domain Version value for the Coordinator
|
||||
string constant internal EIP712_COORDINATOR_DOMAIN_VERSION = "1.0.0";
|
||||
|
||||
// EIP712 Domain Name value for the Exchange
|
||||
string constant internal EIP712_EXCHANGE_DOMAIN_NAME = "0x Protocol";
|
||||
|
||||
// EIP712 Domain Version value for the Exchange
|
||||
string constant internal EIP712_EXCHANGE_DOMAIN_VERSION = "2";
|
||||
|
||||
// Hash of the EIP712 Domain Separator Schema
|
||||
bytes32 constant internal EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH = keccak256(abi.encodePacked(
|
||||
"EIP712Domain(",
|
||||
"string name,",
|
||||
"string version,",
|
||||
"address verifyingContract",
|
||||
")"
|
||||
));
|
||||
|
||||
// Hash of the EIP712 Domain Separator data for the Coordinator
|
||||
// solhint-disable-next-line var-name-mixedcase
|
||||
bytes32 public EIP712_COORDINATOR_DOMAIN_HASH;
|
||||
|
||||
// Hash of the EIP712 Domain Separator data for the Exchange
|
||||
// solhint-disable-next-line var-name-mixedcase
|
||||
bytes32 public EIP712_EXCHANGE_DOMAIN_HASH;
|
||||
|
||||
constructor ()
|
||||
public
|
||||
{
|
||||
EIP712_COORDINATOR_DOMAIN_HASH = keccak256(abi.encodePacked(
|
||||
EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH,
|
||||
keccak256(bytes(EIP712_COORDINATOR_DOMAIN_NAME)),
|
||||
keccak256(bytes(EIP712_COORDINATOR_DOMAIN_VERSION)),
|
||||
uint256(address(this))
|
||||
));
|
||||
|
||||
EIP712_EXCHANGE_DOMAIN_HASH = keccak256(abi.encodePacked(
|
||||
EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH,
|
||||
keccak256(bytes(EIP712_EXCHANGE_DOMAIN_NAME)),
|
||||
keccak256(bytes(EIP712_EXCHANGE_DOMAIN_VERSION)),
|
||||
uint256(address(EXCHANGE))
|
||||
));
|
||||
}
|
||||
|
||||
/// @dev Calculates EIP712 encoding for a hash struct in the EIP712 domain
|
||||
/// of this contract.
|
||||
/// @param hashStruct The EIP712 hash struct.
|
||||
/// @return EIP712 hash applied to this EIP712 Domain.
|
||||
function hashEIP712CoordinatorMessage(bytes32 hashStruct)
|
||||
internal
|
||||
view
|
||||
returns (bytes32 result)
|
||||
{
|
||||
return hashEIP712Message(EIP712_COORDINATOR_DOMAIN_HASH, hashStruct);
|
||||
}
|
||||
|
||||
/// @dev Calculates EIP712 encoding for a hash struct in the EIP712 domain
|
||||
/// of the Exchange contract.
|
||||
/// @param hashStruct The EIP712 hash struct.
|
||||
/// @return EIP712 hash applied to the Exchange EIP712 Domain.
|
||||
function hashEIP712ExchangeMessage(bytes32 hashStruct)
|
||||
internal
|
||||
view
|
||||
returns (bytes32 result)
|
||||
{
|
||||
return hashEIP712Message(EIP712_EXCHANGE_DOMAIN_HASH, hashStruct);
|
||||
}
|
||||
|
||||
/// @dev Calculates EIP712 encoding for a hash struct with a given domain hash.
|
||||
/// @param eip712DomainHash Hash of the domain domain separator data.
|
||||
/// @param hashStruct The EIP712 hash struct.
|
||||
/// @return EIP712 hash applied to the Exchange EIP712 Domain.
|
||||
function hashEIP712Message(bytes32 eip712DomainHash, bytes32 hashStruct)
|
||||
internal
|
||||
pure
|
||||
returns (bytes32 result)
|
||||
{
|
||||
// Assembly for more efficient computing:
|
||||
// keccak256(abi.encodePacked(
|
||||
// EIP191_HEADER,
|
||||
// EIP712_DOMAIN_HASH,
|
||||
// hashStruct
|
||||
// ));
|
||||
|
||||
assembly {
|
||||
// Load free memory pointer
|
||||
let memPtr := mload(64)
|
||||
|
||||
mstore(memPtr, 0x1901000000000000000000000000000000000000000000000000000000000000) // EIP191 header
|
||||
mstore(add(memPtr, 2), eip712DomainHash) // EIP712 domain hash
|
||||
mstore(add(memPtr, 34), hashStruct) // Hash of struct
|
||||
|
||||
// Compute hash
|
||||
result := keccak256(memPtr, 66)
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "./LibEIP712Domain.sol";
|
||||
|
||||
|
||||
contract LibZeroExTransaction is
|
||||
LibEIP712Domain
|
||||
{
|
||||
// Hash for the EIP712 0x transaction schema
|
||||
// keccak256(abi.encodePacked(
|
||||
// "ZeroExTransaction(",
|
||||
// "uint256 salt,",
|
||||
// "address signerAddress,",
|
||||
// "bytes data",
|
||||
// ")"
|
||||
// ));
|
||||
bytes32 constant internal EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH = 0x213c6f636f3ea94e701c0adf9b2624aa45a6c694f9a292c094f9a81c24b5df4c;
|
||||
|
||||
struct ZeroExTransaction {
|
||||
uint256 salt; // Arbitrary number to ensure uniqueness of transaction hash.
|
||||
address signerAddress; // Address of transaction signer.
|
||||
bytes data; // AbiV2 encoded calldata.
|
||||
}
|
||||
|
||||
/// @dev Calculates the EIP712 hash of a 0x transaction using the domain separator of this contract.
|
||||
/// @param transaction 0x transaction containing salt, signerAddress, and data.
|
||||
/// @return EIP712 hash of the transaction with the domain separator of this contract.
|
||||
function getTransactionHash(ZeroExTransaction memory transaction)
|
||||
internal
|
||||
view
|
||||
returns (bytes32 transactionHash)
|
||||
{
|
||||
// Hash the transaction with the domain separator of the Exchange contract.
|
||||
transactionHash = hashEIP712ExchangeMessage(hashZeroExTransaction(transaction));
|
||||
return transactionHash;
|
||||
}
|
||||
|
||||
/// @dev Calculates EIP712 hash of the 0x transaction with no domain separator.
|
||||
/// @param transaction 0x transaction containing salt, signerAddress, and data.
|
||||
/// @return EIP712 hash of the transaction with no domain separator.
|
||||
function hashZeroExTransaction(ZeroExTransaction memory transaction)
|
||||
internal
|
||||
pure
|
||||
returns (bytes32 result)
|
||||
{
|
||||
bytes32 schemaHash = EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH;
|
||||
bytes memory data = transaction.data;
|
||||
uint256 salt = transaction.salt;
|
||||
address signerAddress = transaction.signerAddress;
|
||||
|
||||
// Assembly for more efficiently computing:
|
||||
// keccak256(abi.encodePacked(
|
||||
// EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH,
|
||||
// transaction.salt,
|
||||
// uint256(transaction.signerAddress),
|
||||
// keccak256(transaction.data)
|
||||
// ));
|
||||
|
||||
assembly {
|
||||
// Compute hash of data
|
||||
let dataHash := keccak256(add(data, 32), mload(data))
|
||||
|
||||
// Load free memory pointer
|
||||
let memPtr := mload(64)
|
||||
|
||||
mstore(memPtr, schemaHash) // hash of schema
|
||||
mstore(add(memPtr, 32), salt) // salt
|
||||
mstore(add(memPtr, 64), and(signerAddress, 0xffffffffffffffffffffffffffffffffffffffff)) // signerAddress
|
||||
mstore(add(memPtr, 96), dataHash) // hash of data
|
||||
|
||||
// Compute hash
|
||||
result := keccak256(memPtr, 128)
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental "ABIEncoderV2";
|
||||
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||
import "../interfaces/ICoordinatorApprovalVerifier.sol";
|
||||
|
||||
|
||||
contract MCoordinatorApprovalVerifier is
|
||||
ICoordinatorApprovalVerifier
|
||||
{
|
||||
/// @dev Decodes the orders from Exchange calldata representing any fill method.
|
||||
/// @param data Exchange calldata representing a fill method.
|
||||
/// @return The orders from the Exchange calldata.
|
||||
function decodeFillDataOrders(bytes memory data)
|
||||
internal
|
||||
pure
|
||||
returns (LibOrder.Order[] memory orders);
|
||||
}
|
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "../interfaces/ISignatureValidator.sol";
|
||||
|
||||
|
||||
contract MSignatureValidator is
|
||||
ISignatureValidator
|
||||
{
|
||||
// Allowed signature types.
|
||||
enum SignatureType {
|
||||
Illegal, // 0x00, default value
|
||||
Invalid, // 0x01
|
||||
EIP712, // 0x02
|
||||
EthSign, // 0x03
|
||||
NSignatureTypes // 0x04, number of signature types. Always leave at end.
|
||||
}
|
||||
}
|
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "./MixinCoordinatorRegistryCore.sol";
|
||||
|
||||
|
||||
// solhint-disable no-empty-blocks
|
||||
contract CoordinatorRegistry is
|
||||
MixinCoordinatorRegistryCore
|
||||
{
|
||||
constructor ()
|
||||
public
|
||||
MixinCoordinatorRegistryCore()
|
||||
{}
|
||||
}
|
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "./interfaces/ICoordinatorRegistryCore.sol";
|
||||
|
||||
|
||||
// solhint-disable no-empty-blocks
|
||||
contract MixinCoordinatorRegistryCore is
|
||||
ICoordinatorRegistryCore
|
||||
{
|
||||
// mapping from `coordinatorOperator` -> `coordinatorEndpoint`
|
||||
mapping (address => string) internal coordinatorEndpoints;
|
||||
|
||||
/// @dev Called by a Coordinator operator to set the endpoint of their Coordinator.
|
||||
/// @param coordinatorEndpoint endpoint of the Coordinator.
|
||||
function setCoordinatorEndpoint(string calldata coordinatorEndpoint) external {
|
||||
address coordinatorOperator = msg.sender;
|
||||
coordinatorEndpoints[coordinatorOperator] = coordinatorEndpoint;
|
||||
emit CoordinatorEndpointSet(coordinatorOperator, coordinatorEndpoint);
|
||||
}
|
||||
|
||||
/// @dev Gets the endpoint for a Coordinator.
|
||||
/// @param coordinatorOperator operator of the Coordinator endpoint.
|
||||
function getCoordinatorEndpoint(address coordinatorOperator)
|
||||
external
|
||||
view
|
||||
returns (string memory coordinatorEndpoint)
|
||||
{
|
||||
return coordinatorEndpoints[coordinatorOperator];
|
||||
}
|
||||
}
|
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
|
||||
// solhint-disable no-empty-blocks
|
||||
contract ICoordinatorRegistryCore
|
||||
{
|
||||
/// @dev Emitted when a Coordinator endpoint is set.
|
||||
event CoordinatorEndpointSet(
|
||||
address coordinatorOperator,
|
||||
string coordinatorEndpoint
|
||||
);
|
||||
|
||||
/// @dev Called by a Coordinator operator to set the endpoint of their Coordinator.
|
||||
/// @param coordinatorEndpoint endpoint of the Coordinator.
|
||||
function setCoordinatorEndpoint(string calldata coordinatorEndpoint) external;
|
||||
|
||||
/// @dev Gets the endpoint for a Coordinator.
|
||||
/// @param coordinatorOperator operator of the Coordinator endpoint.
|
||||
function getCoordinatorEndpoint(address coordinatorOperator)
|
||||
external
|
||||
view
|
||||
returns (string memory coordinatorEndpoint);
|
||||
}
|
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental "ABIEncoderV2";
|
||||
|
||||
import "../src/libs/LibConstants.sol";
|
||||
import "../src/libs/LibCoordinatorApproval.sol";
|
||||
import "../src/libs/LibZeroExTransaction.sol";
|
||||
|
||||
|
||||
// solhint-disable no-empty-blocks
|
||||
contract TestLibs is
|
||||
LibConstants,
|
||||
LibCoordinatorApproval,
|
||||
LibZeroExTransaction
|
||||
{
|
||||
constructor (address _exchange)
|
||||
public
|
||||
LibConstants(_exchange)
|
||||
{}
|
||||
|
||||
/// @dev Calculated the EIP712 hash of the Coordinator approval mesasage using the domain separator of this contract.
|
||||
/// @param approval Coordinator approval message containing the transaction hash, transaction signature, and expiration of the approval.
|
||||
/// @return EIP712 hash of the Coordinator approval message with the domain separator of this contract.
|
||||
function publicGetCoordinatorApprovalHash(CoordinatorApproval memory approval)
|
||||
public
|
||||
view
|
||||
returns (bytes32 approvalHash)
|
||||
{
|
||||
approvalHash = getCoordinatorApprovalHash(approval);
|
||||
return approvalHash;
|
||||
}
|
||||
|
||||
/// @dev Calculates the EIP712 hash of a 0x transaction using the domain separator of the Exchange contract.
|
||||
/// @param transaction 0x transaction containing salt, signerAddress, and data.
|
||||
/// @return EIP712 hash of the transaction with the domain separator of the Exchange contract.
|
||||
function publicGetTransactionHash(ZeroExTransaction memory transaction)
|
||||
public
|
||||
view
|
||||
returns (bytes32 transactionHash)
|
||||
{
|
||||
transactionHash = getTransactionHash(transaction);
|
||||
return transactionHash;
|
||||
}
|
||||
}
|
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental "ABIEncoderV2";
|
||||
|
||||
import "../src/libs/LibConstants.sol";
|
||||
import "../src/MixinSignatureValidator.sol";
|
||||
import "../src/MixinCoordinatorApprovalVerifier.sol";
|
||||
|
||||
|
||||
// solhint-disable no-empty-blocks
|
||||
contract TestMixins is
|
||||
LibConstants,
|
||||
MixinSignatureValidator,
|
||||
MixinCoordinatorApprovalVerifier
|
||||
{
|
||||
constructor (address _exchange)
|
||||
public
|
||||
LibConstants(_exchange)
|
||||
{}
|
||||
}
|
@@ -1,88 +0,0 @@
|
||||
{
|
||||
"name": "@0x/contracts-coordinator",
|
||||
"version": "1.1.0",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
"description": "Smart contract extensions of 0x protocol",
|
||||
"main": "lib/src/index.js",
|
||||
"directories": {
|
||||
"test": "test"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "yarn pre_build && tsc -b",
|
||||
"build:ci": "yarn build",
|
||||
"pre_build": "run-s compile generate_contract_wrappers",
|
||||
"test": "yarn run_mocha",
|
||||
"rebuild_and_test": "run-s build test",
|
||||
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
||||
"test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html",
|
||||
"test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha",
|
||||
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
|
||||
"compile": "sol-compiler",
|
||||
"watch": "sol-compiler -w",
|
||||
"clean": "shx rm -rf lib generated-artifacts generated-wrappers",
|
||||
"generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../../node_modules/@0x/abi-gen-templates/contract.handlebars --partials '../../node_modules/@0x/abi-gen-templates/partials/**/*.handlebars' --output generated-wrappers --backend ethers",
|
||||
"lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||
"coverage:report:text": "istanbul report text",
|
||||
"coverage:report:html": "istanbul report html && open coverage/index.html",
|
||||
"profiler:report:html": "istanbul report html && open coverage/index.html",
|
||||
"coverage:report:lcov": "istanbul report lcov",
|
||||
"test:circleci": "yarn test",
|
||||
"contracts:gen": "contracts-gen",
|
||||
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol"
|
||||
},
|
||||
"config": {
|
||||
"abis": "./generated-artifacts/@(Coordinator|CoordinatorRegistry|TestLibs|TestMixins).json",
|
||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/0xProject/0x-monorepo.git"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/0xProject/0x-monorepo/issues"
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^2.0.8",
|
||||
"@0x/contracts-gen": "^1.0.7",
|
||||
"@0x/contracts-test-utils": "^3.1.1",
|
||||
"@0x/dev-utils": "^2.2.0",
|
||||
"@0x/sol-compiler": "^3.1.5",
|
||||
"@0x/tslint-config": "^3.0.0",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/node": "*",
|
||||
"chai": "^4.0.1",
|
||||
"chai-as-promised": "^7.1.0",
|
||||
"chai-bignumber": "^3.0.0",
|
||||
"dirty-chai": "^2.0.1",
|
||||
"make-promises-safe": "^1.1.0",
|
||||
"mocha": "^4.1.0",
|
||||
"npm-run-all": "^4.1.2",
|
||||
"shx": "^0.2.2",
|
||||
"solhint": "^1.4.1",
|
||||
"tslint": "5.11.0",
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^5.0.4",
|
||||
"@0x/contracts-asset-proxy": "^2.1.0",
|
||||
"@0x/contracts-erc20": "^2.1.0",
|
||||
"@0x/contracts-exchange": "1.0.2",
|
||||
"@0x/contracts-exchange-libs": "^2.1.0",
|
||||
"@0x/contracts-utils": "^3.1.0",
|
||||
"@0x/order-utils": "^7.1.1",
|
||||
"@0x/types": "^2.2.1",
|
||||
"@0x/typescript-typings": "^4.2.1",
|
||||
"@0x/utils": "^4.3.0",
|
||||
"@0x/web3-wrapper": "^6.0.4",
|
||||
"ethereum-types": "^2.1.1",
|
||||
"ethereumjs-util": "^5.1.1",
|
||||
"lodash": "^4.17.11"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
@@ -1,17 +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 TestLibs from '../generated-artifacts/TestLibs.json';
|
||||
import * as TestMixins from '../generated-artifacts/TestMixins.json';
|
||||
export const artifacts = {
|
||||
Coordinator: Coordinator as ContractArtifact,
|
||||
CoordinatorRegistry: CoordinatorRegistry as ContractArtifact,
|
||||
TestLibs: TestLibs as ContractArtifact,
|
||||
TestMixins: TestMixins as ContractArtifact,
|
||||
};
|
@@ -1,3 +0,0 @@
|
||||
export * from './artifacts';
|
||||
export * from './wrappers';
|
||||
export * from '../test/utils';
|
@@ -1,9 +0,0 @@
|
||||
/*
|
||||
* -----------------------------------------------------------------------------
|
||||
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
export * from '../generated-wrappers/coordinator';
|
||||
export * from '../generated-wrappers/coordinator_registry';
|
||||
export * from '../generated-wrappers/test_libs';
|
||||
export * from '../generated-wrappers/test_mixins';
|
@@ -1,521 +0,0 @@
|
||||
import { ERC20ProxyContract, ERC20Wrapper } from '@0x/contracts-asset-proxy';
|
||||
import { DummyERC20TokenContract } from '@0x/contracts-erc20';
|
||||
import {
|
||||
artifacts as exchangeArtifacts,
|
||||
ExchangeCancelEventArgs,
|
||||
ExchangeCancelUpToEventArgs,
|
||||
ExchangeContract,
|
||||
ExchangeFillEventArgs,
|
||||
} from '@0x/contracts-exchange';
|
||||
import {
|
||||
chaiSetup,
|
||||
constants as devConstants,
|
||||
expectTransactionFailedAsync,
|
||||
getLatestBlockTimestampAsync,
|
||||
OrderFactory,
|
||||
provider,
|
||||
TransactionFactory,
|
||||
txDefaults,
|
||||
web3Wrapper,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||
import { assetDataUtils, orderHashUtils } from '@0x/order-utils';
|
||||
import { RevertReason, SignedOrder } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as chai from 'chai';
|
||||
import { LogWithDecodedArgs } from 'ethereum-types';
|
||||
|
||||
import { ApprovalFactory, artifacts, constants, CoordinatorContract, exchangeDataEncoder } from '../src';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
web3Wrapper.abiDecoder.addABI(exchangeArtifacts.Exchange.compilerOutput.abi);
|
||||
// tslint:disable:no-unnecessary-type-assertion
|
||||
describe('Coordinator tests', () => {
|
||||
let makerAddress: string;
|
||||
let owner: string;
|
||||
let takerAddress: string;
|
||||
let feeRecipientAddress: string;
|
||||
|
||||
let erc20Proxy: ERC20ProxyContract;
|
||||
let erc20TokenA: DummyERC20TokenContract;
|
||||
let erc20TokenB: DummyERC20TokenContract;
|
||||
let zrxToken: DummyERC20TokenContract;
|
||||
let coordinatorContract: CoordinatorContract;
|
||||
let exchange: ExchangeContract;
|
||||
|
||||
let erc20Wrapper: ERC20Wrapper;
|
||||
let orderFactory: OrderFactory;
|
||||
let takerTransactionFactory: TransactionFactory;
|
||||
let makerTransactionFactory: TransactionFactory;
|
||||
let approvalFactory: ApprovalFactory;
|
||||
|
||||
before(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
after(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
before(async () => {
|
||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
||||
const usedAddresses = ([owner, makerAddress, takerAddress, feeRecipientAddress] = accounts.slice(0, 4));
|
||||
|
||||
erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner);
|
||||
erc20Proxy = await erc20Wrapper.deployProxyAsync();
|
||||
const numDummyErc20ToDeploy = 3;
|
||||
[erc20TokenA, erc20TokenB, zrxToken] = await erc20Wrapper.deployDummyTokensAsync(
|
||||
numDummyErc20ToDeploy,
|
||||
devConstants.DUMMY_TOKEN_DECIMALS,
|
||||
);
|
||||
await erc20Wrapper.setBalancesAndAllowancesAsync();
|
||||
|
||||
exchange = await ExchangeContract.deployFrom0xArtifactAsync(
|
||||
exchangeArtifacts.Exchange,
|
||||
provider,
|
||||
txDefaults,
|
||||
assetDataUtils.encodeERC20AssetData(zrxToken.address),
|
||||
);
|
||||
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { from: owner }),
|
||||
devConstants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await exchange.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, { from: owner }),
|
||||
devConstants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
|
||||
coordinatorContract = await CoordinatorContract.deployFrom0xArtifactAsync(
|
||||
artifacts.Coordinator,
|
||||
provider,
|
||||
txDefaults,
|
||||
exchange.address,
|
||||
);
|
||||
|
||||
// Configure order defaults
|
||||
const defaultOrderParams = {
|
||||
...devConstants.STATIC_ORDER_PARAMS,
|
||||
exchangeAddress: exchange.address,
|
||||
senderAddress: coordinatorContract.address,
|
||||
makerAddress,
|
||||
feeRecipientAddress,
|
||||
makerAssetData: assetDataUtils.encodeERC20AssetData(erc20TokenA.address),
|
||||
takerAssetData: assetDataUtils.encodeERC20AssetData(erc20TokenB.address),
|
||||
};
|
||||
const makerPrivateKey = devConstants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)];
|
||||
const takerPrivateKey = devConstants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(takerAddress)];
|
||||
const feeRecipientPrivateKey = devConstants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(feeRecipientAddress)];
|
||||
orderFactory = new OrderFactory(makerPrivateKey, defaultOrderParams);
|
||||
makerTransactionFactory = new TransactionFactory(makerPrivateKey, exchange.address);
|
||||
takerTransactionFactory = new TransactionFactory(takerPrivateKey, exchange.address);
|
||||
approvalFactory = new ApprovalFactory(feeRecipientPrivateKey, coordinatorContract.address);
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
afterEach(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
|
||||
describe('single order fills', () => {
|
||||
for (const fnName of constants.SINGLE_FILL_FN_NAMES) {
|
||||
it(`${fnName} should fill the order with a signed approval`, async () => {
|
||||
const orders = [await orderFactory.newSignedOrderAsync()];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory.newSignedApproval(
|
||||
transaction,
|
||||
takerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: takerAddress },
|
||||
),
|
||||
devConstants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const fillLogs = transactionReceipt.logs.filter(
|
||||
log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill',
|
||||
);
|
||||
expect(fillLogs.length).to.eq(1);
|
||||
const fillLogArgs = (fillLogs[0] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
|
||||
expect(fillLogArgs.makerAddress).to.eq(makerAddress);
|
||||
expect(fillLogArgs.takerAddress).to.eq(takerAddress);
|
||||
expect(fillLogArgs.senderAddress).to.eq(coordinatorContract.address);
|
||||
expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
|
||||
expect(fillLogArgs.makerAssetData).to.eq(orders[0].makerAssetData);
|
||||
expect(fillLogArgs.takerAssetData).to.eq(orders[0].takerAssetData);
|
||||
expect(fillLogArgs.makerAssetFilledAmount).to.bignumber.eq(orders[0].makerAssetAmount);
|
||||
expect(fillLogArgs.takerAssetFilledAmount).to.bignumber.eq(orders[0].takerAssetAmount);
|
||||
expect(fillLogArgs.makerFeePaid).to.bignumber.eq(orders[0].makerFee);
|
||||
expect(fillLogArgs.takerFeePaid).to.bignumber.eq(orders[0].takerFee);
|
||||
expect(fillLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(orders[0]));
|
||||
});
|
||||
it(`${fnName} should fill the order if called by approver`, async () => {
|
||||
const orders = [await orderFactory.newSignedOrderAsync()];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
feeRecipientAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{ from: feeRecipientAddress },
|
||||
),
|
||||
devConstants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const fillLogs = transactionReceipt.logs.filter(
|
||||
log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill',
|
||||
);
|
||||
expect(fillLogs.length).to.eq(1);
|
||||
const fillLogArgs = (fillLogs[0] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
|
||||
expect(fillLogArgs.makerAddress).to.eq(makerAddress);
|
||||
expect(fillLogArgs.takerAddress).to.eq(takerAddress);
|
||||
expect(fillLogArgs.senderAddress).to.eq(coordinatorContract.address);
|
||||
expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
|
||||
expect(fillLogArgs.makerAssetData).to.eq(orders[0].makerAssetData);
|
||||
expect(fillLogArgs.takerAssetData).to.eq(orders[0].takerAssetData);
|
||||
expect(fillLogArgs.makerAssetFilledAmount).to.bignumber.eq(orders[0].makerAssetAmount);
|
||||
expect(fillLogArgs.takerAssetFilledAmount).to.bignumber.eq(orders[0].takerAssetAmount);
|
||||
expect(fillLogArgs.makerFeePaid).to.bignumber.eq(orders[0].makerFee);
|
||||
expect(fillLogArgs.takerFeePaid).to.bignumber.eq(orders[0].takerFee);
|
||||
expect(fillLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(orders[0]));
|
||||
});
|
||||
it(`${fnName} should revert with no approval signature`, async () => {
|
||||
const orders = [await orderFactory.newSignedOrderAsync()];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
await expectTransactionFailedAsync(
|
||||
coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{
|
||||
from: takerAddress,
|
||||
gas: devConstants.MAX_EXECUTE_TRANSACTION_GAS,
|
||||
},
|
||||
),
|
||||
RevertReason.InvalidApprovalSignature,
|
||||
);
|
||||
});
|
||||
it(`${fnName} should revert with an invalid approval signature`, async () => {
|
||||
const orders = [await orderFactory.newSignedOrderAsync()];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory.newSignedApproval(
|
||||
transaction,
|
||||
takerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`;
|
||||
await expectTransactionFailedAsync(
|
||||
coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[signature],
|
||||
{ from: takerAddress },
|
||||
),
|
||||
RevertReason.InvalidApprovalSignature,
|
||||
);
|
||||
});
|
||||
it(`${fnName} should revert with an expired approval`, async () => {
|
||||
const orders = [await orderFactory.newSignedOrderAsync()];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory.newSignedApproval(
|
||||
transaction,
|
||||
takerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await expectTransactionFailedAsync(
|
||||
coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: takerAddress },
|
||||
),
|
||||
RevertReason.ApprovalExpired,
|
||||
);
|
||||
});
|
||||
it(`${fnName} should revert if not called by tx signer or approver`, async () => {
|
||||
const orders = [await orderFactory.newSignedOrderAsync()];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory.newSignedApproval(
|
||||
transaction,
|
||||
takerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await expectTransactionFailedAsync(
|
||||
coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: owner },
|
||||
),
|
||||
RevertReason.InvalidOrigin,
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
describe('batch order fills', () => {
|
||||
for (const fnName of [...constants.MARKET_FILL_FN_NAMES, ...constants.BATCH_FILL_FN_NAMES]) {
|
||||
it(`${fnName} should fill the orders with a signed approval`, async () => {
|
||||
const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory.newSignedApproval(
|
||||
transaction,
|
||||
takerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: takerAddress, gas: devConstants.MAX_EXECUTE_TRANSACTION_GAS },
|
||||
),
|
||||
devConstants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const fillLogs = transactionReceipt.logs.filter(
|
||||
log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill',
|
||||
);
|
||||
expect(fillLogs.length).to.eq(orders.length);
|
||||
orders.forEach((order, index) => {
|
||||
const fillLogArgs = (fillLogs[index] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
|
||||
expect(fillLogArgs.makerAddress).to.eq(makerAddress);
|
||||
expect(fillLogArgs.takerAddress).to.eq(takerAddress);
|
||||
expect(fillLogArgs.senderAddress).to.eq(coordinatorContract.address);
|
||||
expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
|
||||
expect(fillLogArgs.makerAssetData).to.eq(order.makerAssetData);
|
||||
expect(fillLogArgs.takerAssetData).to.eq(order.takerAssetData);
|
||||
expect(fillLogArgs.makerAssetFilledAmount).to.bignumber.eq(order.makerAssetAmount);
|
||||
expect(fillLogArgs.takerAssetFilledAmount).to.bignumber.eq(order.takerAssetAmount);
|
||||
expect(fillLogArgs.makerFeePaid).to.bignumber.eq(order.makerFee);
|
||||
expect(fillLogArgs.takerFeePaid).to.bignumber.eq(order.takerFee);
|
||||
expect(fillLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(order));
|
||||
});
|
||||
});
|
||||
it(`${fnName} should fill the orders if called by approver`, async () => {
|
||||
const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
feeRecipientAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{ from: feeRecipientAddress, gas: devConstants.MAX_EXECUTE_TRANSACTION_GAS },
|
||||
),
|
||||
devConstants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const fillLogs = transactionReceipt.logs.filter(
|
||||
log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill',
|
||||
);
|
||||
expect(fillLogs.length).to.eq(orders.length);
|
||||
orders.forEach((order, index) => {
|
||||
const fillLogArgs = (fillLogs[index] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
|
||||
expect(fillLogArgs.makerAddress).to.eq(makerAddress);
|
||||
expect(fillLogArgs.takerAddress).to.eq(takerAddress);
|
||||
expect(fillLogArgs.senderAddress).to.eq(coordinatorContract.address);
|
||||
expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
|
||||
expect(fillLogArgs.makerAssetData).to.eq(order.makerAssetData);
|
||||
expect(fillLogArgs.takerAssetData).to.eq(order.takerAssetData);
|
||||
expect(fillLogArgs.makerAssetFilledAmount).to.bignumber.eq(order.makerAssetAmount);
|
||||
expect(fillLogArgs.takerAssetFilledAmount).to.bignumber.eq(order.takerAssetAmount);
|
||||
expect(fillLogArgs.makerFeePaid).to.bignumber.eq(order.makerFee);
|
||||
expect(fillLogArgs.takerFeePaid).to.bignumber.eq(order.takerFee);
|
||||
expect(fillLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(order));
|
||||
});
|
||||
});
|
||||
it(`${fnName} should revert with an invalid approval signature`, async () => {
|
||||
const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory.newSignedApproval(
|
||||
transaction,
|
||||
takerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`;
|
||||
await expectTransactionFailedAsync(
|
||||
coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[signature],
|
||||
{ from: takerAddress },
|
||||
),
|
||||
RevertReason.InvalidApprovalSignature,
|
||||
);
|
||||
});
|
||||
it(`${fnName} should revert with an expired approval`, async () => {
|
||||
const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory.newSignedApproval(
|
||||
transaction,
|
||||
takerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await expectTransactionFailedAsync(
|
||||
coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: takerAddress },
|
||||
),
|
||||
RevertReason.ApprovalExpired,
|
||||
);
|
||||
});
|
||||
it(`${fnName} should revert if not called by tx signer or approver`, async () => {
|
||||
const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = takerTransactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory.newSignedApproval(
|
||||
transaction,
|
||||
takerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await expectTransactionFailedAsync(
|
||||
coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
takerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: owner },
|
||||
),
|
||||
RevertReason.InvalidOrigin,
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
describe('cancels', () => {
|
||||
it('cancelOrder call should be successful without an approval', async () => {
|
||||
const orders = [await orderFactory.newSignedOrderAsync()];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(constants.CANCEL_ORDERS, orders);
|
||||
const transaction = makerTransactionFactory.newSignedTransaction(data);
|
||||
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
makerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{
|
||||
from: makerAddress,
|
||||
},
|
||||
),
|
||||
);
|
||||
const cancelLogs = transactionReceipt.logs.filter(
|
||||
log => (log as LogWithDecodedArgs<ExchangeCancelEventArgs>).event === 'Cancel',
|
||||
);
|
||||
expect(cancelLogs.length).to.eq(1);
|
||||
const cancelLogArgs = (cancelLogs[0] as LogWithDecodedArgs<ExchangeCancelEventArgs>).args;
|
||||
expect(cancelLogArgs.makerAddress).to.eq(makerAddress);
|
||||
expect(cancelLogArgs.senderAddress).to.eq(coordinatorContract.address);
|
||||
expect(cancelLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
|
||||
expect(cancelLogArgs.makerAssetData).to.eq(orders[0].makerAssetData);
|
||||
expect(cancelLogArgs.takerAssetData).to.eq(orders[0].takerAssetData);
|
||||
expect(cancelLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(orders[0]));
|
||||
});
|
||||
it('batchCancelOrders call should be successful without an approval', async () => {
|
||||
const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(constants.BATCH_CANCEL_ORDERS, orders);
|
||||
const transaction = makerTransactionFactory.newSignedTransaction(data);
|
||||
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
makerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{
|
||||
from: makerAddress,
|
||||
},
|
||||
),
|
||||
);
|
||||
const cancelLogs = transactionReceipt.logs.filter(
|
||||
log => (log as LogWithDecodedArgs<ExchangeCancelEventArgs>).event === 'Cancel',
|
||||
);
|
||||
expect(cancelLogs.length).to.eq(orders.length);
|
||||
orders.forEach((order, index) => {
|
||||
const cancelLogArgs = (cancelLogs[index] as LogWithDecodedArgs<ExchangeCancelEventArgs>).args;
|
||||
expect(cancelLogArgs.makerAddress).to.eq(makerAddress);
|
||||
expect(cancelLogArgs.senderAddress).to.eq(coordinatorContract.address);
|
||||
expect(cancelLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
|
||||
expect(cancelLogArgs.makerAssetData).to.eq(order.makerAssetData);
|
||||
expect(cancelLogArgs.takerAssetData).to.eq(order.takerAssetData);
|
||||
expect(cancelLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(order));
|
||||
});
|
||||
});
|
||||
it('cancelOrdersUpTo call should be successful without an approval', async () => {
|
||||
const orders: SignedOrder[] = [];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(constants.CANCEL_ORDERS_UP_TO, orders);
|
||||
const transaction = makerTransactionFactory.newSignedTransaction(data);
|
||||
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await coordinatorContract.executeTransaction.sendTransactionAsync(
|
||||
transaction,
|
||||
makerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{
|
||||
from: makerAddress,
|
||||
},
|
||||
),
|
||||
);
|
||||
const cancelLogs = transactionReceipt.logs.filter(
|
||||
log => (log as LogWithDecodedArgs<ExchangeCancelUpToEventArgs>).event === 'CancelUpTo',
|
||||
);
|
||||
expect(cancelLogs.length).to.eq(1);
|
||||
const cancelLogArgs = (cancelLogs[0] as LogWithDecodedArgs<ExchangeCancelUpToEventArgs>).args;
|
||||
expect(cancelLogArgs.makerAddress).to.eq(makerAddress);
|
||||
expect(cancelLogArgs.senderAddress).to.eq(coordinatorContract.address);
|
||||
expect(cancelLogArgs.orderEpoch).to.bignumber.eq(new BigNumber(1));
|
||||
});
|
||||
});
|
||||
});
|
||||
// tslint:disable:max-file-line-count
|
@@ -1,81 +0,0 @@
|
||||
import { artifacts as exchangeArtifacts } from '@0x/contracts-exchange';
|
||||
import { chaiSetup, provider, web3Wrapper } from '@0x/contracts-test-utils';
|
||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||
import * as chai from 'chai';
|
||||
import { LogWithDecodedArgs } from 'ethereum-types';
|
||||
|
||||
import { CoordinatorRegistryCoordinatorEndpointSetEventArgs } from '../src';
|
||||
|
||||
import { CoordinatorRegistryWrapper } from './utils/coordinator_registry_wrapper';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
web3Wrapper.abiDecoder.addABI(exchangeArtifacts.Exchange.compilerOutput.abi);
|
||||
// tslint:disable:no-unnecessary-type-assertion
|
||||
describe('Coordinator Registry tests', () => {
|
||||
let coordinatorOperator: string;
|
||||
const coordinatorEndpoint = 'http://sometec.0x.org';
|
||||
const nilCoordinatorEndpoint = '';
|
||||
let coordinatorRegistryWrapper: CoordinatorRegistryWrapper;
|
||||
// tests
|
||||
before(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
after(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
before(async () => {
|
||||
// setup accounts (skip owner)
|
||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
||||
[, coordinatorOperator] = accounts;
|
||||
// deploy coordinator registry
|
||||
coordinatorRegistryWrapper = new CoordinatorRegistryWrapper(provider);
|
||||
await coordinatorRegistryWrapper.deployCoordinatorRegistryAsync();
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
afterEach(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
describe('core', () => {
|
||||
it('Should successfully set a Coordinator endpoint', async () => {
|
||||
await coordinatorRegistryWrapper.setCoordinatorEndpointAsync(coordinatorOperator, coordinatorEndpoint);
|
||||
const recordedCoordinatorEndpoint = await coordinatorRegistryWrapper.getCoordinatorEndpointAsync(
|
||||
coordinatorOperator,
|
||||
);
|
||||
expect(recordedCoordinatorEndpoint).to.be.equal(coordinatorEndpoint);
|
||||
});
|
||||
it('Should successfully unset a Coordinator endpoint', async () => {
|
||||
// set Coordinator endpoint
|
||||
await coordinatorRegistryWrapper.setCoordinatorEndpointAsync(coordinatorOperator, coordinatorEndpoint);
|
||||
let recordedCoordinatorEndpoint = await coordinatorRegistryWrapper.getCoordinatorEndpointAsync(
|
||||
coordinatorOperator,
|
||||
);
|
||||
expect(recordedCoordinatorEndpoint).to.be.equal(coordinatorEndpoint);
|
||||
// unset Coordinator endpoint
|
||||
await coordinatorRegistryWrapper.setCoordinatorEndpointAsync(coordinatorOperator, nilCoordinatorEndpoint);
|
||||
recordedCoordinatorEndpoint = await coordinatorRegistryWrapper.getCoordinatorEndpointAsync(
|
||||
coordinatorOperator,
|
||||
);
|
||||
expect(recordedCoordinatorEndpoint).to.be.equal(nilCoordinatorEndpoint);
|
||||
});
|
||||
it('Should emit an event when setting Coordinator endpoint', async () => {
|
||||
// set Coordinator endpoint
|
||||
const txReceipt = await coordinatorRegistryWrapper.setCoordinatorEndpointAsync(
|
||||
coordinatorOperator,
|
||||
coordinatorEndpoint,
|
||||
);
|
||||
const recordedCoordinatorEndpoint = await coordinatorRegistryWrapper.getCoordinatorEndpointAsync(
|
||||
coordinatorOperator,
|
||||
);
|
||||
expect(recordedCoordinatorEndpoint).to.be.equal(coordinatorEndpoint);
|
||||
// validate event
|
||||
expect(txReceipt.logs.length).to.be.equal(1);
|
||||
const log = txReceipt.logs[0] as LogWithDecodedArgs<CoordinatorRegistryCoordinatorEndpointSetEventArgs>;
|
||||
expect(log.args.coordinatorOperator).to.be.equal(coordinatorOperator);
|
||||
expect(log.args.coordinatorEndpoint).to.be.equal(coordinatorEndpoint);
|
||||
});
|
||||
});
|
||||
});
|
@@ -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,79 +0,0 @@
|
||||
import { addressUtils, chaiSetup, constants, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils';
|
||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||
import { transactionHashUtils } from '@0x/order-utils';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as chai from 'chai';
|
||||
|
||||
import { artifacts, hashUtils, TestLibsContract } from '../src';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
|
||||
describe('Libs tests', () => {
|
||||
let testLibs: TestLibsContract;
|
||||
const exchangeAddress = addressUtils.generatePseudoRandomAddress();
|
||||
|
||||
before(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
after(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
before(async () => {
|
||||
testLibs = await TestLibsContract.deployFrom0xArtifactAsync(
|
||||
artifacts.TestLibs,
|
||||
provider,
|
||||
txDefaults,
|
||||
exchangeAddress,
|
||||
);
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
afterEach(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
|
||||
describe('getTransactionHash', () => {
|
||||
it('should return the correct transaction hash', async () => {
|
||||
const tx = {
|
||||
verifyingContractAddress: exchangeAddress,
|
||||
salt: new BigNumber(0),
|
||||
signerAddress: constants.NULL_ADDRESS,
|
||||
data: '0x1234',
|
||||
};
|
||||
const expectedTxHash = transactionHashUtils.getTransactionHashHex(tx);
|
||||
const txHash = await testLibs.publicGetTransactionHash.callAsync(tx);
|
||||
expect(expectedTxHash).to.eq(txHash);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getApprovalHash', () => {
|
||||
it('should return the correct approval hash', async () => {
|
||||
const signedTx = {
|
||||
verifyingContractAddress: exchangeAddress,
|
||||
salt: new BigNumber(0),
|
||||
signerAddress: constants.NULL_ADDRESS,
|
||||
data: '0x1234',
|
||||
signature: '0x5678',
|
||||
};
|
||||
const approvalExpirationTimeSeconds = new BigNumber(0);
|
||||
const txOrigin = constants.NULL_ADDRESS;
|
||||
const approval = {
|
||||
txOrigin,
|
||||
transactionHash: transactionHashUtils.getTransactionHashHex(signedTx),
|
||||
transactionSignature: signedTx.signature,
|
||||
approvalExpirationTimeSeconds,
|
||||
};
|
||||
const expectedApprovalHash = hashUtils.getApprovalHashHex(
|
||||
signedTx,
|
||||
testLibs.address,
|
||||
txOrigin,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const approvalHash = await testLibs.publicGetCoordinatorApprovalHash.callAsync(approval);
|
||||
expect(expectedApprovalHash).to.eq(approvalHash);
|
||||
});
|
||||
});
|
||||
});
|
@@ -1,895 +0,0 @@
|
||||
import {
|
||||
addressUtils,
|
||||
chaiSetup,
|
||||
constants as devConstants,
|
||||
expectContractCallFailedAsync,
|
||||
getLatestBlockTimestampAsync,
|
||||
provider,
|
||||
TransactionFactory,
|
||||
txDefaults,
|
||||
web3Wrapper,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||
import { transactionHashUtils } from '@0x/order-utils';
|
||||
import { RevertReason, SignatureType, SignedOrder } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as chai from 'chai';
|
||||
import * as ethUtil from 'ethereumjs-util';
|
||||
|
||||
import { ApprovalFactory, artifacts, constants, exchangeDataEncoder, TestMixinsContract } from '../src';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
|
||||
describe('Mixins tests', () => {
|
||||
let transactionSignerAddress: string;
|
||||
let approvalSignerAddress1: string;
|
||||
let approvalSignerAddress2: string;
|
||||
let mixins: TestMixinsContract;
|
||||
let transactionFactory: TransactionFactory;
|
||||
let approvalFactory1: ApprovalFactory;
|
||||
let approvalFactory2: ApprovalFactory;
|
||||
let defaultOrder: SignedOrder;
|
||||
const exchangeAddress = addressUtils.generatePseudoRandomAddress();
|
||||
|
||||
before(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
after(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
before(async () => {
|
||||
mixins = await TestMixinsContract.deployFrom0xArtifactAsync(
|
||||
artifacts.TestMixins,
|
||||
provider,
|
||||
txDefaults,
|
||||
exchangeAddress,
|
||||
);
|
||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
||||
[transactionSignerAddress, approvalSignerAddress1, approvalSignerAddress2] = accounts.slice(0, 3);
|
||||
defaultOrder = {
|
||||
exchangeAddress: devConstants.NULL_ADDRESS,
|
||||
makerAddress: devConstants.NULL_ADDRESS,
|
||||
takerAddress: devConstants.NULL_ADDRESS,
|
||||
senderAddress: mixins.address,
|
||||
feeRecipientAddress: approvalSignerAddress1,
|
||||
makerAssetData: devConstants.NULL_BYTES,
|
||||
takerAssetData: devConstants.NULL_BYTES,
|
||||
makerAssetAmount: devConstants.ZERO_AMOUNT,
|
||||
takerAssetAmount: devConstants.ZERO_AMOUNT,
|
||||
makerFee: devConstants.ZERO_AMOUNT,
|
||||
takerFee: devConstants.ZERO_AMOUNT,
|
||||
expirationTimeSeconds: devConstants.ZERO_AMOUNT,
|
||||
salt: devConstants.ZERO_AMOUNT,
|
||||
signature: devConstants.NULL_BYTES,
|
||||
};
|
||||
const transactionSignerPrivateKey =
|
||||
devConstants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(transactionSignerAddress)];
|
||||
const approvalSignerPrivateKey1 = devConstants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(approvalSignerAddress1)];
|
||||
const approvalSignerPrivateKey2 = devConstants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(approvalSignerAddress2)];
|
||||
transactionFactory = new TransactionFactory(transactionSignerPrivateKey, exchangeAddress);
|
||||
approvalFactory1 = new ApprovalFactory(approvalSignerPrivateKey1, mixins.address);
|
||||
approvalFactory2 = new ApprovalFactory(approvalSignerPrivateKey2, mixins.address);
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
afterEach(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
|
||||
describe('getSignerAddress', () => {
|
||||
it('should return the correct address using the EthSign signature type', async () => {
|
||||
const data = devConstants.NULL_BYTES;
|
||||
const transaction = transactionFactory.newSignedTransaction(data, SignatureType.EthSign);
|
||||
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||
const signerAddress = await mixins.getSignerAddress.callAsync(transactionHash, transaction.signature);
|
||||
expect(transaction.signerAddress).to.eq(signerAddress);
|
||||
});
|
||||
it('should return the correct address using the EIP712 signature type', async () => {
|
||||
const data = devConstants.NULL_BYTES;
|
||||
const transaction = transactionFactory.newSignedTransaction(data, SignatureType.EIP712);
|
||||
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||
const signerAddress = await mixins.getSignerAddress.callAsync(transactionHash, transaction.signature);
|
||||
expect(transaction.signerAddress).to.eq(signerAddress);
|
||||
});
|
||||
it('should revert with with the Illegal signature type', async () => {
|
||||
const data = devConstants.NULL_BYTES;
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const illegalSignatureByte = ethUtil.toBuffer(SignatureType.Illegal).toString('hex');
|
||||
transaction.signature = `${transaction.signature.slice(
|
||||
0,
|
||||
transaction.signature.length - 2,
|
||||
)}${illegalSignatureByte}`;
|
||||
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.getSignerAddress.callAsync(transactionHash, transaction.signature),
|
||||
RevertReason.SignatureIllegal,
|
||||
);
|
||||
});
|
||||
it('should revert with with the Invalid signature type', async () => {
|
||||
const data = devConstants.NULL_BYTES;
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const invalidSignatureByte = ethUtil.toBuffer(SignatureType.Invalid).toString('hex');
|
||||
transaction.signature = `0x${invalidSignatureByte}`;
|
||||
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.getSignerAddress.callAsync(transactionHash, transaction.signature),
|
||||
RevertReason.SignatureInvalid,
|
||||
);
|
||||
});
|
||||
it("should revert with with a signature type that doesn't exist", async () => {
|
||||
const data = devConstants.NULL_BYTES;
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const invalidSignatureByte = '04';
|
||||
transaction.signature = `${transaction.signature.slice(
|
||||
0,
|
||||
transaction.signature.length - 2,
|
||||
)}${invalidSignatureByte}`;
|
||||
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.getSignerAddress.callAsync(transactionHash, transaction.signature),
|
||||
RevertReason.SignatureUnsupported,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Single order approvals', () => {
|
||||
for (const fnName of constants.SINGLE_FILL_FN_NAMES) {
|
||||
it(`Should be successful: function=${fnName}, caller=tx_signer, senderAddress=[verifier], approval_sig=[approver1], expiration=[valid]`, async () => {
|
||||
const orders = [defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
});
|
||||
it(`Should be successful: function=${fnName}, caller=tx_signer, senderAddress=[null], approval_sig=[approver1], expiration=[valid]`, async () => {
|
||||
const order = {
|
||||
...defaultOrder,
|
||||
senderAddress: devConstants.NULL_ADDRESS,
|
||||
};
|
||||
const orders = [order];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
});
|
||||
it(`Should be successful: function=${fnName}, caller=approver1, senderAddress=[verifier], approval_sig=[], expiration=[]`, async () => {
|
||||
const orders = [defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
await mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
approvalSignerAddress1,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{ from: approvalSignerAddress1 },
|
||||
);
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
approvalSignerAddress1,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{
|
||||
from: approvalSignerAddress1,
|
||||
},
|
||||
);
|
||||
});
|
||||
it(`Should be successful: function=${fnName}, caller=approver1, senderAddress=[verifier], approval_sig=[approver1], expiration=[invalid]`, async () => {
|
||||
const orders = [defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
approvalSignerAddress1,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: approvalSignerAddress1 },
|
||||
);
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
approvalSignerAddress1,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: approvalSignerAddress1 },
|
||||
);
|
||||
});
|
||||
it(`Should be successful: function=${fnName}, caller=approver1, senderAddress=[verifier], approval_sig=[], expiration=[]`, async () => {
|
||||
const orders = [defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
await mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
approvalSignerAddress1,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{ from: approvalSignerAddress1 },
|
||||
);
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
approvalSignerAddress1,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{
|
||||
from: approvalSignerAddress1,
|
||||
},
|
||||
);
|
||||
});
|
||||
it(`Should revert: function=${fnName}, caller=tx_signer, senderAddress=[verifier], approval_sig=[invalid], expiration=[valid]`, async () => {
|
||||
const orders = [defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`;
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[signature],
|
||||
{ from: transactionSignerAddress },
|
||||
),
|
||||
RevertReason.InvalidApprovalSignature,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[signature],
|
||||
{ from: transactionSignerAddress },
|
||||
),
|
||||
RevertReason.InvalidApprovalSignature,
|
||||
);
|
||||
});
|
||||
it(`Should revert: function=${fnName}, caller=tx_signer, senderAddress=[verifier], approval_sig=[approver1], expiration=[invalid]`, async () => {
|
||||
const orders = [defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
),
|
||||
RevertReason.ApprovalExpired,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
),
|
||||
RevertReason.ApprovalExpired,
|
||||
);
|
||||
});
|
||||
it(`Should revert: function=${fnName}, caller=approver2, senderAddress=[verifier], approval_sig=[approver1], expiration=[valid]`, async () => {
|
||||
const orders = [defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: approvalSignerAddress2 },
|
||||
),
|
||||
RevertReason.InvalidOrigin,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: approvalSignerAddress2 },
|
||||
),
|
||||
RevertReason.InvalidOrigin,
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
describe('Batch order approvals', () => {
|
||||
for (const fnName of [
|
||||
...constants.BATCH_FILL_FN_NAMES,
|
||||
...constants.MARKET_FILL_FN_NAMES,
|
||||
constants.MATCH_ORDERS,
|
||||
]) {
|
||||
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver1], approval_sig=[approver1], expiration=[valid]`, async () => {
|
||||
const orders = [defaultOrder, defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
});
|
||||
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[null,null], feeRecipient=[approver1,approver1], approval_sig=[approver1], expiration=[valid]`, async () => {
|
||||
const orders = [defaultOrder, defaultOrder].map(order => ({
|
||||
...order,
|
||||
senderAddress: devConstants.NULL_ADDRESS,
|
||||
}));
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
});
|
||||
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[null,null], feeRecipient=[approver1,approver1], approval_sig=[], expiration=[]`, async () => {
|
||||
const orders = [defaultOrder, defaultOrder].map(order => ({
|
||||
...order,
|
||||
senderAddress: devConstants.NULL_ADDRESS,
|
||||
}));
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
await mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
});
|
||||
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[verifier,null], feeRecipient=[approver1,approver1], approval_sig=[approver1], expiration=[valid]`, async () => {
|
||||
const orders = [defaultOrder, { ...defaultOrder, senderAddress: devConstants.NULL_ADDRESS }];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
});
|
||||
it(`Should be successful: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver2], approval_sig=[approver1,approver2], expiration=[valid,valid]`, async () => {
|
||||
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval1 = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const approval2 = approvalFactory2.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
await mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds, approvalExpirationTimeSeconds],
|
||||
[approval1.signature, approval2.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds, approvalExpirationTimeSeconds],
|
||||
[approval1.signature, approval2.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
});
|
||||
it(`Should be successful: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver1], approval_sig=[], expiration=[]`, async () => {
|
||||
const orders = [defaultOrder, defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
await mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
approvalSignerAddress1,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{ from: approvalSignerAddress1 },
|
||||
);
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
approvalSignerAddress1,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{ from: approvalSignerAddress1 },
|
||||
);
|
||||
});
|
||||
it(`Should revert: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1,approver2], approval_sig=[approver2], expiration=[valid]`, async () => {
|
||||
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval2 = approvalFactory2.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval2.signature],
|
||||
{ from: approvalSignerAddress1 },
|
||||
),
|
||||
RevertReason.InvalidOrigin,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval2.signature],
|
||||
{ from: approvalSignerAddress1 },
|
||||
),
|
||||
RevertReason.InvalidOrigin,
|
||||
);
|
||||
});
|
||||
it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver1], approval_sig=[], expiration=[]`, async () => {
|
||||
const orders = [defaultOrder, defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{ from: transactionSignerAddress },
|
||||
),
|
||||
RevertReason.InvalidApprovalSignature,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{ from: transactionSignerAddress },
|
||||
),
|
||||
RevertReason.InvalidApprovalSignature,
|
||||
);
|
||||
});
|
||||
it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver1], approval_sig=[invalid], expiration=[valid]`, async () => {
|
||||
const orders = [defaultOrder, defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`;
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[signature],
|
||||
{ from: transactionSignerAddress },
|
||||
),
|
||||
RevertReason.InvalidApprovalSignature,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[signature],
|
||||
{ from: transactionSignerAddress },
|
||||
),
|
||||
RevertReason.InvalidApprovalSignature,
|
||||
);
|
||||
});
|
||||
it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[valid,invalid], expiration=[valid,valid]`, async () => {
|
||||
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval1 = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const approval2 = approvalFactory2.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const approvalSignature2 = `${approval2.signature.slice(0, 4)}FFFFFFFF${approval2.signature.slice(12)}`;
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds, approvalExpirationTimeSeconds],
|
||||
[approval1.signature, approvalSignature2],
|
||||
{ from: transactionSignerAddress },
|
||||
),
|
||||
RevertReason.InvalidApprovalSignature,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds, approvalExpirationTimeSeconds],
|
||||
[approval1.signature, approvalSignature2],
|
||||
{ from: transactionSignerAddress },
|
||||
),
|
||||
RevertReason.InvalidApprovalSignature,
|
||||
);
|
||||
});
|
||||
it(`Should revert: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[invalid], expiration=[valid]`, async () => {
|
||||
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval2 = approvalFactory2.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const approvalSignature2 = `${approval2.signature.slice(0, 4)}FFFFFFFF${approval2.signature.slice(12)}`;
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
approvalSignerAddress1,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approvalSignature2],
|
||||
{ from: approvalSignerAddress1 },
|
||||
),
|
||||
RevertReason.InvalidApprovalSignature,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
approvalSignerAddress1,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approvalSignature2],
|
||||
{ from: approvalSignerAddress1 },
|
||||
),
|
||||
RevertReason.InvalidApprovalSignature,
|
||||
);
|
||||
});
|
||||
it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[valid,valid], expiration=[valid,invalid]`, async () => {
|
||||
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds1 = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approvalExpirationTimeSeconds2 = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER);
|
||||
const approval1 = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds1,
|
||||
);
|
||||
const approval2 = approvalFactory2.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds2,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds1, approvalExpirationTimeSeconds2],
|
||||
[approval1.signature, approval2.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
),
|
||||
RevertReason.ApprovalExpired,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds1, approvalExpirationTimeSeconds2],
|
||||
[approval1.signature, approval2.signature],
|
||||
{ from: transactionSignerAddress },
|
||||
),
|
||||
RevertReason.ApprovalExpired,
|
||||
);
|
||||
});
|
||||
it(`Should revert: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[valid], expiration=[invalid]`, async () => {
|
||||
const orders = [defaultOrder, { ...defaultOrder, feeRecipientAddress: approvalSignerAddress2 }];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).minus(constants.TIME_BUFFER);
|
||||
const approval2 = approvalFactory2.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
approvalSignerAddress1,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval2.signature],
|
||||
{ from: approvalSignerAddress1 },
|
||||
),
|
||||
RevertReason.ApprovalExpired,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
approvalSignerAddress1,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval2.signature],
|
||||
{ from: approvalSignerAddress1 },
|
||||
),
|
||||
RevertReason.ApprovalExpired,
|
||||
);
|
||||
});
|
||||
it(`Should revert: function=${fnName} caller=approver2, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver1], approval_sig=[valid], expiration=[valid]`, async () => {
|
||||
const orders = [defaultOrder, defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
const approvalExpirationTimeSeconds = new BigNumber(currentTimestamp).plus(constants.TIME_BUFFER);
|
||||
const approval1 = approvalFactory1.newSignedApproval(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidTransactionOrdersApproval.callAsync(
|
||||
transaction,
|
||||
orders,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval1.signature],
|
||||
{ from: approvalSignerAddress2 },
|
||||
),
|
||||
RevertReason.InvalidOrigin,
|
||||
);
|
||||
expectContractCallFailedAsync(
|
||||
mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[approvalExpirationTimeSeconds],
|
||||
[approval1.signature],
|
||||
{ from: approvalSignerAddress2 },
|
||||
),
|
||||
RevertReason.InvalidOrigin,
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
describe('cancels', () => {
|
||||
it('should allow the tx signer to call `cancelOrders` without approval', async () => {
|
||||
const orders = [defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(constants.CANCEL_ORDERS, orders);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
});
|
||||
it('should allow the tx signer to call `batchCancelOrders` without approval', async () => {
|
||||
const orders = [defaultOrder, defaultOrder];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(constants.BATCH_CANCEL_ORDERS, orders);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
});
|
||||
it('should allow the tx signer to call `cancelOrdersUpTo` without approval', async () => {
|
||||
const orders: SignedOrder[] = [];
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(constants.CANCEL_ORDERS_UP_TO, orders);
|
||||
const transaction = transactionFactory.newSignedTransaction(data);
|
||||
await mixins.assertValidCoordinatorApprovals.callAsync(
|
||||
transaction,
|
||||
transactionSignerAddress,
|
||||
transaction.signature,
|
||||
[],
|
||||
[],
|
||||
{ from: transactionSignerAddress },
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
// tslint:disable:max-file-line-count
|
@@ -1,38 +0,0 @@
|
||||
import { signingUtils } from '@0x/contracts-test-utils';
|
||||
import { SignatureType, SignedZeroExTransaction } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as ethUtil from 'ethereumjs-util';
|
||||
|
||||
import { hashUtils, SignedCoordinatorApproval } from './index';
|
||||
|
||||
export class ApprovalFactory {
|
||||
private readonly _privateKey: Buffer;
|
||||
private readonly _verifyingContractAddress: string;
|
||||
|
||||
constructor(privateKey: Buffer, verifyingContractAddress: string) {
|
||||
this._privateKey = privateKey;
|
||||
this._verifyingContractAddress = verifyingContractAddress;
|
||||
}
|
||||
|
||||
public newSignedApproval(
|
||||
transaction: SignedZeroExTransaction,
|
||||
txOrigin: string,
|
||||
approvalExpirationTimeSeconds: BigNumber,
|
||||
signatureType: SignatureType = SignatureType.EthSign,
|
||||
): SignedCoordinatorApproval {
|
||||
const approvalHashBuff = hashUtils.getApprovalHashBuffer(
|
||||
transaction,
|
||||
this._verifyingContractAddress,
|
||||
txOrigin,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const signatureBuff = signingUtils.signMessage(approvalHashBuff, this._privateKey, signatureType);
|
||||
const signedApproval = {
|
||||
txOrigin,
|
||||
transaction,
|
||||
approvalExpirationTimeSeconds,
|
||||
signature: ethUtil.addHexPrefix(signatureBuff.toString('hex')),
|
||||
};
|
||||
return signedApproval;
|
||||
}
|
||||
}
|
@@ -1,12 +0,0 @@
|
||||
import { BigNumber } from '@0x/utils';
|
||||
|
||||
export const constants = {
|
||||
SINGLE_FILL_FN_NAMES: ['fillOrder', 'fillOrKillOrder', 'fillOrderNoThrow'],
|
||||
BATCH_FILL_FN_NAMES: ['batchFillOrders', 'batchFillOrKillOrders', 'batchFillOrdersNoThrow'],
|
||||
MARKET_FILL_FN_NAMES: ['marketBuyOrders', 'marketBuyOrdersNoThrow', 'marketSellOrders', 'marketSellOrdersNoThrow'],
|
||||
MATCH_ORDERS: 'matchOrders',
|
||||
CANCEL_ORDERS: 'cancelOrders',
|
||||
BATCH_CANCEL_ORDERS: 'batchCancelOrders',
|
||||
CANCEL_ORDERS_UP_TO: 'cancelOrdersUpTo',
|
||||
TIME_BUFFER: new BigNumber(1000),
|
||||
};
|
@@ -1,65 +0,0 @@
|
||||
import { LogDecoder, txDefaults } from '@0x/contracts-test-utils';
|
||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import { TransactionReceiptWithDecodedLogs, ZeroExProvider } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { artifacts, CoordinatorRegistryContract } from '../../src';
|
||||
|
||||
export class CoordinatorRegistryWrapper {
|
||||
private readonly _web3Wrapper: Web3Wrapper;
|
||||
private readonly _provider: ZeroExProvider;
|
||||
private readonly _logDecoder: LogDecoder;
|
||||
private _coordinatorRegistryContract?: CoordinatorRegistryContract;
|
||||
/**
|
||||
* Instanitates an CoordinatorRegistryWrapper
|
||||
* @param provider Web3 provider to use for all JSON RPC requests
|
||||
* Instance of CoordinatorRegistryWrapper
|
||||
*/
|
||||
constructor(provider: ZeroExProvider) {
|
||||
this._web3Wrapper = new Web3Wrapper(provider);
|
||||
this._provider = provider;
|
||||
this._logDecoder = new LogDecoder(this._web3Wrapper, artifacts);
|
||||
}
|
||||
public async deployCoordinatorRegistryAsync(): Promise<CoordinatorRegistryContract> {
|
||||
this._coordinatorRegistryContract = await CoordinatorRegistryContract.deployFrom0xArtifactAsync(
|
||||
artifacts.CoordinatorRegistry,
|
||||
this._provider,
|
||||
txDefaults,
|
||||
);
|
||||
if (_.isUndefined(this._coordinatorRegistryContract)) {
|
||||
throw new Error(`Failed to deploy Coordinator Registry contract.`);
|
||||
}
|
||||
return this._coordinatorRegistryContract;
|
||||
}
|
||||
public async setCoordinatorEndpointAsync(
|
||||
coordinatorOperator: string,
|
||||
coordinatorEndpoint: string,
|
||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||
this._assertCoordinatorRegistryDeployed();
|
||||
const txReceipt = await this._logDecoder.getTxWithDecodedLogsAsync(
|
||||
await (this
|
||||
._coordinatorRegistryContract as CoordinatorRegistryContract).setCoordinatorEndpoint.sendTransactionAsync(
|
||||
coordinatorEndpoint,
|
||||
{
|
||||
from: coordinatorOperator,
|
||||
},
|
||||
),
|
||||
);
|
||||
return txReceipt;
|
||||
}
|
||||
public async getCoordinatorEndpointAsync(coordinatorOperator: string): Promise<string> {
|
||||
this._assertCoordinatorRegistryDeployed();
|
||||
const coordinatorEndpoint = await (this
|
||||
._coordinatorRegistryContract as CoordinatorRegistryContract).getCoordinatorEndpoint.callAsync(
|
||||
coordinatorOperator,
|
||||
);
|
||||
return coordinatorEndpoint;
|
||||
}
|
||||
private _assertCoordinatorRegistryDeployed(): void {
|
||||
if (_.isUndefined(this._coordinatorRegistryContract)) {
|
||||
throw new Error(
|
||||
'The Coordinator Registry contract was not deployed through the CoordinatorRegistryWrapper. Call `deployCoordinatorRegistryAsync` to deploy.',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,51 +0,0 @@
|
||||
import { artifacts, IExchangeContract } from '@0x/contracts-exchange';
|
||||
import { constants as devConstants, provider } from '@0x/contracts-test-utils';
|
||||
import { SignedOrder } from '@0x/types';
|
||||
|
||||
import { constants } from './index';
|
||||
|
||||
export const exchangeDataEncoder = {
|
||||
encodeOrdersToExchangeData(fnName: string, orders: SignedOrder[]): string {
|
||||
const exchangeInstance = new IExchangeContract(
|
||||
artifacts.IExchange.compilerOutput.abi,
|
||||
devConstants.NULL_ADDRESS,
|
||||
provider,
|
||||
);
|
||||
let data;
|
||||
if (constants.SINGLE_FILL_FN_NAMES.indexOf(fnName) !== -1) {
|
||||
data = (exchangeInstance as any)[fnName].getABIEncodedTransactionData(
|
||||
orders[0],
|
||||
orders[0].takerAssetAmount,
|
||||
orders[0].signature,
|
||||
);
|
||||
} else if (constants.BATCH_FILL_FN_NAMES.indexOf(fnName) !== -1) {
|
||||
data = (exchangeInstance as any)[fnName].getABIEncodedTransactionData(
|
||||
orders,
|
||||
orders.map(order => order.takerAssetAmount),
|
||||
orders.map(order => order.signature),
|
||||
);
|
||||
} else if (constants.MARKET_FILL_FN_NAMES.indexOf(fnName) !== -1) {
|
||||
data = (exchangeInstance as any)[fnName].getABIEncodedTransactionData(
|
||||
orders,
|
||||
orders.map(order => order.takerAssetAmount).reduce((prev, curr) => prev.plus(curr)),
|
||||
orders.map(order => order.signature),
|
||||
);
|
||||
} else if (fnName === constants.MATCH_ORDERS) {
|
||||
data = exchangeInstance.matchOrders.getABIEncodedTransactionData(
|
||||
orders[0],
|
||||
orders[1],
|
||||
orders[0].signature,
|
||||
orders[1].signature,
|
||||
);
|
||||
} else if (fnName === constants.CANCEL_ORDERS) {
|
||||
data = exchangeInstance.cancelOrder.getABIEncodedTransactionData(orders[0]);
|
||||
} else if (fnName === constants.BATCH_CANCEL_ORDERS) {
|
||||
data = exchangeInstance.batchCancelOrders.getABIEncodedTransactionData(orders);
|
||||
} else if (fnName === constants.CANCEL_ORDERS_UP_TO) {
|
||||
data = exchangeInstance.cancelOrdersUpTo.getABIEncodedTransactionData(devConstants.ZERO_AMOUNT);
|
||||
} else {
|
||||
throw new Error(`Error: ${fnName} not a supported function`);
|
||||
}
|
||||
return data;
|
||||
},
|
||||
};
|
@@ -1,33 +0,0 @@
|
||||
import { eip712Utils } from '@0x/order-utils';
|
||||
import { SignedZeroExTransaction } from '@0x/types';
|
||||
import { BigNumber, signTypedDataUtils } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
export const hashUtils = {
|
||||
getApprovalHashBuffer(
|
||||
transaction: SignedZeroExTransaction,
|
||||
verifyingContractAddress: string,
|
||||
txOrigin: string,
|
||||
approvalExpirationTimeSeconds: BigNumber,
|
||||
): Buffer {
|
||||
const typedData = eip712Utils.createCoordinatorApprovalTypedData(
|
||||
transaction,
|
||||
verifyingContractAddress,
|
||||
txOrigin,
|
||||
approvalExpirationTimeSeconds,
|
||||
);
|
||||
const hashBuffer = signTypedDataUtils.generateTypedDataHash(typedData);
|
||||
return hashBuffer;
|
||||
},
|
||||
getApprovalHashHex(
|
||||
transaction: SignedZeroExTransaction,
|
||||
verifyingContractAddress: string,
|
||||
txOrigin: string,
|
||||
approvalExpirationTimeSeconds: BigNumber,
|
||||
): string {
|
||||
const hashHex = `0x${hashUtils
|
||||
.getApprovalHashBuffer(transaction, verifyingContractAddress, txOrigin, approvalExpirationTimeSeconds)
|
||||
.toString('hex')}`;
|
||||
return hashHex;
|
||||
},
|
||||
};
|
@@ -1,5 +0,0 @@
|
||||
export { hashUtils } from './hash_utils';
|
||||
export { ApprovalFactory } from './approval_factory';
|
||||
export { constants } from './constants';
|
||||
export { exchangeDataEncoder } from './exchange_data_encoder';
|
||||
export * from './types';
|
@@ -1,12 +0,0 @@
|
||||
import { SignedZeroExTransaction } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
|
||||
export interface CoordinatorApproval {
|
||||
transaction: SignedZeroExTransaction;
|
||||
txOrigin: string;
|
||||
approvalExpirationTimeSeconds: BigNumber;
|
||||
}
|
||||
|
||||
export interface SignedCoordinatorApproval extends CoordinatorApproval {
|
||||
signature: string;
|
||||
}
|
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig",
|
||||
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
|
||||
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
|
||||
"files": [
|
||||
"generated-artifacts/Coordinator.json",
|
||||
"generated-artifacts/CoordinatorRegistry.json",
|
||||
"generated-artifacts/TestLibs.json",
|
||||
"generated-artifacts/TestMixins.json"
|
||||
],
|
||||
"exclude": ["./deploy/solc/solc_bin"]
|
||||
}
|
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"extends": ["@0x/tslint-config"],
|
||||
"rules": {
|
||||
"custom-no-magic-numbers": false
|
||||
}
|
||||
}
|
@@ -1,30 +0,0 @@
|
||||
[
|
||||
{
|
||||
"version": "1.1.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Run Web3ProviderEngine without excess block polling",
|
||||
"pr": 1695
|
||||
}
|
||||
],
|
||||
"timestamp": 1553183790
|
||||
},
|
||||
{
|
||||
"timestamp": 1553091633,
|
||||
"version": "1.0.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Created ERC1155 contracts package",
|
||||
"pr": 1657
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
@@ -1,18 +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
|
||||
|
||||
## v1.1.0 - _March 21, 2019_
|
||||
|
||||
* Run Web3ProviderEngine without excess block polling (#1695)
|
||||
|
||||
## v1.0.1 - _March 20, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.0 - _Invalid date_
|
||||
|
||||
* Created ERC1155 contracts package (#1657)
|
@@ -1 +0,0 @@
|
||||
[]
|
@@ -1,73 +0,0 @@
|
||||
## ERC1155 Tokens
|
||||
|
||||
This package contains implementations of various [ERC1155](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1155.md) tokens. Addresses of the deployed contracts can be found in the 0x [wiki](https://0xproject.com/wiki#Deployed-Addresses) or the [DEPLOYS](./DEPLOYS.json) file within this package.
|
||||
|
||||
## Installation
|
||||
|
||||
**Install**
|
||||
|
||||
```bash
|
||||
npm install @0x/contracts-erc1155 --save
|
||||
```
|
||||
|
||||
## Bug bounty
|
||||
|
||||
A bug bounty for the 2.0.0 contracts is ongoing! Instructions can be found [here](https://0xproject.com/wiki#Bug-Bounty).
|
||||
|
||||
## Contributing
|
||||
|
||||
We strongly recommend that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository.
|
||||
|
||||
For proposals regarding the 0x protocol's smart contract architecture, message format, or additional functionality, go to the [0x Improvement Proposals (ZEIPs)](https://github.com/0xProject/ZEIPs) repository and follow the contribution guidelines provided therein.
|
||||
|
||||
Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started.
|
||||
|
||||
### Install Dependencies
|
||||
|
||||
If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them:
|
||||
|
||||
```bash
|
||||
yarn config set workspaces-experimental true
|
||||
```
|
||||
|
||||
Then install dependencies
|
||||
|
||||
```bash
|
||||
yarn install
|
||||
```
|
||||
|
||||
### Build
|
||||
|
||||
To build this package and all other monorepo packages that it depends on, run the following from the monorepo root directory:
|
||||
|
||||
```bash
|
||||
PKG=@0x/contracts-erc1155 yarn build
|
||||
```
|
||||
|
||||
Or continuously rebuild on change:
|
||||
|
||||
```bash
|
||||
PKG=@0x/contracts-erc1155 yarn watch
|
||||
```
|
||||
|
||||
### Clean
|
||||
|
||||
```bash
|
||||
yarn clean
|
||||
```
|
||||
|
||||
### Lint
|
||||
|
||||
```bash
|
||||
yarn lint
|
||||
```
|
||||
|
||||
### Run Tests
|
||||
|
||||
```bash
|
||||
yarn test
|
||||
```
|
||||
|
||||
#### Testing options
|
||||
|
||||
Contracts testing options like coverage, profiling, revert traces or backing node choosing - are described [here](../TESTING.md).
|
@@ -1,30 +0,0 @@
|
||||
{
|
||||
"artifactsDir": "generated-artifacts",
|
||||
"contractsDir": "contracts",
|
||||
"useDockerisedSolc": true,
|
||||
"compilerSettings": {
|
||||
"evmVersion": "constantinople",
|
||||
"optimizer": { "enabled": true, "runs": 1000000 },
|
||||
"outputSelection": {
|
||||
"*": {
|
||||
"*": [
|
||||
"abi",
|
||||
"evm.bytecode.object",
|
||||
"evm.bytecode.sourceMap",
|
||||
"evm.deployedBytecode.object",
|
||||
"evm.deployedBytecode.sourceMap"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"contracts": [
|
||||
"src/ERC1155.sol",
|
||||
"src/ERC1155Mintable.sol",
|
||||
"src/MixinNonFungibleToken.sol",
|
||||
"src/interfaces/IERC1155.sol",
|
||||
"src/interfaces/IERC1155Mintable.sol",
|
||||
"src/interfaces/IERC1155Receiver.sol",
|
||||
"src/mixins/MNonFungibleToken.sol",
|
||||
"test/DummyERC1155Receiver.sol"
|
||||
]
|
||||
}
|
@@ -1,247 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/SafeMath.sol";
|
||||
import "@0x/contracts-utils/contracts/src/Address.sol";
|
||||
import "./interfaces/IERC1155.sol";
|
||||
import "./interfaces/IERC1155Receiver.sol";
|
||||
import "./MixinNonFungibleToken.sol";
|
||||
|
||||
|
||||
contract ERC1155 is
|
||||
SafeMath,
|
||||
IERC1155,
|
||||
MixinNonFungibleToken
|
||||
{
|
||||
using Address for address;
|
||||
|
||||
// selectors for receiver callbacks
|
||||
bytes4 constant public ERC1155_RECEIVED = 0xf23a6e61;
|
||||
bytes4 constant public ERC1155_BATCH_RECEIVED = 0xbc197c81;
|
||||
|
||||
// id => (owner => balance)
|
||||
mapping (uint256 => mapping(address => uint256)) internal balances;
|
||||
|
||||
// owner => (operator => approved)
|
||||
mapping (address => mapping(address => bool)) internal operatorApproval;
|
||||
|
||||
/// @notice Transfers value amount of an _id from the _from address to the _to address specified.
|
||||
/// @dev MUST emit TransferSingle event on success.
|
||||
/// Caller must be approved to manage the _from account's tokens (see isApprovedForAll).
|
||||
/// MUST throw if `_to` is the zero address.
|
||||
/// MUST throw if balance of sender for token `_id` is lower than the `_value` sent.
|
||||
/// MUST throw on any other error.
|
||||
/// When transfer is complete, this function MUST check if `_to` is a smart contract (code size > 0).
|
||||
/// If so, it MUST call `onERC1155Received` on `_to` and revert if the return value
|
||||
/// is not `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`.
|
||||
/// @param from Source address
|
||||
/// @param to Target address
|
||||
/// @param id ID of the token type
|
||||
/// @param value Transfer amount
|
||||
/// @param data Additional data with no specified format, sent in call to `_to`
|
||||
function safeTransferFrom(
|
||||
address from,
|
||||
address to,
|
||||
uint256 id,
|
||||
uint256 value,
|
||||
bytes calldata data
|
||||
)
|
||||
external
|
||||
{
|
||||
// sanity checks
|
||||
require(
|
||||
to != address(0x0),
|
||||
"CANNOT_TRANSFER_TO_ADDRESS_ZERO"
|
||||
);
|
||||
require(
|
||||
from == msg.sender || operatorApproval[from][msg.sender] == true,
|
||||
"INSUFFICIENT_ALLOWANCE"
|
||||
);
|
||||
|
||||
// perform transfer
|
||||
if (isNonFungible(id)) {
|
||||
require(
|
||||
value == 1,
|
||||
"AMOUNT_EQUAL_TO_ONE_REQUIRED"
|
||||
);
|
||||
require(
|
||||
nfOwners[id] == from,
|
||||
"NFT_NOT_OWNED_BY_FROM_ADDRESS"
|
||||
);
|
||||
nfOwners[id] = to;
|
||||
// You could keep balance of NF type in base type id like so:
|
||||
// uint256 baseType = getNonFungibleBaseType(_id);
|
||||
// balances[baseType][_from] = balances[baseType][_from].safeSub(_value);
|
||||
// balances[baseType][_to] = balances[baseType][_to].safeAdd(_value);
|
||||
} else {
|
||||
balances[id][from] = safeSub(balances[id][from], value);
|
||||
balances[id][to] = safeAdd(balances[id][to], value);
|
||||
}
|
||||
emit TransferSingle(msg.sender, from, to, id, value);
|
||||
|
||||
// if `to` is a contract then trigger its callback
|
||||
if (to.isContract()) {
|
||||
bytes4 callbackReturnValue = IERC1155Receiver(to).onERC1155Received(
|
||||
msg.sender,
|
||||
from,
|
||||
id,
|
||||
value,
|
||||
data
|
||||
);
|
||||
require(
|
||||
callbackReturnValue == ERC1155_RECEIVED,
|
||||
"BAD_RECEIVER_RETURN_VALUE"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @notice Send multiple types of Tokens from a 3rd party in one transfer (with safety call).
|
||||
/// @dev MUST emit TransferBatch event on success.
|
||||
/// Caller must be approved to manage the _from account's tokens (see isApprovedForAll).
|
||||
/// MUST throw if `_to` is the zero address.
|
||||
/// MUST throw if length of `_ids` is not the same as length of `_values`.
|
||||
/// MUST throw if any of the balance of sender for token `_ids` is lower than the respective `_values` sent.
|
||||
/// MUST throw on any other error.
|
||||
/// When transfer is complete, this function MUST check if `_to` is a smart contract (code size > 0).
|
||||
/// If so, it MUST call `onERC1155BatchReceived` on `_to` and revert if the return value
|
||||
/// is not `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`.
|
||||
/// @param from Source addresses
|
||||
/// @param to Target addresses
|
||||
/// @param ids IDs of each token type
|
||||
/// @param values Transfer amounts per token type
|
||||
/// @param data Additional data with no specified format, sent in call to `_to`
|
||||
function safeBatchTransferFrom(
|
||||
address from,
|
||||
address to,
|
||||
uint256[] calldata ids,
|
||||
uint256[] calldata values,
|
||||
bytes calldata data
|
||||
)
|
||||
external
|
||||
{
|
||||
// sanity checks
|
||||
require(
|
||||
to != address(0x0),
|
||||
"CANNOT_TRANSFER_TO_ADDRESS_ZERO"
|
||||
);
|
||||
require(
|
||||
ids.length == values.length,
|
||||
"TOKEN_AND_VALUES_LENGTH_MISMATCH"
|
||||
);
|
||||
|
||||
// Only supporting a global operator approval allows us to do
|
||||
// only 1 check and not to touch storage to handle allowances.
|
||||
require(
|
||||
from == msg.sender || operatorApproval[from][msg.sender] == true,
|
||||
"INSUFFICIENT_ALLOWANCE"
|
||||
);
|
||||
|
||||
// perform transfers
|
||||
for (uint256 i = 0; i < ids.length; ++i) {
|
||||
// Cache value to local variable to reduce read costs.
|
||||
uint256 id = ids[i];
|
||||
uint256 value = values[i];
|
||||
|
||||
if (isNonFungible(id)) {
|
||||
require(
|
||||
value == 1,
|
||||
"AMOUNT_EQUAL_TO_ONE_REQUIRED"
|
||||
);
|
||||
require(
|
||||
nfOwners[id] == from,
|
||||
"NFT_NOT_OWNED_BY_FROM_ADDRESS"
|
||||
);
|
||||
nfOwners[id] = to;
|
||||
} else {
|
||||
balances[id][from] = safeSub(balances[id][from], value);
|
||||
balances[id][to] = safeAdd(balances[id][to], value);
|
||||
}
|
||||
}
|
||||
emit TransferBatch(msg.sender, from, to, ids, values);
|
||||
|
||||
// if `to` is a contract then trigger its callback
|
||||
if (to.isContract()) {
|
||||
bytes4 callbackReturnValue = IERC1155Receiver(to).onERC1155BatchReceived(
|
||||
msg.sender,
|
||||
from,
|
||||
ids,
|
||||
values,
|
||||
data
|
||||
);
|
||||
require(
|
||||
callbackReturnValue == ERC1155_BATCH_RECEIVED,
|
||||
"BAD_RECEIVER_RETURN_VALUE"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @notice Enable or disable approval for a third party ("operator") to manage all of the caller's tokens.
|
||||
/// @dev MUST emit the ApprovalForAll event on success.
|
||||
/// @param operator Address to add to the set of authorized operators
|
||||
/// @param approved True if the operator is approved, false to revoke approval
|
||||
function setApprovalForAll(address operator, bool approved) external {
|
||||
operatorApproval[msg.sender][operator] = approved;
|
||||
emit ApprovalForAll(msg.sender, operator, approved);
|
||||
}
|
||||
|
||||
/// @notice Queries the approval status of an operator for a given owner.
|
||||
/// @param owner The owner of the Tokens
|
||||
/// @param operator Address of authorized operator
|
||||
/// @return True if the operator is approved, false if not
|
||||
function isApprovedForAll(address owner, address operator) external view returns (bool) {
|
||||
return operatorApproval[owner][operator];
|
||||
}
|
||||
|
||||
/// @notice Get the balance of an account's Tokens.
|
||||
/// @param owner The address of the token holder
|
||||
/// @param id ID of the Token
|
||||
/// @return The _owner's balance of the Token type requested
|
||||
function balanceOf(address owner, uint256 id) external view returns (uint256) {
|
||||
if (isNonFungibleItem(id)) {
|
||||
return nfOwners[id] == owner ? 1 : 0;
|
||||
}
|
||||
return balances[id][owner];
|
||||
}
|
||||
|
||||
/// @notice Get the balance of multiple account/token pairs
|
||||
/// @param owners The addresses of the token holders
|
||||
/// @param ids ID of the Tokens
|
||||
/// @return The _owner's balance of the Token types requested
|
||||
function balanceOfBatch(address[] calldata owners, uint256[] calldata ids) external view returns (uint256[] memory balances_) {
|
||||
// sanity check
|
||||
require(
|
||||
owners.length == ids.length,
|
||||
"OWNERS_AND_IDS_MUST_HAVE_SAME_LENGTH"
|
||||
);
|
||||
|
||||
// get balances
|
||||
balances_ = new uint256[](owners.length);
|
||||
for (uint256 i = 0; i < owners.length; ++i) {
|
||||
uint256 id = ids[i];
|
||||
if (isNonFungibleItem(id)) {
|
||||
balances_[i] = nfOwners[id] == owners[i] ? 1 : 0;
|
||||
} else {
|
||||
balances_[i] = balances[id][owners[i]];
|
||||
}
|
||||
}
|
||||
|
||||
return balances_;
|
||||
}
|
||||
}
|
@@ -1,173 +0,0 @@
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/SafeMath.sol";
|
||||
import "./ERC1155.sol";
|
||||
import "./interfaces/IERC1155Mintable.sol";
|
||||
|
||||
|
||||
/// @dev Mintable form of ERC1155
|
||||
/// Shows how easy it is to mint new items
|
||||
contract ERC1155Mintable is
|
||||
IERC1155Mintable,
|
||||
ERC1155
|
||||
{
|
||||
|
||||
/// token nonce
|
||||
uint256 internal nonce;
|
||||
|
||||
/// mapping from token to creator
|
||||
mapping (uint256 => address) public creators;
|
||||
|
||||
/// mapping from token to max index
|
||||
mapping (uint256 => uint256) public maxIndex;
|
||||
|
||||
/// asserts token is owned by msg.sender
|
||||
modifier creatorOnly(uint256 _id) {
|
||||
require(creators[_id] == msg.sender);
|
||||
_;
|
||||
}
|
||||
|
||||
/// @dev creates a new token
|
||||
/// @param uri URI of token
|
||||
/// @param isNF is non-fungible token
|
||||
/// @return type_ of token (a unique identifier)
|
||||
function create(
|
||||
string calldata uri,
|
||||
bool isNF
|
||||
)
|
||||
external
|
||||
returns (uint256 type_)
|
||||
{
|
||||
// Store the type in the upper 128 bits
|
||||
type_ = (++nonce << 128);
|
||||
|
||||
// Set a flag if this is an NFI.
|
||||
if (isNF) {
|
||||
type_ = type_ | TYPE_NF_BIT;
|
||||
}
|
||||
|
||||
// This will allow restricted access to creators.
|
||||
creators[type_] = msg.sender;
|
||||
|
||||
// emit a Transfer event with Create semantic to help with discovery.
|
||||
emit TransferSingle(
|
||||
msg.sender,
|
||||
address(0x0),
|
||||
address(0x0),
|
||||
type_,
|
||||
0
|
||||
);
|
||||
|
||||
if (bytes(uri).length > 0) {
|
||||
emit URI(uri, type_);
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev mints fungible tokens
|
||||
/// @param id token type
|
||||
/// @param to beneficiaries of minted tokens
|
||||
/// @param quantities amounts of minted tokens
|
||||
function mintFungible(
|
||||
uint256 id,
|
||||
address[] calldata to,
|
||||
uint256[] calldata quantities
|
||||
)
|
||||
external
|
||||
creatorOnly(id)
|
||||
{
|
||||
// sanity checks
|
||||
require(
|
||||
isFungible(id),
|
||||
"TRIED_TO_MINT_FUNGIBLE_FOR_NON_FUNGIBLE_TOKEN"
|
||||
);
|
||||
|
||||
// mint tokens
|
||||
for (uint256 i = 0; i < to.length; ++i) {
|
||||
// cache to reduce number of loads
|
||||
address dst = to[i];
|
||||
uint256 quantity = quantities[i];
|
||||
|
||||
// Grant the items to the caller
|
||||
balances[id][dst] = safeAdd(quantity, balances[id][dst]);
|
||||
|
||||
// Emit the Transfer/Mint event.
|
||||
// the 0x0 source address implies a mint
|
||||
// It will also provide the circulating supply info.
|
||||
emit TransferSingle(
|
||||
msg.sender,
|
||||
address(0x0),
|
||||
dst,
|
||||
id,
|
||||
quantity
|
||||
);
|
||||
|
||||
// if `to` is a contract then trigger its callback
|
||||
if (dst.isContract()) {
|
||||
bytes4 callbackReturnValue = IERC1155Receiver(dst).onERC1155Received(
|
||||
msg.sender,
|
||||
msg.sender,
|
||||
id,
|
||||
quantity,
|
||||
""
|
||||
);
|
||||
require(
|
||||
callbackReturnValue == ERC1155_RECEIVED,
|
||||
"BAD_RECEIVER_RETURN_VALUE"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev mints a non-fungible token
|
||||
/// @param type_ token type
|
||||
/// @param to beneficiaries of minted tokens
|
||||
function mintNonFungible(
|
||||
uint256 type_,
|
||||
address[] calldata to
|
||||
)
|
||||
external
|
||||
creatorOnly(type_)
|
||||
{
|
||||
// No need to check this is a nf type rather than an id since
|
||||
// creatorOnly() will only let a type pass through.
|
||||
require(
|
||||
isNonFungible(type_),
|
||||
"TRIED_TO_MINT_NON_FUNGIBLE_FOR_FUNGIBLE_TOKEN"
|
||||
);
|
||||
|
||||
// Index are 1-based.
|
||||
uint256 index = maxIndex[type_] + 1;
|
||||
|
||||
for (uint256 i = 0; i < to.length; ++i) {
|
||||
// cache to reduce number of loads
|
||||
address dst = to[i];
|
||||
uint256 id = type_ | index + i;
|
||||
|
||||
nfOwners[id] = dst;
|
||||
|
||||
// You could use base-type id to store NF type balances if you wish.
|
||||
// balances[_type][dst] = quantity.safeAdd(balances[_type][dst]);
|
||||
|
||||
emit TransferSingle(msg.sender, address(0x0), dst, id, 1);
|
||||
|
||||
// if `to` is a contract then trigger its callback
|
||||
if (dst.isContract()) {
|
||||
bytes4 callbackReturnValue = IERC1155Receiver(dst).onERC1155Received(
|
||||
msg.sender,
|
||||
msg.sender,
|
||||
id,
|
||||
1,
|
||||
""
|
||||
);
|
||||
require(
|
||||
callbackReturnValue == ERC1155_RECEIVED,
|
||||
"BAD_RECEIVER_RETURN_VALUE"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// record the `maxIndex` of this nft type
|
||||
// this allows us to mint more nft's of this type in a subsequent call.
|
||||
maxIndex[type_] = safeAdd(to.length, maxIndex[type_]);
|
||||
}
|
||||
}
|
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "./mixins/MNonFungibleToken.sol";
|
||||
|
||||
|
||||
contract MixinNonFungibleToken is
|
||||
MNonFungibleToken
|
||||
{
|
||||
/// Use a split bit implementation.
|
||||
/// Store the type in the upper 128 bits..
|
||||
uint256 constant internal TYPE_MASK = uint256(uint128(~0)) << 128;
|
||||
|
||||
/// ..and the non-fungible index in the lower 128
|
||||
uint256 constant internal NF_INDEX_MASK = uint128(~0);
|
||||
|
||||
/// The top bit is a flag to tell if this is a NFI.
|
||||
uint256 constant internal TYPE_NF_BIT = 1 << 255;
|
||||
|
||||
/// mapping of nft to owner
|
||||
mapping (uint256 => address) internal nfOwners;
|
||||
|
||||
/// @dev Returns true if token is non-fungible
|
||||
function isNonFungible(uint256 id) public pure returns(bool) {
|
||||
return id & TYPE_NF_BIT == TYPE_NF_BIT;
|
||||
}
|
||||
|
||||
/// @dev Returns true if token is fungible
|
||||
function isFungible(uint256 id) public pure returns(bool) {
|
||||
return id & TYPE_NF_BIT == 0;
|
||||
}
|
||||
|
||||
/// @dev Returns index of non-fungible token
|
||||
function getNonFungibleIndex(uint256 id) public pure returns(uint256) {
|
||||
return id & NF_INDEX_MASK;
|
||||
}
|
||||
|
||||
/// @dev Returns base type of non-fungible token
|
||||
function getNonFungibleBaseType(uint256 id) public pure returns(uint256) {
|
||||
return id & TYPE_MASK;
|
||||
}
|
||||
|
||||
/// @dev Returns true if input is base-type of a non-fungible token
|
||||
function isNonFungibleBaseType(uint256 id) public pure returns(bool) {
|
||||
// A base type has the NF bit but does not have an index.
|
||||
return (id & TYPE_NF_BIT == TYPE_NF_BIT) && (id & NF_INDEX_MASK == 0);
|
||||
}
|
||||
|
||||
/// @dev Returns true if input is a non-fungible token
|
||||
function isNonFungibleItem(uint256 id) public pure returns(bool) {
|
||||
// A base type has the NF bit but does has an index.
|
||||
return (id & TYPE_NF_BIT == TYPE_NF_BIT) && (id & NF_INDEX_MASK != 0);
|
||||
}
|
||||
|
||||
/// @dev returns owner of a non-fungible token
|
||||
function ownerOf(uint256 id) public view returns (address) {
|
||||
return nfOwners[id];
|
||||
}
|
||||
}
|
@@ -1,152 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
|
||||
/// @title ERC-1155 Multi Token Standard
|
||||
/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1155.md
|
||||
/// Note: The ERC-165 identifier for this interface is 0xd9b67a26.
|
||||
interface IERC1155 {
|
||||
|
||||
/// @dev Either TransferSingle or TransferBatch MUST emit when tokens are transferred,
|
||||
/// including zero value transfers as well as minting or burning.
|
||||
/// Operator will always be msg.sender.
|
||||
/// Either event from address `0x0` signifies a minting operation.
|
||||
/// An event to address `0x0` signifies a burning or melting operation.
|
||||
/// The total value transferred from address 0x0 minus the total value transferred to 0x0 may
|
||||
/// be used by clients and exchanges to be added to the "circulating supply" for a given token ID.
|
||||
/// To define a token ID with no initial balance, the contract SHOULD emit the TransferSingle event
|
||||
/// from `0x0` to `0x0`, with the token creator as `_operator`.
|
||||
event TransferSingle(
|
||||
address indexed operator,
|
||||
address indexed from,
|
||||
address indexed to,
|
||||
uint256 id,
|
||||
uint256 value
|
||||
);
|
||||
|
||||
/// @dev Either TransferSingle or TransferBatch MUST emit when tokens are transferred,
|
||||
/// including zero value transfers as well as minting or burning.
|
||||
///Operator will always be msg.sender.
|
||||
/// Either event from address `0x0` signifies a minting operation.
|
||||
/// An event to address `0x0` signifies a burning or melting operation.
|
||||
/// The total value transferred from address 0x0 minus the total value transferred to 0x0 may
|
||||
/// be used by clients and exchanges to be added to the "circulating supply" for a given token ID.
|
||||
/// To define multiple token IDs with no initial balance, this SHOULD emit the TransferBatch event
|
||||
/// from `0x0` to `0x0`, with the token creator as `_operator`.
|
||||
event TransferBatch(
|
||||
address indexed operator,
|
||||
address indexed from,
|
||||
address indexed to,
|
||||
uint256[] ids,
|
||||
uint256[] values
|
||||
);
|
||||
|
||||
/// @dev MUST emit when an approval is updated.
|
||||
event ApprovalForAll(
|
||||
address indexed owner,
|
||||
address indexed operator,
|
||||
bool approved
|
||||
);
|
||||
|
||||
/// @dev MUST emit when the URI is updated for a token ID.
|
||||
/// URIs are defined in RFC 3986.
|
||||
/// The URI MUST point a JSON file that conforms to the "ERC-1155 Metadata JSON Schema".
|
||||
event URI(
|
||||
string value,
|
||||
uint256 indexed id
|
||||
);
|
||||
|
||||
/// @notice Transfers value amount of an _id from the _from address to the _to address specified.
|
||||
/// @dev MUST emit TransferSingle event on success.
|
||||
/// Caller must be approved to manage the _from account's tokens (see isApprovedForAll).
|
||||
/// MUST throw if `_to` is the zero address.
|
||||
/// MUST throw if balance of sender for token `_id` is lower than the `_value` sent.
|
||||
/// MUST throw on any other error.
|
||||
/// When transfer is complete, this function MUST check if `_to` is a smart contract (code size > 0).
|
||||
/// If so, it MUST call `onERC1155Received` on `_to` and revert if the return value
|
||||
/// is not `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`.
|
||||
/// @param from Source address
|
||||
/// @param to Target address
|
||||
/// @param id ID of the token type
|
||||
/// @param value Transfer amount
|
||||
/// @param data Additional data with no specified format, sent in call to `_to`
|
||||
function safeTransferFrom(
|
||||
address from,
|
||||
address to,
|
||||
uint256 id,
|
||||
uint256 value,
|
||||
bytes calldata data
|
||||
)
|
||||
external;
|
||||
|
||||
/// @notice Send multiple types of Tokens from a 3rd party in one transfer (with safety call).
|
||||
/// @dev MUST emit TransferBatch event on success.
|
||||
/// Caller must be approved to manage the _from account's tokens (see isApprovedForAll).
|
||||
/// MUST throw if `_to` is the zero address.
|
||||
/// MUST throw if length of `_ids` is not the same as length of `_values`.
|
||||
/// MUST throw if any of the balance of sender for token `_ids` is lower than the respective `_values` sent.
|
||||
/// MUST throw on any other error.
|
||||
/// When transfer is complete, this function MUST check if `_to` is a smart contract (code size > 0).
|
||||
/// If so, it MUST call `onERC1155BatchReceived` on `_to` and revert if the return value
|
||||
/// is not `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`.
|
||||
/// @param from Source addresses
|
||||
/// @param to Target addresses
|
||||
/// @param ids IDs of each token type
|
||||
/// @param values Transfer amounts per token type
|
||||
/// @param data Additional data with no specified format, sent in call to `_to`
|
||||
function safeBatchTransferFrom(
|
||||
address from,
|
||||
address to,
|
||||
uint256[] calldata ids,
|
||||
uint256[] calldata values,
|
||||
bytes calldata data
|
||||
)
|
||||
external;
|
||||
|
||||
/// @notice Enable or disable approval for a third party ("operator") to manage all of the caller's tokens.
|
||||
/// @dev MUST emit the ApprovalForAll event on success.
|
||||
/// @param operator Address to add to the set of authorized operators
|
||||
/// @param approved True if the operator is approved, false to revoke approval
|
||||
function setApprovalForAll(address operator, bool approved) external;
|
||||
|
||||
/// @notice Queries the approval status of an operator for a given owner.
|
||||
/// @param owner The owner of the Tokens
|
||||
/// @param operator Address of authorized operator
|
||||
/// @return True if the operator is approved, false if not
|
||||
function isApprovedForAll(address owner, address operator) external view returns (bool);
|
||||
|
||||
/// @notice Get the balance of an account's Tokens.
|
||||
/// @param owner The address of the token holder
|
||||
/// @param id ID of the Token
|
||||
/// @return The _owner's balance of the Token type requested
|
||||
function balanceOf(address owner, uint256 id) external view returns (uint256);
|
||||
|
||||
/// @notice Get the balance of multiple account/token pairs
|
||||
/// @param owners The addresses of the token holders
|
||||
/// @param ids ID of the Tokens
|
||||
/// @return The _owner's balance of the Token types requested
|
||||
function balanceOfBatch(
|
||||
address[] calldata owners,
|
||||
uint256[] calldata ids
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256[] memory balances_);
|
||||
}
|
@@ -1,42 +0,0 @@
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "./IERC1155.sol";
|
||||
|
||||
|
||||
/// @dev Mintable form of ERC1155
|
||||
/// Shows how easy it is to mint new items
|
||||
contract IERC1155Mintable is
|
||||
IERC1155
|
||||
{
|
||||
|
||||
/// @dev creates a new token
|
||||
/// @param uri URI of token
|
||||
/// @param isNF is non-fungible token
|
||||
/// @return _type of token (a unique identifier)
|
||||
function create(
|
||||
string calldata uri,
|
||||
bool isNF
|
||||
)
|
||||
external
|
||||
returns (uint256 type_);
|
||||
|
||||
/// @dev mints fungible tokens
|
||||
/// @param id token type
|
||||
/// @param to beneficiaries of minted tokens
|
||||
/// @param quantities amounts of minted tokens
|
||||
function mintFungible(
|
||||
uint256 id,
|
||||
address[] calldata to,
|
||||
uint256[] calldata quantities
|
||||
)
|
||||
external;
|
||||
|
||||
/// @dev mints a non-fungible token
|
||||
/// @param type_ token type
|
||||
/// @param to beneficiaries of minted tokens
|
||||
function mintNonFungible(
|
||||
uint256 type_,
|
||||
address[] calldata to
|
||||
)
|
||||
external;
|
||||
}
|
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
|
||||
interface IERC1155Receiver {
|
||||
|
||||
/// @notice Handle the receipt of a single ERC1155 token type
|
||||
/// @dev The smart contract calls this function on the recipient
|
||||
/// after a `safeTransferFrom`. This function MAY throw to revert and reject the
|
||||
/// transfer. Return of other than the magic value MUST result in the
|
||||
///transaction being reverted
|
||||
/// Note: the contract address is always the message sender
|
||||
/// @param operator The address which called `safeTransferFrom` function
|
||||
/// @param from The address which previously owned the token
|
||||
/// @param id An array containing the ids of the token being transferred
|
||||
/// @param value An array containing the amount of tokens being transferred
|
||||
/// @param data Additional data with no specified format
|
||||
/// @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
|
||||
function onERC1155Received(
|
||||
address operator,
|
||||
address from,
|
||||
uint256 id,
|
||||
uint256 value,
|
||||
bytes calldata data
|
||||
)
|
||||
external
|
||||
returns(bytes4);
|
||||
|
||||
/// @notice Handle the receipt of multiple ERC1155 token types
|
||||
/// @dev The smart contract calls this function on the recipient
|
||||
/// after a `safeTransferFrom`. This function MAY throw to revert and reject the
|
||||
/// transfer. Return of other than the magic value MUST result in the
|
||||
/// transaction being reverted
|
||||
/// Note: the contract address is always the message sender
|
||||
/// @param operator The address which called `safeTransferFrom` function
|
||||
/// @param from The address which previously owned the token
|
||||
/// @param ids An array containing ids of each token being transferred
|
||||
/// @param values An array containing amounts of each token being transferred
|
||||
/// @param data Additional data with no specified format
|
||||
/// @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
|
||||
function onERC1155BatchReceived(
|
||||
address operator,
|
||||
address from,
|
||||
uint256[] calldata ids,
|
||||
uint256[] calldata values,
|
||||
bytes calldata data
|
||||
)
|
||||
external
|
||||
returns(bytes4);
|
||||
}
|
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
|
||||
contract MNonFungibleToken {
|
||||
|
||||
/// @dev Returns true if token is non-fungible
|
||||
function isNonFungible(uint256 id) public pure returns(bool);
|
||||
|
||||
/// @dev Returns true if token is fungible
|
||||
function isFungible(uint256 _d) public pure returns(bool);
|
||||
|
||||
/// @dev Returns index of non-fungible token
|
||||
function getNonFungibleIndex(uint256 id) public pure returns(uint256);
|
||||
|
||||
/// @dev Returns base type of non-fungible token
|
||||
function getNonFungibleBaseType(uint256 id) public pure returns(uint256);
|
||||
|
||||
/// @dev Returns true if input is base-type of a non-fungible token
|
||||
function isNonFungibleBaseType(uint256 id) public pure returns(bool);
|
||||
|
||||
/// @dev Returns true if input is a non-fungible token
|
||||
function isNonFungibleItem(uint256 id) public pure returns(bool);
|
||||
|
||||
/// @dev returns owner of a non-fungible token
|
||||
function ownerOf(uint256 id) public view returns (address);
|
||||
}
|
@@ -1,126 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "../src/interfaces/IERC1155Receiver.sol";
|
||||
|
||||
|
||||
contract DummyERC1155Receiver is
|
||||
IERC1155Receiver
|
||||
{
|
||||
|
||||
bytes4 constant public ERC1155_RECEIVED = 0xf23a6e61;
|
||||
bytes4 constant public ERC1155_BATCH_RECEIVED = 0xbc197c81;
|
||||
bool internal shouldRejectTransfer;
|
||||
|
||||
event TokenReceived(
|
||||
address operator,
|
||||
address from,
|
||||
uint256 tokenId,
|
||||
uint256 tokenValue,
|
||||
bytes data
|
||||
);
|
||||
|
||||
event BatchTokenReceived(
|
||||
address operator,
|
||||
address from,
|
||||
uint256[] tokenIds,
|
||||
uint256[] tokenValues,
|
||||
bytes data
|
||||
);
|
||||
|
||||
constructor () public {
|
||||
shouldRejectTransfer = false;
|
||||
}
|
||||
|
||||
/// @notice Handle the receipt of a single ERC1155 token type
|
||||
/// @dev The smart contract calls this function on the recipient
|
||||
/// after a `safeTransferFrom`. This function MAY throw to revert and reject the
|
||||
/// transfer. Return of other than the magic value MUST result in the
|
||||
///transaction being reverted
|
||||
/// Note: the contract address is always the message sender
|
||||
/// @param operator The address which called `safeTransferFrom` function
|
||||
/// @param from The address which previously owned the token
|
||||
/// @param id An array containing the ids of the token being transferred
|
||||
/// @param value An array containing the amount of tokens being transferred
|
||||
/// @param data Additional data with no specified format
|
||||
/// @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
|
||||
function onERC1155Received(
|
||||
address operator,
|
||||
address from,
|
||||
uint256 id,
|
||||
uint256 value,
|
||||
bytes calldata data
|
||||
)
|
||||
external
|
||||
returns(bytes4)
|
||||
{
|
||||
if (shouldRejectTransfer) {
|
||||
revert("TRANSFER_REJECTED");
|
||||
}
|
||||
emit TokenReceived(
|
||||
operator,
|
||||
from,
|
||||
id,
|
||||
value,
|
||||
data
|
||||
);
|
||||
return ERC1155_RECEIVED;
|
||||
}
|
||||
|
||||
/// @notice Handle the receipt of multiple ERC1155 token types
|
||||
/// @dev The smart contract calls this function on the recipient
|
||||
/// after a `safeTransferFrom`. This function MAY throw to revert and reject the
|
||||
/// transfer. Return of other than the magic value MUST result in the
|
||||
/// transaction being reverted
|
||||
/// Note: the contract address is always the message sender
|
||||
/// @param operator The address which called `safeTransferFrom` function
|
||||
/// @param from The address which previously owned the token
|
||||
/// @param ids An array containing ids of each token being transferred
|
||||
/// @param values An array containing amounts of each token being transferred
|
||||
/// @param data Additional data with no specified format
|
||||
/// @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
|
||||
function onERC1155BatchReceived(
|
||||
address operator,
|
||||
address from,
|
||||
uint256[] calldata ids,
|
||||
uint256[] calldata values,
|
||||
bytes calldata data
|
||||
)
|
||||
external
|
||||
returns (bytes4)
|
||||
{
|
||||
if (shouldRejectTransfer) {
|
||||
revert("TRANSFER_REJECTED");
|
||||
}
|
||||
emit BatchTokenReceived(
|
||||
operator,
|
||||
from,
|
||||
ids,
|
||||
values,
|
||||
data
|
||||
);
|
||||
return ERC1155_BATCH_RECEIVED;
|
||||
}
|
||||
|
||||
// @dev If set to true then all future transfers will be rejected.
|
||||
function setRejectTransferFlag(bool _shouldRejectTransfer) external {
|
||||
shouldRejectTransfer = _shouldRejectTransfer;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user