Compare commits

..

80 Commits

Author SHA1 Message Date
Fabio Berger
dfeea78615 Publish
- 0x.js@0.38.6
 - @0xproject/abi-gen@0.3.4
 - @0xproject/assert@0.2.14
 - @0xproject/base-contract@0.3.6
 - @0xproject/connect@0.6.17
 - @0xproject/contract-wrappers@0.1.1
 - contracts@2.1.35
 - @0xproject/dev-utils@0.4.6
 - @0xproject/fill-scenarios@0.0.6
 - @0xproject/json-schemas@0.8.3
 - @0xproject/metacoin@0.0.10
 - @0xproject/migrations@0.0.10
 - @0xproject/order-utils@0.0.9
 - @0xproject/order-watcher@0.0.8
 - @0xproject/react-docs-example@0.0.15
 - @0xproject/react-docs@0.0.16
 - @0xproject/react-shared@0.2.3
 - @0xproject/sol-compiler@0.5.4
 - @0xproject/sol-cov@0.1.3
 - @0xproject/sra-report@0.1.6
 - @0xproject/subproviders@0.10.6
 - @0xproject/testnet-faucets@1.0.36
 - @0xproject/typescript-typings@0.4.3
 - @0xproject/utils@0.7.3
 - @0xproject/web3-wrapper@0.7.3
 - @0xproject/website@0.0.39
2018-07-18 15:10:58 +02:00
Fabio Berger
c8b93adcd0 Updated CHANGELOGS 2018-07-18 15:10:52 +02:00
Fabio Berger
2b02ddc588 Merge pull request #885 from 0xProject/bug-fixes
Development Bug Fixes
2018-07-18 14:21:21 +02:00
Fabio Berger
80c7c23151 Add missing dep 2018-07-18 12:55:49 +02:00
Fabio Berger
c068b574e5 Add PR nr. 2018-07-17 18:49:00 +02:00
Fabio Berger
9a20d4e1c9 Update comments 2018-07-17 18:48:53 +02:00
Fabio Berger
cca471a1ce Add back ethers-contracts types for Aquaduct 2018-07-17 17:05:37 +02:00
Fabio Berger
66d63e8f4f Add isVerbose flag to contract-wrappers subscribe methods 2018-07-17 16:55:55 +02:00
Fabio Berger
e678fed55b Fix bug where we accidentally force Blockstream to always call bound methods with latest param 2018-07-17 16:47:10 +02:00
Fabio Berger
9b10349b7c Remove stateLayer option from OrderWatcher 2018-07-17 16:46:06 +02:00
Fabio Berger
48838e1e98 Add .vscode to gitignore 2018-07-10 14:44:49 +02:00
Fabio Berger
eafd666ecd Move sol-compiler to deps to fix type error 2018-07-10 14:44:07 +02:00
Fabio Berger
599554b785 Publish
- 0x.js@0.38.5
 - @0xproject/abi-gen@0.3.3
 - @0xproject/assert@0.2.13
 - @0xproject/base-contract@0.3.5
 - @0xproject/connect@0.6.16
 - @0xproject/contract-wrappers@0.1.0
 - contracts@2.1.34
 - @0xproject/dev-utils@0.4.5
 - @0xproject/fill-scenarios@0.0.5
 - @0xproject/json-schemas@0.8.2
 - @0xproject/metacoin@0.0.9
 - @0xproject/migrations@0.0.9
 - @0xproject/monorepo-scripts@0.2.2
 - @0xproject/order-utils@0.0.8
 - @0xproject/order-watcher@0.0.7
 - @0xproject/react-docs-example@0.0.14
 - @0xproject/react-docs@0.0.15
 - @0xproject/react-shared@0.2.2
 - @0xproject/sol-compiler@0.5.3
 - @0xproject/sol-cov@0.1.2
 - @0xproject/sol-resolver@0.0.8
 - @0xproject/sra-report@0.1.5
 - @0xproject/subproviders@0.10.5
 - @0xproject/testnet-faucets@1.0.35
 - @0xproject/tslint-config@0.4.21
 - @0xproject/types@0.8.2
 - @0xproject/typescript-typings@0.4.2
 - @0xproject/utils@0.7.2
 - @0xproject/web3-wrapper@0.7.2
 - @0xproject/website@0.0.38
2018-07-09 17:24:22 +02:00
Fabio Berger
d57d282cab Updated CHANGELOGS 2018-07-09 17:24:12 +02:00
Fabio Berger
562bef98e2 reset dep cache 2018-07-09 16:57:40 +02:00
Fabio Berger
900f819be8 Use node v9 everywhere 2018-07-09 16:54:34 +02:00
Fabio Berger
e7337c2451 Update yarn.lock 2018-07-09 16:30:46 +02:00
Fabio Berger
dc81193132 Update package so works with node v9 2018-07-09 16:22:46 +02:00
Fabio Berger
fa868b4963 Bump up node version to v9 2018-07-09 16:09:31 +02:00
Fabio Berger
62945ad363 Updated contract-wrappers to use blockstream v5 and stop unsubscribing on blocksteam error 2018-07-09 15:35:08 +02:00
Fabio Berger
61c38d35be Update yarn.lock 2018-07-09 14:14:03 +02:00
Fabio Berger
4e0ec72896 Merge branch 'development' of github.com:0xProject/0x-monorepo into development
* 'development' of github.com:0xProject/0x-monorepo:
  Add missing import
  Extend from DecodedLogArgs and fix type names
  Update ethers typings & changelog
  Back-port order-watcher changes from v2-prototype
  Back-port over utils changes from v2-prototype
2018-07-09 14:10:29 +02:00
Fabio Berger
c28289e9a8 Back-port prePublish fixes 2018-07-09 14:05:29 +02:00
Fabio Berger
e7e9ddf232 remove excess new line 2018-07-09 14:03:17 +02:00
Fabio Berger
f03784ba37 update web3-provider-engine in subproviders 2018-07-09 14:01:47 +02:00
Fabio Berger
2445e23c0b Merge pull request #842 from 0xProject/back-port-v2-prototype
Back-port changes from v2-prototype in order to publish working Order-watcher
2018-07-09 13:53:15 +02:00
Fabio Berger
cd29483187 merge development 2018-07-09 13:52:21 +02:00
Fabio Berger
9bacbdab48 Add missing import 2018-07-09 13:47:33 +02:00
Fabio Berger
551d484fb7 Extend from DecodedLogArgs and fix type names 2018-07-09 13:03:53 +02:00
Fabio Berger
ff66432b83 Update ethers typings & changelog 2018-07-09 12:33:44 +02:00
Fabio Berger
a7ae82a2d1 Back-port order-watcher changes from v2-prototype 2018-07-09 12:31:06 +02:00
Fabio Berger
5f34b5a80e Back-port over utils changes from v2-prototype 2018-07-09 11:48:30 +02:00
Leonid Logvinov
8fca2a8116 Merge pull request #768 from galkin/development
Improve 'web3-provider-engine' typings
2018-06-27 09:24:33 +03:00
Nikita Galkin
5472500119 Improve 'web3-provider-engine' typings 2018-06-25 12:22:54 +03:00
Fabio Berger
8fb5c29b4b Publish
- 0x.js@0.38.4
 - @0xproject/abi-gen@0.3.2
 - @0xproject/assert@0.2.12
 - @0xproject/base-contract@0.3.4
 - @0xproject/connect@0.6.15
 - @0xproject/contract-wrappers@0.0.5
 - contracts@2.1.33
 - @0xproject/dev-utils@0.4.4
 - @0xproject/fill-scenarios@0.0.4
 - @0xproject/json-schemas@0.8.1
 - @0xproject/metacoin@0.0.8
 - @0xproject/migrations@0.0.8
 - @0xproject/monorepo-scripts@0.2.1
 - @0xproject/order-utils@0.0.7
 - @0xproject/order-watcher@0.0.6
 - @0xproject/react-docs-example@0.0.13
 - @0xproject/react-docs@0.0.14
 - @0xproject/react-shared@0.2.1
 - @0xproject/sol-compiler@0.5.2
 - @0xproject/sol-cov@0.1.1
 - @0xproject/sol-resolver@0.0.7
 - @0xproject/sra-report@0.1.4
 - @0xproject/subproviders@0.10.4
 - @0xproject/testnet-faucets@1.0.34
 - @0xproject/tslint-config@0.4.20
 - @0xproject/types@0.8.1
 - @0xproject/typescript-typings@0.4.1
 - @0xproject/utils@0.7.1
 - @0xproject/web3-wrapper@0.7.1
 - @0xproject/website@0.0.37
2018-06-19 10:48:07 +02:00
Fabio Berger
40a061a5ca Updated CHANGELOGS 2018-06-19 10:48:00 +02:00
Fabio Berger
2ef82592a3 Improve error message 2018-06-19 10:36:19 +02:00
Fabio Berger
14b5448d70 Add missing timestamp 2018-06-19 10:35:22 +02:00
Fabio Berger
e7d45e47bf Remove the hotfix for Ethersjs and instead update to the fixed version in all packages 2018-06-19 10:30:49 +02:00
Fabio Berger
28268d4355 Update versions and CHANGELOG's to account for unpublished versions on NPM 2018-06-18 23:18:42 +02:00
Fabio Berger
50fa02c1d1 Small fixes 2018-06-18 23:16:44 +02:00
Fabio Berger
b36ff9103d Merge pull request #718 from 0xProject/improvement/publishing-flow
Improve Pre-publishing flow
2018-06-18 23:01:36 +02:00
Fabio Berger
f032c2466c Use semver package instead of getNextPatchVersion 2018-06-18 19:22:31 +02:00
Fabio Berger
b63ddc9be4 Use semver external librarty instead of semverUtils 2018-06-18 19:00:39 +02:00
Fabio Berger
171430b617 small fixes 2018-06-18 17:58:17 +02:00
Fabio Berger
d561043774 Show all errors of a given kind at once rather then throwing after the first instance is encountered 2018-06-18 17:41:03 +02:00
Fabio Berger
b1871e9ddd fix package.json 2018-06-18 16:57:22 +02:00
Fabio Berger
b4a5e7258c Add the following prePublish checks:
- That package version matches latest NPM package version (including unpublished versions)
- That changelogs are properly formatted (lastEntry version >= currentVersion, no accidental timestamps)
- Checks for and removes any git tags locally and remotely that might be left-over from a previous failed publish

Moved changelog related helpers to changelogUtils and other small cleanup refactors.
2018-06-18 16:55:59 +02:00
Fabio Berger
3c75debdf9 Remove the remove_tags script 2018-06-18 16:46:54 +02:00
Fabio Berger
84d1053f73 Add missing timestamp to web3-wrapper changelog 2018-06-15 17:11:30 +02:00
Fabio Berger
f9df42f5d9 Fix incorrect merges 2018-06-15 16:58:36 +02:00
Leonid Logvinov
4414ef0a0f fix cherry-pick 2018-06-15 16:51:13 +02:00
Leonid Logvinov
b7729ada38 fix cherry-pick 2018-06-15 16:48:29 +02:00
Leonid Logvinov
3da67feeb2 Add prepublish_checks 2018-06-15 16:45:18 +02:00
Leonid Logvinov
8a0d563a32 Add CHANGELOG entry 2018-06-15 16:44:48 +02:00
Leonid Logvinov
612fc4a949 Fix cherry-pick 2018-06-15 16:44:35 +02:00
Fabio Berger
bf915ce403 Merge pull request #709 from 0xProject/feature/pin-deps
[DEVELOPMENT] Ethersjs Issue Hot Fix
2018-06-15 14:14:45 +02:00
Fabio Berger
0f9ea9773e Add lib/test and ganache.log to npmignore 2018-06-15 14:14:30 +02:00
Fabio Berger
447a3a6c26 Fix prettier 2018-06-15 13:53:26 +02:00
Fabio Berger
f1cc16c44d Export OrderWatcherConfig from 0x.js 2018-06-15 13:38:25 +02:00
Fabio Berger
aae16b6343 Add ganache.log to gitignore 2018-06-15 13:37:08 +02:00
Fabio Berger
834e1538d1 Fix typos, versions and other mistypings 2018-06-15 11:48:52 +02:00
Leonid Logvinov
afc489bc2c Pin all external dependencies 2018-06-14 21:25:36 -07:00
Leonid Logvinov
3e061e7364 Fix the is not a function bug caused by breaking patch version https://github.com/ethers-io/ethers.js/issues/201 2018-06-14 12:31:57 -07:00
Leonid Logvinov
6395c2a8b2 Update changelogs 2018-06-04 11:19:59 -07:00
Leonid Logvinov
c8225288cd v0.7.0 2018-06-04 11:15:00 -07:00
Leonid Logvinov
4a108aa67d Set default params and jsonrpc in Web3Wrapper._sendRawPayloadAsync 2018-06-04 11:11:18 -07:00
Leonid Logvinov
fd9b3e0dcf Add default jsonRpcId to raw payloads sent from web3Wrapper 2018-06-01 16:01:04 -07:00
Leonid Logvinov
f94b647e61 Publish
- 0x.js@0.38.3
 - contracts@2.1.32
 - @0xproject/order-watcher@0.0.5
 - @0xproject/sra-report@0.1.3
 - @0xproject/testnet-faucets@1.0.33
 - @0xproject/website@0.0.36
2018-05-29 11:29:07 -07:00
Leonid Logvinov
2eccc3efaf Updated CHANGELOGS 2018-05-29 11:29:01 -07:00
Leonid Logvinov
972341725e Remove UMD bundles from order-watcher 2018-05-29 11:13:50 -07:00
Leonid Logvinov
40b10fd29d Publish
- 0x.js@0.38.2
 - @0xproject/contract-wrappers@0.0.4
 - contracts@2.1.31
 - @0xproject/order-watcher@0.0.4
 - @0xproject/sra-report@0.1.2
 - @0xproject/testnet-faucets@1.0.32
 - @0xproject/website@0.0.35
2018-05-29 11:12:33 -07:00
Leonid Logvinov
b31fcffc76 Updated CHANGELOGS 2018-05-29 11:12:26 -07:00
Leonid Logvinov
ec222ea0cd Remove UMD bundles from contract-wrappers 2018-05-29 11:04:00 -07:00
Leonid Logvinov
5533220da0 Publish
- 0x.js@0.38.1
 - @0xproject/connect@0.6.14
 - @0xproject/contract-wrappers@0.0.3
 - contracts@2.1.30
 - @0xproject/fill-scenarios@0.0.3
 - @0xproject/order-utils@0.0.6
 - @0xproject/order-watcher@0.0.3
 - @0xproject/sra-report@0.1.1
 - @0xproject/testnet-faucets@1.0.31
 - @0xproject/website@0.0.34
2018-05-29 11:01:25 -07:00
Leonid Logvinov
6ee7024457 Updated CHANGELOGS 2018-05-29 11:01:18 -07:00
Fabio Berger
f839ac9c58 Merge branch 'development' of github.com:0xProject/0x-monorepo into development
* 'development' of github.com:0xProject/0x-monorepo:
  Correct documentation variable name
  Publish
  Updated CHANGELOGS
  Updated CHANGELOGS
  Change publish command name
2018-05-22 16:39:15 -07:00
Fabio Berger
1a8b1460a6 Fix signature verification test 2018-05-22 16:38:57 -07:00
Leonid Logvinov
feac0779a4 Merge pull request #608 from prettymuchbryce/docs-fix
Correct documentation variable name
2018-05-22 15:38:27 -07:00
Bryce Neal
d9eeb0421c Correct documentation variable name 2018-05-22 13:37:38 -07:00
2931 changed files with 65336 additions and 206540 deletions

View File

@@ -1,414 +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:
# HACK(albrow): Without this, yarn commands will sometimes
# fail with a "permission denied" error.
name: Set npm path
command: npm set prefix=/home/circleci/npm && echo 'export PATH=$HOME/circleci/npm/bin:$PATH' >> /home/circleci/.bashrc
- run:
name: install-yarn
command: npm install --global yarn@1.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
- save_cache:
key: python-contract-wrappers-{{ .Environment.CIRCLE_SHA1 }}
paths:
- ~/repo/packages/python-contract-wrappers/generated
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 @0x/contracts-utils @0x/contracts-exchange-libs @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-extensions @0x/contracts-asset-proxy @0x/contracts-exchange @0x/contracts-exchange-forwarder @0x/contracts-coordinator @0x/contracts-dev-utils
test-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:circleci @0x/contracts-multisig @0x/contracts-utils @0x/contracts-exchange-libs @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-extensions @0x/contracts-asset-proxy @0x/contracts-exchange @0x/contracts-exchange-forwarder @0x/contracts-coordinator @0x/contracts-dev-utils
test-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-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/asset-buyer
- run: yarn wsrun test:circleci @0x/contract-artifacts
- run: yarn wsrun test:circleci @0x/assert
- run: yarn wsrun test:circleci @0x/base-contract
- run: yarn wsrun test:circleci @0x/connect
- run: yarn wsrun test:circleci @0x/contract-wrappers
- 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-asset-buyer-{{ .Environment.CIRCLE_SHA1 }}
paths:
- ~/repo/packages/asset-buyer/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
environment:
RPC_URL: http://localhost:8545
NETWORK_ID: 50
WHITELIST_ALL_TOKENS: True
command: bash -c "until curl -sfd'{\"method\":\"net_listening\"}' http://localhost:8545 | grep true; do continue; done; forever ts/lib/index.js"
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: installed-py-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
- restore_cache:
key: python-contract-wrappers-{{ .Environment.CIRCLE_SHA1 }}
- run:
command: |
cd python-packages
python -m ensurepip
./pre_install
./install
- save_cache:
key: installed-py-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
paths:
- '/usr/local/bin'
- '/usr/local/lib/python3.7/site-packages'
- run:
command: |
cd python-packages
./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: installed-py-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
- run:
command: |
cd python-packages/order_utils
python -m ensurepip
python -m pip install .
- save_cache:
key: installed-py-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
paths:
- '/usr/local/bin'
- '/usr/local/lib/python3.7/site-packages'
- '.eggs'
- '.mypy_cache'
- '.pytest_cache'
- '.tox'
- run:
command: |
cd python-packages/order_utils
tox
static-tests-python:
working_directory: ~/repo
docker:
- image: 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: installed-py-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
- restore_cache:
key: python-contract-wrappers-{{ .Environment.CIRCLE_SHA1 }}
- run:
command: |
python -m ensurepip
cd python-packages
./pre_install
./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-asset-buyer-{{ .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:9
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-v1-{{ checksum "package.json" }}
- run:
name: yarn
command: yarn --frozen-lockfile
- save_cache:
key: dependency-cache-v1-{{ 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:9
working_directory: ~/repo
steps:
- restore_cache:
keys:
- repo-{{ .Environment.CIRCLE_SHA1 }}
- run: yarn test:installation
test-0xjs:
docker:
- image: circleci/node:9
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:9
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:9
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:9
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:9
steps:
- restore_cache:
keys:
- repo-{{ .Environment.CIRCLE_SHA1 }}
- run: yarn lerna:run lint
prettier:
working_directory: ~/repo
docker:
- image: circleci/node:9
steps:
- restore_cache:
keys:
- repo-{{ .Environment.CIRCLE_SHA1 }}
- run: yarn prettier:ci
submit-coverage:
docker:
- image: circleci/node:9
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
# TODO(albrow): Tests always fail on Geth right now because our fork
# is outdated. Uncomment once we have updated our Geth fork.
# - test-contracts-geth:
# requires:
# - build
- test-rest:
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:
requires:
- build
# skip python tox run for now, as we don't yet have multiple test environments to support.
#- test-rest-python
- test-0xjs:
requires:
- build
- test-contracts:
requires:
- build
- test-sol-compiler:
requires:
- build
- test-rest:
requires:
- build
- prettier:
requires:
- build
- lint:
requires:
- build
- submit-coverage:
requires:
- test-0xjs
- test-sol-compiler
- test-rest
- test-contracts

6
.gitattributes vendored
View File

@@ -1,7 +1 @@
*.sol linguist-language=Solidity
# Automatically collapse generated files in GitHub.
*.svg linguist-generated=true
packages/contract-artifacts/artifacts/*json linguist-generated=true
packages/abi-gen-wrappers/src/generated-wrappers/*.ts liguist-generated=true

View File

@@ -1,43 +0,0 @@
python: ['python-packages']
contracts: ['contracts']
@0x/sol-doc: ['packages/sol-doc']
@0x/sol-resolver: ['packages/sol-resolver']
@0x/contracts-gen: ['packages/contracts-gen']
@0x/sra-spec: ['packages/sra-spec']
@0x/subproviders: ['packages/subproviders']
@0x/contract-addresses: ['packages/contract-addresses']
@0x/migrations: ['packages/migrations']
@0x/web3-wrapper: ['packages/web3-wrapper']
@0x/sol-compiler: ['packages/sol-compiler']
@0x/types: ['packages/types']
@0x/instant: ['packages/instant']
@0x/abi-gen-templates: ['packages/abi-gen-templates']
@0x/abi-gen: ['packages/abi-gen']
@0x/website: ['packages/website']
@0x/sol-coverage: ['packages/sol-coverage']
@0x/sol-profiler: ['packages/sol-profiler']
@0x/sol-trace: ['packages/sol-trace']
@0x/sol-tracing-utils: ['packages/sol-tracing-utils']
@0x/utils: ['packages/utils']
@0x/tslint-config: ['packages/tslint-config']
@0x/asset-buyer: ['packages/asset-buyer']
@0x/order-watcher: ['packages/order-watcher']
@0x/react-docs: ['packages/react-docs']
@0x/order-utils: ['packages/order-utils']
@0x/react-shared: ['packages/react-shared']
@0x/assert: ['packages/assert']
@0x/base-contract: ['packages/base-contract']
@0x/typescript-typings: ['packages/typescript-typings']
0x.js: ['packages/0x.js']
@0x/abi-gen-wrappers: ['packages/abi-gen-wrappers']
@0x/metacoin: ['packages/metacoin']
@0x/contract-artifacts: ['packages/contract-artifacts']
@0x/dev-utils: ['packages/dev-utils']
@0x/contract-wrappers: ['packages/contract-wrappers']
@0x/json-schemas: ['packages/json-schemas']
@0x/ethereum-types: ['ethereum-types']
@0x/connect: ['packages/connect']
@0x/fill-scenarios: ['packages/fill-scenarios']
@0x/dev-tools-pages: ['packages/dev-tools-pages']
@0x/testnet-faucets: ['packages/testnet-faucets']
@0x/monorepo-scripts: ['packages/monorepo-scripts']

19
.github/stale.yml vendored
View File

@@ -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.

56
.gitignore vendored
View File

@@ -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
@@ -76,48 +71,25 @@ TODO.md
.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/
contracts/dev-utils/generated-artifacts/
packages/sol-tracing-utils/test/fixtures/artifacts/
packages/sol-cov/test/fixtures/artifacts/
packages/metacoin/artifacts/
python-packages/contract_artifacts/src/zero_ex/contract_artifacts/artifacts/
packages/order-watcher/test/artifacts/
packages/contract-wrappers/test/artifacts/
# generated contract wrappers
packages/abi-gen-wrappers/src/generated-wrappers/
packages/python-contract-wrappers/generated/
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/
contracts/dev-utils/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
# cli test output
packages/abi-gen/test-cli/output
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/
@@ -125,12 +97,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

View File

@@ -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
/contracts/dev-utils/generated-wrappers
/contracts/dev-utils/generated-artifacts
/packages/abi-gen/test-cli/output
/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

View File

@@ -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/ @feuGeneA
packages/base-contract/ @xianny
packages/connect/ @fragosti
packages/abi-gen-templates/ @feuGeneA @xianny
packages/contract-addresses/ @albrow
packages/contract-artifacts/ @albrow
packages/dev-utils/ @LogvinovLeon @fabioberger
packages/devnet/ @albrow
packages/ethereum-types/ @LogvinovLeon
packages/metacoin/ @LogvinovLeon
packages/monorepo-scripts/ @fabioberger
packages/order-utils/ @fabioberger @LogvinovLeon
packages/python-contract-wrappers/ @feuGeneA
packages/sol-compiler/ @LogvinovLeon
packages/sol-coverage/ @LogvinovLeon
packages/sol-profiler/ @LogvinovLeon
packages/sol-trace/ @LogvinovLeon
packages/sol-tracing-utils/ @LogvinovLeon
packages/sol-resolver/ @LogvinovLeon
packages/subproviders/ @fabioberger @dekz
packages/verdaccio/ @albrow
packages/web3-wrapper/ @LogvinovLeon @fabioberger
python-packages/ @feuGeneA
packages/utils/ @hysz
# Protocol/smart contracts
contracts/ @abandeali1 @hysz

View File

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

View File

@@ -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`.

View File

@@ -2,7 +2,7 @@
<!--- Before submitting please check to see if this issue was already reported -->
<!--- Prefix your issue title with the package name it relates to (e.g., `0x.js: ` or `general:`) -->
<!--- Provide a general summary of the issue in the Title above -->
## Expected Behavior
@@ -46,14 +46,10 @@
<!--- Include as many relevant details about the environment you experienced the bug in -->
| Package | Version |
| ------: | :------ |
<!-- For example:
| `0x.js` | 2.0.4 |
| `Exchange Contract` | v2 |
-->
| Package | Version |
| ------------------: | :------ |
| `0x.js` | 0.25.0 |
| `Exchange Contract` | v1 |
| Network |
| ------- |

View File

@@ -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.

View File

@@ -1,26 +1,41 @@
<!--- Thank you for taking the time to submit a Pull Request -->
<!--- Provide a general summary of the issue in the Title above -->
## Description
<!--- Describe your changes in detail -->
## Testing instructions
## Motivation and Context
<!--- Please describe how reviewers can test your changes -->
<!--- Why is this change required? What problem does it solve? -->
<!--- If it fixes an open issue, please link to the issue here. -->
## How Has This Been Tested?
<!--- Please describe in detail how you tested your changes. -->
<!--- Include details of your testing environment, and the tests you ran to -->
<!--- see how your change affects other areas of the code, etc. -->
## Types of changes
<!--- What types of changes does your code introduce? Uncomment all the bullets that apply: -->
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
<!-- * Bug fix (non-breaking change which fixes an issue) -->
<!-- * New feature (non-breaking change which adds functionality) -->
<!-- * Breaking change (fix or feature that would cause existing functionality to change) -->
* [ ] Bug fix (non-breaking change which fixes an issue)
* [ ] New feature (non-breaking change which adds functionality)
* [ ] Breaking change (fix or feature that would cause existing functionality to change)
## Checklist:
<!--- The following points should be used to indicate the progress of your PR. Put an `x` in all the boxes that apply right now, and come back over time and check them off as you make progress. If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
- [ ] Prefix PR title with `[WIP]` if necessary.
- [ ] Add tests to cover changes as needed.
- [ ] Update documentation as needed.
- [ ] Add new entries to the relevant CHANGELOG.jsons.
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
* [ ] Change requires a change to the documentation.
* [ ] Added tests to cover my changes.
* [ ] Added new entries to the relevant CHANGELOG.jsons.
* [ ] Labeled this PR with the 'WIP' label if it is a work in progress.
* [ ] Labeled this PR with the labels corresponding to the changed package.

162
README.md
View File

@@ -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
[![CircleCI](https://circleci.com/gh/0xProject/0x-monorepo.svg?style=svg&circle-token=61bf7cd8c9b4e11b132089dfcffdd1be277d1e0c)](https://circleci.com/gh/0xProject/0x-monorepo)
[![Coverage Status](https://coveralls.io/repos/github/0xProject/0x-monorepo/badge.svg?branch=development)](https://coveralls.io/github/0xProject/0x-monorepo?branch=development)
@@ -18,105 +15,60 @@ If you're developing on 0x now or are interested in using 0x infrastructure in t
[![Join the chat at https://gitter.im/0xProject/Lobby](https://badges.gitter.im/0xProject/Lobby.svg)](https://gitter.im/0xProject/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
## 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) | [![npm](https://img.shields.io/npm/v/0x.js.svg)](https://www.npmjs.com/package/0x.js) | A Javascript library for interacting with the 0x protocol |
| [`@0xproject/abi-gen`](/packages/abi-gen) | [![npm](https://img.shields.io/npm/v/@0xproject/abi-gen.svg)](https://www.npmjs.com/package/@0xproject/abi-gen) | Tool to generate TS wrappers from smart contract ABIs |
| [`@0xproject/assert`](/packages/assert) | [![npm](https://img.shields.io/npm/v/@0xproject/assert.svg)](https://www.npmjs.com/package/@0xproject/assert) | Type and schema assertions used by our packages |
| [`@0xproject/base-contract`](/packages/base-contract) | [![npm](https://img.shields.io/npm/v/@0xproject/base-contract.svg)](https://www.npmjs.com/package/@0xproject/base-contract) | BaseContract used by auto-generated `abi-gen` wrapper contracts |
| [`@0xproject/connect`](/packages/connect) | [![npm](https://img.shields.io/npm/v/@0xproject/connect.svg)](https://www.npmjs.com/package/@0xproject/connect) | A Javascript library for interacting with the Standard Relayer API |
| [`@0xproject/sol-compiler`](/packages/sol-compiler) | [![npm](https://img.shields.io/npm/v/@0xproject/sol-compiler.svg)](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) | [![npm](https://img.shields.io/npm/v/@0xproject/dev-utils.svg)](https://www.npmjs.com/package/@0xproject/dev-utils) | Dev utils to be shared across 0x projects and packages |
| [`@0xproject/json-schemas`](/packages/json-schemas) | [![npm](https://img.shields.io/npm/v/@0xproject/json-schemas.svg)](https://www.npmjs.com/package/@0xproject/json-schemas) | 0x-related json schemas |
| [`@0xproject/monorepo-scripts`](/packages/monorepo-scripts) | [![npm](https://img.shields.io/npm/v/@0xproject/monorepo-scripts.svg)](https://www.npmjs.com/package/@0xproject/monorepo-scripts) | Monorepo scripts |
| [`@0xproject/react-docs`](/packages/react-docs) | [![npm](https://img.shields.io/npm/v/@0xproject/react-docs.svg)](https://www.npmjs.com/package/@0xproject/react-docs) | React documentation component for rendering TypeDoc & Doxity generated JSON |
| [`@0xproject/react-shared`](/packages/react-shared) | [![npm](https://img.shields.io/npm/v/@0xproject/react-shared.svg)](https://www.npmjs.com/package/@0xproject/react-shared) | 0x shared react components |
| [`@0xproject/sra-report`](/packages/sra-report) | [![npm](https://img.shields.io/npm/v/@0xproject/sra-report.svg)](https://www.npmjs.com/package/@0xproject/sra-report) | Generate reports for standard relayer API compliance |
| [`@0xproject/sol-cov`](/packages/sol-cov) | [![npm](https://img.shields.io/npm/v/@0xproject/sol-cov.svg)](https://www.npmjs.com/package/@0xproject/sol-cov) | Solidity test coverage tool |
| [`@0xproject/subproviders`](/packages/subproviders) | [![npm](https://img.shields.io/npm/v/@0xproject/subproviders.svg)](https://www.npmjs.com/package/@0xproject/subproviders) | Useful web3 subproviders (e.g LedgerSubprovider) |
| [`@0xproject/tslint-config`](/packages/tslint-config) | [![npm](https://img.shields.io/npm/v/@0xproject/tslint-config.svg)](https://www.npmjs.com/package/@0xproject/tslint-config) | Custom 0x development TSLint rules |
| [`@0xproject/types`](/packages/types) | [![npm](https://img.shields.io/npm/v/@0xproject/types.svg)](https://www.npmjs.com/package/@0xproject/types) | Shared type declarations |
| [`@0xproject/typescript-typings`](/packages/typescript-typings) | [![npm](https://img.shields.io/npm/v/@0xproject/typescript-typings.svg)](https://www.npmjs.com/package/@0xproject/typescript-typings) | Repository of types for external packages |
| [`@0xproject/utils`](/packages/utils) | [![npm](https://img.shields.io/npm/v/@0xproject/utils.svg)](https://www.npmjs.com/package/@0xproject/utils) | Shared utilities |
| [`@0xproject/web3-wrapper`](/packages/web3-wrapper) | [![npm](https://img.shields.io/npm/v/@0xproject/web3-wrapper.svg)](https://www.npmjs.com/package/@0xproject/web3-wrapper) | Web3 wrapper |
### Python Packages
### Private Packages
| Package | Version | Description |
| -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
| [`0x-contract-addresses`](/python-packages/contract_addresses) | [![PyPI](https://img.shields.io/pypi/v/0x-contract-addresses.svg)](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) | [![PyPI](https://img.shields.io/pypi/v/0x-contract-artifacts.svg)](https://pypi.org/project/0x-contract-artifacts/) | 0x smart contract compilation artifacts |
| [`0x-json-schemas`](/python-packages/json_schemas) | [![PyPI](https://img.shields.io/pypi/v/0x-json-schemas.svg)](https://pypi.org/project/0x-json-schemas/) | 0x-related JSON schemas |
| [`0x-order-utils`](/python-packages/order_utils) | [![PyPI](https://img.shields.io/pypi/v/0x-order-utils.svg)](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) | [![PyPI](https://img.shields.io/pypi/v/0x-sra-client.svg)](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) | [![npm](https://img.shields.io/npm/v/@0x/contracts-asset-proxy.svg)](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) | [![npm](https://img.shields.io/npm/v/@0x/contracts-erc20.svg)](https://www.npmjs.com/package/@0x/contracts-erc20) | Implementations of various ERC20 tokens |
| [`@0x/contracts-erc721`](/contracts/erc721) | [![npm](https://img.shields.io/npm/v/@0x/contracts-erc721.svg)](https://www.npmjs.com/package/@0x/contracts-erc721) | Implementations of various ERC721 tokens |
| [`@0x/contracts-erc1155`](/contracts/erc1155) | [![npm](https://img.shields.io/npm/v/@0x/contracts-erc1155.svg)](https://www.npmjs.com/package/@0x/contracts-erc1155) | Implementations of various ERC1155 tokens |
| [`@0x/contracts-exchange`](/contracts/exchange) | [![npm](https://img.shields.io/npm/v/@0x/contracts-exchange.svg)](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) | [![npm](https://img.shields.io/npm/v/@0x/contracts-exchange-forwarder.svg)](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) | [![npm](https://img.shields.io/npm/v/@0x/contracts-exchange-libs.svg)](https://www.npmjs.com/package/@0x/contracts-exchange-libs) | Protocol specific libraries used within the [`Exchange`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#exchange) contract |
| [`@0x/contracts-extensions`](/contracts/extensions) | [![npm](https://img.shields.io/npm/v/@0x/contracts-extensions.svg)](https://www.npmjs.com/package/@0x/contracts-extensions) | Contracts that interact with and extend the functionality of the core protocol |
| [`@0x/contracts-multisig`](/contracts/multisig) | [![npm](https://img.shields.io/npm/v/@0x/contracts-multisig.svg)](https://www.npmjs.com/package/@0x/contracts-multisig) | Various implementations of multisignature wallets, including the [`AssetProxyOwner`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#assetproxyowner) contract that has permissions to upgrade the protocol |
| [`@0x/contracts-test-utils`](/contracts/test-utils) | [![npm](https://img.shields.io/npm/v/@0x/contracts-test-utils.svg)](https://www.npmjs.com/package/@0x/contracts-test-utils) | Typescript/Javascript shared utilities used for testing contracts |
| [`@0x/contracts-utils`](/contracts/utils) | [![npm](https://img.shields.io/npm/v/@0x/contracts-utils.svg)](https://www.npmjs.com/package/@0x/contracts-utils) | Generic libraries and utilities used throughout all of the contracts |
| [`@0x/contracts-coordinator`](/contracts/coordinator) | [![npm](https://img.shields.io/npm/v/@0x/contracts-coordinator.svg)](https://www.npmjs.com/package/@0x/contracts-coordinator) | A contract that allows users to execute 0x transactions with permission from a Coordinator |
| [`@0x/contracts-dev-utils`](/contracts/dev-utils) | [![npm](https://img.shields.io/npm/v/@0x/contracts-dev-utils.svg)](https://www.npmjs.com/package/@0x/contracts-dev-utils) | A contract contains utility functions for developers (such as validating many orders using a single eth_call) |
### Typescript/Javascript Packages
#### 0x-specific packages
| Package | Version | Description |
| -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
| [`0x.js`](/packages/0x.js) | [![npm](https://img.shields.io/npm/v/0x.js.svg)](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) | [![npm](https://img.shields.io/npm/v/@0x/contract-addresses.svg)](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) | [![npm](https://img.shields.io/npm/v/@0x/contract-wrappers.svg)](https://www.npmjs.com/package/@0x/contract-wrappers) | JS/TS wrappers for interacting with the 0x smart contracts |
| [`@0x/order-utils`](/packages/order-utils) | [![npm](https://img.shields.io/npm/v/@0x/order-utils.svg)](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) | [![npm](https://img.shields.io/npm/v/@0x/json-schemas.svg)](https://www.npmjs.com/package/@0x/json-schemas) | 0x-related JSON schemas |
| [`@0x/order-watcher`](/packages/order-watcher) | [![npm](https://img.shields.io/npm/v/@0x/order-watcher.svg)](https://www.npmjs.com/package/@0x/order-watcher) | An order watcher daemon that watches for order validity |
| [`@0x/migrations`](/packages/migrations) | [![npm](https://img.shields.io/npm/v/@0x/migrations.svg)](https://www.npmjs.com/package/@0x/migrations) | Migration tool for deploying 0x smart contracts on private testnets |
| [`@0x/contract-artifacts`](/packages/contract-artifacts) | [![npm](https://img.shields.io/npm/v/@0x/contract-artifacts.svg)](https://www.npmjs.com/package/@0x/contract-artifacts) | 0x smart contract compilation artifacts |
| [`@0x/abi-gen-wrappers`](/packages/abi-gen-wrappers) | [![npm](https://img.shields.io/npm/v/@0x/abi-gen-wrappers.svg)](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) | [![npm](https://img.shields.io/npm/v/@0x/sra-spec.svg)](https://www.npmjs.com/package/@0x/sra-spec) | OpenAPI specification for the Standard Relayer API |
| [`@0x/connect`](/packages/connect) | [![npm](https://img.shields.io/npm/v/@0x/connect.svg)](https://www.npmjs.com/package/@0x/connect) | An HTTP/WS client for interacting with the Standard Relayer API |
| [`@0x/asset-buyer`](/packages/asset-buyer) | [![npm](https://img.shields.io/npm/v/@0x/asset-buyer.svg)](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) | [![npm](https://img.shields.io/npm/v/@0x/web3-wrapper.svg)](https://www.npmjs.com/package/@0x/web3-wrapper) | An Ethereum JSON RPC client |
| [`@0x/sol-compiler`](/packages/sol-compiler) | [![npm](https://img.shields.io/npm/v/@0x/sol-compiler.svg)](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) | [![npm](https://img.shields.io/npm/v/@0x/sol-coverage.svg)](https://www.npmjs.com/package/@0x/sol-coverage) | A solidity test coverage tool |
| [`@0x/sol-profiler`](/packages/sol-profiler) | [![npm](https://img.shields.io/npm/v/@0x/sol-profiler.svg)](https://www.npmjs.com/package/@0x/sol-profiler) | A solidity gas cost profiler |
| [`@0x/sol-trace`](/packages/sol-trace) | [![npm](https://img.shields.io/npm/v/@0x/sol-trace.svg)](https://www.npmjs.com/package/@0x/sol-trace) | A solidity stack trace tool |
| [`@0x/sol-resolver`](/packages/sol-resolver) | [![npm](https://img.shields.io/npm/v/@0x/sol-resolver.svg)](https://www.npmjs.com/package/@0x/sol-resolver) | Import resolver for smart contracts dependencies |
| [`@0x/subproviders`](/packages/subproviders) | [![npm](https://img.shields.io/npm/v/@0x/subproviders.svg)](https://www.npmjs.com/package/@0x/subproviders) | Web3 provider middlewares (e.g. LedgerSubprovider) |
| [`@0x/sol-doc`](/packages/sol-doc) | [![npm](https://img.shields.io/npm/v/@0x/sol-doc.svg)](https://www.npmjs.com/package/@0x/sol-doc) | Solidity documentation generator |
#### Utilities
| Package | Version | Description |
| -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| [`@0x/abi-gen`](/packages/abi-gen) | [![npm](https://img.shields.io/npm/v/@0x/abi-gen.svg)](https://www.npmjs.com/package/@0x/abi-gen) | Tool to generate TS wrappers from smart contract ABIs |
| [`@0x/tslint-config`](/packages/tslint-config) | [![npm](https://img.shields.io/npm/v/@0x/tslint-config.svg)](https://www.npmjs.com/package/@0x/tslint-config) | Custom TSLint rules used by the 0x core team |
| [`@0x/types`](/packages/types) | [![npm](https://img.shields.io/npm/v/@0x/types.svg)](https://www.npmjs.com/package/@0x/types) | Shared type declarations |
| [`@0x/typescript-typings`](/packages/typescript-typings) | [![npm](https://img.shields.io/npm/v/@0x/typescript-typings.svg)](https://www.npmjs.com/package/@0x/typescript-typings) | Repository of types for external packages |
| [`@0x/utils`](/packages/utils) | [![npm](https://img.shields.io/npm/v/@0x/utils.svg)](https://www.npmjs.com/package/@0x/utils) | Shared utilities |
| [`@0x/react-docs`](/packages/react-docs) | [![npm](https://img.shields.io/npm/v/@0x/react-docs.svg)](https://www.npmjs.com/package/@0x/react-docs) | React documentation component for rendering TypeDoc & sol-doc generated JSON |
| [`@0x/react-shared`](/packages/react-shared) | [![npm](https://img.shields.io/npm/v/@0x/react-shared.svg)](https://www.npmjs.com/package/@0x/react-shared) | 0x shared react components |
| [`@0x/assert`](/packages/assert) | [![npm](https://img.shields.io/npm/v/@0x/assert.svg)](https://www.npmjs.com/package/@0x/assert) | Type and schema assertions used by our packages |
| [`@0x/base-contract`](/packages/base-contract) | [![npm](https://img.shields.io/npm/v/@0x/base-contract.svg)](https://www.npmjs.com/package/@0x/base-contract) | BaseContract used by auto-generated `abi-gen` wrapper contracts |
| [`@0x/dev-utils`](/packages/dev-utils) | [![npm](https://img.shields.io/npm/v/@0x/dev-utils.svg)](https://www.npmjs.com/package/@0x/dev-utils) | Dev utils to be shared across 0x packages |
| [`@0x/fill-scenarios`](/packages/fill-scenarios) | [![npm](https://img.shields.io/npm/v/@0x/fill-scenarios.svg)](https://www.npmjs.com/package/@0x/fill-scenarios) | 0x order fill scenario generator |
#### Private Packages
| 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
@@ -127,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
@@ -150,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
@@ -173,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
@@ -229,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
```

View File

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

View File

@@ -1,16 +0,0 @@
#### Deployed Contract Packages
| Contract | Package | Version | Git Tag |
| --------------- | ------------------------------------------------------------------- | -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| AssetProxyOwner | [`@0x/contracts-multisig`](/contracts/multisig) | [v1.0.2](https://www.npmjs.com/package/@0x/contracts-multisig/v/1.0.2) | [@0x/contracts-multisig@1.0.2](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-multisig@1.0.2) |
| ERC20Proxy | [`@0x/contracts-asset-proxy`](/contracts/asset-proxy) | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-asset-proxy/v/1.0.1) | [@0x/contracts-asset-proxy@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-asset-proxy@1.0.1) |
| ERC721Proxy | [`@0x/contracts-asset-proxy`](/contracts/asset-proxy) | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-asset-proxy/v/1.0.1) | [@0x/contracts-asset-proxy@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-asset-proxy@1.0.1) |
| Exchange | [`@0x/contracts-exchange`](/contracts/exchange) | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-exchange/v/1.0.1) | [@0x/contracts-exchange@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-exchange@1.0.1) |
| DutchAuction | [`@0x/contracts-extensions`](/contracts/extensions) | [v1.0.2](https://www.npmjs.com/package/@0x/contracts-extensions/v/1.0.2) | [@0x/contracts-extensions@1.0.2](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-extensions@1.0.2) |
| Forwarder | [`@0x/contracts-exchange-forwarder`](/contracts/exchange-forwarder) | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-exchange-forwarder/v/1.0.1) | [@0x/contracts-exchange-forwarder@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-exchange-forwarder@1.0.1) |
| MultiAssetProxy | [`@0x/contracts-asset-proxy`](/contracts/asset-proxy) | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-asset-proxy/v/1.0.1) | [@0x/contracts-asset-proxy@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-asset-proxy@1.0.1) |
| ZRXToken | [`@0x/contracts-erc20`](/contracts/erc20) | [v1.0.1](https://www.npmjs.com/package/@0x/contracts-erc20/v/1.0.1) | [@0x/contracts-erc20@1.0.1](https://github.com/0xProject/0x-monorepo/releases/tag/@0x/contracts-erc20@1.0.1) |
#### Development
Building solidity files will update the contract artifact in `{package-name}/generated-artifacts/{contract}.json`, but does not automatically update the `abi-gen-wrappers` package, which are generated from the artifact JSON. To ensure consistency, clean and rebuild `abi-gen-wrappers` after any changes to the artifact JSON.

View File

@@ -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.

View File

@@ -1,206 +0,0 @@
[
{
"timestamp": 1563193019,
"version": "2.2.2",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1563047529,
"version": "2.2.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "2.2.0",
"changes": [
{
"note": "Add `LibAssetProxyIds` contract",
"pr": 1835
},
{
"note": "Updated ERC1155 Asset Proxy. Less optimization. More explicit handling of edge cases.",
"pr": 1852
},
{
"note": "Implement StaticCallProxy",
"pr": 1863
}
],
"timestamp": 1563006338
},
{
"timestamp": 1558712885,
"version": "2.1.5",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1557961111,
"version": "2.1.4",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1557799313,
"version": "2.1.3",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "2.1.2",
"changes": [
{
"note": "Update tests to use contract-built-in `awaitTransactionSuccessAsync`",
"pr": 1797
}
],
"timestamp": 1557507213
},
{
"version": "2.1.1",
"changes": [
{
"note": "Dependencies updated"
}
],
"timestamp": 1554997931
},
{
"version": "2.1.0",
"changes": [
{
"note": "Run Web3ProviderEngine without excess block polling",
"pr": 1695
}
],
"timestamp": 1553183790
},
{
"version": "2.0.0",
"changes": [
{
"note": "Do not reexport external dependencies",
"pr": 1682
},
{
"note": "Add ERC1155Proxy",
"pr": 1661
},
{
"note": "Bumped solidity version to ^0.5.5",
"pr": 1701
},
{
"note": "Integration testing for ERC1155Proxy",
"pr": 1673
}
],
"timestamp": 1553091633
},
{
"timestamp": 1551479279,
"version": "1.0.9",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1551299797,
"version": "1.0.8",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1551220833,
"version": "1.0.7",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1551130135,
"version": "1.0.6",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1549733923,
"version": "1.0.5",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "1.0.4",
"changes": [
{
"note": "Dependencies updated"
}
],
"timestamp": 1549547375
},
{
"version": "1.0.3",
"changes": [
{
"note": "Fake publish to enable pinning"
}
],
"timestamp": 1549504360
},
{
"timestamp": 1549452781,
"version": "1.0.2",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1549373905,
"version": "1.0.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "1.0.0",
"changes": [
{
"note": "Move all AssetProxy contracts out of contracts-protocol to new package",
"pr": 1539
}
]
}
]

View File

@@ -1,91 +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.2.2 - _July 15, 2019_
* Dependencies updated
## v2.2.1 - _July 13, 2019_
* Dependencies updated
## v2.2.0 - _July 13, 2019_
* Add `LibAssetProxyIds` contract (#1835)
* Updated ERC1155 Asset Proxy. Less optimization. More explicit handling of edge cases. (#1852)
* Implement StaticCallProxy (#1863)
## v2.1.5 - _May 24, 2019_
* Dependencies updated
## v2.1.4 - _May 15, 2019_
* Dependencies updated
## v2.1.3 - _May 14, 2019_
* Dependencies updated
## v2.1.2 - _May 10, 2019_
* Update tests to use contract-built-in `awaitTransactionSuccessAsync` (#1797)
## v2.1.1 - _April 11, 2019_
* Dependencies updated
## v2.1.0 - _March 21, 2019_
* Run Web3ProviderEngine without excess block polling (#1695)
## v2.0.0 - _March 20, 2019_
* Do not reexport external dependencies (#1682)
* Add ERC1155Proxy (#1661)
* Bumped solidity version to ^0.5.5 (#1701)
* Integration testing for ERC1155Proxy (#1673)
## v1.0.9 - _March 1, 2019_
* Dependencies updated
## v1.0.8 - _February 27, 2019_
* Dependencies updated
## v1.0.7 - _February 26, 2019_
* Dependencies updated
## v1.0.6 - _February 25, 2019_
* Dependencies updated
## v1.0.5 - _February 9, 2019_
* Dependencies updated
## v1.0.4 - _February 7, 2019_
* Dependencies updated
## v1.0.3 - _February 7, 2019_
* Fake publish to enable pinning
## v1.0.2 - _February 6, 2019_
* Dependencies updated
## v1.0.1 - _February 5, 2019_
* Dependencies updated
## v1.0.0 - _Invalid date_
* Move all AssetProxy contracts out of contracts-protocol to new package (#1539)

View File

@@ -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"
}
}
]
}
]

View File

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

View File

@@ -1,38 +0,0 @@
{
"artifactsDir": "./generated-artifacts",
"contractsDir": "./contracts",
"useDockerisedSolc": false,
"isOfflineMode": false,
"compilerSettings": {
"evmVersion": "constantinople",
"optimizer": {
"enabled": true,
"runs": 1000000,
"details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
},
"outputSelection": {
"*": {
"*": [
"abi",
"devdoc",
"evm.bytecode.object",
"evm.bytecode.sourceMap",
"evm.deployedBytecode.object",
"evm.deployedBytecode.sourceMap"
]
}
}
},
"contracts": [
"src/ERC1155Proxy.sol",
"src/ERC20Proxy.sol",
"src/ERC721Proxy.sol",
"src/MixinAuthorizable.sol",
"src/MultiAssetProxy.sol",
"src/StaticCallProxy.sol",
"src/interfaces/IAssetData.sol",
"src/interfaces/IAssetProxy.sol",
"src/interfaces/IAuthorizable.sol",
"test/TestStaticCallTarget.sol"
]
}

View File

@@ -1,375 +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.9;
import "./MixinAuthorizable.sol";
contract ERC1155Proxy is
MixinAuthorizable
{
// Id of this proxy.
bytes4 constant internal PROXY_ID = bytes4(keccak256("ERC1155Assets(address,uint256[],uint256[],bytes)"));
// solhint-disable-next-line payable-fallback
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 exist 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 calldata (Table #1) and assetData (Table #2) to memory (Table #3).
// Once Table #3 ha been constructed in memory, the destination erc1155 contract is called using this
// as its calldata. This process is divided into three steps, below.
////////// STEP 1/3 - Map calldata to memory (Table #1 -> Table #3) //////////
// Store the safeBatchTransferFrom function selector, which is computed using:
// bytes4(keccak256("safeBatchTransferFrom(address,address,uint256[],uint256[],bytes)"))
mstore(0, 0x2eb2c2d600000000000000000000000000000000000000000000000000000000)
// Copy `from` and `to` fields from calldata (Table #1) into memory (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 2/3 - Map assetData to memory (Table #2 -> Table #3) //////////
// 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 `values` from assetData (Table #2) are multiplied by `amount` (Table #1)
// when they are copied into memory.
// Load offset to `assetData`
let assetDataOffset := add(calldataload(4), 4)
// Load length in bytes of `assetData`
let assetDataLength := calldataload(assetDataOffset)
// Assert that the length of asset data:
// 1. Must be at least 132 bytes (Table #2)
// 2. Must be a multiple of 32 (excluding the 4-byte selector)
if or(lt(assetDataLength, 132), mod(sub(assetDataLength, 4), 32)) {
// Revert with `Error("INVALID_ASSET_DATA_LENGTH")`
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
mstore(64, 0x00000019494e56414c49445f41535345545f444154415f4c454e475448000000)
mstore(96, 0)
revert(0, 100)
}
// End of asset data in calldata
// +32 for length field
let assetDataEnd := add(assetDataOffset, add(assetDataLength, 32))
if gt(assetDataEnd, calldatasize()) {
// Revert with `Error("INVALID_ASSET_DATA_END")`
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
mstore(64, 0x00000016494e56414c49445f41535345545f444154415f454e44000000000000)
mstore(96, 0)
revert(0, 100)
}
// Load offset to parameters section in asset data
let paramsInAssetDataOffset := add(assetDataOffset, 36)
// Offset of end of Data Area in memory.
// This value will grow as we construct Table #3.
let dataAreaEndOffset := 164
// Load amount by which to scale values
let amount := calldataload(100)
// Store pointer to `ids` (Table #3)
// Subtract 4 for `safeBatchTransferFrom` selector
mstore(68, sub(dataAreaEndOffset, 4))
// Ensure length of `ids` does not overflow
let idsOffset := add(paramsInAssetDataOffset, calldataload(add(assetDataOffset, 68)))
let idsLength := calldataload(idsOffset)
let idsLengthInBytes := mul(idsLength, 32)
if sub(div(idsLengthInBytes, 32), idsLength) {
// Revert with `Error("UINT256_OVERFLOW")`
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
mstore(64, 0x0000001055494e543235365f4f564552464c4f57000000000000000000000000)
mstore(96, 0)
revert(0, 100)
}
// Ensure `ids` does not resolve to outside of `assetData`
let idsBegin := add(idsOffset, 32)
let idsEnd := add(idsBegin, idsLengthInBytes)
if gt(idsEnd, assetDataEnd) {
// Revert with `Error("INVALID_IDS_OFFSET")`
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
mstore(64, 0x00000012494e56414c49445f4944535f4f464653455400000000000000000000)
mstore(96, 0)
revert(0, 100)
}
// Copy `ids` from `assetData` (Table #2) to memory (Table #3)
calldatacopy(
dataAreaEndOffset,
idsOffset,
add(idsLengthInBytes, 32)
)
dataAreaEndOffset := add(dataAreaEndOffset, add(idsLengthInBytes, 32))
// Store pointer to `values` (Table #3)
// Subtract 4 for `safeBatchTrasferFrom` selector
mstore(100, sub(dataAreaEndOffset, 4))
// Ensure length of `values` does not overflow
let valuesOffset := add(paramsInAssetDataOffset, calldataload(add(assetDataOffset, 100)))
let valuesLength := calldataload(valuesOffset)
let valuesLengthInBytes := mul(valuesLength, 32)
if sub(div(valuesLengthInBytes, 32), valuesLength) {
// Revert with `Error("UINT256_OVERFLOW")`
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
mstore(64, 0x0000001055494e543235365f4f564552464c4f57000000000000000000000000)
mstore(96, 0)
revert(0, 100)
}
// Ensure `values` does not resolve to outside of `assetData`
let valuesBegin := add(valuesOffset, 32)
let valuesEnd := add(valuesBegin, valuesLengthInBytes)
if gt(valuesEnd, assetDataEnd) {
// Revert with `Error("INVALID_VALUES_OFFSET")`
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
mstore(64, 0x00000015494e56414c49445f56414c5545535f4f464653455400000000000000)
mstore(96, 0)
revert(0, 100)
}
// Store length of `values`
mstore(dataAreaEndOffset, valuesLength)
dataAreaEndOffset := add(dataAreaEndOffset, 32)
// Scale and store elements of `values`
for { let currentValueOffset := valuesBegin }
lt(currentValueOffset, valuesEnd)
{ currentValueOffset := add(currentValueOffset, 32) }
{
// Load value and generate scaled value
let currentValue := calldataload(currentValueOffset)
let currentValueScaled := mul(currentValue, amount)
// Revert if `amount` != 0 and multiplication resulted in an overflow
if iszero(or(
iszero(amount),
eq(div(currentValueScaled, amount), currentValue)
)) {
// Revert with `Error("UINT256_OVERFLOW")`
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
mstore(64, 0x0000001055494e543235365f4f564552464c4f57000000000000000000000000)
mstore(96, 0)
revert(0, 100)
}
// There was no overflow, store the scaled token value
mstore(dataAreaEndOffset, currentValueScaled)
dataAreaEndOffset := add(dataAreaEndOffset, 32)
}
// Store pointer to `data` (Table #3)
// Subtract 4 for `safeBatchTrasferFrom` selector
mstore(132, sub(dataAreaEndOffset, 4))
// Ensure `data` does not resolve to outside of `assetData`
let dataOffset := add(paramsInAssetDataOffset, calldataload(add(assetDataOffset, 132)))
let dataLengthInBytes := calldataload(dataOffset)
let dataBegin := add(dataOffset, 32)
let dataEnd := add(dataBegin, dataLengthInBytes)
if gt(dataEnd, assetDataEnd) {
// Revert with `Error("INVALID_DATA_OFFSET")`
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
mstore(64, 0x00000013494e56414c49445f444154415f4f4646534554000000000000000000)
mstore(96, 0)
revert(0, 100)
}
// Copy `data` from `assetData` (Table #2) to memory (Table #3)
calldatacopy(
dataAreaEndOffset,
dataOffset,
add(dataLengthInBytes, 32)
)
// Update the end of data offset to be word-aligned
let dataLengthInWords := div(add(dataLengthInBytes, 31), 32)
let dataLengthInBytesWordAligned := mul(dataLengthInWords, 32)
dataAreaEndOffset := add(dataAreaEndOffset, add(dataLengthInBytesWordAligned, 32))
////////// STEP 3/3 - Execute Transfer //////////
// Load the address of the destination erc1155 contract from asset data (Table #2)
// +32 bytes for assetData Length
// +4 bytes for assetProxyId
let assetAddress := and(
calldataload(add(assetDataOffset, 36)),
0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff
)
// 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
dataAreaEndOffset, // length of input is the end of the Data Area (Table #3)
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;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,207 +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.9;
contract StaticCallProxy {
// Id of this proxy.
bytes4 constant internal PROXY_ID = bytes4(keccak256("StaticCall(address,bytes,bytes32)"));
// 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) {
// `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
// (***): Note that the `from`, `to`, and `amount` params in calldata are ignored in this function.
//
// WARNING: The ABIv2 specification allows additional padding between
// the Params and Data section. This will result in a larger
// offset to assetData.
// Load offset to `assetData`
let assetDataOffset := add(calldataload(4), 4)
// Validate length of `assetData`
let assetDataLen := calldataload(assetDataOffset)
if or(lt(assetDataLen, 100), mod(sub(assetDataLen, 4), 32)) {
// Revert with `Error("INVALID_ASSET_DATA_LENGTH")`
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
mstore(64, 0x00000019494e56414c49445f41535345545f444154415f4c454e475448000000)
mstore(96, 0)
revert(0, 100)
}
// Ensure that `assetData` ends inside of calldata
let assetDataEnd := add(assetDataOffset, add(assetDataLen, 32))
if gt(assetDataEnd, calldatasize()) {
// Revert with `Error("INVALID_ASSET_DATA_END")`
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
mstore(64, 0x00000016494e56414c49445f41535345545f444154415f454e44000000000000)
mstore(96, 0)
revert(0, 100)
}
// Asset data is encoded as follows:
// | Area | Offset | Length | Contents |
// |----------|-------------|---------|--------------------------------------|
// | Header | 0 | 4 | assetProxyId |
// | Params | | 4 * 32 | function parameters: |
// | | 4 | | 1. address of callTarget |
// | | 36 | | 2. offset to staticCallData (*) |
// | | 68 | | 3. expected 32 byte hash of output |
// | Data | | | staticCallData: |
// | | 100 | 32 | 1. staticCallData Length |
// | | 132 | a | 2. staticCallData Contents |
// In order to find the offset to `staticCallData`, we must add:
// assetDataOffset
// + 32 (assetData len)
// + 4 (proxyId)
// + 32 (callTarget)
let paramsInAssetDataOffset := add(assetDataOffset, 36)
let staticCallDataOffset := add(paramsInAssetDataOffset, calldataload(add(assetDataOffset, 68)))
// Load length of `staticCallData`
let staticCallDataLen := calldataload(staticCallDataOffset)
// Ensure `staticCallData` does not begin to outside of `assetData`
let staticCallDataBegin := add(staticCallDataOffset, 32)
let staticCallDataEnd := add(staticCallDataBegin, staticCallDataLen)
if gt(staticCallDataEnd, assetDataEnd) {
// Revert with `Error("INVALID_STATIC_CALL_DATA_OFFSET")`
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
mstore(64, 0x0000001f494e56414c49445f5354415449435f43414c4c5f444154415f4f4646)
mstore(96, 0x5345540000000000000000000000000000000000000000000000000000000000)
revert(0, 100)
}
// Copy `staticCallData` into memory
calldatacopy(
0, // memory can be safely overwritten from beginning
staticCallDataBegin, // start of `staticCallData`
staticCallDataLen // copy the entire `staticCallData`
)
// In order to find the offset to `callTarget`, we must add:
// assetDataOffset
// + 32 (assetData len)
// + 4 (proxyId)
let callTarget := and(
calldataload(add(assetDataOffset, 36)),
0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff
)
// Perform `callTarget.staticcall(staticCallData)`
let success := staticcall(
gas, // forward all gas
callTarget, // call address `callTarget`
0, // pointer to start of input
staticCallDataLen, // length of input
0, // start of memory can be safely overwritten
0 // don't copy output to memory
)
// Copy entire output to start of memory
let outputLen := returndatasize()
returndatacopy(
0, // copy to memory at 0
0, // copy from return data at 0
outputLen // copy all return data
)
// Revert with reason given by `callTarget` if staticcall is unsuccessful
if iszero(success) {
revert(0, outputLen)
}
// Calculate hash of output
let callResultHash := keccak256(0, outputLen)
// In order to find the offset to `expectedCallResultHash`, we must add:
// assetDataOffset
// + 32 (assetData len)
// + 4 (proxyId)
// + 32 (callTarget)
// + 32 (staticCallDataOffset)
let expectedResultHash := calldataload(add(assetDataOffset, 100))
if sub(callResultHash, expectedResultHash) {
// Revert with `Error("UNEXPECTED_STATIC_CALL_RESULT")`
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
mstore(64, 0x0000001d554e45585045435445445f5354415449435f43414c4c5f524553554c)
mstore(96, 0x5400000000000000000000000000000000000000000000000000000000000000)
revert(0, 100)
}
// Return if output matched expected output
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;
}
}

View File

@@ -1,58 +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 tokenAddress)
external;
function ERC721Token(
address tokenAddress,
uint256 tokenId
)
external;
function ERC1155Assets(
address tokenAddress,
uint256[] calldata tokenIds,
uint256[] calldata tokenValues,
bytes calldata callbackData
)
external;
function MultiAsset(
uint256[] calldata amounts,
bytes[] calldata nestedAssetData
)
external;
function StaticCall(
address callTarget,
bytes calldata staticCallData,
bytes32 callResultHash
)
external;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,82 +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.9;
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
contract TestStaticCallTarget {
using LibBytes for bytes;
uint256 internal _state;
function updateState()
external
{
_state++;
}
function assertEvenNumber(uint256 target)
external
pure
{
require(
target % 2 == 0,
"TARGET_NOT_EVEN"
);
}
function isOddNumber(uint256 target)
external
pure
returns (bool isOdd)
{
isOdd = target % 2 == 1;
return isOdd;
}
function noInputFunction()
external
pure
{
assert(msg.data.length == 4 && msg.data.readBytes4(0) == bytes4(keccak256("noInputFunction()")));
}
function dynamicInputFunction(bytes calldata a)
external
pure
{
bytes memory abiEncodedData = abi.encodeWithSignature("dynamicInputFunction(bytes)", a);
assert(msg.data.equals(abiEncodedData));
}
function returnComplexType(uint256 a, uint256 b)
external
view
returns (bytes memory result)
{
result = abi.encodePacked(
address(this),
a,
b
);
return result;
}
}

View File

@@ -1,88 +0,0 @@
{
"name": "@0x/contracts-asset-proxy",
"version": "2.2.2",
"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",
"fix": "tslint --fix --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
"coverage:report:text": "istanbul report text",
"coverage:report: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|StaticCallProxy|TestStaticCallTarget).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.1.1",
"@0x/contracts-gen": "^1.0.10",
"@0x/contracts-test-utils": "^3.1.10",
"@0x/dev-utils": "^2.2.4",
"@0x/sol-compiler": "^3.1.9",
"@0x/tslint-config": "^3.0.1",
"@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.1.1",
"@0x/contracts-erc1155": "^1.1.9",
"@0x/contracts-erc20": "^2.2.8",
"@0x/contracts-erc721": "^2.1.9",
"@0x/contracts-utils": "^3.1.9",
"@0x/order-utils": "^8.2.2",
"@0x/types": "^2.4.0",
"@0x/typescript-typings": "^4.2.3",
"@0x/utils": "^4.4.0",
"@0x/web3-wrapper": "^6.0.7",
"ethereum-types": "^2.1.3",
"ethereumjs-util": "^5.1.1",
"lodash": "^4.17.11"
},
"publishConfig": {
"access": "public"
}
}

View File

@@ -1,29 +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';
import * as StaticCallProxy from '../generated-artifacts/StaticCallProxy.json';
import * as TestStaticCallTarget from '../generated-artifacts/TestStaticCallTarget.json';
export const artifacts = {
ERC1155Proxy: ERC1155Proxy as ContractArtifact,
ERC20Proxy: ERC20Proxy as ContractArtifact,
ERC721Proxy: ERC721Proxy as ContractArtifact,
MixinAuthorizable: MixinAuthorizable as ContractArtifact,
MultiAssetProxy: MultiAssetProxy as ContractArtifact,
StaticCallProxy: StaticCallProxy as ContractArtifact,
IAssetData: IAssetData as ContractArtifact,
IAssetProxy: IAssetProxy as ContractArtifact,
IAuthorizable: IAuthorizable as ContractArtifact,
TestStaticCallTarget: TestStaticCallTarget as ContractArtifact,
};

View File

@@ -1,3 +0,0 @@
export * from './artifacts';
export * from './wrappers';
export * from '../test/utils';

View File

@@ -1,15 +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';
export * from '../generated-wrappers/static_call_proxy';
export * from '../generated-wrappers/test_static_call_target';

View File

@@ -1,215 +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 authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
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 authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
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 authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
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 authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
address,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await authorizable.removeAuthorizedAddress.awaitTransactionSuccessAsync(
address,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
const isAuthorized = await authorizable.authorized.callAsync(address);
expect(isAuthorized).to.be.false();
});
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 authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
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 authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
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 authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
address1,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
address2,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
const address1Index = new BigNumber(0);
return expectTransactionFailedAsync(
authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address2, address1Index, {
from: owner,
}),
RevertReason.AuthorizedAddressMismatch,
);
});
it('should allow owner to remove an authorized address', async () => {
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
address,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
const index = new BigNumber(0);
await authorizable.removeAuthorizedAddressAtIndex.awaitTransactionSuccessAsync(
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 authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
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 authorizable.removeAuthorizedAddress.awaitTransactionSuccessAsync(
address,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS,
);
const afterRemove = await authorizable.getAuthorizedAddresses.callAsync();
expect(afterRemove).to.have.length(0);
});
});
});

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -1,250 +0,0 @@
import {
chaiSetup,
constants,
expectTransactionFailedAsync,
expectTransactionFailedWithoutReasonAsync,
provider,
txDefaults,
web3Wrapper,
} from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { assetDataUtils } from '@0x/order-utils';
import { AssetProxyId, RevertReason } from '@0x/types';
import { AbiEncoder, BigNumber } from '@0x/utils';
import * as chai from 'chai';
import * as ethUtil from 'ethereumjs-util';
import { artifacts, IAssetProxyContract, StaticCallProxyContract, TestStaticCallTargetContract } from '../src';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
describe('StaticCallProxy', () => {
const amount = constants.ZERO_AMOUNT;
let fromAddress: string;
let toAddress: string;
let staticCallProxy: IAssetProxyContract;
let staticCallTarget: TestStaticCallTargetContract;
before(async () => {
await blockchainLifecycle.startAsync();
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
before(async () => {
const accounts = await web3Wrapper.getAvailableAddressesAsync();
[fromAddress, toAddress] = accounts.slice(0, 2);
const staticCallProxyWithoutTransferFrom = await StaticCallProxyContract.deployFrom0xArtifactAsync(
artifacts.StaticCallProxy,
provider,
txDefaults,
);
staticCallProxy = new IAssetProxyContract(staticCallProxyWithoutTransferFrom.address, provider, txDefaults);
staticCallTarget = await TestStaticCallTargetContract.deployFrom0xArtifactAsync(
artifacts.TestStaticCallTarget,
provider,
txDefaults,
);
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('general', () => {
it('should revert if undefined function is called', async () => {
const undefinedSelector = '0x01020304';
await expectTransactionFailedWithoutReasonAsync(
web3Wrapper.sendTransactionAsync({
from: fromAddress,
to: staticCallProxy.address,
value: constants.ZERO_AMOUNT,
data: undefinedSelector,
}),
);
});
it('should have an id of 0xc339d10a', async () => {
const proxyId = await staticCallProxy.getProxyId.callAsync();
const expectedProxyId = AssetProxyId.StaticCall;
expect(proxyId).to.equal(expectedProxyId);
});
});
describe('transferFrom', () => {
it('should revert if assetData lies outside the bounds of calldata', async () => {
const staticCallData = staticCallTarget.noInputFunction.getABIEncodedTransactionData();
const expectedResultHash = constants.KECCAK256_NULL;
const assetData = assetDataUtils.encodeStaticCallAssetData(
staticCallTarget.address,
staticCallData,
expectedResultHash,
);
const txData = staticCallProxy.transferFrom.getABIEncodedTransactionData(
assetData,
fromAddress,
toAddress,
amount,
);
const offsetToAssetData = '0000000000000000000000000000000000000000000000000000000000000080';
const txDataEndBuffer = ethUtil.toBuffer((txData.length - 2) / 2 - 4);
const paddedTxDataEndBuffer = ethUtil.setLengthLeft(txDataEndBuffer, 32);
const invalidOffsetToAssetData = ethUtil.bufferToHex(paddedTxDataEndBuffer).slice(2);
const newAssetData = '0000000000000000000000000000000000000000000000000000000000000304';
const badTxData = `${txData.replace(offsetToAssetData, invalidOffsetToAssetData)}${newAssetData}`;
await expectTransactionFailedAsync(
web3Wrapper.sendTransactionAsync({
to: staticCallProxy.address,
from: fromAddress,
data: badTxData,
}),
RevertReason.InvalidAssetDataEnd,
);
});
it('should revert if the length of assetData, excluding the proxyId, is not a multiple of 32', async () => {
const staticCallData = staticCallTarget.noInputFunction.getABIEncodedTransactionData();
const expectedResultHash = constants.KECCAK256_NULL;
const assetData = `${assetDataUtils.encodeStaticCallAssetData(
staticCallTarget.address,
staticCallData,
expectedResultHash,
)}01`;
await expectTransactionFailedAsync(
staticCallProxy.transferFrom.sendTransactionAsync(assetData, fromAddress, toAddress, amount),
RevertReason.InvalidAssetDataLength,
);
});
it('should revert if the length of assetData is less than 100 bytes', async () => {
const staticCallData = constants.NULL_BYTES;
const expectedResultHash = constants.KECCAK256_NULL;
const assetData = assetDataUtils
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash)
.slice(0, -128);
const assetDataByteLen = (assetData.length - 2) / 2;
expect((assetDataByteLen - 4) % 32).to.equal(0);
await expectTransactionFailedAsync(
staticCallProxy.transferFrom.sendTransactionAsync(assetData, fromAddress, toAddress, amount),
RevertReason.InvalidAssetDataLength,
);
});
it('should revert if the offset to `staticCallData` points to outside of assetData', async () => {
const staticCallData = staticCallTarget.noInputFunction.getABIEncodedTransactionData();
const expectedResultHash = constants.KECCAK256_NULL;
const assetData = assetDataUtils.encodeStaticCallAssetData(
staticCallTarget.address,
staticCallData,
expectedResultHash,
);
const offsetToStaticCallData = '0000000000000000000000000000000000000000000000000000000000000060';
const assetDataEndBuffer = ethUtil.toBuffer((assetData.length - 2) / 2 - 4);
const paddedAssetDataEndBuffer = ethUtil.setLengthLeft(assetDataEndBuffer, 32);
const invalidOffsetToStaticCallData = ethUtil.bufferToHex(paddedAssetDataEndBuffer).slice(2);
const newStaticCallData = '0000000000000000000000000000000000000000000000000000000000000304';
const badAssetData = `${assetData.replace(
offsetToStaticCallData,
invalidOffsetToStaticCallData,
)}${newStaticCallData}`;
await expectTransactionFailedAsync(
staticCallProxy.transferFrom.sendTransactionAsync(badAssetData, fromAddress, toAddress, amount),
RevertReason.InvalidStaticCallDataOffset,
);
});
it('should revert if the callTarget attempts to write to state', async () => {
const staticCallData = staticCallTarget.updateState.getABIEncodedTransactionData();
const expectedResultHash = constants.KECCAK256_NULL;
const assetData = assetDataUtils.encodeStaticCallAssetData(
staticCallTarget.address,
staticCallData,
expectedResultHash,
);
await expectTransactionFailedWithoutReasonAsync(
staticCallProxy.transferFrom.sendTransactionAsync(assetData, fromAddress, toAddress, amount),
);
});
it('should revert with data provided by the callTarget if the staticcall reverts', async () => {
const staticCallData = staticCallTarget.assertEvenNumber.getABIEncodedTransactionData(new BigNumber(1));
const expectedResultHash = constants.KECCAK256_NULL;
const assetData = assetDataUtils.encodeStaticCallAssetData(
staticCallTarget.address,
staticCallData,
expectedResultHash,
);
await expectTransactionFailedAsync(
staticCallProxy.transferFrom.sendTransactionAsync(assetData, fromAddress, toAddress, amount),
RevertReason.TargetNotEven,
);
});
it('should revert if the hash of the output is different than expected expected', async () => {
const staticCallData = staticCallTarget.isOddNumber.getABIEncodedTransactionData(new BigNumber(0));
const trueAsBuffer = ethUtil.toBuffer('0x0000000000000000000000000000000000000000000000000000000000000001');
const expectedResultHash = ethUtil.bufferToHex(ethUtil.sha3(trueAsBuffer));
const assetData = assetDataUtils.encodeStaticCallAssetData(
staticCallTarget.address,
staticCallData,
expectedResultHash,
);
await expectTransactionFailedAsync(
staticCallProxy.transferFrom.sendTransactionAsync(assetData, fromAddress, toAddress, amount),
RevertReason.UnexpectedStaticCallResult,
);
});
it('should be successful if a function call with no inputs is successful', async () => {
const staticCallData = staticCallTarget.noInputFunction.getABIEncodedTransactionData();
const expectedResultHash = constants.KECCAK256_NULL;
const assetData = assetDataUtils.encodeStaticCallAssetData(
staticCallTarget.address,
staticCallData,
expectedResultHash,
);
await staticCallProxy.transferFrom.awaitTransactionSuccessAsync(assetData, fromAddress, toAddress, amount);
});
it('should be successful if a function call with one static input returns the correct value', async () => {
const staticCallData = staticCallTarget.isOddNumber.getABIEncodedTransactionData(new BigNumber(1));
const trueAsBuffer = ethUtil.toBuffer('0x0000000000000000000000000000000000000000000000000000000000000001');
const expectedResultHash = ethUtil.bufferToHex(ethUtil.sha3(trueAsBuffer));
const assetData = assetDataUtils.encodeStaticCallAssetData(
staticCallTarget.address,
staticCallData,
expectedResultHash,
);
await staticCallProxy.transferFrom.awaitTransactionSuccessAsync(assetData, fromAddress, toAddress, amount);
});
it('should be successful if a function with one dynamic input is successful', async () => {
const dynamicInput = '0x0102030405060708';
const staticCallData = staticCallTarget.dynamicInputFunction.getABIEncodedTransactionData(dynamicInput);
const expectedResultHash = constants.KECCAK256_NULL;
const assetData = assetDataUtils.encodeStaticCallAssetData(
staticCallTarget.address,
staticCallData,
expectedResultHash,
);
await staticCallProxy.transferFrom.awaitTransactionSuccessAsync(assetData, fromAddress, toAddress, amount);
});
it('should be successful if a function call returns a complex type', async () => {
const a = new BigNumber(1);
const b = new BigNumber(2);
const staticCallData = staticCallTarget.returnComplexType.getABIEncodedTransactionData(a, b);
const abiEncoder = new AbiEncoder.DynamicBytes({
name: '',
type: 'bytes',
});
const aHex = '0000000000000000000000000000000000000000000000000000000000000001';
const bHex = '0000000000000000000000000000000000000000000000000000000000000002';
const expectedResults = `${staticCallTarget.address}${aHex}${bHex}`;
const offset = '0000000000000000000000000000000000000000000000000000000000000020';
const encodedExpectedResultWithOffset = `0x${offset}${abiEncoder.encode(expectedResults).slice(2)}`;
const expectedResultHash = ethUtil.bufferToHex(
ethUtil.sha3(ethUtil.toBuffer(encodedExpectedResultWithOffset)),
);
const assetData = assetDataUtils.encodeStaticCallAssetData(
staticCallTarget.address,
staticCallData,
expectedResultHash,
);
await staticCallProxy.transferFrom.awaitTransactionSuccessAsync(assetData, fromAddress, toAddress, amount);
});
});
});

View File

@@ -1,401 +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(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 generates abi-encoded tx data for transferring erc1155 fungible/non-fungible tokens.
* @param from source address
* @param to destination address
* @param contractAddress address of erc155 contract
* @param tokensToTransfer array of erc1155 tokens to transfer
* @param valuesToTransfer array of corresponding values for each erc1155 token to transfer
* @param valueMultiplier each value in `valuesToTransfer` is multiplied by this
* @param receiverCallbackData callback data if `to` is a contract
* @param authorizedSender sender of `transferFrom` transaction
* @param extraData extra data to append to `transferFrom` transaction. Optional.
* @return abi encoded tx data.
*/
public getTransferFromAbiEncodedTxData(
from: string,
to: string,
contractAddress: string,
tokensToTransfer: BigNumber[],
valuesToTransfer: BigNumber[],
valueMultiplier: BigNumber,
receiverCallbackData: string,
authorizedSender: string,
assetData_?: string,
): string {
this._validateProxyContractExistsOrThrow();
const assetData =
assetData_ === undefined
? assetDataUtils.encodeERC1155AssetData(
contractAddress,
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
)
: assetData_;
const data = this._assetProxyInterface.transferFrom.getABIEncodedTransactionData(
assetData,
from,
to,
valueMultiplier,
);
return data;
}
/**
* @dev transfers erc1155 fungible/non-fungible tokens.
* @param txData: abi-encoded tx data
* @param authorizedSender sender of `transferFrom` transaction
*/
public async transferFromRawAsync(
txData: string,
authorizedSender: string,
): Promise<TransactionReceiptWithDecodedLogs> {
const txHash = await this._web3Wrapper.sendTransactionAsync({
to: (this._proxyContract as ERC1155ProxyContract).address,
data: txData,
from: authorizedSender,
gas: 300000,
});
const txReceipt = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
return txReceipt;
}
/**
* @dev transfers erc1155 fungible/non-fungible tokens.
* @param from source address
* @param to destination address
* @param contractAddress address of erc155 contract
* @param tokensToTransfer array of erc1155 tokens to transfer
* @param valuesToTransfer array of corresponding values for each erc1155 token to transfer
* @param valueMultiplier each value in `valuesToTransfer` is multiplied by this
* @param receiverCallbackData callback data if `to` is a contract
* @param authorizedSender sender of `transferFrom` transaction
* @param extraData extra data to append to `transferFrom` transaction. Optional.
* @return tranasction hash.
*/
public async transferFromAsync(
from: string,
to: string,
contractAddress: string,
tokensToTransfer: BigNumber[],
valuesToTransfer: BigNumber[],
valueMultiplier: BigNumber,
receiverCallbackData: string,
authorizedSender: string,
assetData_?: string,
): Promise<TransactionReceiptWithDecodedLogs> {
this._validateProxyContractExistsOrThrow();
const assetData =
assetData_ === undefined
? assetDataUtils.encodeERC1155AssetData(
contractAddress,
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
)
: assetData_;
const data = this._assetProxyInterface.transferFrom.getABIEncodedTransactionData(
assetData,
from,
to,
valueMultiplier,
);
const txHash = await this._web3Wrapper.sendTransactionAsync({
to: (this._proxyContract as ERC1155ProxyContract).address,
data,
from: authorizedSender,
gas: 300000,
});
const txReceipt = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
return txReceipt;
}
/**
* @dev For each deployed ERC1155 contract, this function mints a set of fungible/non-fungible
* tokens for each token owner address (`_tokenOwnerAddresses`).
* @return Balances of each token owner, across all ERC1155 contracts and tokens.
*/
public async setBalancesAndAllowancesAsync(): Promise<ERC1155HoldingsByOwner> {
this._validateDummyTokenContractsExistOrThrow();
this._validateProxyContractExistsOrThrow();
this._initialTokenIdsByOwner = {
fungible: {},
nonFungible: {},
};
const fungibleHoldingsByOwner: ERC1155FungibleHoldingsByOwner = {};
const nonFungibleHoldingsByOwner: ERC1155NonFungibleHoldingsByOwner = {};
// Set balances accordingly
for (const dummyWrapper of this._dummyTokenWrappers) {
const dummyAddress = dummyWrapper.getContract().address;
// tslint:disable-next-line:no-unused-variable
for (const i of _.times(constants.NUM_ERC1155_FUNGIBLE_TOKENS_MINT)) {
// Create a fungible token
const tokenId = await dummyWrapper.mintFungibleTokensAsync(
this._tokenOwnerAddresses,
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
);
const tokenIdAsString = tokenId.toString();
this._fungibleTokenIds.push(tokenIdAsString);
// Mint tokens for each owner for this token
for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
// tslint:disable-next-line:no-unused-variable
if (fungibleHoldingsByOwner[tokenOwnerAddress] === undefined) {
fungibleHoldingsByOwner[tokenOwnerAddress] = {};
}
if (fungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress] === undefined) {
fungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress] = {};
}
fungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress][tokenIdAsString] =
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE;
await dummyWrapper.setApprovalForAllAsync(
tokenOwnerAddress,
(this._proxyContract as ERC1155ProxyContract).address,
true,
);
}
}
// Non-fungible tokens
// tslint:disable-next-line:no-unused-variable
for (const j of _.times(constants.NUM_ERC1155_NONFUNGIBLE_TOKENS_MINT)) {
const [tokenId, nftIds] = await dummyWrapper.mintNonFungibleTokensAsync(this._tokenOwnerAddresses);
const tokenIdAsString = tokenId.toString();
this._nonFungibleTokenIds.push(tokenIdAsString);
_.each(this._tokenOwnerAddresses, async (tokenOwnerAddress: string, i: number) => {
if (nonFungibleHoldingsByOwner[tokenOwnerAddress] === undefined) {
nonFungibleHoldingsByOwner[tokenOwnerAddress] = {};
}
if (nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress] === undefined) {
nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress] = {};
}
if (nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress][tokenIdAsString] === undefined) {
nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress][tokenIdAsString] = [];
}
this._nfts.push({ id: nftIds[i], tokenId });
nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress][tokenIdAsString].push(nftIds[i]);
await dummyWrapper.setApprovalForAllAsync(
tokenOwnerAddress,
(this._proxyContract as ERC1155ProxyContract).address,
true,
);
});
}
}
this._initialTokenIdsByOwner = {
fungible: fungibleHoldingsByOwner,
nonFungible: nonFungibleHoldingsByOwner,
};
return this._initialTokenIdsByOwner;
}
/**
* @dev For each deployed ERC1155 contract, this function quieries the set of fungible/non-fungible
* tokens for each token owner address (`_tokenOwnerAddresses`).
* @return Balances of each token owner, across all ERC1155 contracts and tokens.
*/
public async getBalancesAsync(): Promise<ERC1155HoldingsByOwner> {
this._validateDummyTokenContractsExistOrThrow();
this._validateBalancesAndAllowancesSetOrThrow();
const tokenHoldingsByOwner: ERC1155FungibleHoldingsByOwner = {};
const nonFungibleHoldingsByOwner: ERC1155NonFungibleHoldingsByOwner = {};
for (const dummyTokenWrapper of this._dummyTokenWrappers) {
const tokenContract = dummyTokenWrapper.getContract();
const tokenAddress = tokenContract.address;
// Construct batch balance call
const tokenOwners: string[] = [];
const tokenIds: BigNumber[] = [];
for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
for (const tokenId of this._fungibleTokenIds) {
tokenOwners.push(tokenOwnerAddress);
tokenIds.push(new BigNumber(tokenId));
}
for (const nft of this._nfts) {
tokenOwners.push(tokenOwnerAddress);
tokenIds.push(nft.id);
}
}
const balances = await dummyTokenWrapper.getBalancesAsync(tokenOwners, tokenIds);
// Parse out balances into fungible / non-fungible token holdings
let i = 0;
for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
// Fungible tokens
for (const tokenId of this._fungibleTokenIds) {
if (tokenHoldingsByOwner[tokenOwnerAddress] === undefined) {
tokenHoldingsByOwner[tokenOwnerAddress] = {};
}
if (tokenHoldingsByOwner[tokenOwnerAddress][tokenAddress] === undefined) {
tokenHoldingsByOwner[tokenOwnerAddress][tokenAddress] = {};
}
tokenHoldingsByOwner[tokenOwnerAddress][tokenAddress][tokenId] = balances[i++];
}
// Non-fungible tokens
for (const nft of this._nfts) {
if (nonFungibleHoldingsByOwner[tokenOwnerAddress] === undefined) {
nonFungibleHoldingsByOwner[tokenOwnerAddress] = {};
}
if (nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress] === undefined) {
nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress] = {};
}
if (
nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress][nft.tokenId.toString()] ===
undefined
) {
nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress][nft.tokenId.toString()] = [];
}
const isOwner = balances[i++];
if (isOwner.isEqualTo(1)) {
nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress][nft.tokenId.toString()].push(
nft.id,
);
}
}
}
}
const holdingsByOwner = {
fungible: tokenHoldingsByOwner,
nonFungible: nonFungibleHoldingsByOwner,
};
return holdingsByOwner;
}
/**
* @dev 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 (tokenWrapper === undefined) {
throw new Error(`Contract: ${contractAddress} was not deployed through ERC1155ProxyWrapper`);
}
return tokenWrapper;
}
private _getContractFromAddress(tokenAddress: string): ERC1155MintableContract {
const tokenContractIfExists = _.find(this._dummyTokenWrappers, c => c.getContract().address === tokenAddress);
if (tokenContractIfExists === undefined) {
throw new Error(`Token: ${tokenAddress} was not deployed through ERC1155ProxyWrapper`);
}
return tokenContractIfExists.getContract();
}
private _validateDummyTokenContractsExistOrThrow(): void {
if (this._dummyTokenWrappers === undefined) {
throw new Error('Dummy ERC1155 tokens not yet deployed, please call "deployDummyTokensAsync"');
}
}
private _validateProxyContractExistsOrThrow(): void {
if (this._proxyContract === undefined) {
throw new Error('ERC1155 proxy contract not yet deployed, please call "deployProxyAsync"');
}
}
private _validateBalancesAndAllowancesSetOrThrow(): void {
if (
_.keys(this._initialTokenIdsByOwner.fungible).length === 0 ||
_.keys(this._initialTokenIdsByOwner.nonFungible).length === 0
) {
throw new Error(
'Dummy ERC1155 balances and allowances not yet set, please call "setBalancesAndAllowancesAsync"',
);
}
}
}

View File

@@ -1,171 +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 { 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 _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._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 dummyTokenContract.setBalance.awaitTransactionSuccessAsync(
tokenOwnerAddress,
constants.INITIAL_ERC20_BALANCE,
{ from: this._contractOwnerAddress },
constants.AWAIT_TRANSACTION_MINED_MS,
);
await dummyTokenContract.approve.awaitTransactionSuccessAsync(
(this._proxyContract as ERC20ProxyContract).address,
constants.INITIAL_ERC20_ALLOWANCE,
{ from: tokenOwnerAddress },
constants.AWAIT_TRANSACTION_MINED_MS,
);
}
}
}
public async getBalanceAsync(userAddress: string, assetData: string): Promise<BigNumber> {
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 tokenContract.setBalance.awaitTransactionSuccessAsync(
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 tokenContract.approve.awaitTransactionSuccessAsync(
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 (balancesByOwner[tokenOwnerAddress] === undefined) {
balancesByOwner[tokenOwnerAddress] = {};
}
const wrappedBalance = new BigNumber(balance);
balancesByOwner[tokenOwnerAddress][tokenAddress] = wrappedBalance;
});
return balancesByOwner;
}
public addDummyTokenContract(dummy: DummyERC20TokenContract): void {
if (this._dummyTokenContracts !== undefined) {
this._dummyTokenContracts.push(dummy);
}
}
public addTokenOwnerAddress(address: string): void {
this._tokenOwnerAddresses.push(address);
}
public getTokenOwnerAddresses(): string[] {
return this._tokenOwnerAddresses;
}
public getTokenAddresses(): string[] {
const tokenAddresses = _.map(this._dummyTokenContracts, dummyTokenContract => dummyTokenContract.address);
return tokenAddresses;
}
private _getTokenContractFromAssetData(assetData: string): DummyERC20TokenContract {
const erc20ProxyData = assetDataUtils.decodeERC20AssetData(assetData);
const tokenAddress = erc20ProxyData.tokenAddress;
const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === tokenAddress);
if (tokenContractIfExists === undefined) {
throw new Error(`Token: ${tokenAddress} was not deployed through ERC20Wrapper`);
}
return tokenContractIfExists;
}
private _validateDummyTokenContractsExistOrThrow(): void {
if (this._dummyTokenContracts === undefined) {
throw new Error('Dummy ERC20 tokens not yet deployed, please call "deployDummyTokensAsync"');
}
}
private _validateProxyContractExistsOrThrow(): void {
if (this._proxyContract === undefined) {
throw new Error('ERC20 proxy contract not yet deployed, please call "deployProxyAsync"');
}
}
}

View File

@@ -1,233 +0,0 @@
import { artifacts as erc721Artifacts, DummyERC721TokenContract } from '@0x/contracts-erc721';
import { constants, ERC721TokenIdsByOwner, txDefaults } from '@0x/contracts-test-utils';
import { generatePseudoRandomSalt } from '@0x/order-utils';
import { BigNumber } from '@0x/utils';
import { ZeroExProvider } from 'ethereum-types';
import * as _ from 'lodash';
import { artifacts, ERC721ProxyContract } from '../../src';
export class ERC721Wrapper {
private readonly _tokenOwnerAddresses: string[];
private readonly _contractOwnerAddress: string;
private readonly _provider: ZeroExProvider;
private readonly _dummyTokenContracts: DummyERC721TokenContract[];
private _proxyContract?: ERC721ProxyContract;
private _proxyIdIfExists?: string;
private _initialTokenIdsByOwner: ERC721TokenIdsByOwner = {};
constructor(provider: ZeroExProvider, tokenOwnerAddresses: string[], contractOwnerAddress: string) {
this._provider = provider;
this._dummyTokenContracts = [];
this._tokenOwnerAddresses = tokenOwnerAddresses;
this._contractOwnerAddress = contractOwnerAddress;
}
public async deployDummyTokensAsync(): Promise<DummyERC721TokenContract[]> {
// tslint:disable-next-line:no-unused-variable
for (const i of _.times(constants.NUM_DUMMY_ERC721_TO_DEPLOY)) {
this._dummyTokenContracts.push(
await DummyERC721TokenContract.deployFrom0xArtifactAsync(
erc721Artifacts.DummyERC721Token,
this._provider,
txDefaults,
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 (this._initialTokenIdsByOwner[tokenOwnerAddress] === undefined) {
this._initialTokenIdsByOwner[tokenOwnerAddress] = {
[dummyTokenContract.address]: [],
};
}
if (this._initialTokenIdsByOwner[tokenOwnerAddress][dummyTokenContract.address] === undefined) {
this._initialTokenIdsByOwner[tokenOwnerAddress][dummyTokenContract.address] = [];
}
this._initialTokenIdsByOwner[tokenOwnerAddress][dummyTokenContract.address].push(tokenId);
await this.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 tokenContract.setApprovalForAll.awaitTransactionSuccessAsync(
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 tokenContract.approve.awaitTransactionSuccessAsync(
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 tokenContract.transferFrom.awaitTransactionSuccessAsync(
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 tokenContract.mint.awaitTransactionSuccessAsync(
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 tokenContract.burn.awaitTransactionSuccessAsync(
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 (tokenIdsByOwner[tokenOwnerAddress] === undefined) {
tokenIdsByOwner[tokenOwnerAddress] = {
[tokenAddress]: [],
};
}
if (tokenIdsByOwner[tokenOwnerAddress][tokenAddress] === undefined) {
tokenIdsByOwner[tokenOwnerAddress][tokenAddress] = [];
}
tokenIdsByOwner[tokenOwnerAddress][tokenAddress].push(tokenId);
});
return tokenIdsByOwner;
}
public getTokenOwnerAddresses(): string[] {
return this._tokenOwnerAddresses;
}
public getTokenAddresses(): string[] {
const tokenAddresses = _.map(this._dummyTokenContracts, dummyTokenContract => dummyTokenContract.address);
return tokenAddresses;
}
private _getTokenContractFromAssetData(tokenAddress: string): DummyERC721TokenContract {
const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === tokenAddress);
if (tokenContractIfExists === undefined) {
throw new Error(`Token: ${tokenAddress} was not deployed through ERC20Wrapper`);
}
return tokenContractIfExists;
}
private _validateDummyTokenContractsExistOrThrow(): void {
if (this._dummyTokenContracts === undefined) {
throw new Error('Dummy ERC721 tokens not yet deployed, please call "deployDummyTokensAsync"');
}
}
private _validateProxyContractExistsOrThrow(): void {
if (this._proxyContract === undefined) {
throw new Error('ERC721 proxy contract not yet deployed, please call "deployProxyAsync"');
}
}
private _validateBalancesAndAllowancesSetOrThrow(): void {
if (_.keys(this._initialTokenIdsByOwner).length === 0) {
throw new Error(
'Dummy ERC721 balances and allowances not yet set, please call "setBalancesAndAllowancesAsync"',
);
}
}
}

View File

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

View File

@@ -1,18 +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",
"generated-artifacts/StaticCallProxy.json",
"generated-artifacts/TestStaticCallTarget.json"
],
"exclude": ["./deploy/solc/solc_bin"]
}

View File

@@ -1,110 +0,0 @@
[
{
"timestamp": 1563193019,
"version": "2.0.7",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1563047529,
"version": "2.0.6",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1563006338,
"version": "2.0.5",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1558712885,
"version": "2.0.4",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1557961111,
"version": "2.0.3",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1557799313,
"version": "2.0.2",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1557507213,
"version": "2.0.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "2.0.0",
"changes": [
{
"note": "Make `decodeOrdersFromFillData`, `getCoordinatorApprovalHash`, and `getTransactionHash` public",
"pr": 1729
},
{
"note": "Make `assertValidTransactionOrdersApproval` internal",
"pr": 1729
}
],
"timestamp": 1554997931
},
{
"version": "1.1.0",
"changes": [
{
"note": "Run Web3ProviderEngine without excess block polling",
"pr": 1695
}
],
"timestamp": 1553183790
},
{
"version": "1.0.0",
"changes": [
{
"note": "Created Coordinator package"
},
{
"note": "Use separate EIP712 domains for transactions and approvals",
"pr": 1705
},
{
"note": "Add `SignatureType.Invalid`",
"pr": 1705
},
{
"note": "Set `evmVersion` to `constantinople`",
"pr": 1707
}
],
"timestamp": 1553091633
}
]

View File

@@ -1,50 +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.0.7 - _July 15, 2019_
* Dependencies updated
## v2.0.6 - _July 13, 2019_
* Dependencies updated
## v2.0.5 - _July 13, 2019_
* Dependencies updated
## v2.0.4 - _May 24, 2019_
* Dependencies updated
## v2.0.3 - _May 15, 2019_
* Dependencies updated
## v2.0.2 - _May 14, 2019_
* Dependencies updated
## v2.0.1 - _May 10, 2019_
* Dependencies updated
## v2.0.0 - _April 11, 2019_
* Make `decodeOrdersFromFillData`, `getCoordinatorApprovalHash`, and `getTransactionHash` public (#1729)
* Make `assertValidTransactionOrdersApproval` internal (#1729)
## v1.1.0 - _March 21, 2019_
* Run Web3ProviderEngine without excess block polling (#1695)
## v1.0.0 - _March 20, 2019_
* Created Coordinator package
* Use separate EIP712 domains for transactions and approvals (#1705)
* Add `SignatureType.Invalid` (#1705)
* Set `evmVersion` to `constantinople` (#1707)

View File

@@ -1 +0,0 @@
[]

View File

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

View File

@@ -1,26 +0,0 @@
{
"artifactsDir": "./generated-artifacts",
"contractsDir": "./contracts",
"useDockerisedSolc": false,
"compilerSettings": {
"evmVersion": "constantinople",
"optimizer": {
"enabled": true,
"runs": 1000000,
"details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
},
"outputSelection": {
"*": {
"*": [
"abi",
"devdoc",
"evm.bytecode.object",
"evm.bytecode.sourceMap",
"evm.deployedBytecode.object",
"evm.deployedBytecode.sourceMap"
]
}
}
},
"contracts": ["src/Coordinator.sol", "src/registry/CoordinatorRegistry.sol"]
}

View File

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

View File

@@ -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 = decodeOrdersFromFillData(transaction.data);
// No approval is required for non-fill methods
if (orders.length > 0) {
// Revert if approval is invalid for transaction orders
assertValidTransactionOrdersApproval(
transaction,
orders,
txOrigin,
transactionSignature,
approvalExpirationTimeSeconds,
approvalSignatures
);
}
}
/// @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 decodeOrdersFromFillData(bytes memory data)
public
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;
}
/// @dev Validates that the feeRecipients of a batch of order have approved a 0x transaction.
/// @param transaction 0x transaction containing salt, signerAddress, and data.
/// @param orders Array of order structs containing order specifications.
/// @param txOrigin Required signer of Ethereum transaction calling this function.
/// @param transactionSignature Proof that the transaction has been signed by the signer.
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order.
function assertValidTransactionOrdersApproval(
LibZeroExTransaction.ZeroExTransaction memory transaction,
LibOrder.Order[] memory orders,
address txOrigin,
bytes memory transactionSignature,
uint256[] memory approvalExpirationTimeSeconds,
bytes[] memory approvalSignatures
)
internal
view
{
// 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"
);
}
}
}

View File

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

View File

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

View File

@@ -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;
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 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 decodeOrdersFromFillData(bytes memory data)
public
pure
returns (LibOrder.Order[] memory orders);
}

View File

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

View File

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

View File

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

View File

@@ -1,34 +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/ITransactions.sol";
contract LibConstants {
// solhint-disable-next-line var-name-mixedcase
ITransactions internal EXCHANGE;
constructor (address _exchange)
public
{
EXCHANGE = ITransactions(_exchange);
}
}

View File

@@ -1,98 +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 "./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.
bytes transactionSignature; // Signature of the 0x transaction.
uint256 approvalExpirationTimeSeconds; // Timestamp in seconds for which the approval 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)
public
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.txOrigin,
// approval.transactionHash,
// keccak256(approval.transactionSignature)
// approval.approvalExpirationTimeSeconds,
// ));
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;
}
}

View File

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

View File

@@ -1,95 +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 "./LibEIP712Domain.sol";
contract LibZeroExTransaction is
LibEIP712Domain
{
// Hash for the EIP712 0x transaction schema
// keccak256(abi.encodePacked(
// "ZeroExTransaction(",
// "uint256 salt,",
// "address signerAddress,",
// "bytes data",
// ")"
// ));
bytes32 constant internal EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH = 0x213c6f636f3ea94e701c0adf9b2624aa45a6c694f9a292c094f9a81c24b5df4c;
struct ZeroExTransaction {
uint256 salt; // Arbitrary number to ensure uniqueness of transaction hash.
address signerAddress; // Address of transaction signer.
bytes data; // AbiV2 encoded calldata.
}
/// @dev Calculates the EIP712 hash of a 0x transaction using the domain separator of the Exchange contract.
/// @param transaction 0x transaction containing salt, signerAddress, and data.
/// @return EIP712 hash of the transaction with the domain separator of this contract.
function getTransactionHash(ZeroExTransaction memory transaction)
public
view
returns (bytes32 transactionHash)
{
// Hash the transaction with the domain separator of the Exchange contract.
transactionHash = hashEIP712ExchangeMessage(hashZeroExTransaction(transaction));
return transactionHash;
}
/// @dev Calculates EIP712 hash of the 0x transaction with no domain separator.
/// @param transaction 0x transaction containing salt, signerAddress, and data.
/// @return EIP712 hash of the transaction with no domain separator.
function hashZeroExTransaction(ZeroExTransaction memory transaction)
internal
pure
returns (bytes32 result)
{
bytes32 schemaHash = EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH;
bytes memory data = transaction.data;
uint256 salt = transaction.salt;
address signerAddress = transaction.signerAddress;
// Assembly for more efficiently computing:
// keccak256(abi.encodePacked(
// EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH,
// transaction.salt,
// uint256(transaction.signerAddress),
// keccak256(transaction.data)
// ));
assembly {
// Compute hash of data
let dataHash := keccak256(add(data, 32), mload(data))
// Load free memory pointer
let memPtr := mload(64)
mstore(memPtr, schemaHash) // hash of schema
mstore(add(memPtr, 32), salt) // salt
mstore(add(memPtr, 64), and(signerAddress, 0xffffffffffffffffffffffffffffffffffffffff)) // signerAddress
mstore(add(memPtr, 96), dataHash) // hash of data
// Compute hash
result := keccak256(memPtr, 128)
}
return result;
}
}

View File

@@ -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;
pragma experimental "ABIEncoderV2";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "../interfaces/ICoordinatorApprovalVerifier.sol";
contract MCoordinatorApprovalVerifier is
ICoordinatorApprovalVerifier
{
/// @dev Validates that the feeRecipients of a batch of order have approved a 0x transaction.
/// @param transaction 0x transaction containing salt, signerAddress, and data.
/// @param orders Array of order structs containing order specifications.
/// @param txOrigin Required signer of Ethereum transaction calling this function.
/// @param transactionSignature Proof that the transaction has been signed by the signer.
/// @param approvalExpirationTimeSeconds Array of expiration times in seconds for which each corresponding approval signature expires.
/// @param approvalSignatures Array of signatures that correspond to the feeRecipients of each order.
function assertValidTransactionOrdersApproval(
LibZeroExTransaction.ZeroExTransaction memory transaction,
LibOrder.Order[] memory orders,
address txOrigin,
bytes memory transactionSignature,
uint256[] memory approvalExpirationTimeSeconds,
bytes[] memory approvalSignatures
)
internal
view;
}

View File

@@ -1,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.
}
}

View File

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

View File

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

View File

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

View File

@@ -1,89 +0,0 @@
{
"name": "@0x/contracts-coordinator",
"version": "2.0.7",
"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",
"fix": "tslint --fix --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
"coverage:report:text": "istanbul report text",
"coverage:report: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).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.1.1",
"@0x/contracts-gen": "^1.0.10",
"@0x/contracts-test-utils": "^3.1.10",
"@0x/dev-utils": "^2.2.4",
"@0x/sol-compiler": "^3.1.9",
"@0x/tslint-config": "^3.0.1",
"@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.1.1",
"@0x/contracts-asset-proxy": "^2.2.2",
"@0x/contracts-erc20": "^2.2.8",
"@0x/contracts-exchange": "^2.1.8",
"@0x/contracts-exchange-libs": "^3.0.2",
"@0x/contracts-utils": "^3.1.9",
"@0x/order-utils": "^8.2.2",
"@0x/types": "^2.4.0",
"@0x/typescript-typings": "^4.2.3",
"@0x/utils": "^4.4.0",
"@0x/web3-wrapper": "^6.0.7",
"ethereum-types": "^2.1.3",
"ethereumjs-util": "^5.1.1",
"lodash": "^4.17.11"
},
"publishConfig": {
"access": "public"
}
}

View File

@@ -1,13 +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';
export const artifacts = {
Coordinator: Coordinator as ContractArtifact,
CoordinatorRegistry: CoordinatorRegistry as ContractArtifact,
};

View File

@@ -1,3 +0,0 @@
export * from './artifacts';
export * from './wrappers';
export * from '../test/utils';

View File

@@ -1,7 +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';

View File

@@ -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_ORDER, orders);
const transaction = makerTransactionFactory.newSignedTransaction(data);
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
await coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
makerAddress,
transaction.signature,
[],
[],
{
from: makerAddress,
},
),
);
const cancelLogs = transactionReceipt.logs.filter(
log => (log as LogWithDecodedArgs<ExchangeCancelEventArgs>).event === 'Cancel',
);
expect(cancelLogs.length).to.eq(1);
const cancelLogArgs = (cancelLogs[0] as LogWithDecodedArgs<ExchangeCancelEventArgs>).args;
expect(cancelLogArgs.makerAddress).to.eq(makerAddress);
expect(cancelLogArgs.senderAddress).to.eq(coordinatorContract.address);
expect(cancelLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
expect(cancelLogArgs.makerAssetData).to.eq(orders[0].makerAssetData);
expect(cancelLogArgs.takerAssetData).to.eq(orders[0].takerAssetData);
expect(cancelLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(orders[0]));
});
it('batchCancelOrders call should be successful without an approval', async () => {
const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(constants.BATCH_CANCEL_ORDERS, orders);
const transaction = makerTransactionFactory.newSignedTransaction(data);
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
await coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
makerAddress,
transaction.signature,
[],
[],
{
from: makerAddress,
},
),
);
const cancelLogs = transactionReceipt.logs.filter(
log => (log as LogWithDecodedArgs<ExchangeCancelEventArgs>).event === 'Cancel',
);
expect(cancelLogs.length).to.eq(orders.length);
orders.forEach((order, index) => {
const cancelLogArgs = (cancelLogs[index] as LogWithDecodedArgs<ExchangeCancelEventArgs>).args;
expect(cancelLogArgs.makerAddress).to.eq(makerAddress);
expect(cancelLogArgs.senderAddress).to.eq(coordinatorContract.address);
expect(cancelLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
expect(cancelLogArgs.makerAssetData).to.eq(order.makerAssetData);
expect(cancelLogArgs.takerAssetData).to.eq(order.takerAssetData);
expect(cancelLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(order));
});
});
it('cancelOrdersUpTo call should be successful without an approval', async () => {
const orders: SignedOrder[] = [];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(constants.CANCEL_ORDERS_UP_TO, orders);
const transaction = makerTransactionFactory.newSignedTransaction(data);
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
await coordinatorContract.executeTransaction.sendTransactionAsync(
transaction,
makerAddress,
transaction.signature,
[],
[],
{
from: makerAddress,
},
),
);
const cancelLogs = transactionReceipt.logs.filter(
log => (log as LogWithDecodedArgs<ExchangeCancelUpToEventArgs>).event === 'CancelUpTo',
);
expect(cancelLogs.length).to.eq(1);
const cancelLogArgs = (cancelLogs[0] as LogWithDecodedArgs<ExchangeCancelUpToEventArgs>).args;
expect(cancelLogArgs.makerAddress).to.eq(makerAddress);
expect(cancelLogArgs.senderAddress).to.eq(coordinatorContract.address);
expect(cancelLogArgs.orderEpoch).to.bignumber.eq(new BigNumber(1));
});
});
});
// tslint:disable:max-file-line-count

View File

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

View File

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

View File

@@ -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, CoordinatorContract, hashUtils } from '../src';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
describe('Libs tests', () => {
let coordinatorContract: CoordinatorContract;
const exchangeAddress = addressUtils.generatePseudoRandomAddress();
before(async () => {
await blockchainLifecycle.startAsync();
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
before(async () => {
coordinatorContract = await CoordinatorContract.deployFrom0xArtifactAsync(
artifacts.Coordinator,
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 coordinatorContract.getTransactionHash.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,
coordinatorContract.address,
txOrigin,
approvalExpirationTimeSeconds,
);
const approvalHash = await coordinatorContract.getCoordinatorApprovalHash.callAsync(approval);
expect(expectedApprovalHash).to.eq(approvalHash);
});
});
});

View File

@@ -1,728 +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, CoordinatorContract, exchangeDataEncoder } 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: CoordinatorContract;
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 CoordinatorContract.deployFrom0xArtifactAsync(
artifacts.Coordinator,
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('decodeOrdersFromFillData', () => {
for (const fnName of constants.SINGLE_FILL_FN_NAMES) {
it(`should correctly decode the orders for ${fnName} data`, async () => {
const orders = [defaultOrder];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const decodedOrders = await mixins.decodeOrdersFromFillData.callAsync(data);
const decodedSignedOrders = decodedOrders.map(order => ({
...order,
exchangeAddress: devConstants.NULL_ADDRESS,
signature: devConstants.NULL_BYTES,
}));
expect(orders).to.deep.eq(decodedSignedOrders);
});
}
for (const fnName of constants.BATCH_FILL_FN_NAMES) {
it(`should correctly decode the orders for ${fnName} data`, async () => {
const orders = [defaultOrder, defaultOrder];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const decodedOrders = await mixins.decodeOrdersFromFillData.callAsync(data);
const decodedSignedOrders = decodedOrders.map(order => ({
...order,
exchangeAddress: devConstants.NULL_ADDRESS,
signature: devConstants.NULL_BYTES,
}));
expect(orders).to.deep.eq(decodedSignedOrders);
});
}
for (const fnName of constants.MARKET_FILL_FN_NAMES) {
it(`should correctly decode the orders for ${fnName} data`, async () => {
const orders = [defaultOrder, defaultOrder];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const decodedOrders = await mixins.decodeOrdersFromFillData.callAsync(data);
const decodedSignedOrders = decodedOrders.map(order => ({
...order,
exchangeAddress: devConstants.NULL_ADDRESS,
signature: devConstants.NULL_BYTES,
}));
expect(orders).to.deep.eq(decodedSignedOrders);
});
}
for (const fnName of [constants.CANCEL_ORDER, constants.BATCH_CANCEL_ORDERS, constants.CANCEL_ORDERS_UP_TO]) {
it(`should correctly decode the orders for ${fnName} data`, async () => {
const orders = [defaultOrder, defaultOrder];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const decodedOrders = await mixins.decodeOrdersFromFillData.callAsync(data);
const emptyArray: any[] = [];
expect(emptyArray).to.deep.eq(decodedOrders);
});
}
it('should decode an empty array for invalid data', async () => {
const data = '0x0123456789';
const decodedOrders = await mixins.decodeOrdersFromFillData.callAsync(data);
const emptyArray: any[] = [];
expect(emptyArray).to.deep.eq(decodedOrders);
});
it('should revert if data is less than 4 bytes long', async () => {
const data = '0x010203';
await expectContractCallFailedAsync(
mixins.decodeOrdersFromFillData.callAsync(data),
RevertReason.LibBytesGreaterOrEqualTo4LengthRequired,
);
});
});
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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.assertValidCoordinatorApprovals.callAsync(
transaction,
transactionSignerAddress,
transaction.signature,
[approvalExpirationTimeSeconds],
[approval1.signature],
{ from: approvalSignerAddress2 },
),
RevertReason.InvalidOrigin,
);
});
}
});
describe('cancels', () => {
it('should allow the tx signer to call `cancelOrder` without approval', async () => {
const orders = [defaultOrder];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(constants.CANCEL_ORDER, 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

View File

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

View File

@@ -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_ORDER: 'cancelOrder',
BATCH_CANCEL_ORDERS: 'batchCancelOrders',
CANCEL_ORDERS_UP_TO: 'cancelOrdersUpTo',
TIME_BUFFER: new BigNumber(1000),
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,7 +0,0 @@
{
"extends": "../../tsconfig",
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
"files": ["generated-artifacts/Coordinator.json", "generated-artifacts/CoordinatorRegistry.json"],
"exclude": ["./deploy/solc/solc_bin"]
}

View File

@@ -1,6 +0,0 @@
{
"extends": ["@0x/tslint-config"],
"rules": {
"custom-no-magic-numbers": false
}
}

View File

@@ -1,58 +0,0 @@
[
{
"timestamp": 1563193019,
"version": "0.0.4",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1563047529,
"version": "0.0.3",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1563006338,
"version": "0.0.2",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "0.0.1",
"changes": [
{
"note": "Create dev-utils package",
"pr": 1848
},
{
"note": "Add `LibAssetData` and `LibTransactionDecoder` contracts",
"pr": 1848
},
{
"note": "Refactor `LibAssetData` to only check 0x-specific allowances",
"pr": 1848
},
{
"note": "Refactor `LibAssetData` balance/allowance checks to never revert",
"pr": 1848
},
{
"note": "Refactor `OrderValidationUtils` to calculate `fillableTakerAssetAmount`",
"pr": 1848
},
{
"note": "Add support for StaticCallProxy",
"pr": 1863
}
]
}
]

View File

@@ -1,27 +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
## v0.0.4 - _July 15, 2019_
* Dependencies updated
## v0.0.3 - _July 13, 2019_
* Dependencies updated
## v0.0.2 - _July 13, 2019_
* Dependencies updated
## v0.0.1 - _Invalid date_
* Create dev-utils package (#1848)
* Add `LibAssetData` and `LibTransactionDecoder` contracts (#1848)
* Refactor `LibAssetData` to only check 0x-specific allowances (#1848)
* Refactor `LibAssetData` balance/allowance checks to never revert (#1848)
* Refactor `OrderValidationUtils` to calculate `fillableTakerAssetAmount` (#1848)
* Add support for StaticCallProxy (#1863)

View File

@@ -1 +0,0 @@
[]

View File

@@ -1,73 +0,0 @@
## Dev-Utils
This package implements various utilities for developers. For example, the `DevUtils` contract can query batches of balances or allowances given some `assetData`, can validate batches of orders, and can decode 0x-specific calldata. 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-dev-utils --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-extensions yarn build
```
Or continuously rebuild on change:
```bash
PKG=@0x/contracts-extensions 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).

View File

@@ -1,32 +0,0 @@
{
"artifactsDir": "./generated-artifacts",
"contractsDir": "./contracts",
"useDockerisedSolc": false,
"isOfflineMode": false,
"compilerSettings": {
"evmVersion": "constantinople",
"optimizer": {
"enabled": true,
"runs": 1000000,
"details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
},
"outputSelection": {
"*": {
"*": [
"abi",
"devdoc",
"evm.bytecode.object",
"evm.bytecode.sourceMap",
"evm.deployedBytecode.object",
"evm.deployedBytecode.sourceMap"
]
}
}
},
"contracts": [
"src/DevUtils.sol",
"src/LibAssetData.sol",
"src/LibTransactionDecoder.sol",
"src/EthBalanceChecker.sol"
]
}

View File

@@ -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 "./OrderValidationUtils.sol";
import "./LibTransactionDecoder.sol";
import "./EthBalanceChecker.sol";
// solhint-disable no-empty-blocks
contract DevUtils is
OrderValidationUtils,
LibTransactionDecoder,
EthBalanceChecker
{
constructor (address _exchange, bytes memory _zrxAssetData)
public
OrderValidationUtils(_exchange, _zrxAssetData)
{}
}

View File

@@ -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;
contract EthBalanceChecker {
/// @dev Batch fetches ETH balances
/// @param addresses Array of addresses.
/// @return Array of ETH balances.
function getEthBalances(address[] memory addresses)
public
view
returns (uint256[] memory)
{
uint256[] memory balances = new uint256[](addresses.length);
for (uint256 i = 0; i != addresses.length; i++) {
balances[i] = addresses[i].balance;
}
return balances;
}
}

View File

@@ -1,541 +0,0 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
import "@0x/contracts-asset-proxy/contracts/src/libs/LibAssetProxyIds.sol";
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
contract LibAssetData is
LibAssetProxyIds
{
// 2^256 - 1
uint256 constant internal _MAX_UINT256 = uint256(-1);
// ERC20 selectors
bytes4 constant internal _ERC20_BALANCE_OF_SELECTOR = 0x70a08231;
bytes4 constant internal _ERC20_ALLOWANCE_SELECTOR = 0xdd62ed3e;
// ERC721 selectors
bytes4 constant internal _ERC721_OWNER_OF_SELECTOR = 0x6352211e;
bytes4 constant internal _ERC721_IS_APPROVED_FOR_ALL_SELECTOR = 0xe985e9c5;
bytes4 constant internal _ERC721_GET_APPROVED_SELECTOR = 0x081812fc;
// ERC1155 selectors
bytes4 constant internal _ERC1155_BALANCE_OF_SELECTOR = 0x00fdd58e;
bytes4 constant internal _ERC1155_IS_APPROVED_FOR_ALL_SELECTOR = 0xe985e9c5;
// `transferFrom` selector for all AssetProxy contracts
bytes4 constant internal _ASSET_PROXY_TRANSFER_FROM_SELECTOR = 0xa85e59e4;
using LibBytes for bytes;
// solhint-disable var-name-mixedcase
IExchange internal _EXCHANGE;
address internal _ERC20_PROXY_ADDRESS;
address internal _ERC721_PROXY_ADDRESS;
address internal _ERC1155_PROXY_ADDRESS;
address internal _STATIC_CALL_PROXY_ADDRESS;
// solhint-enable var-name-mixedcase
constructor (address _exchange)
public
{
_EXCHANGE = IExchange(_exchange);
_ERC20_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(ERC20_PROXY_ID);
_ERC721_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(ERC721_PROXY_ID);
_ERC1155_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(ERC1155_PROXY_ID);
_STATIC_CALL_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(STATIC_CALL_PROXY_ID);
}
/// @dev Returns the owner's balance of the assets(s) specified in
/// assetData. When the asset data contains multiple assets (eg in
/// ERC1155 or Multi-Asset), the return value indicates how many
/// complete "baskets" of those assets are owned by owner.
/// @param ownerAddress Owner of the assets specified by assetData.
/// @param assetData Details of asset, encoded per the AssetProxy contract specification.
/// @return Number of assets (or asset baskets) held by owner.
function getBalance(address ownerAddress, bytes memory assetData)
public
view
returns (uint256 balance)
{
// Get id of AssetProxy contract
bytes4 assetProxyId = assetData.readBytes4(0);
if (assetProxyId == ERC20_PROXY_ID) {
// Get ERC20 token address
address tokenAddress = assetData.readAddress(16);
// Encode data for `balanceOf(ownerAddress)`
bytes memory balanceOfData = abi.encodeWithSelector(_ERC20_BALANCE_OF_SELECTOR, ownerAddress);
// Query balance
(bool success, bytes memory returnData) = tokenAddress.staticcall(balanceOfData);
balance = success && returnData.length == 32 ? returnData.readUint256(0) : 0;
} else if (assetProxyId == ERC721_PROXY_ID) {
// Get ERC721 token address and id
(, address tokenAddress, uint256 tokenId) = decodeERC721AssetData(assetData);
// Check if id is owned by ownerAddress
balance = getERC721TokenOwner(tokenAddress, tokenId) == ownerAddress ? 1 : 0;
} else if (assetProxyId == ERC1155_PROXY_ID) {
// Get ERC1155 token address, array of ids, and array of values
(, address tokenAddress, uint256[] memory tokenIds, uint256[] memory tokenValues,) = decodeERC1155AssetData(assetData);
uint256 length = tokenIds.length;
for (uint256 i = 0; i != length; i++) {
// Encode data for `balanceOf(ownerAddress, tokenIds[i])
bytes memory balanceOfData = abi.encodeWithSelector(
_ERC1155_BALANCE_OF_SELECTOR,
ownerAddress,
tokenIds[i]
);
// Query balance
(bool success, bytes memory returnData) = tokenAddress.staticcall(balanceOfData);
uint256 totalBalance = success && returnData.length == 32 ? returnData.readUint256(0) : 0;
// Scale total balance down by corresponding value in assetData
uint256 scaledBalance = totalBalance / tokenValues[i];
if (scaledBalance < balance || balance == 0) {
balance = scaledBalance;
}
}
} else if (assetProxyId == STATIC_CALL_PROXY_ID) {
// Encode data for `staticCallProxy.transferFrom(assetData,...)`
bytes memory transferFromData = abi.encodeWithSelector(
_ASSET_PROXY_TRANSFER_FROM_SELECTOR,
assetData,
address(0), // `from` address is not used
address(0), // `to` address is not used
0 // `amount` is not used
);
// Check if staticcall would be successful
(bool success,) = _STATIC_CALL_PROXY_ADDRESS.staticcall(transferFromData);
// Success means that the staticcall can be made an unlimited amount of times
balance = success ? _MAX_UINT256 : 0;
} else if (assetProxyId == MULTI_ASSET_PROXY_ID) {
// Get array of values and array of assetDatas
(, uint256[] memory assetAmounts, bytes[] memory nestedAssetData) = decodeMultiAssetData(assetData);
uint256 length = nestedAssetData.length;
for (uint256 i = 0; i != length; i++) {
// Query balance of individual assetData
uint256 totalBalance = getBalance(ownerAddress, nestedAssetData[i]);
// Scale total balance down by corresponding value in assetData
uint256 scaledBalance = totalBalance / assetAmounts[i];
if (scaledBalance < balance || balance == 0) {
balance = scaledBalance;
}
}
}
// Balance will be 0 if assetProxyId is unknown
return balance;
}
/// @dev Calls getBalance() for each element of assetData.
/// @param ownerAddress Owner of the assets specified by assetData.
/// @param assetData Array of asset details, each encoded per the AssetProxy contract specification.
/// @return Array of asset balances from getBalance(), with each element
/// corresponding to the same-indexed element in the assetData input.
function getBatchBalances(address ownerAddress, bytes[] memory assetData)
public
view
returns (uint256[] memory balances)
{
uint256 length = assetData.length;
balances = new uint256[](length);
for (uint256 i = 0; i != length; i++) {
balances[i] = getBalance(ownerAddress, assetData[i]);
}
return balances;
}
/// @dev Returns the number of asset(s) (described by assetData) that
/// the corresponding AssetProxy contract is authorized to spend. When the asset data contains
/// multiple assets (eg for Multi-Asset), the return value indicates
/// how many complete "baskets" of those assets may be spent by all of the corresponding
/// AssetProxy contracts.
/// @param ownerAddress Owner of the assets specified by assetData.
/// @param assetData Details of asset, encoded per the AssetProxy contract specification.
/// @return Number of assets (or asset baskets) that the corresponding AssetProxy is authorized to spend.
function getAssetProxyAllowance(address ownerAddress, bytes memory assetData)
public
view
returns (uint256 allowance)
{
// Get id of AssetProxy contract
bytes4 assetProxyId = assetData.readBytes4(0);
if (assetProxyId == MULTI_ASSET_PROXY_ID) {
// Get array of values and array of assetDatas
(, uint256[] memory amounts, bytes[] memory nestedAssetData) = decodeMultiAssetData(assetData);
uint256 length = nestedAssetData.length;
for (uint256 i = 0; i != length; i++) {
// Query allowance of individual assetData
uint256 totalAllowance = getAssetProxyAllowance(ownerAddress, nestedAssetData[i]);
// Scale total allowance down by corresponding value in assetData
uint256 scaledAllowance = totalAllowance / amounts[i];
if (scaledAllowance < allowance || allowance == 0) {
allowance = scaledAllowance;
}
}
return allowance;
}
if (assetProxyId == ERC20_PROXY_ID) {
// Get ERC20 token address
address tokenAddress = assetData.readAddress(16);
// Encode data for `allowance(ownerAddress, _ERC20_PROXY_ADDRESS)`
bytes memory allowanceData = abi.encodeWithSelector(
_ERC20_ALLOWANCE_SELECTOR,
ownerAddress,
_ERC20_PROXY_ADDRESS
);
// Query allowance
(bool success, bytes memory returnData) = tokenAddress.staticcall(allowanceData);
allowance = success && returnData.length == 32 ? returnData.readUint256(0) : 0;
} else if (assetProxyId == ERC721_PROXY_ID) {
// Get ERC721 token address and id
(, address tokenAddress, uint256 tokenId) = decodeERC721AssetData(assetData);
// Encode data for `isApprovedForAll(ownerAddress, _ERC721_PROXY_ADDRESS)`
bytes memory isApprovedForAllData = abi.encodeWithSelector(
_ERC721_IS_APPROVED_FOR_ALL_SELECTOR,
ownerAddress,
_ERC721_PROXY_ADDRESS
);
(bool success, bytes memory returnData) = tokenAddress.staticcall(isApprovedForAllData);
// If not approved for all, call `getApproved(tokenId)`
if (!success || returnData.length != 32 || returnData.readUint256(0) != 1) {
// Encode data for `getApproved(tokenId)`
bytes memory getApprovedData = abi.encodeWithSelector(_ERC721_GET_APPROVED_SELECTOR, tokenId);
(success, returnData) = tokenAddress.staticcall(getApprovedData);
// Allowance is 1 if successful and the approved address is the ERC721Proxy
allowance = success && returnData.length == 32 && returnData.readAddress(12) == _ERC721_PROXY_ADDRESS ? 1 : 0;
} else {
// Allowance is 2^256 - 1 if `isApprovedForAll` returned true
allowance = _MAX_UINT256;
}
} else if (assetProxyId == ERC1155_PROXY_ID) {
// Get ERC1155 token address
(, address tokenAddress, , , ) = decodeERC1155AssetData(assetData);
// Encode data for `isApprovedForAll(ownerAddress, _ERC1155_PROXY_ADDRESS)`
bytes memory isApprovedForAllData = abi.encodeWithSelector(
_ERC1155_IS_APPROVED_FOR_ALL_SELECTOR,
ownerAddress,
_ERC1155_PROXY_ADDRESS
);
// Query allowance
(bool success, bytes memory returnData) = tokenAddress.staticcall(isApprovedForAllData);
allowance = success && returnData.length == 32 && returnData.readUint256(0) == 1 ? _MAX_UINT256 : 0;
} else if (assetProxyId == STATIC_CALL_PROXY_ID) {
// The StaticCallProxy does not require any approvals
allowance = _MAX_UINT256;
}
// Allowance will be 0 if the assetProxyId is unknown
return allowance;
}
/// @dev Calls getAssetProxyAllowance() for each element of assetData.
/// @param ownerAddress Owner of the assets specified by assetData.
/// @param assetData Array of asset details, each encoded per the AssetProxy contract specification.
/// @return An array of asset allowances from getAllowance(), with each
/// element corresponding to the same-indexed element in the assetData input.
function getBatchAssetProxyAllowances(address ownerAddress, bytes[] memory assetData)
public
view
returns (uint256[] memory allowances)
{
uint256 length = assetData.length;
allowances = new uint256[](length);
for (uint256 i = 0; i != length; i++) {
allowances[i] = getAssetProxyAllowance(ownerAddress, assetData[i]);
}
return allowances;
}
/// @dev Calls getBalance() and getAllowance() for assetData.
/// @param ownerAddress Owner of the assets specified by assetData.
/// @param assetData Details of asset, encoded per the AssetProxy contract specification.
/// @return Number of assets (or asset baskets) held by owner, and number
/// of assets (or asset baskets) that the corresponding AssetProxy is authorized to spend.
function getBalanceAndAssetProxyAllowance(address ownerAddress, bytes memory assetData)
public
view
returns (uint256 balance, uint256 allowance)
{
balance = getBalance(ownerAddress, assetData);
allowance = getAssetProxyAllowance(ownerAddress, assetData);
return (balance, allowance);
}
/// @dev Calls getBatchBalances() and getBatchAllowances() for each element of assetData.
/// @param ownerAddress Owner of the assets specified by assetData.
/// @param assetData Array of asset details, each encoded per the AssetProxy contract specification.
/// @return An array of asset balances from getBalance(), and an array of
/// asset allowances from getAllowance(), with each element
/// corresponding to the same-indexed element in the assetData input.
function getBatchBalancesAndAssetProxyAllowances(address ownerAddress, bytes[] memory assetData)
public
view
returns (uint256[] memory balances, uint256[] memory allowances)
{
balances = getBatchBalances(ownerAddress, assetData);
allowances = getBatchAssetProxyAllowances(ownerAddress, assetData);
return (balances, allowances);
}
/// @dev Encode ERC-20 asset data into the format described in the AssetProxy contract specification.
/// @param tokenAddress The address of the ERC-20 contract hosting the asset to be traded.
/// @return AssetProxy-compliant data describing the asset.
function encodeERC20AssetData(address tokenAddress)
public
pure
returns (bytes memory assetData)
{
assetData = abi.encodeWithSelector(ERC20_PROXY_ID, tokenAddress);
return assetData;
}
/// @dev Decode ERC-20 asset data from the format described in the AssetProxy contract specification.
/// @param assetData AssetProxy-compliant asset data describing an ERC-20 asset.
/// @return The ERC-20 AssetProxy identifier, and the address of the ERC-20
/// contract hosting this asset.
function decodeERC20AssetData(bytes memory assetData)
public
pure
returns (
bytes4 assetProxyId,
address tokenAddress
)
{
assetProxyId = assetData.readBytes4(0);
require(
assetProxyId == ERC20_PROXY_ID,
"WRONG_PROXY_ID"
);
tokenAddress = assetData.readAddress(16);
return (assetProxyId, tokenAddress);
}
/// @dev Encode ERC-721 asset data into the format described in the AssetProxy specification.
/// @param tokenAddress The address of the ERC-721 contract hosting the asset to be traded.
/// @param tokenId The identifier of the specific asset to be traded.
/// @return AssetProxy-compliant asset data describing the asset.
function encodeERC721AssetData(address tokenAddress, uint256 tokenId)
public
pure
returns (bytes memory assetData)
{
assetData = abi.encodeWithSelector(
ERC721_PROXY_ID,
tokenAddress,
tokenId
);
return assetData;
}
/// @dev Decode ERC-721 asset data from the format described in the AssetProxy contract specification.
/// @param assetData AssetProxy-compliant asset data describing an ERC-721 asset.
/// @return The ERC-721 AssetProxy identifier, the address of the ERC-721
/// contract hosting this asset, and the identifier of the specific
/// asset to be traded.
function decodeERC721AssetData(bytes memory assetData)
public
pure
returns (
bytes4 assetProxyId,
address tokenAddress,
uint256 tokenId
)
{
assetProxyId = assetData.readBytes4(0);
require(
assetProxyId == ERC721_PROXY_ID,
"WRONG_PROXY_ID"
);
tokenAddress = assetData.readAddress(16);
tokenId = assetData.readUint256(36);
return (assetProxyId, tokenAddress, tokenId);
}
/// @dev Encode ERC-1155 asset data into the format described in the AssetProxy contract specification.
/// @param tokenAddress The address of the ERC-1155 contract hosting the asset(s) to be traded.
/// @param tokenIds The identifiers of the specific assets to be traded.
/// @param tokenValues The amounts of each asset to be traded.
/// @param callbackData Data to be passed to receiving contracts when a transfer is performed.
/// @return AssetProxy-compliant asset data describing the set of assets.
function encodeERC1155AssetData(
address tokenAddress,
uint256[] memory tokenIds,
uint256[] memory tokenValues,
bytes memory callbackData
)
public
pure
returns (bytes memory assetData)
{
assetData = abi.encodeWithSelector(
ERC1155_PROXY_ID,
tokenAddress,
tokenIds,
tokenValues,
callbackData
);
return assetData;
}
/// @dev Decode ERC-1155 asset data from the format described in the AssetProxy contract specification.
/// @param assetData AssetProxy-compliant asset data describing an ERC-1155 set of assets.
/// @return The ERC-1155 AssetProxy identifier, the address of the ERC-1155
/// contract hosting the assets, an array of the identifiers of the
/// assets to be traded, an array of asset amounts to be traded, and
/// callback data. Each element of the arrays corresponds to the
/// same-indexed element of the other array. Return values specified as
/// `memory` are returned as pointers to locations within the memory of
/// the input parameter `assetData`.
function decodeERC1155AssetData(bytes memory assetData)
public
pure
returns (
bytes4 assetProxyId,
address tokenAddress,
uint256[] memory tokenIds,
uint256[] memory tokenValues,
bytes memory callbackData
)
{
assetProxyId = assetData.readBytes4(0);
require(
assetProxyId == ERC1155_PROXY_ID,
"WRONG_PROXY_ID"
);
assembly {
// Skip selector and length to get to the first parameter:
assetData := add(assetData, 36)
// Read the value of the first parameter:
tokenAddress := mload(assetData)
// Point to the next parameter's data:
tokenIds := add(assetData, mload(add(assetData, 32)))
// Point to the next parameter's data:
tokenValues := add(assetData, mload(add(assetData, 64)))
// Point to the next parameter's data:
callbackData := add(assetData, mload(add(assetData, 96)))
}
return (
assetProxyId,
tokenAddress,
tokenIds,
tokenValues,
callbackData
);
}
/// @dev Encode data for multiple assets, per the AssetProxy contract specification.
/// @param amounts The amounts of each asset to be traded.
/// @param nestedAssetData AssetProxy-compliant data describing each asset to be traded.
/// @return AssetProxy-compliant data describing the set of assets.
function encodeMultiAssetData(uint256[] memory amounts, bytes[] memory nestedAssetData)
public
pure
returns (bytes memory assetData)
{
assetData = abi.encodeWithSelector(
MULTI_ASSET_PROXY_ID,
amounts,
nestedAssetData
);
return assetData;
}
/// @dev Decode multi-asset data from the format described in the AssetProxy contract specification.
/// @param assetData AssetProxy-compliant data describing a multi-asset basket.
/// @return The Multi-Asset AssetProxy identifier, an array of the amounts
/// of the assets to be traded, and an array of the
/// AssetProxy-compliant data describing each asset to be traded. Each
/// element of the arrays corresponds to the same-indexed element of the other array.
function decodeMultiAssetData(bytes memory assetData)
public
pure
returns (
bytes4 assetProxyId,
uint256[] memory amounts,
bytes[] memory nestedAssetData
)
{
assetProxyId = assetData.readBytes4(0);
require(
assetProxyId == MULTI_ASSET_PROXY_ID,
"WRONG_PROXY_ID"
);
// solhint-disable indent
(amounts, nestedAssetData) = abi.decode(
assetData.slice(4, assetData.length),
(uint256[], bytes[])
);
// solhint-enable indent
}
/// @dev Calls `asset.ownerOf(tokenId)`, but returns a null owner instead of reverting on an unowned asset.
/// @param tokenAddress Address of ERC721 asset.
/// @param tokenId The identifier for the specific NFT.
/// @return Owner of tokenId or null address if unowned.
function getERC721TokenOwner(address tokenAddress, uint256 tokenId)
public
view
returns (address ownerAddress)
{
bytes memory ownerOfCalldata = abi.encodeWithSelector(
_ERC721_OWNER_OF_SELECTOR,
tokenId
);
(bool success, bytes memory returnData) = tokenAddress.staticcall(ownerOfCalldata);
ownerAddress = (success && returnData.length == 32) ? returnData.readAddress(12) : address(0);
return ownerAddress;
}
}

View File

@@ -1,193 +0,0 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.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";
contract LibTransactionDecoder is
LibExchangeSelectors
{
using LibBytes for bytes;
/// @dev Decodes the call data for an Exchange contract method call.
/// @param transactionData ABI-encoded calldata for an Exchange
/// contract method call.
/// @return The name of the function called, and the parameters it was
/// given. For single-order fills and cancels, the arrays will have
/// just one element.
function decodeZeroExTransactionData(bytes memory transactionData)
public
pure
returns(
string memory functionName,
LibOrder.Order[] memory orders,
uint256[] memory takerAssetFillAmounts,
bytes[] memory signatures
)
{
bytes4 functionSelector = transactionData.readBytes4(0);
if (functionSelector == BATCH_CANCEL_ORDERS_SELECTOR) {
functionName = "batchCancelOrders";
} else if (functionSelector == BATCH_FILL_ORDERS_SELECTOR) {
functionName = "batchFillOrders";
} else if (functionSelector == BATCH_FILL_ORDERS_NO_THROW_SELECTOR) {
functionName = "batchFillOrdersNoThrow";
} else if (functionSelector == BATCH_FILL_OR_KILL_ORDERS_SELECTOR) {
functionName = "batchFillOrKillOrders";
} else if (functionSelector == CANCEL_ORDER_SELECTOR) {
functionName = "cancelOrder";
} else if (functionSelector == FILL_ORDER_SELECTOR) {
functionName = "fillOrder";
} else if (functionSelector == FILL_ORDER_NO_THROW_SELECTOR) {
functionName = "fillOrderNoThrow";
} else if (functionSelector == FILL_OR_KILL_ORDER_SELECTOR) {
functionName = "fillOrKillOrder";
} else if (functionSelector == MARKET_BUY_ORDERS_SELECTOR) {
functionName = "marketBuyOrders";
} else if (functionSelector == MARKET_BUY_ORDERS_NO_THROW_SELECTOR) {
functionName = "marketBuyOrdersNoThrow";
} else if (functionSelector == MARKET_SELL_ORDERS_SELECTOR) {
functionName = "marketSellOrders";
} else if (functionSelector == MARKET_SELL_ORDERS_NO_THROW_SELECTOR) {
functionName = "marketSellOrdersNoThrow";
} else if (functionSelector == MATCH_ORDERS_SELECTOR) {
functionName = "matchOrders";
} else if (
functionSelector == CANCEL_ORDERS_UP_TO_SELECTOR ||
functionSelector == EXECUTE_TRANSACTION_SELECTOR
// TODO: add new noThrow cancel functions when https://github.com/0xProject/ZEIPs/issues/35 is merged.
) {
revert("UNIMPLEMENTED");
} else {
revert("UNKNOWN_FUNCTION_SELECTOR");
}
if (functionSelector == BATCH_CANCEL_ORDERS_SELECTOR) {
// solhint-disable-next-line indent
orders = abi.decode(transactionData.slice(4, transactionData.length), (LibOrder.Order[]));
takerAssetFillAmounts = new uint256[](0);
signatures = new bytes[](0);
} else if (
functionSelector == BATCH_FILL_OR_KILL_ORDERS_SELECTOR ||
functionSelector == BATCH_FILL_ORDERS_NO_THROW_SELECTOR ||
functionSelector == BATCH_FILL_ORDERS_SELECTOR
) {
(orders, takerAssetFillAmounts, signatures) = _makeReturnValuesForBatchFill(transactionData);
} else if (functionSelector == CANCEL_ORDER_SELECTOR) {
orders = new LibOrder.Order[](1);
orders[0] = abi.decode(transactionData.slice(4, transactionData.length), (LibOrder.Order));
takerAssetFillAmounts = new uint256[](0);
signatures = new bytes[](0);
} else if (
functionSelector == FILL_OR_KILL_ORDER_SELECTOR ||
functionSelector == FILL_ORDER_SELECTOR ||
functionSelector == FILL_ORDER_NO_THROW_SELECTOR
) {
(orders, takerAssetFillAmounts, signatures) = _makeReturnValuesForSingleOrderFill(transactionData);
} else if (
functionSelector == MARKET_BUY_ORDERS_SELECTOR ||
functionSelector == MARKET_BUY_ORDERS_NO_THROW_SELECTOR ||
functionSelector == MARKET_SELL_ORDERS_SELECTOR ||
functionSelector == MARKET_SELL_ORDERS_NO_THROW_SELECTOR
) {
(orders, takerAssetFillAmounts, signatures) = _makeReturnValuesForMarketFill(transactionData);
} else if (functionSelector == MATCH_ORDERS_SELECTOR) {
(
LibOrder.Order memory leftOrder,
LibOrder.Order memory rightOrder,
bytes memory leftSignature,
bytes memory rightSignature
) = abi.decode(
transactionData.slice(4, transactionData.length),
(LibOrder.Order, LibOrder.Order, bytes, bytes)
);
orders = new LibOrder.Order[](2);
orders[0] = leftOrder;
orders[1] = rightOrder;
takerAssetFillAmounts = new uint256[](2);
takerAssetFillAmounts[0] = leftOrder.takerAssetAmount;
takerAssetFillAmounts[1] = rightOrder.takerAssetAmount;
signatures = new bytes[](2);
signatures[0] = leftSignature;
signatures[1] = rightSignature;
}
}
function _makeReturnValuesForSingleOrderFill(bytes memory transactionData)
private
pure
returns(
LibOrder.Order[] memory orders,
uint256[] memory takerAssetFillAmounts,
bytes[] memory signatures
)
{
orders = new LibOrder.Order[](1);
takerAssetFillAmounts = new uint256[](1);
signatures = new bytes[](1);
// solhint-disable-next-line indent
(orders[0], takerAssetFillAmounts[0], signatures[0]) = abi.decode(
transactionData.slice(4, transactionData.length),
(LibOrder.Order, uint256, bytes)
);
}
function _makeReturnValuesForBatchFill(bytes memory transactionData)
private
pure
returns(
LibOrder.Order[] memory orders,
uint256[] memory takerAssetFillAmounts,
bytes[] memory signatures
)
{
// solhint-disable-next-line indent
(orders, takerAssetFillAmounts, signatures) = abi.decode(
transactionData.slice(4, transactionData.length),
// solhint-disable-next-line indent
(LibOrder.Order[], uint256[], bytes[])
);
}
function _makeReturnValuesForMarketFill(bytes memory transactionData)
private
pure
returns(
LibOrder.Order[] memory orders,
uint256[] memory takerAssetFillAmounts,
bytes[] memory signatures
)
{
takerAssetFillAmounts = new uint256[](1);
// solhint-disable-next-line indent
(orders, takerAssetFillAmounts[0], signatures) = abi.decode(
transactionData.slice(4, transactionData.length),
// solhint-disable-next-line indent
(LibOrder.Order[], uint256, bytes[])
);
}
}

View File

@@ -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;
pragma experimental ABIEncoderV2;
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
import "./LibAssetData.sol";
contract OrderValidationUtils is
LibAssetData,
LibMath
{
using LibBytes for bytes;
// solhint-disable var-name-mixedcase
bytes internal _ZRX_ASSET_DATA;
// solhint-enable var-name-mixedcase
constructor (address _exchange, bytes memory _zrxAssetData)
public
LibAssetData(_exchange)
{
_ZRX_ASSET_DATA = _zrxAssetData;
}
/// @dev Fetches all order-relevant information needed to validate if the supplied order is fillable.
/// @param order The order structure.
/// @param signature Signature provided by maker that proves the order's authenticity.
/// `0x01` can always be provided if the signature does not need to be validated.
/// @return The orderInfo (hash, status, and `takerAssetAmount` already filled for the given order),
/// fillableTakerAssetAmount (amount of the order's `takerAssetAmount` that is fillable given all on-chain state),
/// and isValidSignature (validity of the provided signature).
/// NOTE: If the `takerAssetData` encodes data for multiple assets, `fillableTakerAssetAmount` will represent a "scaled"
/// amount, meaning it must be multiplied by all the individual asset amounts within the `takerAssetData` to get the final
/// amount of each asset that can be filled.
function getOrderRelevantState(LibOrder.Order memory order, bytes memory signature)
public
view
returns (
LibOrder.OrderInfo memory orderInfo,
uint256 fillableTakerAssetAmount,
bool isValidSignature
)
{
// Get info specific to order
orderInfo = _EXCHANGE.getOrderInfo(order);
// Validate the maker's signature
address makerAddress = order.makerAddress;
isValidSignature = _EXCHANGE.isValidSignature(
orderInfo.orderHash,
makerAddress,
signature
);
// Get the transferable amount of the `makerAsset`
uint256 transferableMakerAssetAmount = getTransferableAssetAmount(makerAddress, order.makerAssetData);
// Assign to stack variables to reduce redundant mloads/sloads
uint256 takerAssetAmount = order.takerAssetAmount;
uint256 makerFee = order.makerFee;
bytes memory zrxAssetData = _ZRX_ASSET_DATA;
// Get the amount of `takerAsset` that is transferable to maker given the transferability of `makerAsset`, `makerFeeAsset`,
// and the total amounts specified in the order
uint256 transferableTakerAssetAmount;
if (order.makerAssetData.equals(zrxAssetData)) {
// If `makerAsset` equals `makerFeeAsset`, the % that can be filled is
// transferableMakerAssetAmount / (makerAssetAmount + makerFee)
transferableTakerAssetAmount = getPartialAmountFloor(
transferableMakerAssetAmount,
safeAdd(order.makerAssetAmount, makerFee),
takerAssetAmount
);
} else {
// Get the transferable amount of the `makerFeeAsset`
uint256 transferableMakerFeeAssetAmount = getTransferableAssetAmount(makerAddress, zrxAssetData);
// If `makerFee` is 0, the % that can be filled is (transferableMakerAssetAmount / makerAssetAmount)
if (makerFee == 0) {
transferableTakerAssetAmount = getPartialAmountFloor(
transferableMakerAssetAmount,
order.makerAssetAmount,
takerAssetAmount
);
// If `makerAsset` does not equal `makerFeeAsset`, the % that can be filled is the lower of
// (transferableMakerAssetAmount / makerAssetAmount) and (transferableMakerAssetFeeAmount / makerFee)
} else {
uint256 transferableMakerToTakerAmount = getPartialAmountFloor(
transferableMakerAssetAmount,
order.makerAssetAmount,
takerAssetAmount
);
uint256 transferableMakerFeeToTakerAmount = getPartialAmountFloor(
transferableMakerFeeAssetAmount,
makerFee,
takerAssetAmount
);
transferableTakerAssetAmount = min256(transferableMakerToTakerAmount, transferableMakerFeeToTakerAmount);
}
}
// `fillableTakerAssetAmount` is the lower of the order's remaining `takerAssetAmount` and the `transferableTakerAssetAmount`
fillableTakerAssetAmount = min256(
safeSub(takerAssetAmount, orderInfo.orderTakerAssetFilledAmount),
transferableTakerAssetAmount
);
return (orderInfo, fillableTakerAssetAmount, isValidSignature);
}
/// @dev Fetches all order-relevant information needed to validate if the supplied orders are fillable.
/// @param orders Array of order structures.
/// @param signatures Array of signatures provided by makers that prove the authenticity of the orders.
/// `0x01` can always be provided if a signature does not need to be validated.
/// @return The ordersInfo (array of the hash, status, and `takerAssetAmount` already filled for each order),
/// fillableTakerAssetAmounts (array of amounts for each order's `takerAssetAmount` that is fillable given all on-chain state),
/// and isValidSignature (array containing the validity of each provided signature).
/// NOTE: If the `takerAssetData` encodes data for multiple assets, each element of `fillableTakerAssetAmounts`
/// will represent a "scaled" amount, meaning it must be multiplied by all the individual asset amounts within
/// the `takerAssetData` to get the final amount of each asset that can be filled.
function getOrderRelevantStates(LibOrder.Order[] memory orders, bytes[] memory signatures)
public
view
returns (
LibOrder.OrderInfo[] memory ordersInfo,
uint256[] memory fillableTakerAssetAmounts,
bool[] memory isValidSignature
)
{
uint256 length = orders.length;
ordersInfo = new LibOrder.OrderInfo[](length);
fillableTakerAssetAmounts = new uint256[](length);
isValidSignature = new bool[](length);
for (uint256 i = 0; i != length; i++) {
(ordersInfo[i], fillableTakerAssetAmounts[i], isValidSignature[i]) = getOrderRelevantState(
orders[i],
signatures[i]
);
}
return (ordersInfo, fillableTakerAssetAmounts, isValidSignature);
}
/// @dev Gets the amount of an asset transferable by the owner.
/// @param ownerAddress Address of the owner of the asset.
/// @param assetData Description of tokens, per the AssetProxy contract specification.
/// @return The amount of the asset tranferable by the owner.
/// NOTE: If the `assetData` encodes data for multiple assets, the `transferableAssetAmount`
/// will represent the amount of times the entire `assetData` can be transferred. To calculate
/// the total individual transferable amounts, this scaled `transferableAmount` must be multiplied by
/// the individual asset amounts located within the `assetData`.
function getTransferableAssetAmount(address ownerAddress, bytes memory assetData)
public
view
returns (uint256 transferableAssetAmount)
{
(uint256 balance, uint256 allowance) = getBalanceAndAssetProxyAllowance(ownerAddress, assetData);
transferableAssetAmount = min256(balance, allowance);
return transferableAssetAmount;
}
}

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