Merge branch 'development' into dev-dropdown

* development: (120 commits)
  Exit with non-error code at end of publishRelease
  Publish
  Updated CHANGELOGS
  Fix prettier
  Update changelog files for RC packages
  Skip doc generation for local publishes since we test this in a separate CI test
  Fix typo
  Use absolute path
  Run yarn a second time if the first fails
  Try relative to root dir
  Fix command
  Move md files to lib folder during build
  Change exit code to failure
  Fix sra-spec `main` and `types` in package.json
  Actual relative path
  Try relative path
  Issue #1025 BlockParam unroll
  Fix command
  Move md files to lib folder during build
  Fix sra-spec `main` and `types` in package.json
  ...
This commit is contained in:
Fabio Berger 2018-08-27 15:28:57 +01:00
commit b8241c0f80
171 changed files with 3499 additions and 1204 deletions

View File

@ -18,11 +18,11 @@ jobs:
- yarn-packages-master - yarn-packages-master
- yarn-packages- - yarn-packages-
- run: - run:
name: yarn name: install-yarn
command: yarn --frozen-lockfile install || true command: sudo npm install --global yarn@1.9.4
- run: - run:
name: yarn name: yarn
command: yarn --frozen-lockfile install command: yarn --frozen-lockfile install || yarn --frozen-lockfile install
- save_cache: - save_cache:
name: Save Yarn Package Cache name: Save Yarn Package Cache
key: yarn-packages-{{ .Branch }}-{{ checksum "yarn.lock" }} key: yarn-packages-{{ .Branch }}-{{ checksum "yarn.lock" }}

View File

@ -15,7 +15,7 @@ lib
/packages/contract-wrappers/src/artifacts /packages/contract-wrappers/src/artifacts
/packages/order-watcher/src/artifacts /packages/order-watcher/src/artifacts
/packages/metacoin/artifacts /packages/metacoin/artifacts
/packages/sra-api/public/ /packages/sra-spec/public/
/packages/contract-wrappers/test/artifacts /packages/contract-wrappers/test/artifacts
/packages/order-watcher/test/artifacts /packages/order-watcher/test/artifacts
/packages/migrations/artifacts/1.0.0 /packages/migrations/artifacts/1.0.0

View File

@ -33,7 +33,7 @@ If you're developing on 0x now or are interested in using 0x infrastructure in t
| [`@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/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-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/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-api`](/packages/sra-api) | [![npm](https://img.shields.io/npm/v/@0xproject/sra-api.svg)](https://www.npmjs.com/package/@0xproject/sra-api) | OpenAPI specification for the standard relayer API | | [`@0xproject/sra-spec`](/packages/sra-spec) | [![npm](https://img.shields.io/npm/v/@0xproject/sra-spec.svg)](https://www.npmjs.com/package/@0xproject/sra-spec) | OpenAPI specification for the standard relayer API |
| [`@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/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/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/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) |

View File

@ -1,4 +1,22 @@
[ [
{
"version": "1.0.1-rc.6",
"changes": [
{
"note": "Fix missing `BlockParamLiteral` type import issue"
}
],
"timestamp": 1535377027
},
{
"version": "1.0.1-rc.5",
"changes": [
{
"note":
"Fix `main` and `types` package.json entries so that they point to the new location of index.d.ts and index.js"
}
]
},
{ {
"version": "1.0.1-rc.4", "version": "1.0.1-rc.4",
"changes": [ "changes": [
@ -7,7 +25,8 @@
"Re-organize the exported interface of 0x.js. Remove the `ZeroEx` class, and instead export the same exports as `0x.js`'s sub-packages: `@0xproject/contract-wrappers`, `@0xproject/order-utils` and `@0xproject/order-watcher`", "Re-organize the exported interface of 0x.js. Remove the `ZeroEx` class, and instead export the same exports as `0x.js`'s sub-packages: `@0xproject/contract-wrappers`, `@0xproject/order-utils` and `@0xproject/order-watcher`",
"pr": 963 "pr": 963
} }
] ],
"timestamp": 1535133899
}, },
{ {
"version": "1.0.1-rc.3", "version": "1.0.1-rc.3",

View File

@ -5,7 +5,19 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG CHANGELOG
## v1.0.1-rc.3 - _August 13, 2018_ ## v1.0.1-rc.6 - _August 27, 2018_
* Fix missing `BlockParamLiteral` type import issue
## v1.0.1-rc.5 - _Invalid date_
* Fix `main` and `types` package.json entries so that they point to the new location of index.d.ts and index.js
## v1.0.1-rc.4 - _August 24, 2018_
* Re-organize the exported interface of 0x.js. Remove the `ZeroEx` class, and instead export the same exports as `0x.js`'s sub-packages: `@0xproject/contract-wrappers`, `@0xproject/order-utils` and `@0xproject/order-watcher` (#963)
## v1.0.1-rc.3 - _August 14, 2018_
* Dependencies updated * Dependencies updated
* Update ecSignOrderHashAsync to return the signature as a string for immediate use in contracts (#914) * Update ecSignOrderHashAsync to return the signature as a string for immediate use in contracts (#914)
@ -22,11 +34,11 @@ CHANGELOG
* Dependencies updated * Dependencies updated
## v1.0.0-rc.2 - _July 19, 2018_ ## v1.0.0-rc.2 - _July 20, 2018_
* Remove `zeroEx.assetData` and instead re-export it's static functions directly off `ZeroEx` * Remove `zeroEx.assetData` and instead re-export it's static functions directly off `ZeroEx`
## v1.0.0-rc.1 - _July 19, 2018_ ## v1.0.0-rc.1 - _July 20, 2018_
* Remove tokenRegistry wrapper (#863) * Remove tokenRegistry wrapper (#863)
* Rename `zeroEx.token` to `zeroEx.erc20Token`, and add `zeroEx.erc721Token` (#863) * Rename `zeroEx.token` to `zeroEx.erc20Token`, and add `zeroEx.erc721Token` (#863)
@ -66,7 +78,7 @@ CHANGELOG
* Renamed createOrderStateWatcher to createOrderWatcherAsync since it is now async (#579) * Renamed createOrderStateWatcher to createOrderWatcherAsync since it is now async (#579)
* Renamed ZeroExError to ContractWrappersErrors since they now lives in the @0xproject/contract-wrappers subpackage (#579) * Renamed ZeroExError to ContractWrappersErrors since they now lives in the @0xproject/contract-wrappers subpackage (#579)
## v0.37.2 - _May 4, 2018_ ## v0.37.2 - _May 5, 2018_
* Dependencies updated * Dependencies updated

View File

@ -1,6 +1,6 @@
{ {
"name": "0x.js", "name": "0x.js",
"version": "1.0.1-rc.3", "version": "1.0.1-rc.6",
"engines": { "engines": {
"node": ">=6.12" "node": ">=6.12"
}, },
@ -12,15 +12,14 @@
"tokens", "tokens",
"exchange" "exchange"
], ],
"main": "lib/src/index.js", "main": "lib/index.js",
"types": "lib/src/index.d.ts", "types": "lib/index.d.ts",
"scripts": { "scripts": {
"watch_without_deps": "tsc -w", "watch_without_deps": "tsc -w",
"build": "yarn build:all", "build": "yarn build:all",
"build:all": "run-p build:umd:prod build:commonjs", "build:all": "run-p build:umd:prod build:commonjs",
"lint": "tslint --project . --exclude **/src/generated_contract_wrappers/**/*", "lint": "tslint --project . --exclude **/src/generated_contract_wrappers/**/*",
"test:circleci": "run-s test:coverage", "test:circleci": "run-s test:coverage",
"test": "yarn run_mocha",
"rebuild_and_test": "run-s build test", "rebuild_and_test": "run-s build test",
"test:coverage": "nyc npm run test --all && yarn coverage:report:lcov", "test:coverage": "nyc npm run test --all && yarn coverage:report:lcov",
"coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info", "coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info",
@ -43,11 +42,11 @@
}, },
"license": "Apache-2.0", "license": "Apache-2.0",
"devDependencies": { "devDependencies": {
"@0xproject/abi-gen": "^1.0.5", "@0xproject/abi-gen": "^1.0.7",
"@0xproject/dev-utils": "^1.0.4", "@0xproject/dev-utils": "^1.0.6",
"@0xproject/migrations": "^1.0.4", "@0xproject/migrations": "^1.0.6",
"@0xproject/monorepo-scripts": "^1.0.5", "@0xproject/monorepo-scripts": "^1.0.7",
"@0xproject/tslint-config": "^1.0.5", "@0xproject/tslint-config": "^1.0.6",
"@types/lodash": "4.14.104", "@types/lodash": "4.14.104",
"@types/mocha": "^2.2.42", "@types/mocha": "^2.2.42",
"@types/node": "^8.0.53", "@types/node": "^8.0.53",
@ -74,17 +73,17 @@
"webpack": "^3.1.0" "webpack": "^3.1.0"
}, },
"dependencies": { "dependencies": {
"@0xproject/assert": "^1.0.5", "@0xproject/assert": "^1.0.7",
"@0xproject/base-contract": "^2.0.0-rc.1", "@0xproject/base-contract": "^2.0.1",
"@0xproject/contract-wrappers": "^1.0.1-rc.3", "@0xproject/contract-wrappers": "^1.0.1-rc.5",
"@0xproject/order-utils": "^1.0.1-rc.3", "@0xproject/order-utils": "^1.0.1-rc.6",
"@0xproject/order-watcher": "1.0.1-rc.3", "@0xproject/order-watcher": "^1.0.1-rc.5",
"@0xproject/subproviders": "^1.0.5", "@0xproject/subproviders": "^2.0.1",
"@0xproject/types": "^1.0.1-rc.4", "@0xproject/types": "^1.0.1-rc.6",
"@0xproject/typescript-typings": "^1.0.4", "@0xproject/typescript-typings": "^1.0.5",
"@0xproject/utils": "^1.0.5", "@0xproject/utils": "^1.0.7",
"@0xproject/web3-wrapper": "^1.2.0", "@0xproject/web3-wrapper": "^2.0.1",
"ethereum-types": "^1.0.4", "ethereum-types": "^1.0.5",
"ethers": "3.0.22", "ethers": "3.0.22",
"lodash": "^4.17.5", "lodash": "^4.17.5",
"web3-provider-engine": "14.0.6" "web3-provider-engine": "14.0.6"

View File

@ -1,4 +1,22 @@
[ [
{
"timestamp": 1535377027,
"version": "1.0.7",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1535133899,
"version": "1.0.6",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{ {
"timestamp": 1534210131, "timestamp": 1534210131,
"version": "1.0.5", "version": "1.0.5",

View File

@ -5,7 +5,15 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG CHANGELOG
## v1.0.5 - _August 13, 2018_ ## v1.0.7 - _August 27, 2018_
* Dependencies updated
## v1.0.6 - _August 24, 2018_
* Dependencies updated
## v1.0.5 - _August 14, 2018_
* Dependencies updated * Dependencies updated
@ -25,7 +33,7 @@ CHANGELOG
* Fix the abi-gen entry point in package.json (#901) * Fix the abi-gen entry point in package.json (#901)
## v1.0.0 - _July 19, 2018_ ## v1.0.0 - _July 20, 2018_
* Convert e_r_c to erc in generated file names (#822) * Convert e_r_c to erc in generated file names (#822)
* Remove the output directory before writing to it (#822) * Remove the output directory before writing to it (#822)
@ -43,7 +51,7 @@ CHANGELOG
* Dependencies updated * Dependencies updated
## v0.3.1 - _May 31, 2018_ ## v0.3.1 - _June 1, 2018_
* Incorrect publish that was unpublished * Incorrect publish that was unpublished
@ -51,7 +59,7 @@ CHANGELOG
* Properly export the executable binary (#588) * Properly export the executable binary (#588)
## v0.2.13 - _May 4, 2018_ ## v0.2.13 - _May 5, 2018_
* Dependencies updated * Dependencies updated

View File

@ -1,6 +1,6 @@
{ {
"name": "@0xproject/abi-gen", "name": "@0xproject/abi-gen",
"version": "1.0.5", "version": "1.0.7",
"engines": { "engines": {
"node": ">=6.12" "node": ">=6.12"
}, },
@ -31,10 +31,10 @@
}, },
"homepage": "https://github.com/0xProject/0x-monorepo/packages/abi-gen/README.md", "homepage": "https://github.com/0xProject/0x-monorepo/packages/abi-gen/README.md",
"dependencies": { "dependencies": {
"@0xproject/typescript-typings": "^1.0.4", "@0xproject/typescript-typings": "^1.0.5",
"@0xproject/utils": "^1.0.5", "@0xproject/utils": "^1.0.7",
"chalk": "^2.3.0", "chalk": "^2.3.0",
"ethereum-types": "^1.0.4", "ethereum-types": "^1.0.5",
"glob": "^7.1.2", "glob": "^7.1.2",
"handlebars": "^4.0.11", "handlebars": "^4.0.11",
"lodash": "^4.17.5", "lodash": "^4.17.5",
@ -45,7 +45,7 @@
"yargs": "^10.0.3" "yargs": "^10.0.3"
}, },
"devDependencies": { "devDependencies": {
"@0xproject/tslint-config": "^1.0.5", "@0xproject/tslint-config": "^1.0.6",
"@types/glob": "5.0.35", "@types/glob": "5.0.35",
"@types/handlebars": "^4.0.36", "@types/handlebars": "^4.0.36",
"@types/mkdirp": "^0.5.1", "@types/mkdirp": "^0.5.1",

View File

@ -1,4 +1,22 @@
[ [
{
"timestamp": 1535377027,
"version": "1.0.7",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1535133899,
"version": "1.0.6",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{ {
"timestamp": 1534210131, "timestamp": 1534210131,
"version": "1.0.5", "version": "1.0.5",

View File

@ -5,7 +5,15 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG CHANGELOG
## v1.0.5 - _August 13, 2018_ ## v1.0.7 - _August 27, 2018_
* Dependencies updated
## v1.0.6 - _August 24, 2018_
* Dependencies updated
## v1.0.5 - _August 14, 2018_
* Dependencies updated * Dependencies updated
@ -25,7 +33,7 @@ CHANGELOG
* Dependencies updated * Dependencies updated
## v1.0.0 - _July 19, 2018_ ## v1.0.0 - _July 20, 2018_
* Fix bug in string enum assertion. We erroneously were checking against the enum keys, not values (#821) * Fix bug in string enum assertion. We erroneously were checking against the enum keys, not values (#821)
@ -41,7 +49,7 @@ CHANGELOG
* Dependencies updated * Dependencies updated
## v0.2.11 - _May 31, 2018_ ## v0.2.11 - _June 1, 2018_
* Incorrect publish that was unpublished * Incorrect publish that was unpublished
@ -49,7 +57,7 @@ CHANGELOG
* Dependencies updated * Dependencies updated
## v0.2.9 - _May 4, 2018_ ## v0.2.9 - _May 5, 2018_
* Dependencies updated * Dependencies updated

View File

@ -1,6 +1,6 @@
{ {
"name": "@0xproject/assert", "name": "@0xproject/assert",
"version": "1.0.5", "version": "1.0.7",
"engines": { "engines": {
"node": ">=6.12" "node": ">=6.12"
}, },
@ -29,7 +29,7 @@
}, },
"homepage": "https://github.com/0xProject/0x-monorepo/packages/assert/README.md", "homepage": "https://github.com/0xProject/0x-monorepo/packages/assert/README.md",
"devDependencies": { "devDependencies": {
"@0xproject/tslint-config": "^1.0.5", "@0xproject/tslint-config": "^1.0.6",
"@types/lodash": "4.14.104", "@types/lodash": "4.14.104",
"@types/mocha": "^2.2.42", "@types/mocha": "^2.2.42",
"@types/valid-url": "^1.0.2", "@types/valid-url": "^1.0.2",
@ -45,9 +45,9 @@
"typescript": "3.0.1" "typescript": "3.0.1"
}, },
"dependencies": { "dependencies": {
"@0xproject/json-schemas": "^1.0.1-rc.4", "@0xproject/json-schemas": "^1.0.1-rc.6",
"@0xproject/typescript-typings": "^1.0.4", "@0xproject/typescript-typings": "^1.0.5",
"@0xproject/utils": "^1.0.5", "@0xproject/utils": "^1.0.7",
"lodash": "^4.17.5", "lodash": "^4.17.5",
"valid-url": "^1.0.9" "valid-url": "^1.0.9"
}, },

View File

@ -1,4 +1,22 @@
[ [
{
"timestamp": 1535377027,
"version": "2.0.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1535133899,
"version": "2.0.0",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{ {
"version": "2.0.0-rc.1", "version": "2.0.0-rc.1",
"changes": [ "changes": [

View File

@ -5,7 +5,15 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG CHANGELOG
## v2.0.0-rc.1 - _August 13, 2018_ ## v2.0.1 - _August 27, 2018_
* Dependencies updated
## v2.0.0 - _August 24, 2018_
* Dependencies updated
## v2.0.0-rc.1 - _August 14, 2018_
* Added strict encoding/decoding checks for sendTransaction and call (#915) * Added strict encoding/decoding checks for sendTransaction and call (#915)
@ -25,7 +33,7 @@ CHANGELOG
* Dependencies updated * Dependencies updated
## v1.0.0 - _July 19, 2018_ ## v1.0.0 - _July 20, 2018_
* Dependencies updated * Dependencies updated
@ -41,7 +49,7 @@ CHANGELOG
* Update EthersJs to fix the `value.toLowerCase()` is not a function bug caused by `ethers.js` breaking patch version https://github.com/ethers-io/ethers.js/issues/201 * Update EthersJs to fix the `value.toLowerCase()` is not a function bug caused by `ethers.js` breaking patch version https://github.com/ethers-io/ethers.js/issues/201
## v0.3.3 - _May 31, 2018_ ## v0.3.3 - _June 1, 2018_
* Incorrect publish that was unpublished * Incorrect publish that was unpublished
@ -49,7 +57,7 @@ CHANGELOG
* Dependencies updated * Dependencies updated
## v0.3.1 - _May 4, 2018_ ## v0.3.1 - _May 5, 2018_
* Dependencies updated * Dependencies updated

View File

@ -1,6 +1,6 @@
{ {
"name": "@0xproject/base-contract", "name": "@0xproject/base-contract",
"version": "2.0.0-rc.1", "version": "2.0.1",
"engines": { "engines": {
"node": ">=6.12" "node": ">=6.12"
}, },
@ -29,7 +29,7 @@
}, },
"homepage": "https://github.com/0xProject/0x-monorepo/packages/base-contract/README.md", "homepage": "https://github.com/0xProject/0x-monorepo/packages/base-contract/README.md",
"devDependencies": { "devDependencies": {
"@0xproject/tslint-config": "^1.0.5", "@0xproject/tslint-config": "^1.0.6",
"@types/lodash": "4.14.104", "@types/lodash": "4.14.104",
"chai": "^4.0.1", "chai": "^4.0.1",
"copyfiles": "^2.0.0", "copyfiles": "^2.0.0",
@ -41,10 +41,10 @@
"typescript": "3.0.1" "typescript": "3.0.1"
}, },
"dependencies": { "dependencies": {
"@0xproject/typescript-typings": "^1.0.4", "@0xproject/typescript-typings": "^1.0.5",
"@0xproject/utils": "^1.0.5", "@0xproject/utils": "^1.0.7",
"@0xproject/web3-wrapper": "^1.2.0", "@0xproject/web3-wrapper": "^2.0.1",
"ethereum-types": "^1.0.4", "ethereum-types": "^1.0.5",
"ethers": "3.0.22", "ethers": "3.0.22",
"lodash": "^4.17.5" "lodash": "^4.17.5"
}, },

View File

@ -1,4 +1,13 @@
[ [
{
"version": "2.0.0-rc.2",
"changes": [
{
"note": "Dependencies updated"
}
],
"timestamp": 1535377027
},
{ {
"version": "2.0.0-rc.1", "version": "2.0.0-rc.1",
"changes": [ "changes": [
@ -10,7 +19,8 @@
"note": "Stopped exporting `Order` type", "note": "Stopped exporting `Order` type",
"pr": 924 "pr": 924
} }
] ],
"timestamp": 1535133899
}, },
{ {
"timestamp": 1534210131, "timestamp": 1534210131,

View File

@ -5,7 +5,16 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG CHANGELOG
## v1.0.5 - _August 13, 2018_ ## v2.0.0-rc.2 - _August 27, 2018_
* Dependencies updated
## v2.0.0-rc.1 - _August 24, 2018_
* Updated for SRA v2 (#974)
* Stopped exporting `Order` type (#924)
## v1.0.5 - _August 14, 2018_
* Dependencies updated * Dependencies updated
@ -25,7 +34,7 @@ CHANGELOG
* Dependencies updated * Dependencies updated
## v1.0.0 - _July 19, 2018_ ## v1.0.0 - _July 20, 2018_
* Remove `WebSocketOrderbookChannel` from the public interface and replace with `orderbookChannelFactory` * Remove `WebSocketOrderbookChannel` from the public interface and replace with `orderbookChannelFactory`
@ -49,7 +58,7 @@ CHANGELOG
* Dependencies updated * Dependencies updated
## v0.6.12 - _May 4, 2018_ ## v0.6.12 - _May 5, 2018_
* Dependencies updated * Dependencies updated

View File

@ -1,6 +1,6 @@
{ {
"name": "@0xproject/connect", "name": "@0xproject/connect",
"version": "1.0.5", "version": "2.0.0-rc.2",
"engines": { "engines": {
"node": ">=6.12" "node": ">=6.12"
}, },
@ -44,11 +44,11 @@
}, },
"homepage": "https://github.com/0xProject/0x-monorepo/packages/connect/README.md", "homepage": "https://github.com/0xProject/0x-monorepo/packages/connect/README.md",
"dependencies": { "dependencies": {
"@0xproject/assert": "^1.0.5", "@0xproject/assert": "^1.0.7",
"@0xproject/json-schemas": "^1.0.1-rc.4", "@0xproject/json-schemas": "^1.0.1-rc.6",
"@0xproject/types": "^1.0.1-rc.4", "@0xproject/types": "^1.0.1-rc.6",
"@0xproject/typescript-typings": "^1.0.4", "@0xproject/typescript-typings": "^1.0.5",
"@0xproject/utils": "^1.0.5", "@0xproject/utils": "^1.0.7",
"lodash": "^4.17.5", "lodash": "^4.17.5",
"query-string": "^5.0.1", "query-string": "^5.0.1",
"sinon": "^4.0.0", "sinon": "^4.0.0",
@ -56,7 +56,7 @@
"websocket": "^1.0.25" "websocket": "^1.0.25"
}, },
"devDependencies": { "devDependencies": {
"@0xproject/tslint-config": "^1.0.5", "@0xproject/tslint-config": "^1.0.6",
"@types/fetch-mock": "^6.0.3", "@types/fetch-mock": "^6.0.3",
"@types/lodash": "4.14.104", "@types/lodash": "4.14.104",
"@types/mocha": "^2.2.42", "@types/mocha": "^2.2.42",

View File

@ -1,4 +1,13 @@
[ [
{
"version": "1.0.1-rc.5",
"changes": [
{
"note": "Fix missing `BlockParamLiteral` type import issue"
}
],
"timestamp": 1535377027
},
{ {
"version": "1.0.1-rc.4", "version": "1.0.1-rc.4",
"changes": [ "changes": [
@ -16,7 +25,8 @@
"note": "Added Transaction Encoder for use with 0x Exchange executeTransaction", "note": "Added Transaction Encoder for use with 0x Exchange executeTransaction",
"pr": 975 "pr": 975
} }
] ],
"timestamp": 1535133899
}, },
{ {
"version": "1.0.1-rc.3", "version": "1.0.1-rc.3",

View File

@ -5,7 +5,17 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG CHANGELOG
## v1.0.1-rc.3 - _August 13, 2018_ ## v1.0.1-rc.5 - _August 27, 2018_
* Fix missing `BlockParamLiteral` type import issue
## v1.0.1-rc.4 - _August 24, 2018_
* Export missing types: `TransactionEncoder`, `ContractAbi`, `JSONRPCRequestPayload`, `JSONRPCResponsePayload`, `JSONRPCErrorCallback`, `AbiDefinition`, `FunctionAbi`, `EventAbi`, `EventParameter`, `DecodedLogArgs`, `MethodAbi`, `ConstructorAbi`, `FallbackAbi`, `DataItem`, `ConstructorStateMutability`, `StateMutability` & `ExchangeSignatureValidatorApprovalEventArgs` (#924)
* Remove superfluous exported types: `ContractEvent`, `Token`, `OrderFillRequest`, `ContractEventArgs`, `LogEvent`, `OnOrderStateChangeCallback`, `ECSignature`, `OrderStateValid`, `OrderStateInvalid`, `OrderState`, `FilterObject`, `TransactionReceipt` & `TransactionReceiptWithDecodedLogs` (#924)
* Added Transaction Encoder for use with 0x Exchange executeTransaction (#975)
## v1.0.1-rc.3 - _August 14, 2018_
* Added strict encoding/decoding checks for sendTransaction and call (#915) * Added strict encoding/decoding checks for sendTransaction and call (#915)
* Add ForwarderWrapper (#934) * Add ForwarderWrapper (#934)
@ -23,7 +33,7 @@ CHANGELOG
* Dependencies updated * Dependencies updated
## v1.0.0-rc.1 - _July 19, 2018_ ## v1.0.0-rc.1 - _July 20, 2018_
* Update blockstream to v5.0 and propogate up caught errors to active subscriptions (#815) * Update blockstream to v5.0 and propogate up caught errors to active subscriptions (#815)
* Update to v2 of 0x rpotocol (#822) * Update to v2 of 0x rpotocol (#822)

View File

@ -1,6 +1,6 @@
{ {
"name": "@0xproject/contract-wrappers", "name": "@0xproject/contract-wrappers",
"version": "1.0.1-rc.3", "version": "1.0.1-rc.5",
"description": "Smart TS wrappers for 0x smart contracts", "description": "Smart TS wrappers for 0x smart contracts",
"keywords": [ "keywords": [
"0xproject", "0xproject",
@ -44,11 +44,11 @@
"node": ">=6.0.0" "node": ">=6.0.0"
}, },
"devDependencies": { "devDependencies": {
"@0xproject/abi-gen": "^1.0.5", "@0xproject/abi-gen": "^1.0.7",
"@0xproject/dev-utils": "^1.0.4", "@0xproject/dev-utils": "^1.0.6",
"@0xproject/migrations": "^1.0.4", "@0xproject/migrations": "^1.0.6",
"@0xproject/subproviders": "^1.0.5", "@0xproject/subproviders": "^2.0.1",
"@0xproject/tslint-config": "^1.0.5", "@0xproject/tslint-config": "^1.0.6",
"@types/lodash": "4.14.104", "@types/lodash": "4.14.104",
"@types/mocha": "^2.2.42", "@types/mocha": "^2.2.42",
"@types/node": "^8.0.53", "@types/node": "^8.0.53",
@ -74,16 +74,16 @@
"web3-provider-engine": "14.0.6" "web3-provider-engine": "14.0.6"
}, },
"dependencies": { "dependencies": {
"@0xproject/assert": "^1.0.5", "@0xproject/assert": "^1.0.7",
"@0xproject/base-contract": "^2.0.0-rc.1", "@0xproject/base-contract": "^2.0.1",
"@0xproject/fill-scenarios": "^1.0.1-rc.3", "@0xproject/fill-scenarios": "^1.0.1-rc.5",
"@0xproject/json-schemas": "^1.0.1-rc.4", "@0xproject/json-schemas": "^1.0.1-rc.6",
"@0xproject/order-utils": "^1.0.1-rc.3", "@0xproject/order-utils": "^1.0.1-rc.6",
"@0xproject/types": "^1.0.1-rc.4", "@0xproject/types": "^1.0.1-rc.6",
"@0xproject/typescript-typings": "^1.0.4", "@0xproject/typescript-typings": "^1.0.5",
"@0xproject/utils": "^1.0.5", "@0xproject/utils": "^1.0.7",
"@0xproject/web3-wrapper": "^1.2.0", "@0xproject/web3-wrapper": "^2.0.1",
"ethereum-types": "^1.0.4", "ethereum-types": "^1.0.5",
"ethereumjs-blockstream": "5.0.0", "ethereumjs-blockstream": "5.0.0",
"ethereumjs-util": "^5.1.1", "ethereumjs-util": "^5.1.1",
"ethers": "3.0.22", "ethers": "3.0.22",

View File

@ -1,7 +1,14 @@
import { AbiDecoder, intervalUtils, logUtils } from '@0xproject/utils'; import { AbiDecoder, intervalUtils, logUtils } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper'; import { Web3Wrapper } from '@0xproject/web3-wrapper';
import { ContractArtifact } from 'ethereum-types'; import {
import { BlockParamLiteral, ContractAbi, FilterObject, LogEntry, LogWithDecodedArgs, RawLog } from 'ethereum-types'; BlockParamLiteral,
ContractAbi,
ContractArtifact,
FilterObject,
LogEntry,
LogWithDecodedArgs,
RawLog,
} from 'ethereum-types';
import { Block, BlockAndLogStreamer, Log } from 'ethereumjs-blockstream'; import { Block, BlockAndLogStreamer, Log } from 'ethereumjs-blockstream';
import * as _ from 'lodash'; import * as _ from 'lodash';

View File

@ -2,7 +2,7 @@
// tslint:disable:no-unused-variable // tslint:disable:no-unused-variable
// tslint:disable:no-unbound-method // tslint:disable:no-unbound-method
import { BaseContract } from '@0xproject/base-contract'; import { BaseContract } from '@0xproject/base-contract';
import { BlockParam, CallData, ContractAbi, ContractArtifact, DecodedLogArgs, MethodAbi, Provider, TxData, TxDataPayable } from 'ethereum-types'; import { BlockParam, BlockParamLiteral, CallData, ContractAbi, ContractArtifact, DecodedLogArgs, MethodAbi, Provider, TxData, TxDataPayable } from 'ethereum-types';
import { BigNumber, classUtils, logUtils } from '@0xproject/utils'; import { BigNumber, classUtils, logUtils } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper'; import { Web3Wrapper } from '@0xproject/web3-wrapper';
import * as ethers from 'ethers'; import * as ethers from 'ethers';

View File

@ -40,6 +40,7 @@
"MultiSigWallet", "MultiSigWallet",
"MultiSigWalletWithTimeLock", "MultiSigWalletWithTimeLock",
"OrderValidator", "OrderValidator",
"ReentrantERC20Token",
"TestAssetProxyOwner", "TestAssetProxyOwner",
"TestAssetProxyDispatcher", "TestAssetProxyDispatcher",
"TestConstants", "TestConstants",
@ -47,6 +48,7 @@
"TestLibs", "TestLibs",
"TestExchangeInternals", "TestExchangeInternals",
"TestSignatureValidator", "TestSignatureValidator",
"TestStaticCallReceiver",
"TokenRegistry", "TokenRegistry",
"Validator", "Validator",
"Wallet", "Wallet",

View File

@ -1,7 +1,7 @@
{ {
"private": true, "private": true,
"name": "contracts", "name": "contracts",
"version": "2.1.40", "version": "2.1.42",
"engines": { "engines": {
"node": ">=6.12" "node": ">=6.12"
}, },
@ -20,14 +20,11 @@
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov", "test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
"test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html", "test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html",
"test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha", "test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha",
"run_mocha": "run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
"mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
"compile": "sol-compiler --contracts-dir src", "compile": "sol-compiler --contracts-dir src",
"clean": "shx rm -rf lib generated_contract_wrappers", "clean": "shx rm -rf lib generated_contract_wrappers",
"generate_contract_wrappers": "generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../contract_templates/contract.handlebars --partials '../contract_templates/partials/**/*.handlebars' --output generated_contract_wrappers --backend ethers",
"abi-gen --abis ${npm_package_config_abis} --template ../contract_templates/contract.handlebars --partials '../contract_templates/partials/**/*.handlebars' --output generated_contract_wrappers --backend ethers", "lint": "tslint --project . --exclude **/src/generated_contract_wrappers/**/* --exclude **/lib/**/* && yarn lint-contracts",
"lint":
"tslint --project . --exclude **/src/generated_contract_wrappers/**/* --exclude **/lib/**/* && yarn lint-contracts",
"coverage:report:text": "istanbul report text", "coverage:report:text": "istanbul report text",
"coverage:report:html": "istanbul report html && open coverage/index.html", "coverage:report:html": "istanbul report html && open coverage/index.html",
"profiler:report:html": "istanbul report html && open coverage/index.html", "profiler:report:html": "istanbul report html && open coverage/index.html",
@ -36,8 +33,7 @@
"lint-contracts": "solhint src/2.0.0/**/**/**/**/*.sol" "lint-contracts": "solhint src/2.0.0/**/**/**/**/*.sol"
}, },
"config": { "config": {
"abis": "abis": "../migrations/artifacts/2.0.0/@(AssetProxyOwner|DummyERC20Token|DummyERC721Receiver|DummyERC721Token|DummyNoReturnERC20Token|ERC20Proxy|ERC721Proxy|Forwarder|Exchange|ExchangeWrapper|IAssetData|IAssetProxy|InvalidERC721Receiver|MixinAuthorizable|MultiSigWallet|MultiSigWalletWithTimeLock|OrderValidator|ReentrantERC20Token|TestAssetProxyOwner|TestAssetProxyDispatcher|TestConstants|TestExchangeInternals|TestLibBytes|TestLibs|TestSignatureValidator|TestStaticCallReceiver|Validator|Wallet|TokenRegistry|Whitelist|WETH9|ZRXToken).json"
"../migrations/artifacts/2.0.0/@(AssetProxyOwner|DummyERC20Token|DummyERC721Receiver|DummyERC721Token|DummyNoReturnERC20Token|ERC20Proxy|ERC721Proxy|Forwarder|Exchange|ExchangeWrapper|IAssetData|IAssetProxy|InvalidERC721Receiver|MixinAuthorizable|MultiSigWallet|MultiSigWalletWithTimeLock|OrderValidator|TestAssetProxyOwner|TestAssetProxyDispatcher|TestConstants|TestExchangeInternals|TestLibBytes|TestLibs|TestSignatureValidator|Validator|Wallet|TokenRegistry|Whitelist|WETH9|ZRXToken).json"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@ -50,12 +46,12 @@
}, },
"homepage": "https://github.com/0xProject/0x-monorepo/packages/contracts/README.md", "homepage": "https://github.com/0xProject/0x-monorepo/packages/contracts/README.md",
"devDependencies": { "devDependencies": {
"@0xproject/abi-gen": "^1.0.5", "@0xproject/abi-gen": "^1.0.7",
"@0xproject/dev-utils": "^1.0.4", "@0xproject/dev-utils": "^1.0.6",
"@0xproject/sol-cov": "^2.0.0", "@0xproject/sol-compiler": "^1.1.1",
"@0xproject/subproviders": "^1.0.5", "@0xproject/sol-cov": "^2.1.1",
"@0xproject/tslint-config": "^1.0.5", "@0xproject/subproviders": "^2.0.1",
"@0xproject/sol-compiler": "^1.0.5", "@0xproject/tslint-config": "^1.0.6",
"@types/bn.js": "^4.11.0", "@types/bn.js": "^4.11.0",
"@types/ethereumjs-abi": "^0.6.0", "@types/ethereumjs-abi": "^0.6.0",
"@types/lodash": "4.14.104", "@types/lodash": "4.14.104",
@ -77,15 +73,15 @@
"yargs": "^10.0.3" "yargs": "^10.0.3"
}, },
"dependencies": { "dependencies": {
"@0xproject/base-contract": "^2.0.0-rc.1", "@0xproject/base-contract": "^2.0.1",
"@0xproject/order-utils": "^1.0.1-rc.3", "@0xproject/order-utils": "^1.0.1-rc.6",
"@0xproject/types": "^1.0.1-rc.4", "@0xproject/types": "^1.0.1-rc.6",
"@0xproject/typescript-typings": "^1.0.4", "@0xproject/typescript-typings": "^1.0.5",
"@0xproject/utils": "^1.0.5", "@0xproject/utils": "^1.0.7",
"@0xproject/web3-wrapper": "^1.2.0", "@0xproject/web3-wrapper": "^2.0.1",
"@types/js-combinatorics": "^0.5.29", "@types/js-combinatorics": "^0.5.29",
"bn.js": "^4.11.8", "bn.js": "^4.11.8",
"ethereum-types": "^1.0.4", "ethereum-types": "^1.0.5",
"ethereumjs-abi": "0.6.5", "ethereumjs-abi": "0.6.5",
"ethereumjs-util": "^5.1.1", "ethereumjs-util": "^5.1.1",
"ethers": "3.0.22", "ethers": "3.0.22",

View File

@ -37,7 +37,7 @@ contract Whitelist is
bytes internal TX_ORIGIN_SIGNATURE; bytes internal TX_ORIGIN_SIGNATURE;
// solhint-enable var-name-mixedcase // solhint-enable var-name-mixedcase
byte constant internal VALIDATOR_SIGNATURE_BYTE = "\x06"; byte constant internal VALIDATOR_SIGNATURE_BYTE = "\x05";
constructor (address _exchange) constructor (address _exchange)
public public

View File

@ -163,7 +163,7 @@ contract MixinExchangeWrapper is
// Convert the remaining amount of makerAsset to buy into remaining amount // Convert the remaining amount of makerAsset to buy into remaining amount
// of takerAsset to sell, assuming entire amount can be sold in the current order // of takerAsset to sell, assuming entire amount can be sold in the current order
uint256 remainingTakerAssetFillAmount = getPartialAmount( uint256 remainingTakerAssetFillAmount = getPartialAmountFloor(
orders[i].takerAssetAmount, orders[i].takerAssetAmount,
orders[i].makerAssetAmount, orders[i].makerAssetAmount,
remainingMakerAssetFillAmount remainingMakerAssetFillAmount
@ -231,7 +231,7 @@ contract MixinExchangeWrapper is
// Convert the remaining amount of ZRX to buy into remaining amount // Convert the remaining amount of ZRX to buy into remaining amount
// of WETH to sell, assuming entire amount can be sold in the current order. // of WETH to sell, assuming entire amount can be sold in the current order.
uint256 remainingWethSellAmount = getPartialAmount( uint256 remainingWethSellAmount = getPartialAmountFloor(
orders[i].takerAssetAmount, orders[i].takerAssetAmount,
safeSub(orders[i].makerAssetAmount, orders[i].takerFee), // our exchange rate after fees safeSub(orders[i].makerAssetAmount, orders[i].takerFee), // our exchange rate after fees
remainingZrxBuyAmount remainingZrxBuyAmount

View File

@ -87,7 +87,7 @@ contract MixinForwarderCore is
uint256 makerAssetAmountPurchased; uint256 makerAssetAmountPurchased;
if (orders[0].makerAssetData.equals(ZRX_ASSET_DATA)) { if (orders[0].makerAssetData.equals(ZRX_ASSET_DATA)) {
// Calculate amount of WETH that won't be spent on ETH fees. // Calculate amount of WETH that won't be spent on ETH fees.
wethSellAmount = getPartialAmount( wethSellAmount = getPartialAmountFloor(
PERCENTAGE_DENOMINATOR, PERCENTAGE_DENOMINATOR,
safeAdd(PERCENTAGE_DENOMINATOR, feePercentage), safeAdd(PERCENTAGE_DENOMINATOR, feePercentage),
msg.value msg.value
@ -103,7 +103,7 @@ contract MixinForwarderCore is
makerAssetAmountPurchased = safeSub(orderFillResults.makerAssetFilledAmount, orderFillResults.takerFeePaid); makerAssetAmountPurchased = safeSub(orderFillResults.makerAssetFilledAmount, orderFillResults.takerFeePaid);
} else { } else {
// 5% of WETH is reserved for filling feeOrders and paying feeRecipient. // 5% of WETH is reserved for filling feeOrders and paying feeRecipient.
wethSellAmount = getPartialAmount( wethSellAmount = getPartialAmountFloor(
MAX_WETH_FILL_PERCENTAGE, MAX_WETH_FILL_PERCENTAGE,
PERCENTAGE_DENOMINATOR, PERCENTAGE_DENOMINATOR,
msg.value msg.value

View File

@ -82,7 +82,7 @@ contract MixinWeth is
uint256 wethRemaining = safeSub(msg.value, wethSold); uint256 wethRemaining = safeSub(msg.value, wethSold);
// Calculate ETH fee to pay to feeRecipient. // Calculate ETH fee to pay to feeRecipient.
uint256 ethFee = getPartialAmount( uint256 ethFee = getPartialAmountFloor(
feePercentage, feePercentage,
PERCENTAGE_DENOMINATOR, PERCENTAGE_DENOMINATOR,
wethSoldExcludingFeeOrders wethSoldExcludingFeeOrders

View File

@ -118,6 +118,9 @@ contract ERC20Proxy is
mstore(96, 0) mstore(96, 0)
revert(0, 100) revert(0, 100)
} }
// Revert if undefined function is called
revert(0, 0)
} }
} }

View File

@ -152,6 +152,9 @@ contract ERC721Proxy is
mstore(96, 0) mstore(96, 0)
revert(0, 100) revert(0, 100)
} }
// Revert if undefined function is called
revert(0, 0)
} }
} }

View File

@ -83,7 +83,7 @@ contract MixinAssetProxyDispatcher is
internal internal
{ {
// Do nothing if no amount should be transferred. // Do nothing if no amount should be transferred.
if (amount > 0) { if (amount > 0 && from != to) {
// Ensure assetData length is valid // Ensure assetData length is valid
require( require(
assetData.length > 3, assetData.length > 3,

View File

@ -19,6 +19,7 @@
pragma solidity 0.4.24; pragma solidity 0.4.24;
pragma experimental ABIEncoderV2; pragma experimental ABIEncoderV2;
import "../../utils/ReentrancyGuard/ReentrancyGuard.sol";
import "./libs/LibConstants.sol"; import "./libs/LibConstants.sol";
import "./libs/LibFillResults.sol"; import "./libs/LibFillResults.sol";
import "./libs/LibOrder.sol"; import "./libs/LibOrder.sol";
@ -30,6 +31,7 @@ import "./mixins/MAssetProxyDispatcher.sol";
contract MixinExchangeCore is contract MixinExchangeCore is
ReentrancyGuard,
LibConstants, LibConstants,
LibMath, LibMath,
LibOrder, LibOrder,
@ -54,6 +56,7 @@ contract MixinExchangeCore is
/// @param targetOrderEpoch Orders created with a salt less or equal to this value will be cancelled. /// @param targetOrderEpoch Orders created with a salt less or equal to this value will be cancelled.
function cancelOrdersUpTo(uint256 targetOrderEpoch) function cancelOrdersUpTo(uint256 targetOrderEpoch)
external external
nonReentrant
{ {
address makerAddress = getCurrentContextAddress(); address makerAddress = getCurrentContextAddress();
// If this function is called via `executeTransaction`, we only update the orderEpoch for the makerAddress/msg.sender combination. // If this function is called via `executeTransaction`, we only update the orderEpoch for the makerAddress/msg.sender combination.
@ -86,43 +89,14 @@ contract MixinExchangeCore is
bytes memory signature bytes memory signature
) )
public public
nonReentrant
returns (FillResults memory fillResults) returns (FillResults memory fillResults)
{ {
// Fetch order info fillResults = fillOrderInternal(
OrderInfo memory orderInfo = getOrderInfo(order);
// Fetch taker address
address takerAddress = getCurrentContextAddress();
// Get amount of takerAsset to fill
uint256 remainingTakerAssetAmount = safeSub(order.takerAssetAmount, orderInfo.orderTakerAssetFilledAmount);
uint256 takerAssetFilledAmount = min256(takerAssetFillAmount, remainingTakerAssetAmount);
// Validate context
assertValidFill(
order, order,
orderInfo,
takerAddress,
takerAssetFillAmount, takerAssetFillAmount,
takerAssetFilledAmount,
signature signature
); );
// Compute proportional fill amounts
fillResults = calculateFillResults(order, takerAssetFilledAmount);
// Update exchange internal state
updateFilledState(
order,
takerAddress,
orderInfo.orderHash,
orderInfo.orderTakerAssetFilledAmount,
fillResults
);
// Settle order
settleOrder(order, takerAddress, fillResults);
return fillResults; return fillResults;
} }
@ -131,6 +105,7 @@ contract MixinExchangeCore is
/// @param order Order to cancel. Order must be OrderStatus.FILLABLE. /// @param order Order to cancel. Order must be OrderStatus.FILLABLE.
function cancelOrder(Order memory order) function cancelOrder(Order memory order)
public public
nonReentrant
{ {
// Fetch current order status // Fetch current order status
OrderInfo memory orderInfo = getOrderInfo(order); OrderInfo memory orderInfo = getOrderInfo(order);
@ -203,6 +178,64 @@ contract MixinExchangeCore is
return orderInfo; return orderInfo;
} }
/// @dev Fills the input order.
/// @param order Order struct containing order specifications.
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
/// @param signature Proof that order has been created by maker.
/// @return Amounts filled and fees paid by maker and taker.
function fillOrderInternal(
Order memory order,
uint256 takerAssetFillAmount,
bytes memory signature
)
internal
returns (FillResults memory fillResults)
{
// Fetch order info
OrderInfo memory orderInfo = getOrderInfo(order);
// Fetch taker address
address takerAddress = getCurrentContextAddress();
// Assert that the order is fillable by taker
assertFillableOrder(
order,
orderInfo,
takerAddress,
signature
);
// Get amount of takerAsset to fill
uint256 remainingTakerAssetAmount = safeSub(order.takerAssetAmount, orderInfo.orderTakerAssetFilledAmount);
uint256 takerAssetFilledAmount = min256(takerAssetFillAmount, remainingTakerAssetAmount);
// Validate context
assertValidFill(
order,
orderInfo,
takerAssetFillAmount,
takerAssetFilledAmount,
fillResults.makerAssetFilledAmount
);
// Compute proportional fill amounts
fillResults = calculateFillResults(order, takerAssetFilledAmount);
// Update exchange internal state
updateFilledState(
order,
takerAddress,
orderInfo.orderHash,
orderInfo.orderTakerAssetFilledAmount,
fillResults
);
// Settle order
settleOrder(order, takerAddress, fillResults);
return fillResults;
}
/// @dev Updates state with results of a fill order. /// @dev Updates state with results of a fill order.
/// @param order that was filled. /// @param order that was filled.
/// @param takerAddress Address of taker who filled the order. /// @param takerAddress Address of taker who filled the order.
@ -264,15 +297,11 @@ contract MixinExchangeCore is
/// @param order to be filled. /// @param order to be filled.
/// @param orderInfo OrderStatus, orderHash, and amount already filled of order. /// @param orderInfo OrderStatus, orderHash, and amount already filled of order.
/// @param takerAddress Address of order taker. /// @param takerAddress Address of order taker.
/// @param takerAssetFillAmount Desired amount of order to fill by taker.
/// @param takerAssetFilledAmount Amount of takerAsset that will be filled.
/// @param signature Proof that the orders was created by its maker. /// @param signature Proof that the orders was created by its maker.
function assertValidFill( function assertFillableOrder(
Order memory order, Order memory order,
OrderInfo memory orderInfo, OrderInfo memory orderInfo,
address takerAddress, address takerAddress,
uint256 takerAssetFillAmount,
uint256 takerAssetFilledAmount,
bytes memory signature bytes memory signature
) )
internal internal
@ -284,12 +313,6 @@ contract MixinExchangeCore is
"ORDER_UNFILLABLE" "ORDER_UNFILLABLE"
); );
// Revert if fill amount is invalid
require(
takerAssetFillAmount != 0,
"INVALID_TAKER_AMOUNT"
);
// Validate sender is allowed to fill this order // Validate sender is allowed to fill this order
if (order.senderAddress != address(0)) { if (order.senderAddress != address(0)) {
require( require(
@ -317,10 +340,74 @@ contract MixinExchangeCore is
"INVALID_ORDER_SIGNATURE" "INVALID_ORDER_SIGNATURE"
); );
} }
}
/// @dev Validates context for fillOrder. Succeeds or throws.
/// @param order to be filled.
/// @param orderInfo OrderStatus, orderHash, and amount already filled of order.
/// @param takerAssetFillAmount Desired amount of order to fill by taker.
/// @param takerAssetFilledAmount Amount of takerAsset that will be filled.
/// @param makerAssetFilledAmount Amount of makerAsset that will be transfered.
function assertValidFill(
Order memory order,
OrderInfo memory orderInfo,
uint256 takerAssetFillAmount, // TODO: use FillResults
uint256 takerAssetFilledAmount,
uint256 makerAssetFilledAmount
)
internal
view
{
// Revert if fill amount is invalid
// TODO: reconsider necessity for v2.1
require(
takerAssetFillAmount != 0,
"INVALID_TAKER_AMOUNT"
);
// Make sure taker does not pay more than desired amount
// NOTE: This assertion should never fail, it is here
// as an extra defence against potential bugs.
require(
takerAssetFilledAmount <= takerAssetFillAmount,
"TAKER_OVERPAY"
);
// Make sure order is not overfilled
// NOTE: This assertion should never fail, it is here
// as an extra defence against potential bugs.
require(
safeAdd(orderInfo.orderTakerAssetFilledAmount, takerAssetFilledAmount) <= order.takerAssetAmount,
"ORDER_OVERFILL"
);
// Make sure order is filled at acceptable price.
// The order has an implied price from the makers perspective:
// order price = order.makerAssetAmount / order.takerAssetAmount
// i.e. the number of makerAsset maker is paying per takerAsset. The
// maker is guaranteed to get this price or a better (lower) one. The
// actual price maker is getting in this fill is:
// fill price = makerAssetFilledAmount / takerAssetFilledAmount
// We need `fill price <= order price` for the fill to be fair to maker.
// This amounts to:
// makerAssetFilledAmount order.makerAssetAmount
// ------------------------ <= -----------------------
// takerAssetFilledAmount order.takerAssetAmount
// or, equivalently:
// makerAssetFilledAmount * order.takerAssetAmount <=
// order.makerAssetAmount * takerAssetFilledAmount
// NOTE: This assertion should never fail, it is here
// as an extra defence against potential bugs.
require(
safeMul(makerAssetFilledAmount, order.takerAssetAmount)
<=
safeMul(order.makerAssetAmount, takerAssetFilledAmount),
"INVALID_FILL_PRICE"
);
// Validate fill order rounding // Validate fill order rounding
require( require(
!isRoundingError( !isRoundingErrorFloor(
takerAssetFilledAmount, takerAssetFilledAmount,
order.takerAssetAmount, order.takerAssetAmount,
order.makerAssetAmount order.makerAssetAmount
@ -376,17 +463,17 @@ contract MixinExchangeCore is
{ {
// Compute proportional transfer amounts // Compute proportional transfer amounts
fillResults.takerAssetFilledAmount = takerAssetFilledAmount; fillResults.takerAssetFilledAmount = takerAssetFilledAmount;
fillResults.makerAssetFilledAmount = getPartialAmount( fillResults.makerAssetFilledAmount = getPartialAmountFloor(
takerAssetFilledAmount, takerAssetFilledAmount,
order.takerAssetAmount, order.takerAssetAmount,
order.makerAssetAmount order.makerAssetAmount
); );
fillResults.makerFeePaid = getPartialAmount( fillResults.makerFeePaid = getPartialAmountFloor(
takerAssetFilledAmount, takerAssetFilledAmount,
order.takerAssetAmount, order.takerAssetAmount,
order.makerFee order.makerFee
); );
fillResults.takerFeePaid = getPartialAmount( fillResults.takerFeePaid = getPartialAmountFloor(
takerAssetFilledAmount, takerAssetFilledAmount,
order.takerAssetAmount, order.takerAssetAmount,
order.takerFee order.takerFee

View File

@ -14,6 +14,7 @@
pragma solidity 0.4.24; pragma solidity 0.4.24;
pragma experimental ABIEncoderV2; pragma experimental ABIEncoderV2;
import "../../utils/ReentrancyGuard/ReentrancyGuard.sol";
import "./libs/LibConstants.sol"; import "./libs/LibConstants.sol";
import "./libs/LibMath.sol"; import "./libs/LibMath.sol";
import "./libs/LibOrder.sol"; import "./libs/LibOrder.sol";
@ -25,6 +26,7 @@ import "./mixins/MAssetProxyDispatcher.sol";
contract MixinMatchOrders is contract MixinMatchOrders is
ReentrancyGuard,
LibConstants, LibConstants,
LibMath, LibMath,
MAssetProxyDispatcher, MAssetProxyDispatcher,
@ -48,6 +50,7 @@ contract MixinMatchOrders is
bytes memory rightSignature bytes memory rightSignature
) )
public public
nonReentrant
returns (LibFillResults.MatchedFillResults memory matchedFillResults) returns (LibFillResults.MatchedFillResults memory matchedFillResults)
{ {
// We assume that rightOrder.takerAssetData == leftOrder.makerAssetData and rightOrder.makerAssetData == leftOrder.takerAssetData. // We assume that rightOrder.takerAssetData == leftOrder.makerAssetData and rightOrder.makerAssetData == leftOrder.takerAssetData.
@ -63,6 +66,18 @@ contract MixinMatchOrders is
address takerAddress = getCurrentContextAddress(); address takerAddress = getCurrentContextAddress();
// Either our context is valid or we revert // Either our context is valid or we revert
assertFillableOrder(
leftOrder,
leftOrderInfo,
takerAddress,
leftSignature
);
assertFillableOrder(
rightOrder,
rightOrderInfo,
takerAddress,
rightSignature
);
assertValidMatch(leftOrder, rightOrder); assertValidMatch(leftOrder, rightOrder);
// Compute proportional fill amounts // Compute proportional fill amounts
@ -77,18 +92,16 @@ contract MixinMatchOrders is
assertValidFill( assertValidFill(
leftOrder, leftOrder,
leftOrderInfo, leftOrderInfo,
takerAddress,
matchedFillResults.left.takerAssetFilledAmount, matchedFillResults.left.takerAssetFilledAmount,
matchedFillResults.left.takerAssetFilledAmount, matchedFillResults.left.takerAssetFilledAmount,
leftSignature matchedFillResults.left.makerAssetFilledAmount
); );
assertValidFill( assertValidFill(
rightOrder, rightOrder,
rightOrderInfo, rightOrderInfo,
takerAddress,
matchedFillResults.right.takerAssetFilledAmount, matchedFillResults.right.takerAssetFilledAmount,
matchedFillResults.right.takerAssetFilledAmount, matchedFillResults.right.takerAssetFilledAmount,
rightSignature matchedFillResults.right.makerAssetFilledAmount
); );
// Update exchange state // Update exchange state
@ -162,62 +175,85 @@ contract MixinMatchOrders is
pure pure
returns (LibFillResults.MatchedFillResults memory matchedFillResults) returns (LibFillResults.MatchedFillResults memory matchedFillResults)
{ {
// We settle orders at the exchange rate of the right order. // Derive maker asset amounts for left & right orders, given store taker assert amounts
// The amount saved by the left maker goes to the taker.
// Either the left or right order will be fully filled; possibly both.
// The left order is fully filled iff the right order can sell more than left can buy.
// That is: the amount required to fill the left order is less than or equal to
// the amount we can spend from the right order:
// <leftTakerAssetAmountRemaining> <= <rightTakerAssetAmountRemaining> * <rightMakerToTakerRatio>
// <leftTakerAssetAmountRemaining> <= <rightTakerAssetAmountRemaining> * <rightOrder.makerAssetAmount> / <rightOrder.takerAssetAmount>
// <leftTakerAssetAmountRemaining> * <rightOrder.takerAssetAmount> <= <rightTakerAssetAmountRemaining> * <rightOrder.makerAssetAmount>
uint256 leftTakerAssetAmountRemaining = safeSub(leftOrder.takerAssetAmount, leftOrderTakerAssetFilledAmount); uint256 leftTakerAssetAmountRemaining = safeSub(leftOrder.takerAssetAmount, leftOrderTakerAssetFilledAmount);
uint256 leftMakerAssetAmountRemaining = getPartialAmountFloor(
leftOrder.makerAssetAmount,
leftOrder.takerAssetAmount,
leftTakerAssetAmountRemaining
);
uint256 rightTakerAssetAmountRemaining = safeSub(rightOrder.takerAssetAmount, rightOrderTakerAssetFilledAmount); uint256 rightTakerAssetAmountRemaining = safeSub(rightOrder.takerAssetAmount, rightOrderTakerAssetFilledAmount);
uint256 leftTakerAssetFilledAmount; uint256 rightMakerAssetAmountRemaining = getPartialAmountFloor(
uint256 rightTakerAssetFilledAmount; rightOrder.makerAssetAmount,
if ( rightOrder.takerAssetAmount,
safeMul(leftTakerAssetAmountRemaining, rightOrder.takerAssetAmount) <= rightTakerAssetAmountRemaining
safeMul(rightTakerAssetAmountRemaining, rightOrder.makerAssetAmount) );
) {
// Left order will be fully filled: maximally fill left
leftTakerAssetFilledAmount = leftTakerAssetAmountRemaining;
// The right order receives an amount proportional to how much was spent. // Calculate fill results for maker and taker assets: at least one order will be fully filled.
rightTakerAssetFilledAmount = getPartialAmount( // The maximum amount the left maker can buy is `leftTakerAssetAmountRemaining`
rightOrder.takerAssetAmount, // The maximum amount the right maker can sell is `rightMakerAssetAmountRemaining`
rightOrder.makerAssetAmount, // We have two distinct cases for calculating the fill results:
leftTakerAssetFilledAmount // Case 1.
// If the left maker can buy more than the right maker can sell, then only the right order is fully filled.
// If the left maker can buy exactly what the right maker can sell, then both orders are fully filled.
// Case 2.
// If the left maker cannot buy more than the right maker can sell, then only the left order is fully filled.
if (leftTakerAssetAmountRemaining >= rightMakerAssetAmountRemaining) {
// Case 1: Right order is fully filled
matchedFillResults.right.makerAssetFilledAmount = rightMakerAssetAmountRemaining;
matchedFillResults.right.takerAssetFilledAmount = rightTakerAssetAmountRemaining;
matchedFillResults.left.takerAssetFilledAmount = matchedFillResults.right.makerAssetFilledAmount;
// Round down to ensure the maker's exchange rate does not exceed the price specified by the order.
// We favor the maker when the exchange rate must be rounded.
matchedFillResults.left.makerAssetFilledAmount = getPartialAmountFloor(
leftOrder.makerAssetAmount,
leftOrder.takerAssetAmount,
matchedFillResults.left.takerAssetFilledAmount
); );
} else { } else {
// Right order will be fully filled: maximally fill right // Case 2: Left order is fully filled
rightTakerAssetFilledAmount = rightTakerAssetAmountRemaining; matchedFillResults.left.makerAssetFilledAmount = leftMakerAssetAmountRemaining;
matchedFillResults.left.takerAssetFilledAmount = leftTakerAssetAmountRemaining;
// The left order receives an amount proportional to how much was spent. matchedFillResults.right.makerAssetFilledAmount = matchedFillResults.left.takerAssetFilledAmount;
leftTakerAssetFilledAmount = getPartialAmount( // Round up to ensure the maker's exchange rate does not exceed the price specified by the order.
rightOrder.makerAssetAmount, // We favor the maker when the exchange rate must be rounded.
matchedFillResults.right.takerAssetFilledAmount = getPartialAmountCeil(
rightOrder.takerAssetAmount, rightOrder.takerAssetAmount,
rightTakerAssetFilledAmount rightOrder.makerAssetAmount,
matchedFillResults.right.makerAssetFilledAmount
); );
} }
// Calculate fill results for left order
matchedFillResults.left = calculateFillResults(
leftOrder,
leftTakerAssetFilledAmount
);
// Calculate fill results for right order
matchedFillResults.right = calculateFillResults(
rightOrder,
rightTakerAssetFilledAmount
);
// Calculate amount given to taker // Calculate amount given to taker
matchedFillResults.leftMakerAssetSpreadAmount = safeSub( matchedFillResults.leftMakerAssetSpreadAmount = safeSub(
matchedFillResults.left.makerAssetFilledAmount, matchedFillResults.left.makerAssetFilledAmount,
matchedFillResults.right.takerAssetFilledAmount matchedFillResults.right.takerAssetFilledAmount
); );
// Compute fees for left order
matchedFillResults.left.makerFeePaid = getPartialAmountFloor(
matchedFillResults.left.makerAssetFilledAmount,
leftOrder.makerAssetAmount,
leftOrder.makerFee
);
matchedFillResults.left.takerFeePaid = getPartialAmountFloor(
matchedFillResults.left.takerAssetFilledAmount,
leftOrder.takerAssetAmount,
leftOrder.takerFee
);
// Compute fees for right order
matchedFillResults.right.makerFeePaid = getPartialAmountFloor(
matchedFillResults.right.makerAssetFilledAmount,
rightOrder.makerAssetAmount,
rightOrder.makerFee
);
matchedFillResults.right.takerFeePaid = getPartialAmountFloor(
matchedFillResults.right.takerAssetFilledAmount,
rightOrder.takerAssetAmount,
rightOrder.takerFee
);
// Return fill results // Return fill results
return matchedFillResults; return matchedFillResults;
} }

View File

@ -19,6 +19,7 @@
pragma solidity 0.4.24; pragma solidity 0.4.24;
import "../../utils/LibBytes/LibBytes.sol"; import "../../utils/LibBytes/LibBytes.sol";
import "../../utils/ReentrancyGuard/ReentrancyGuard.sol";
import "./mixins/MSignatureValidator.sol"; import "./mixins/MSignatureValidator.sol";
import "./mixins/MTransactions.sol"; import "./mixins/MTransactions.sol";
import "./interfaces/IWallet.sol"; import "./interfaces/IWallet.sol";
@ -26,6 +27,7 @@ import "./interfaces/IValidator.sol";
contract MixinSignatureValidator is contract MixinSignatureValidator is
ReentrancyGuard,
MSignatureValidator, MSignatureValidator,
MTransactions MTransactions
{ {
@ -48,14 +50,16 @@ contract MixinSignatureValidator is
) )
external external
{ {
require( if (signerAddress != msg.sender) {
isValidSignature( require(
hash, isValidSignature(
signerAddress, hash,
signature signerAddress,
), signature
"INVALID_SIGNATURE" ),
); "INVALID_SIGNATURE"
);
}
preSigned[hash][signerAddress] = true; preSigned[hash][signerAddress] = true;
} }
@ -67,6 +71,7 @@ contract MixinSignatureValidator is
bool approval bool approval
) )
external external
nonReentrant
{ {
address signerAddress = getCurrentContextAddress(); address signerAddress = getCurrentContextAddress();
allowedValidators[signerAddress][validatorAddress] = approval; allowedValidators[signerAddress][validatorAddress] = approval;
@ -172,26 +177,14 @@ contract MixinSignatureValidator is
isValid = signerAddress == recovered; isValid = signerAddress == recovered;
return isValid; return isValid;
// Implicitly signed by caller.
// The signer has initiated the call. In the case of non-contract
// accounts it means the transaction itself was signed.
// Example: let's say for a particular operation three signatures
// A, B and C are required. To submit the transaction, A and B can
// give a signature to C, who can then submit the transaction using
// `Caller` for his own signature. Or A and C can sign and B can
// submit using `Caller`. Having `Caller` allows this flexibility.
} else if (signatureType == SignatureType.Caller) {
require(
signature.length == 0,
"LENGTH_0_REQUIRED"
);
isValid = signerAddress == msg.sender;
return isValid;
// Signature verified by wallet contract. // Signature verified by wallet contract.
// If used with an order, the maker of the order is the wallet contract. // If used with an order, the maker of the order is the wallet contract.
} else if (signatureType == SignatureType.Wallet) { } else if (signatureType == SignatureType.Wallet) {
isValid = IWallet(signerAddress).isValidSignature(hash, signature); isValid = isValidWalletSignature(
hash,
signerAddress,
signature
);
return isValid; return isValid;
// Signature verified by validator contract. // Signature verified by validator contract.
@ -209,7 +202,8 @@ contract MixinSignatureValidator is
if (!allowedValidators[signerAddress][validatorAddress]) { if (!allowedValidators[signerAddress][validatorAddress]) {
return false; return false;
} }
isValid = IValidator(validatorAddress).isValidSignature( isValid = isValidValidatorSignature(
validatorAddress,
hash, hash,
signerAddress, signerAddress,
signature signature
@ -220,34 +214,6 @@ contract MixinSignatureValidator is
} else if (signatureType == SignatureType.PreSigned) { } else if (signatureType == SignatureType.PreSigned) {
isValid = preSigned[hash][signerAddress]; isValid = preSigned[hash][signerAddress];
return isValid; return isValid;
// Signature from Trezor hardware wallet.
// It differs from web3.eth_sign in the encoding of message length
// (Bitcoin varint encoding vs ascii-decimal, the latter is not
// self-terminating which leads to ambiguities).
// See also:
// https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer
// https://github.com/trezor/trezor-mcu/blob/master/firmware/ethereum.c#L602
// https://github.com/trezor/trezor-mcu/blob/master/firmware/crypto.c#L36
} else if (signatureType == SignatureType.Trezor) {
require(
signature.length == 65,
"LENGTH_65_REQUIRED"
);
v = uint8(signature[0]);
r = signature.readBytes32(1);
s = signature.readBytes32(33);
recovered = ecrecover(
keccak256(abi.encodePacked(
"\x19Ethereum Signed Message:\n\x20",
hash
)),
v,
r,
s
);
isValid = signerAddress == recovered;
return isValid;
} }
// Anything else is illegal (We do not return false because // Anything else is illegal (We do not return false because
@ -257,4 +223,102 @@ contract MixinSignatureValidator is
// signature was invalid.) // signature was invalid.)
revert("SIGNATURE_UNSUPPORTED"); revert("SIGNATURE_UNSUPPORTED");
} }
/// @dev Verifies signature using logic defined by Wallet contract.
/// @param hash Any 32 byte hash.
/// @param walletAddress Address that should have signed the given hash
/// and defines its own signature verification method.
/// @param signature Proof that the hash has been signed by signer.
/// @return True if signature is valid for given wallet..
function isValidWalletSignature(
bytes32 hash,
address walletAddress,
bytes signature
)
internal
view
returns (bool isValid)
{
bytes memory calldata = abi.encodeWithSelector(
IWallet(walletAddress).isValidSignature.selector,
hash,
signature
);
assembly {
let cdStart := add(calldata, 32)
let success := staticcall(
gas, // forward all gas
walletAddress, // address of Wallet contract
cdStart, // pointer to start of input
mload(calldata), // length of input
cdStart, // write input over output
32 // output size is 32 bytes
)
switch success
case 0 {
// Revert with `Error("WALLET_ERROR")`
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
mstore(64, 0x0000000c57414c4c45545f4552524f5200000000000000000000000000000000)
mstore(96, 0)
revert(0, 100)
}
case 1 {
// Signature is valid if call did not revert and returned true
isValid := mload(cdStart)
}
}
return isValid;
}
/// @dev Verifies signature using logic defined by Validator contract.
/// @param validatorAddress Address of validator contract.
/// @param hash Any 32 byte hash.
/// @param signerAddress Address that should have signed the given hash.
/// @param signature Proof that the hash has been signed by signer.
/// @return True if the address recovered from the provided signature matches the input signer address.
function isValidValidatorSignature(
address validatorAddress,
bytes32 hash,
address signerAddress,
bytes signature
)
internal
view
returns (bool isValid)
{
bytes memory calldata = abi.encodeWithSelector(
IValidator(signerAddress).isValidSignature.selector,
hash,
signerAddress,
signature
);
assembly {
let cdStart := add(calldata, 32)
let success := staticcall(
gas, // forward all gas
validatorAddress, // address of Validator contract
cdStart, // pointer to start of input
mload(calldata), // length of input
cdStart, // write input over output
32 // output size is 32 bytes
)
switch success
case 0 {
// Revert with `Error("VALIDATOR_ERROR")`
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
mstore(64, 0x0000000f56414c494441544f525f4552524f5200000000000000000000000000)
mstore(96, 0)
revert(0, 100)
}
case 1 {
// Signature is valid if call did not revert and returned true
isValid := mload(cdStart)
}
}
return isValid;
}
} }

View File

@ -155,7 +155,8 @@ contract MixinTransactions is
view view
returns (address) returns (address)
{ {
address contextAddress = currentContextAddress == address(0) ? msg.sender : currentContextAddress; address currentContextAddress_ = currentContextAddress;
address contextAddress = currentContextAddress_ == address(0) ? msg.sender : currentContextAddress_;
return contextAddress; return contextAddress;
} }
} }

View File

@ -19,18 +19,22 @@
pragma solidity 0.4.24; pragma solidity 0.4.24;
pragma experimental ABIEncoderV2; pragma experimental ABIEncoderV2;
import "../../utils/ReentrancyGuard/ReentrancyGuard.sol";
import "./libs/LibMath.sol"; import "./libs/LibMath.sol";
import "./libs/LibOrder.sol"; import "./libs/LibOrder.sol";
import "./libs/LibFillResults.sol"; import "./libs/LibFillResults.sol";
import "./libs/LibAbiEncoder.sol"; import "./libs/LibAbiEncoder.sol";
import "./mixins/MExchangeCore.sol"; import "./mixins/MExchangeCore.sol";
import "./mixins/MWrapperFunctions.sol";
contract MixinWrapperFunctions is contract MixinWrapperFunctions is
ReentrancyGuard,
LibMath, LibMath,
LibFillResults, LibFillResults,
LibAbiEncoder, LibAbiEncoder,
MExchangeCore MExchangeCore,
MWrapperFunctions
{ {
/// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled. /// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled.
@ -43,17 +47,14 @@ contract MixinWrapperFunctions is
bytes memory signature bytes memory signature
) )
public public
nonReentrant
returns (FillResults memory fillResults) returns (FillResults memory fillResults)
{ {
fillResults = fillOrder( fillResults = fillOrKillOrderInternal(
order, order,
takerAssetFillAmount, takerAssetFillAmount,
signature signature
); );
require(
fillResults.takerAssetFilledAmount == takerAssetFillAmount,
"COMPLETE_FILL_FAILED"
);
return fillResults; return fillResults;
} }
@ -88,14 +89,7 @@ contract MixinWrapperFunctions is
fillOrderCalldata, // write output over input fillOrderCalldata, // write output over input
128 // output size is 128 bytes 128 // output size is 128 bytes
) )
switch success if success {
case 0 {
mstore(fillResults, 0)
mstore(add(fillResults, 32), 0)
mstore(add(fillResults, 64), 0)
mstore(add(fillResults, 96), 0)
}
case 1 {
mstore(fillResults, mload(fillOrderCalldata)) mstore(fillResults, mload(fillOrderCalldata))
mstore(add(fillResults, 32), mload(add(fillOrderCalldata, 32))) mstore(add(fillResults, 32), mload(add(fillOrderCalldata, 32)))
mstore(add(fillResults, 64), mload(add(fillOrderCalldata, 64))) mstore(add(fillResults, 64), mload(add(fillOrderCalldata, 64)))
@ -117,11 +111,12 @@ contract MixinWrapperFunctions is
bytes[] memory signatures bytes[] memory signatures
) )
public public
nonReentrant
returns (FillResults memory totalFillResults) returns (FillResults memory totalFillResults)
{ {
uint256 ordersLength = orders.length; uint256 ordersLength = orders.length;
for (uint256 i = 0; i != ordersLength; i++) { for (uint256 i = 0; i != ordersLength; i++) {
FillResults memory singleFillResults = fillOrder( FillResults memory singleFillResults = fillOrderInternal(
orders[i], orders[i],
takerAssetFillAmounts[i], takerAssetFillAmounts[i],
signatures[i] signatures[i]
@ -143,11 +138,12 @@ contract MixinWrapperFunctions is
bytes[] memory signatures bytes[] memory signatures
) )
public public
nonReentrant
returns (FillResults memory totalFillResults) returns (FillResults memory totalFillResults)
{ {
uint256 ordersLength = orders.length; uint256 ordersLength = orders.length;
for (uint256 i = 0; i != ordersLength; i++) { for (uint256 i = 0; i != ordersLength; i++) {
FillResults memory singleFillResults = fillOrKillOrder( FillResults memory singleFillResults = fillOrKillOrderInternal(
orders[i], orders[i],
takerAssetFillAmounts[i], takerAssetFillAmounts[i],
signatures[i] signatures[i]
@ -195,6 +191,7 @@ contract MixinWrapperFunctions is
bytes[] memory signatures bytes[] memory signatures
) )
public public
nonReentrant
returns (FillResults memory totalFillResults) returns (FillResults memory totalFillResults)
{ {
bytes memory takerAssetData = orders[0].takerAssetData; bytes memory takerAssetData = orders[0].takerAssetData;
@ -210,7 +207,7 @@ contract MixinWrapperFunctions is
uint256 remainingTakerAssetFillAmount = safeSub(takerAssetFillAmount, totalFillResults.takerAssetFilledAmount); uint256 remainingTakerAssetFillAmount = safeSub(takerAssetFillAmount, totalFillResults.takerAssetFilledAmount);
// Attempt to sell the remaining amount of takerAsset // Attempt to sell the remaining amount of takerAsset
FillResults memory singleFillResults = fillOrder( FillResults memory singleFillResults = fillOrderInternal(
orders[i], orders[i],
remainingTakerAssetFillAmount, remainingTakerAssetFillAmount,
signatures[i] signatures[i]
@ -282,6 +279,7 @@ contract MixinWrapperFunctions is
bytes[] memory signatures bytes[] memory signatures
) )
public public
nonReentrant
returns (FillResults memory totalFillResults) returns (FillResults memory totalFillResults)
{ {
bytes memory makerAssetData = orders[0].makerAssetData; bytes memory makerAssetData = orders[0].makerAssetData;
@ -298,14 +296,14 @@ contract MixinWrapperFunctions is
// Convert the remaining amount of makerAsset to buy into remaining amount // Convert the remaining amount of makerAsset to buy into remaining amount
// of takerAsset to sell, assuming entire amount can be sold in the current order // of takerAsset to sell, assuming entire amount can be sold in the current order
uint256 remainingTakerAssetFillAmount = getPartialAmount( uint256 remainingTakerAssetFillAmount = getPartialAmountFloor(
orders[i].takerAssetAmount, orders[i].takerAssetAmount,
orders[i].makerAssetAmount, orders[i].makerAssetAmount,
remainingMakerAssetFillAmount remainingMakerAssetFillAmount
); );
// Attempt to sell the remaining amount of takerAsset // Attempt to sell the remaining amount of takerAsset
FillResults memory singleFillResults = fillOrder( FillResults memory singleFillResults = fillOrderInternal(
orders[i], orders[i],
remainingTakerAssetFillAmount, remainingTakerAssetFillAmount,
signatures[i] signatures[i]
@ -350,7 +348,7 @@ contract MixinWrapperFunctions is
// Convert the remaining amount of makerAsset to buy into remaining amount // Convert the remaining amount of makerAsset to buy into remaining amount
// of takerAsset to sell, assuming entire amount can be sold in the current order // of takerAsset to sell, assuming entire amount can be sold in the current order
uint256 remainingTakerAssetFillAmount = getPartialAmount( uint256 remainingTakerAssetFillAmount = getPartialAmountFloor(
orders[i].takerAssetAmount, orders[i].takerAssetAmount,
orders[i].makerAssetAmount, orders[i].makerAssetAmount,
remainingMakerAssetFillAmount remainingMakerAssetFillAmount
@ -400,4 +398,28 @@ contract MixinWrapperFunctions is
} }
return ordersInfo; return ordersInfo;
} }
/// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled.
/// @param order Order struct containing order specifications.
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
/// @param signature Proof that order has been created by maker.
function fillOrKillOrderInternal(
LibOrder.Order memory order,
uint256 takerAssetFillAmount,
bytes memory signature
)
internal
returns (FillResults memory fillResults)
{
fillResults = fillOrderInternal(
order,
takerAssetFillAmount,
signature
);
require(
fillResults.takerAssetFilledAmount == takerAssetFillAmount,
"COMPLETE_FILL_FAILED"
);
return fillResults;
}
} }

View File

@ -25,12 +25,12 @@ contract LibMath is
SafeMath SafeMath
{ {
/// @dev Calculates partial value given a numerator and denominator. /// @dev Calculates partial value given a numerator and denominator rounded down.
/// @param numerator Numerator. /// @param numerator Numerator.
/// @param denominator Denominator. /// @param denominator Denominator.
/// @param target Value to calculate partial of. /// @param target Value to calculate partial of.
/// @return Partial value of target. /// @return Partial value of target rounded down.
function getPartialAmount( function getPartialAmountFloor(
uint256 numerator, uint256 numerator,
uint256 denominator, uint256 denominator,
uint256 target uint256 target
@ -39,6 +39,11 @@ contract LibMath is
pure pure
returns (uint256 partialAmount) returns (uint256 partialAmount)
{ {
require(
denominator > 0,
"DIVISION_BY_ZERO"
);
partialAmount = safeDiv( partialAmount = safeDiv(
safeMul(numerator, target), safeMul(numerator, target),
denominator denominator
@ -46,12 +51,44 @@ contract LibMath is
return partialAmount; return partialAmount;
} }
/// @dev Checks if rounding error > 0.1%. /// @dev Calculates partial value given a numerator and denominator rounded down.
/// @param numerator Numerator.
/// @param denominator Denominator.
/// @param target Value to calculate partial of.
/// @return Partial value of target rounded up.
function getPartialAmountCeil(
uint256 numerator,
uint256 denominator,
uint256 target
)
internal
pure
returns (uint256 partialAmount)
{
require(
denominator > 0,
"DIVISION_BY_ZERO"
);
// safeDiv computes `floor(a / b)`. We use the identity (a, b integer):
// ceil(a / b) = floor((a + b - 1) / b)
// To implement `ceil(a / b)` using safeDiv.
partialAmount = safeDiv(
safeAdd(
safeMul(numerator, target),
safeSub(denominator, 1)
),
denominator
);
return partialAmount;
}
/// @dev Checks if rounding error >= 0.1% when rounding down.
/// @param numerator Numerator. /// @param numerator Numerator.
/// @param denominator Denominator. /// @param denominator Denominator.
/// @param target Value to multiply with numerator/denominator. /// @param target Value to multiply with numerator/denominator.
/// @return Rounding error is present. /// @return Rounding error is present.
function isRoundingError( function isRoundingErrorFloor(
uint256 numerator, uint256 numerator,
uint256 denominator, uint256 denominator,
uint256 target uint256 target
@ -60,16 +97,73 @@ contract LibMath is
pure pure
returns (bool isError) returns (bool isError)
{ {
uint256 remainder = mulmod(target, numerator, denominator); require(
if (remainder == 0) { denominator > 0,
return false; // No rounding error. "DIVISION_BY_ZERO"
);
// The absolute rounding error is the difference between the rounded
// value and the ideal value. The relative rounding error is the
// absolute rounding error divided by the absolute value of the
// ideal value. This is undefined when the ideal value is zero.
//
// The ideal value is `numerator * target / denominator`.
// Let's call `numerator * target % denominator` the remainder.
// The absolute error is `remainder / denominator`.
//
// When the ideal value is zero, we require the absolute error to
// be zero. Fortunately, this is always the case. The ideal value is
// zero iff `numerator == 0` and/or `target == 0`. In this case the
// remainder and absolute error are also zero.
if (target == 0 || numerator == 0) {
return false;
} }
uint256 errPercentageTimes1000000 = safeDiv( // Otherwise, we want the relative rounding error to be strictly
safeMul(remainder, 1000000), // less than 0.1%.
safeMul(numerator, target) // The relative error is `remainder / (numerator * target)`.
// We want the relative error less than 1 / 1000:
// remainder / (numerator * denominator) < 1 / 1000
// or equivalently:
// 1000 * remainder < numerator * target
// so we have a rounding error iff:
// 1000 * remainder >= numerator * target
uint256 remainder = mulmod(target, numerator, denominator);
isError = safeMul(1000, remainder) >= safeMul(numerator, target);
return isError;
}
/// @dev Checks if rounding error >= 0.1% when rounding up.
/// @param numerator Numerator.
/// @param denominator Denominator.
/// @param target Value to multiply with numerator/denominator.
/// @return Rounding error is present.
function isRoundingErrorCeil(
uint256 numerator,
uint256 denominator,
uint256 target
)
internal
pure
returns (bool isError)
{
require(
denominator > 0,
"DIVISION_BY_ZERO"
); );
isError = errPercentageTimes1000000 > 1000;
// See the comments in `isRoundingError`.
if (target == 0 || numerator == 0) {
// When either is zero, the ideal value and rounded value are zero
// and there is no rounding error. (Although the relative error
// is undefined.)
return false;
}
// Compute remainder as before
uint256 remainder = mulmod(target, numerator, denominator);
// TODO: safeMod
remainder = safeSub(denominator, remainder) % denominator;
isError = safeMul(1000, remainder) >= safeMul(numerator, target);
return isError; return isError;
} }
} }

View File

@ -59,6 +59,19 @@ contract MExchangeCore is
uint256 orderEpoch // Orders with specified makerAddress and senderAddress with a salt less than this value are considered cancelled. uint256 orderEpoch // Orders with specified makerAddress and senderAddress with a salt less than this value are considered cancelled.
); );
/// @dev Fills the input order.
/// @param order Order struct containing order specifications.
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
/// @param signature Proof that order has been created by maker.
/// @return Amounts filled and fees paid by maker and taker.
function fillOrderInternal(
LibOrder.Order memory order,
uint256 takerAssetFillAmount,
bytes memory signature
)
internal
returns (LibFillResults.FillResults memory fillResults);
/// @dev Updates state with results of a fill order. /// @dev Updates state with results of a fill order.
/// @param order that was filled. /// @param order that was filled.
/// @param takerAddress Address of taker who filled the order. /// @param takerAddress Address of taker who filled the order.
@ -86,18 +99,30 @@ contract MExchangeCore is
/// @dev Validates context for fillOrder. Succeeds or throws. /// @dev Validates context for fillOrder. Succeeds or throws.
/// @param order to be filled. /// @param order to be filled.
/// @param orderInfo Status, orderHash, and amount already filled of order. /// @param orderInfo OrderStatus, orderHash, and amount already filled of order.
/// @param takerAddress Address of order taker. /// @param takerAddress Address of order taker.
/// @param takerAssetFillAmount Desired amount of order to fill by taker.
/// @param takerAssetFilledAmount Amount of takerAsset that will be filled.
/// @param signature Proof that the orders was created by its maker. /// @param signature Proof that the orders was created by its maker.
function assertValidFill( function assertFillableOrder(
LibOrder.Order memory order, LibOrder.Order memory order,
LibOrder.OrderInfo memory orderInfo, LibOrder.OrderInfo memory orderInfo,
address takerAddress, address takerAddress,
bytes memory signature
)
internal
view;
/// @dev Validates context for fillOrder. Succeeds or throws.
/// @param order to be filled.
/// @param orderInfo Status, orderHash, and amount already filled of order.
/// @param takerAssetFillAmount Desired amount of order to fill by taker.
/// @param takerAssetFilledAmount Amount of takerAsset that will be filled.
/// @param makerAssetFilledAmount Amount of makerAsset that will be transfered.
function assertValidFill(
LibOrder.Order memory order,
LibOrder.OrderInfo memory orderInfo,
uint256 takerAssetFillAmount, uint256 takerAssetFillAmount,
uint256 takerAssetFilledAmount, uint256 takerAssetFilledAmount,
bytes memory signature uint256 makerAssetFilledAmount
) )
internal internal
view; view;

View File

@ -36,11 +36,40 @@ contract MSignatureValidator is
Invalid, // 0x01 Invalid, // 0x01
EIP712, // 0x02 EIP712, // 0x02
EthSign, // 0x03 EthSign, // 0x03
Caller, // 0x04 Wallet, // 0x04
Wallet, // 0x05 Validator, // 0x05
Validator, // 0x06 PreSigned, // 0x06
PreSigned, // 0x07 NSignatureTypes // 0x07, number of signature types. Always leave at end.
Trezor, // 0x08
NSignatureTypes // 0x09, number of signature types. Always leave at end.
} }
/// @dev Verifies signature using logic defined by Wallet contract.
/// @param hash Any 32 byte hash.
/// @param walletAddress Address that should have signed the given hash
/// and defines its own signature verification method.
/// @param signature Proof that the hash has been signed by signer.
/// @return True if the address recovered from the provided signature matches the input signer address.
function isValidWalletSignature(
bytes32 hash,
address walletAddress,
bytes signature
)
internal
view
returns (bool isValid);
/// @dev Verifies signature using logic defined by Validator contract.
/// @param validatorAddress Address of validator contract.
/// @param hash Any 32 byte hash.
/// @param signerAddress Address that should have signed the given hash.
/// @param signature Proof that the hash has been signed by signer.
/// @return True if the address recovered from the provided signature matches the input signer address.
function isValidValidatorSignature(
address validatorAddress,
bytes32 hash,
address signerAddress,
bytes signature
)
internal
view
returns (bool isValid);
} }

View File

@ -0,0 +1,40 @@
/*
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.4.24;
pragma experimental ABIEncoderV2;
import "../libs/LibOrder.sol";
import "../libs/LibFillResults.sol";
import "../interfaces/IWrapperFunctions.sol";
contract MWrapperFunctions {
/// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled.
/// @param order LibOrder.Order struct containing order specifications.
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
/// @param signature Proof that order has been created by maker.
function fillOrKillOrderInternal(
LibOrder.Order memory order,
uint256 takerAssetFillAmount,
bytes memory signature
)
internal
returns (LibFillResults.FillResults memory fillResults);
}

View File

@ -0,0 +1,182 @@
/*
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.4.24;
pragma experimental ABIEncoderV2;
import "../../utils/LibBytes/LibBytes.sol";
import "../../tokens/ERC20Token/ERC20Token.sol";
import "../../protocol/Exchange/interfaces/IExchange.sol";
import "../../protocol/Exchange/libs/LibOrder.sol";
contract ReentrantERC20Token is
ERC20Token
{
using LibBytes for bytes;
// solhint-disable-next-line var-name-mixedcase
IExchange internal EXCHANGE;
bytes internal constant REENTRANCY_ILLEGAL_REVERT_REASON = abi.encodeWithSelector(
bytes4(keccak256("Error(string)")),
"REENTRANCY_ILLEGAL"
);
// All of these functions are potentially vulnerable to reentrancy
// We do not test any "noThrow" functions because `fillOrderNoThrow` makes a delegatecall to `fillOrder`
enum ExchangeFunction {
FILL_ORDER,
FILL_OR_KILL_ORDER,
BATCH_FILL_ORDERS,
BATCH_FILL_OR_KILL_ORDERS,
MARKET_BUY_ORDERS,
MARKET_SELL_ORDERS,
MATCH_ORDERS,
CANCEL_ORDER,
CANCEL_ORDERS_UP_TO,
SET_SIGNATURE_VALIDATOR_APPROVAL
}
uint8 internal currentFunctionId = 0;
constructor (address _exchange)
public
{
EXCHANGE = IExchange(_exchange);
}
/// @dev Set the current function that will be called when `transferFrom` is called.
/// @param _currentFunctionId Id that corresponds to function name.
function setCurrentFunction(uint8 _currentFunctionId)
external
{
currentFunctionId = _currentFunctionId;
}
/// @dev A version of `transferFrom` that attempts to reenter the Exchange contract.
/// @param _from The address of the sender
/// @param _to The address of the recipient
/// @param _value The amount of token to be transferred
function transferFrom(
address _from,
address _to,
uint256 _value
)
external
returns (bool)
{
// This order would normally be invalid, but it will be used strictly for testing reentrnacy.
// Any reentrancy checks will happen before any other checks that invalidate the order.
LibOrder.Order memory order;
// Initialize remaining null parameters
bytes memory signature;
LibOrder.Order[] memory orders;
uint256[] memory takerAssetFillAmounts;
bytes[] memory signatures;
bytes memory calldata;
// Create calldata for function that corresponds to currentFunctionId
if (currentFunctionId == uint8(ExchangeFunction.FILL_ORDER)) {
calldata = abi.encodeWithSelector(
EXCHANGE.fillOrder.selector,
order,
0,
signature
);
} else if (currentFunctionId == uint8(ExchangeFunction.FILL_OR_KILL_ORDER)) {
calldata = abi.encodeWithSelector(
EXCHANGE.fillOrKillOrder.selector,
order,
0,
signature
);
} else if (currentFunctionId == uint8(ExchangeFunction.BATCH_FILL_ORDERS)) {
calldata = abi.encodeWithSelector(
EXCHANGE.batchFillOrders.selector,
orders,
takerAssetFillAmounts,
signatures
);
} else if (currentFunctionId == uint8(ExchangeFunction.BATCH_FILL_OR_KILL_ORDERS)) {
calldata = abi.encodeWithSelector(
EXCHANGE.batchFillOrKillOrders.selector,
orders,
takerAssetFillAmounts,
signatures
);
} else if (currentFunctionId == uint8(ExchangeFunction.MARKET_BUY_ORDERS)) {
calldata = abi.encodeWithSelector(
EXCHANGE.marketBuyOrders.selector,
orders,
0,
signatures
);
} else if (currentFunctionId == uint8(ExchangeFunction.MARKET_SELL_ORDERS)) {
calldata = abi.encodeWithSelector(
EXCHANGE.marketSellOrders.selector,
orders,
0,
signatures
);
} else if (currentFunctionId == uint8(ExchangeFunction.MATCH_ORDERS)) {
calldata = abi.encodeWithSelector(
EXCHANGE.matchOrders.selector,
order,
order,
signature,
signature
);
} else if (currentFunctionId == uint8(ExchangeFunction.CANCEL_ORDER)) {
calldata = abi.encodeWithSelector(
EXCHANGE.cancelOrder.selector,
order
);
} else if (currentFunctionId == uint8(ExchangeFunction.CANCEL_ORDERS_UP_TO)) {
calldata = abi.encodeWithSelector(
EXCHANGE.cancelOrdersUpTo.selector,
0
);
} else if (currentFunctionId == uint8(ExchangeFunction.SET_SIGNATURE_VALIDATOR_APPROVAL)) {
calldata = abi.encodeWithSelector(
EXCHANGE.setSignatureValidatorApproval.selector,
address(0),
false
);
}
// Call Exchange function, swallow error
address(EXCHANGE).call(calldata);
// Revert reason is 100 bytes
bytes memory returnData = new bytes(100);
// Copy return data
assembly {
returndatacopy(add(returnData, 32), 0, 100)
}
// Revert if function reverted with REENTRANCY_ILLEGAL error
require(!REENTRANCY_ILLEGAL_REVERT_REASON.equals(returnData));
// Transfer will return true if function failed for any other reason
return true;
}
}

View File

@ -67,7 +67,7 @@ contract TestExchangeInternals is
/// @param denominator Denominator. /// @param denominator Denominator.
/// @param target Value to calculate partial of. /// @param target Value to calculate partial of.
/// @return Partial value of target. /// @return Partial value of target.
function publicGetPartialAmount( function publicGetPartialAmountFloor(
uint256 numerator, uint256 numerator,
uint256 denominator, uint256 denominator,
uint256 target uint256 target
@ -76,15 +76,32 @@ contract TestExchangeInternals is
pure pure
returns (uint256 partialAmount) returns (uint256 partialAmount)
{ {
return getPartialAmount(numerator, denominator, target); return getPartialAmountFloor(numerator, denominator, target);
} }
/// @dev Checks if rounding error > 0.1%. /// @dev Calculates partial value given a numerator and denominator.
/// @param numerator Numerator.
/// @param denominator Denominator.
/// @param target Value to calculate partial of.
/// @return Partial value of target.
function publicGetPartialAmountCeil(
uint256 numerator,
uint256 denominator,
uint256 target
)
public
pure
returns (uint256 partialAmount)
{
return getPartialAmountCeil(numerator, denominator, target);
}
/// @dev Checks if rounding error >= 0.1%.
/// @param numerator Numerator. /// @param numerator Numerator.
/// @param denominator Denominator. /// @param denominator Denominator.
/// @param target Value to multiply with numerator/denominator. /// @param target Value to multiply with numerator/denominator.
/// @return Rounding error is present. /// @return Rounding error is present.
function publicIsRoundingError( function publicIsRoundingErrorFloor(
uint256 numerator, uint256 numerator,
uint256 denominator, uint256 denominator,
uint256 target uint256 target
@ -93,7 +110,24 @@ contract TestExchangeInternals is
pure pure
returns (bool isError) returns (bool isError)
{ {
return isRoundingError(numerator, denominator, target); return isRoundingErrorFloor(numerator, denominator, target);
}
/// @dev Checks if rounding error >= 0.1%.
/// @param numerator Numerator.
/// @param denominator Denominator.
/// @param target Value to multiply with numerator/denominator.
/// @return Rounding error is present.
function publicIsRoundingErrorCeil(
uint256 numerator,
uint256 denominator,
uint256 target
)
public
pure
returns (bool isError)
{
return isRoundingErrorCeil(numerator, denominator, target);
} }
/// @dev Updates state with results of a fill order. /// @dev Updates state with results of a fill order.

View File

@ -49,7 +49,7 @@ contract TestLibs is
return fillOrderCalldata; return fillOrderCalldata;
} }
function publicGetPartialAmount( function publicGetPartialAmountFloor(
uint256 numerator, uint256 numerator,
uint256 denominator, uint256 denominator,
uint256 target uint256 target
@ -58,7 +58,7 @@ contract TestLibs is
pure pure
returns (uint256 partialAmount) returns (uint256 partialAmount)
{ {
partialAmount = getPartialAmount( partialAmount = getPartialAmountFloor(
numerator, numerator,
denominator, denominator,
target target
@ -66,7 +66,24 @@ contract TestLibs is
return partialAmount; return partialAmount;
} }
function publicIsRoundingError( function publicGetPartialAmountCeil(
uint256 numerator,
uint256 denominator,
uint256 target
)
public
pure
returns (uint256 partialAmount)
{
partialAmount = getPartialAmountCeil(
numerator,
denominator,
target
);
return partialAmount;
}
function publicIsRoundingErrorFloor(
uint256 numerator, uint256 numerator,
uint256 denominator, uint256 denominator,
uint256 target uint256 target
@ -75,7 +92,24 @@ contract TestLibs is
pure pure
returns (bool isError) returns (bool isError)
{ {
isError = isRoundingError( isError = isRoundingErrorFloor(
numerator,
denominator,
target
);
return isError;
}
function publicIsRoundingErrorCeil(
uint256 numerator,
uint256 denominator,
uint256 target
)
public
pure
returns (bool isError)
{
isError = isRoundingErrorCeil(
numerator, numerator,
denominator, denominator,
target target

View File

@ -0,0 +1,81 @@
/*
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.4.24;
import "../../tokens/ERC20Token/IERC20Token.sol";
// solhint-disable no-unused-vars
contract TestStaticCallReceiver {
uint256 internal state = 1;
/// @dev Updates state and returns true. Intended to be used with `Validator` signature type.
/// @param hash Message hash that is signed.
/// @param signerAddress Address that should have signed the given hash.
/// @param signature Proof of signing.
/// @return Validity of order signature.
function isValidSignature(
bytes32 hash,
address signerAddress,
bytes signature
)
external
returns (bool isValid)
{
updateState();
return true;
}
/// @dev Updates state and returns true. Intended to be used with `Wallet` signature type.
/// @param hash Message hash that is signed.
/// @param signature Proof of signing.
/// @return Validity of order signature.
function isValidSignature(
bytes32 hash,
bytes signature
)
external
returns (bool isValid)
{
updateState();
return true;
}
/// @dev Approves an ERC20 token to spend tokens from this address.
/// @param token Address of ERC20 token.
/// @param spender Address that will spend tokens.
/// @param value Amount of tokens spender is approved to spend.
function approveERC20(
address token,
address spender,
uint256 value
)
external
{
IERC20Token(token).approve(spender, value);
}
/// @dev Increments state variable.
function updateState()
internal
{
state++;
}
}

View File

@ -0,0 +1,44 @@
/*
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.4.24;
contract ReentrancyGuard {
// Locked state of mutex
bool private locked = false;
/// @dev Functions with this modifer cannot be reentered. The mutex will be locked
/// before function execution and unlocked after.
modifier nonReentrant() {
// Ensure mutex is unlocked
require(
!locked,
"REENTRANCY_ILLEGAL"
);
// Lock mutex before function call
locked = true;
// Perform function call
_;
// Unlock mutex after function call
locked = false;
}
}

View File

@ -12,7 +12,7 @@ import { ERC20ProxyContract } from '../../generated_contract_wrappers/erc20_prox
import { ERC721ProxyContract } from '../../generated_contract_wrappers/erc721_proxy'; import { ERC721ProxyContract } from '../../generated_contract_wrappers/erc721_proxy';
import { IAssetProxyContract } from '../../generated_contract_wrappers/i_asset_proxy'; import { IAssetProxyContract } from '../../generated_contract_wrappers/i_asset_proxy';
import { artifacts } from '../utils/artifacts'; import { artifacts } from '../utils/artifacts';
import { expectTransactionFailedAsync } from '../utils/assertions'; import { expectTransactionFailedAsync, expectTransactionFailedWithoutReasonAsync } from '../utils/assertions';
import { chaiSetup } from '../utils/chai_setup'; import { chaiSetup } from '../utils/chai_setup';
import { constants } from '../utils/constants'; import { constants } from '../utils/constants';
import { ERC20Wrapper } from '../utils/erc20_wrapper'; import { ERC20Wrapper } from '../utils/erc20_wrapper';
@ -99,6 +99,17 @@ describe('Asset Transfer Proxies', () => {
await blockchainLifecycle.revertAsync(); await blockchainLifecycle.revertAsync();
}); });
describe('Transfer Proxy - ERC20', () => { describe('Transfer Proxy - ERC20', () => {
it('should revert if undefined function is called', async () => {
const undefinedSelector = '0x01020304';
await expectTransactionFailedWithoutReasonAsync(
web3Wrapper.sendTransactionAsync({
from: owner,
to: erc20Proxy.address,
value: constants.ZERO_AMOUNT,
data: undefinedSelector,
}),
);
});
describe('transferFrom', () => { describe('transferFrom', () => {
it('should successfully transfer tokens', async () => { it('should successfully transfer tokens', async () => {
// Construct ERC20 asset data // Construct ERC20 asset data
@ -219,6 +230,17 @@ describe('Asset Transfer Proxies', () => {
}); });
describe('Transfer Proxy - ERC721', () => { describe('Transfer Proxy - ERC721', () => {
it('should revert if undefined function is called', async () => {
const undefinedSelector = '0x01020304';
await expectTransactionFailedWithoutReasonAsync(
web3Wrapper.sendTransactionAsync({
from: owner,
to: erc721Proxy.address,
value: constants.ZERO_AMOUNT,
data: undefinedSelector,
}),
);
});
describe('transferFrom', () => { describe('transferFrom', () => {
it('should successfully transfer tokens', async () => { it('should successfully transfer tokens', async () => {
// Construct ERC721 asset data // Construct ERC721 asset data

View File

@ -1,6 +1,6 @@
import { BlockchainLifecycle } from '@0xproject/dev-utils'; import { BlockchainLifecycle } from '@0xproject/dev-utils';
import { assetDataUtils, orderHashUtils } from '@0xproject/order-utils'; import { assetDataUtils, orderHashUtils } from '@0xproject/order-utils';
import { RevertReason, SignedOrder } from '@0xproject/types'; import { RevertReason, SignatureType, SignedOrder } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils'; import { BigNumber } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper'; import { Web3Wrapper } from '@0xproject/web3-wrapper';
import * as chai from 'chai'; import * as chai from 'chai';
@ -14,6 +14,8 @@ import { DummyNoReturnERC20TokenContract } from '../../generated_contract_wrappe
import { ERC20ProxyContract } from '../../generated_contract_wrappers/erc20_proxy'; import { ERC20ProxyContract } from '../../generated_contract_wrappers/erc20_proxy';
import { ERC721ProxyContract } from '../../generated_contract_wrappers/erc721_proxy'; import { ERC721ProxyContract } from '../../generated_contract_wrappers/erc721_proxy';
import { ExchangeCancelEventArgs, ExchangeContract } from '../../generated_contract_wrappers/exchange'; import { ExchangeCancelEventArgs, ExchangeContract } from '../../generated_contract_wrappers/exchange';
import { ReentrantERC20TokenContract } from '../../generated_contract_wrappers/reentrant_erc20_token';
import { TestStaticCallReceiverContract } from '../../generated_contract_wrappers/test_static_call_receiver';
import { artifacts } from '../utils/artifacts'; import { artifacts } from '../utils/artifacts';
import { expectTransactionFailedAsync } from '../utils/assertions'; import { expectTransactionFailedAsync } from '../utils/assertions';
import { getLatestBlockTimestampAsync, increaseTimeAndMineBlockAsync } from '../utils/block_timestamp'; import { getLatestBlockTimestampAsync, increaseTimeAndMineBlockAsync } from '../utils/block_timestamp';
@ -41,9 +43,12 @@ describe('Exchange core', () => {
let zrxToken: DummyERC20TokenContract; let zrxToken: DummyERC20TokenContract;
let erc721Token: DummyERC721TokenContract; let erc721Token: DummyERC721TokenContract;
let noReturnErc20Token: DummyNoReturnERC20TokenContract; let noReturnErc20Token: DummyNoReturnERC20TokenContract;
let reentrantErc20Token: ReentrantERC20TokenContract;
let exchange: ExchangeContract; let exchange: ExchangeContract;
let erc20Proxy: ERC20ProxyContract; let erc20Proxy: ERC20ProxyContract;
let erc721Proxy: ERC721ProxyContract; let erc721Proxy: ERC721ProxyContract;
let maliciousWallet: TestStaticCallReceiverContract;
let maliciousValidator: TestStaticCallReceiverContract;
let signedOrder: SignedOrder; let signedOrder: SignedOrder;
let erc20Balances: ERC20BalancesByOwner; let erc20Balances: ERC20BalancesByOwner;
@ -109,6 +114,18 @@ describe('Exchange core', () => {
constants.AWAIT_TRANSACTION_MINED_MS, constants.AWAIT_TRANSACTION_MINED_MS,
); );
maliciousWallet = maliciousValidator = await TestStaticCallReceiverContract.deployFrom0xArtifactAsync(
artifacts.TestStaticCallReceiver,
provider,
txDefaults,
);
reentrantErc20Token = await ReentrantERC20TokenContract.deployFrom0xArtifactAsync(
artifacts.ReentrantERC20Token,
provider,
txDefaults,
exchange.address,
);
defaultMakerAssetAddress = erc20TokenA.address; defaultMakerAssetAddress = erc20TokenA.address;
defaultTakerAssetAddress = erc20TokenB.address; defaultTakerAssetAddress = erc20TokenB.address;
@ -135,6 +152,26 @@ describe('Exchange core', () => {
signedOrder = await orderFactory.newSignedOrderAsync(); signedOrder = await orderFactory.newSignedOrderAsync();
}); });
const reentrancyTest = (functionNames: string[]) => {
_.forEach(functionNames, async (functionName: string, functionId: number) => {
const description = `should not allow fillOrder to reenter the Exchange contract via ${functionName}`;
it(description, async () => {
signedOrder = await orderFactory.newSignedOrderAsync({
makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address),
});
await web3Wrapper.awaitTransactionSuccessAsync(
await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await expectTransactionFailedAsync(
exchangeWrapper.fillOrderAsync(signedOrder, takerAddress),
RevertReason.TransferFailed,
);
});
});
};
describe('fillOrder reentrancy tests', () => reentrancyTest(constants.FUNCTIONS_WITH_MUTEX));
it('should throw if signature is invalid', async () => { it('should throw if signature is invalid', async () => {
signedOrder = await orderFactory.newSignedOrderAsync({ signedOrder = await orderFactory.newSignedOrderAsync({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
@ -161,6 +198,51 @@ describe('Exchange core', () => {
RevertReason.OrderUnfillable, RevertReason.OrderUnfillable,
); );
}); });
it('should revert if `isValidSignature` tries to update state when SignatureType=Wallet', async () => {
const maliciousMakerAddress = maliciousWallet.address;
await web3Wrapper.awaitTransactionSuccessAsync(
await erc20TokenA.setBalance.sendTransactionAsync(
maliciousMakerAddress,
constants.INITIAL_ERC20_BALANCE,
),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await web3Wrapper.awaitTransactionSuccessAsync(
await maliciousWallet.approveERC20.sendTransactionAsync(
erc20TokenA.address,
erc20Proxy.address,
constants.INITIAL_ERC20_ALLOWANCE,
),
constants.AWAIT_TRANSACTION_MINED_MS,
);
signedOrder = await orderFactory.newSignedOrderAsync({
makerAddress: maliciousMakerAddress,
makerFee: constants.ZERO_AMOUNT,
});
signedOrder.signature = `0x0${SignatureType.Wallet}`;
await expectTransactionFailedAsync(
exchangeWrapper.fillOrderAsync(signedOrder, takerAddress),
RevertReason.WalletError,
);
});
it('should revert if `isValidSignature` tries to update state when SignatureType=Validator', async () => {
const isApproved = true;
await web3Wrapper.awaitTransactionSuccessAsync(
await exchange.setSignatureValidatorApproval.sendTransactionAsync(
maliciousValidator.address,
isApproved,
{ from: makerAddress },
),
constants.AWAIT_TRANSACTION_MINED_MS,
);
signedOrder.signature = `${maliciousValidator.address}0${SignatureType.Validator}`;
await expectTransactionFailedAsync(
exchangeWrapper.fillOrderAsync(signedOrder, takerAddress),
RevertReason.ValidatorError,
);
});
}); });
describe('Testing exchange of ERC20 tokens with no return values', () => { describe('Testing exchange of ERC20 tokens with no return values', () => {
@ -448,7 +530,7 @@ describe('Exchange core', () => {
// HACK(albrow): We need to hardcode the gas estimate here because // HACK(albrow): We need to hardcode the gas estimate here because
// the Geth gas estimator doesn't work with the way we use // the Geth gas estimator doesn't work with the way we use
// delegatecall and swallow errors. // delegatecall and swallow errors.
gas: 490000, gas: 600000,
}); });
const newBalances = await erc20Wrapper.getBalancesAsync(); const newBalances = await erc20Wrapper.getBalancesAsync();

View File

@ -1,6 +1,7 @@
import { BlockchainLifecycle } from '@0xproject/dev-utils'; import { BlockchainLifecycle } from '@0xproject/dev-utils';
import { Order, RevertReason, SignedOrder } from '@0xproject/types'; import { Order, RevertReason, SignedOrder } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils'; import { BigNumber } from '@0xproject/utils';
import * as chai from 'chai';
import * as _ from 'lodash'; import * as _ from 'lodash';
import { TestExchangeInternalsContract } from '../../generated_contract_wrappers/test_exchange_internals'; import { TestExchangeInternalsContract } from '../../generated_contract_wrappers/test_exchange_internals';
@ -16,6 +17,8 @@ import { FillResults } from '../utils/types';
import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper'; import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper';
chaiSetup.configure(); chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
const MAX_UINT256 = new BigNumber(2).pow(256).minus(1); const MAX_UINT256 = new BigNumber(2).pow(256).minus(1);
@ -43,26 +46,11 @@ const emptySignedOrder: SignedOrder = {
const overflowErrorForCall = new Error(RevertReason.Uint256Overflow); const overflowErrorForCall = new Error(RevertReason.Uint256Overflow);
async function referenceGetPartialAmountAsync(
numerator: BigNumber,
denominator: BigNumber,
target: BigNumber,
): Promise<BigNumber> {
const invalidOpcodeErrorForCall = new Error(await getInvalidOpcodeErrorMessageForCallAsync());
const product = numerator.mul(target);
if (product.greaterThan(MAX_UINT256)) {
throw overflowErrorForCall;
}
if (denominator.eq(0)) {
throw invalidOpcodeErrorForCall;
}
return product.dividedToIntegerBy(denominator);
}
describe('Exchange core internal functions', () => { describe('Exchange core internal functions', () => {
let testExchange: TestExchangeInternalsContract; let testExchange: TestExchangeInternalsContract;
let invalidOpcodeErrorForCall: Error | undefined; let invalidOpcodeErrorForCall: Error | undefined;
let overflowErrorForSendTransaction: Error | undefined; let overflowErrorForSendTransaction: Error | undefined;
let divisionByZeroErrorForCall: Error | undefined;
before(async () => { before(async () => {
await blockchainLifecycle.startAsync(); await blockchainLifecycle.startAsync();
@ -79,11 +67,29 @@ describe('Exchange core internal functions', () => {
overflowErrorForSendTransaction = new Error( overflowErrorForSendTransaction = new Error(
await getRevertReasonOrErrorMessageForSendTransactionAsync(RevertReason.Uint256Overflow), await getRevertReasonOrErrorMessageForSendTransactionAsync(RevertReason.Uint256Overflow),
); );
divisionByZeroErrorForCall = new Error(
await getRevertReasonOrErrorMessageForSendTransactionAsync(RevertReason.DivisionByZero),
);
invalidOpcodeErrorForCall = new Error(await getInvalidOpcodeErrorMessageForCallAsync()); invalidOpcodeErrorForCall = new Error(await getInvalidOpcodeErrorMessageForCallAsync());
}); });
// Note(albrow): Don't forget to add beforeEach and afterEach calls to reset // Note(albrow): Don't forget to add beforeEach and afterEach calls to reset
// the blockchain state for any tests which modify it! // the blockchain state for any tests which modify it!
async function referenceGetPartialAmountFloorAsync(
numerator: BigNumber,
denominator: BigNumber,
target: BigNumber,
): Promise<BigNumber> {
if (denominator.eq(0)) {
throw divisionByZeroErrorForCall;
}
const product = numerator.mul(target);
if (product.greaterThan(MAX_UINT256)) {
throw overflowErrorForCall;
}
return product.dividedToIntegerBy(denominator);
}
describe('addFillResults', async () => { describe('addFillResults', async () => {
function makeFillResults(value: BigNumber): FillResults { function makeFillResults(value: BigNumber): FillResults {
return { return {
@ -159,18 +165,18 @@ describe('Exchange core internal functions', () => {
// implementation or the Solidity implementation of // implementation or the Solidity implementation of
// calculateFillResults. // calculateFillResults.
return { return {
makerAssetFilledAmount: await referenceGetPartialAmountAsync( makerAssetFilledAmount: await referenceGetPartialAmountFloorAsync(
takerAssetFilledAmount, takerAssetFilledAmount,
orderTakerAssetAmount, orderTakerAssetAmount,
otherAmount, otherAmount,
), ),
takerAssetFilledAmount, takerAssetFilledAmount,
makerFeePaid: await referenceGetPartialAmountAsync( makerFeePaid: await referenceGetPartialAmountFloorAsync(
takerAssetFilledAmount, takerAssetFilledAmount,
orderTakerAssetAmount, orderTakerAssetAmount,
otherAmount, otherAmount,
), ),
takerFeePaid: await referenceGetPartialAmountAsync( takerFeePaid: await referenceGetPartialAmountFloorAsync(
takerAssetFilledAmount, takerAssetFilledAmount,
orderTakerAssetAmount, orderTakerAssetAmount,
otherAmount, otherAmount,
@ -193,18 +199,55 @@ describe('Exchange core internal functions', () => {
); );
}); });
describe('getPartialAmount', async () => { describe('getPartialAmountFloor', async () => {
async function testGetPartialAmountAsync( async function testGetPartialAmountFloorAsync(
numerator: BigNumber, numerator: BigNumber,
denominator: BigNumber, denominator: BigNumber,
target: BigNumber, target: BigNumber,
): Promise<BigNumber> { ): Promise<BigNumber> {
return testExchange.publicGetPartialAmount.callAsync(numerator, denominator, target); return testExchange.publicGetPartialAmountFloor.callAsync(numerator, denominator, target);
} }
await testCombinatoriallyWithReferenceFuncAsync( await testCombinatoriallyWithReferenceFuncAsync(
'getPartialAmount', 'getPartialAmount',
referenceGetPartialAmountAsync, referenceGetPartialAmountFloorAsync,
testGetPartialAmountAsync, testGetPartialAmountFloorAsync,
[uint256Values, uint256Values, uint256Values],
);
});
describe('getPartialAmountCeil', async () => {
async function referenceGetPartialAmountCeilAsync(
numerator: BigNumber,
denominator: BigNumber,
target: BigNumber,
): Promise<BigNumber> {
if (denominator.eq(0)) {
throw divisionByZeroErrorForCall;
}
const product = numerator.mul(target);
const offset = product.add(denominator.sub(1));
if (offset.greaterThan(MAX_UINT256)) {
throw overflowErrorForCall;
}
const result = offset.dividedToIntegerBy(denominator);
if (product.mod(denominator).eq(0)) {
expect(result.mul(denominator)).to.be.bignumber.eq(product);
} else {
expect(result.mul(denominator)).to.be.bignumber.gt(product);
}
return result;
}
async function testGetPartialAmountCeilAsync(
numerator: BigNumber,
denominator: BigNumber,
target: BigNumber,
): Promise<BigNumber> {
return testExchange.publicGetPartialAmountCeil.callAsync(numerator, denominator, target);
}
await testCombinatoriallyWithReferenceFuncAsync(
'getPartialAmountCeil',
referenceGetPartialAmountCeilAsync,
testGetPartialAmountCeilAsync,
[uint256Values, uint256Values, uint256Values], [uint256Values, uint256Values, uint256Values],
); );
}); });
@ -215,33 +258,33 @@ describe('Exchange core internal functions', () => {
denominator: BigNumber, denominator: BigNumber,
target: BigNumber, target: BigNumber,
): Promise<boolean> { ): Promise<boolean> {
const product = numerator.mul(target);
if (denominator.eq(0)) { if (denominator.eq(0)) {
throw invalidOpcodeErrorForCall; throw divisionByZeroErrorForCall;
} }
const remainder = product.mod(denominator); if (numerator.eq(0)) {
if (remainder.eq(0)) {
return false; return false;
} }
if (target.eq(0)) {
return false;
}
const product = numerator.mul(target);
const remainder = product.mod(denominator);
const remainderTimes1000 = remainder.mul('1000');
const isError = remainderTimes1000.gt(product);
if (product.greaterThan(MAX_UINT256)) { if (product.greaterThan(MAX_UINT256)) {
throw overflowErrorForCall; throw overflowErrorForCall;
} }
if (product.eq(0)) { if (remainderTimes1000.greaterThan(MAX_UINT256)) {
throw invalidOpcodeErrorForCall;
}
const remainderTimes1000000 = remainder.mul('1000000');
if (remainderTimes1000000.greaterThan(MAX_UINT256)) {
throw overflowErrorForCall; throw overflowErrorForCall;
} }
const errPercentageTimes1000000 = remainderTimes1000000.dividedToIntegerBy(product); return isError;
return errPercentageTimes1000000.greaterThan('1000');
} }
async function testIsRoundingErrorAsync( async function testIsRoundingErrorAsync(
numerator: BigNumber, numerator: BigNumber,
denominator: BigNumber, denominator: BigNumber,
target: BigNumber, target: BigNumber,
): Promise<boolean> { ): Promise<boolean> {
return testExchange.publicIsRoundingError.callAsync(numerator, denominator, target); return testExchange.publicIsRoundingErrorFloor.callAsync(numerator, denominator, target);
} }
await testCombinatoriallyWithReferenceFuncAsync( await testCombinatoriallyWithReferenceFuncAsync(
'isRoundingError', 'isRoundingError',
@ -251,6 +294,49 @@ describe('Exchange core internal functions', () => {
); );
}); });
describe('isRoundingErrorCeil', async () => {
async function referenceIsRoundingErrorAsync(
numerator: BigNumber,
denominator: BigNumber,
target: BigNumber,
): Promise<boolean> {
if (denominator.eq(0)) {
throw divisionByZeroErrorForCall;
}
if (numerator.eq(0)) {
return false;
}
if (target.eq(0)) {
return false;
}
const product = numerator.mul(target);
const remainder = product.mod(denominator);
const error = denominator.sub(remainder).mod(denominator);
const errorTimes1000 = error.mul('1000');
const isError = errorTimes1000.gt(product);
if (product.greaterThan(MAX_UINT256)) {
throw overflowErrorForCall;
}
if (errorTimes1000.greaterThan(MAX_UINT256)) {
throw overflowErrorForCall;
}
return isError;
}
async function testIsRoundingErrorCeilAsync(
numerator: BigNumber,
denominator: BigNumber,
target: BigNumber,
): Promise<boolean> {
return testExchange.publicIsRoundingErrorCeil.callAsync(numerator, denominator, target);
}
await testCombinatoriallyWithReferenceFuncAsync(
'isRoundingErrorCeil',
referenceIsRoundingErrorAsync,
testIsRoundingErrorCeilAsync,
[uint256Values, uint256Values, uint256Values],
);
});
describe('updateFilledState', async () => { describe('updateFilledState', async () => {
// Note(albrow): Since updateFilledState modifies the state by calling // Note(albrow): Since updateFilledState modifies the state by calling
// sendTransaction, we must reset the state after each test. // sendTransaction, we must reset the state after each test.

View File

@ -71,29 +71,57 @@ describe('Exchange libs', () => {
// combinatorial tests in test/exchange/internal. They test specific edge // combinatorial tests in test/exchange/internal. They test specific edge
// cases that are not covered by the combinatorial tests. // cases that are not covered by the combinatorial tests.
describe('LibMath', () => { describe('LibMath', () => {
it('should return false if there is a rounding error of 0.1%', async () => { describe('isRoundingError', () => {
const numerator = new BigNumber(20); it('should return true if there is a rounding error of 0.1%', async () => {
const denominator = new BigNumber(999); const numerator = new BigNumber(20);
const target = new BigNumber(50); const denominator = new BigNumber(999);
// rounding error = ((20*50/999) - floor(20*50/999)) / (20*50/999) = 0.1% const target = new BigNumber(50);
const isRoundingError = await libs.publicIsRoundingError.callAsync(numerator, denominator, target); // rounding error = ((20*50/999) - floor(20*50/999)) / (20*50/999) = 0.1%
expect(isRoundingError).to.be.false(); const isRoundingError = await libs.publicIsRoundingErrorFloor.callAsync(numerator, denominator, target);
expect(isRoundingError).to.be.true();
});
it('should return false if there is a rounding of 0.09%', async () => {
const numerator = new BigNumber(20);
const denominator = new BigNumber(9991);
const target = new BigNumber(500);
// rounding error = ((20*500/9991) - floor(20*500/9991)) / (20*500/9991) = 0.09%
const isRoundingError = await libs.publicIsRoundingErrorFloor.callAsync(numerator, denominator, target);
expect(isRoundingError).to.be.false();
});
it('should return true if there is a rounding error of 0.11%', async () => {
const numerator = new BigNumber(20);
const denominator = new BigNumber(9989);
const target = new BigNumber(500);
// rounding error = ((20*500/9989) - floor(20*500/9989)) / (20*500/9989) = 0.011%
const isRoundingError = await libs.publicIsRoundingErrorFloor.callAsync(numerator, denominator, target);
expect(isRoundingError).to.be.true();
});
}); });
it('should return false if there is a rounding of 0.09%', async () => { describe('isRoundingErrorCeil', () => {
const numerator = new BigNumber(20); it('should return true if there is a rounding error of 0.1%', async () => {
const denominator = new BigNumber(9991); const numerator = new BigNumber(20);
const target = new BigNumber(500); const denominator = new BigNumber(1001);
// rounding error = ((20*500/9991) - floor(20*500/9991)) / (20*500/9991) = 0.09% const target = new BigNumber(50);
const isRoundingError = await libs.publicIsRoundingError.callAsync(numerator, denominator, target); // rounding error = (ceil(20*50/1001) - (20*50/1001)) / (20*50/1001) = 0.1%
expect(isRoundingError).to.be.false(); const isRoundingError = await libs.publicIsRoundingErrorCeil.callAsync(numerator, denominator, target);
}); expect(isRoundingError).to.be.true();
it('should return true if there is a rounding error of 0.11%', async () => { });
const numerator = new BigNumber(20); it('should return false if there is a rounding of 0.09%', async () => {
const denominator = new BigNumber(9989); const numerator = new BigNumber(20);
const target = new BigNumber(500); const denominator = new BigNumber(10009);
// rounding error = ((20*500/9989) - floor(20*500/9989)) / (20*500/9989) = 0.011% const target = new BigNumber(500);
const isRoundingError = await libs.publicIsRoundingError.callAsync(numerator, denominator, target); // rounding error = (ceil(20*500/10009) - (20*500/10009)) / (20*500/10009) = 0.09%
expect(isRoundingError).to.be.true(); const isRoundingError = await libs.publicIsRoundingErrorCeil.callAsync(numerator, denominator, target);
expect(isRoundingError).to.be.false();
});
it('should return true if there is a rounding error of 0.11%', async () => {
const numerator = new BigNumber(20);
const denominator = new BigNumber(10011);
const target = new BigNumber(500);
// rounding error = (ceil(20*500/10011) - (20*500/10011)) / (20*500/10011) = 0.11%
const isRoundingError = await libs.publicIsRoundingErrorCeil.callAsync(numerator, denominator, target);
expect(isRoundingError).to.be.true();
});
}); });
}); });

File diff suppressed because it is too large Load Diff

View File

@ -9,11 +9,12 @@ import {
TestSignatureValidatorContract, TestSignatureValidatorContract,
TestSignatureValidatorSignatureValidatorApprovalEventArgs, TestSignatureValidatorSignatureValidatorApprovalEventArgs,
} from '../../generated_contract_wrappers/test_signature_validator'; } from '../../generated_contract_wrappers/test_signature_validator';
import { TestStaticCallReceiverContract } from '../../generated_contract_wrappers/test_static_call_receiver';
import { ValidatorContract } from '../../generated_contract_wrappers/validator'; import { ValidatorContract } from '../../generated_contract_wrappers/validator';
import { WalletContract } from '../../generated_contract_wrappers/wallet'; import { WalletContract } from '../../generated_contract_wrappers/wallet';
import { addressUtils } from '../utils/address_utils'; import { addressUtils } from '../utils/address_utils';
import { artifacts } from '../utils/artifacts'; import { artifacts } from '../utils/artifacts';
import { expectContractCallFailed } from '../utils/assertions'; import { expectContractCallFailed, expectContractCallFailedWithoutReasonAsync } from '../utils/assertions';
import { chaiSetup } from '../utils/chai_setup'; import { chaiSetup } from '../utils/chai_setup';
import { constants } from '../utils/constants'; import { constants } from '../utils/constants';
import { LogDecoder } from '../utils/log_decoder'; import { LogDecoder } from '../utils/log_decoder';
@ -31,6 +32,8 @@ describe('MixinSignatureValidator', () => {
let signatureValidator: TestSignatureValidatorContract; let signatureValidator: TestSignatureValidatorContract;
let testWallet: WalletContract; let testWallet: WalletContract;
let testValidator: ValidatorContract; let testValidator: ValidatorContract;
let maliciousWallet: TestStaticCallReceiverContract;
let maliciousValidator: TestStaticCallReceiverContract;
let signerAddress: string; let signerAddress: string;
let signerPrivateKey: Buffer; let signerPrivateKey: Buffer;
let notSignerAddress: string; let notSignerAddress: string;
@ -65,6 +68,11 @@ describe('MixinSignatureValidator', () => {
txDefaults, txDefaults,
signerAddress, signerAddress,
); );
maliciousWallet = maliciousValidator = await TestStaticCallReceiverContract.deployFrom0xArtifactAsync(
artifacts.TestStaticCallReceiver,
provider,
txDefaults,
);
signatureValidatorLogDecoder = new LogDecoder(web3Wrapper); signatureValidatorLogDecoder = new LogDecoder(web3Wrapper);
await web3Wrapper.awaitTransactionSuccessAsync( await web3Wrapper.awaitTransactionSuccessAsync(
await signatureValidator.setSignatureValidatorApproval.sendTransactionAsync(testValidator.address, true, { await signatureValidator.setSignatureValidatorApproval.sendTransactionAsync(testValidator.address, true, {
@ -72,6 +80,16 @@ describe('MixinSignatureValidator', () => {
}), }),
constants.AWAIT_TRANSACTION_MINED_MS, constants.AWAIT_TRANSACTION_MINED_MS,
); );
await web3Wrapper.awaitTransactionSuccessAsync(
await signatureValidator.setSignatureValidatorApproval.sendTransactionAsync(
maliciousValidator.address,
true,
{
from: signerAddress,
},
),
constants.AWAIT_TRANSACTION_MINED_MS,
);
const defaultOrderParams = { const defaultOrderParams = {
...constants.STATIC_ORDER_PARAMS, ...constants.STATIC_ORDER_PARAMS,
@ -263,32 +281,6 @@ describe('MixinSignatureValidator', () => {
expect(isValidSignature).to.be.false(); expect(isValidSignature).to.be.false();
}); });
it('should return true when SignatureType=Caller and signer is caller', async () => {
const signature = ethUtil.toBuffer(`0x${SignatureType.Caller}`);
const signatureHex = ethUtil.bufferToHex(signature);
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
const isValidSignature = await signatureValidator.publicIsValidSignature.callAsync(
orderHashHex,
signerAddress,
signatureHex,
{ from: signerAddress },
);
expect(isValidSignature).to.be.true();
});
it('should return false when SignatureType=Caller and signer is not caller', async () => {
const signature = ethUtil.toBuffer(`0x${SignatureType.Caller}`);
const signatureHex = ethUtil.bufferToHex(signature);
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
const isValidSignature = await signatureValidator.publicIsValidSignature.callAsync(
orderHashHex,
signerAddress,
signatureHex,
{ from: notSignerAddress },
);
expect(isValidSignature).to.be.false();
});
it('should return true when SignatureType=Wallet and signature is valid', async () => { it('should return true when SignatureType=Wallet and signature is valid', async () => {
// Create EIP712 signature // Create EIP712 signature
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
@ -334,6 +326,29 @@ describe('MixinSignatureValidator', () => {
expect(isValidSignature).to.be.false(); expect(isValidSignature).to.be.false();
}); });
it('should revert when `isValidSignature` attempts to update state and SignatureType=Wallet', async () => {
// Create EIP712 signature
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
const orderHashBuffer = ethUtil.toBuffer(orderHashHex);
const ecSignature = ethUtil.ecsign(orderHashBuffer, signerPrivateKey);
// Create 0x signature from EIP712 signature
const signature = Buffer.concat([
ethUtil.toBuffer(ecSignature.v),
ecSignature.r,
ecSignature.s,
ethUtil.toBuffer(`0x${SignatureType.Wallet}`),
]);
const signatureHex = ethUtil.bufferToHex(signature);
await expectContractCallFailed(
signatureValidator.publicIsValidSignature.callAsync(
orderHashHex,
maliciousWallet.address,
signatureHex,
),
RevertReason.WalletError,
);
});
it('should return true when SignatureType=Validator, signature is valid and validator is approved', async () => { it('should return true when SignatureType=Validator, signature is valid and validator is approved', async () => {
const validatorAddress = ethUtil.toBuffer(`${testValidator.address}`); const validatorAddress = ethUtil.toBuffer(`${testValidator.address}`);
const signatureType = ethUtil.toBuffer(`0x${SignatureType.Validator}`); const signatureType = ethUtil.toBuffer(`0x${SignatureType.Validator}`);
@ -364,6 +379,17 @@ describe('MixinSignatureValidator', () => {
expect(isValidSignature).to.be.false(); expect(isValidSignature).to.be.false();
}); });
it('should revert when `isValidSignature` attempts to update state and SignatureType=Validator', async () => {
const validatorAddress = ethUtil.toBuffer(`${maliciousValidator.address}`);
const signatureType = ethUtil.toBuffer(`0x${SignatureType.Validator}`);
const signature = Buffer.concat([validatorAddress, signatureType]);
const signatureHex = ethUtil.bufferToHex(signature);
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
await expectContractCallFailed(
signatureValidator.publicIsValidSignature.callAsync(orderHashHex, signerAddress, signatureHex),
RevertReason.ValidatorError,
);
});
it('should return false when SignatureType=Validator, signature is valid and validator is not approved', async () => { it('should return false when SignatureType=Validator, signature is valid and validator is not approved', async () => {
// Set approval of signature validator to false // Set approval of signature validator to false
await web3Wrapper.awaitTransactionSuccessAsync( await web3Wrapper.awaitTransactionSuccessAsync(
@ -388,53 +414,6 @@ describe('MixinSignatureValidator', () => {
expect(isValidSignature).to.be.false(); expect(isValidSignature).to.be.false();
}); });
it('should return true when SignatureType=Trezor and signature is valid', async () => {
// Create Trezor signature
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
const orderHashWithTrezorPrefixHex = signatureUtils.addSignedMessagePrefix(orderHashHex, SignerType.Trezor);
const orderHashWithTrezorPrefixBuffer = ethUtil.toBuffer(orderHashWithTrezorPrefixHex);
const ecSignature = ethUtil.ecsign(orderHashWithTrezorPrefixBuffer, signerPrivateKey);
// Create 0x signature from Trezor signature
const signature = Buffer.concat([
ethUtil.toBuffer(ecSignature.v),
ecSignature.r,
ecSignature.s,
ethUtil.toBuffer(`0x${SignatureType.Trezor}`),
]);
const signatureHex = ethUtil.bufferToHex(signature);
// Validate signature
const isValidSignature = await signatureValidator.publicIsValidSignature.callAsync(
orderHashHex,
signerAddress,
signatureHex,
);
expect(isValidSignature).to.be.true();
});
it('should return false when SignatureType=Trezor and signature is invalid', async () => {
// Create Trezor signature
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
const orderHashWithTrezorPrefixHex = signatureUtils.addSignedMessagePrefix(orderHashHex, SignerType.Trezor);
const orderHashWithTrezorPrefixBuffer = ethUtil.toBuffer(orderHashWithTrezorPrefixHex);
const ecSignature = ethUtil.ecsign(orderHashWithTrezorPrefixBuffer, signerPrivateKey);
// Create 0x signature from Trezor signature
const signature = Buffer.concat([
ethUtil.toBuffer(ecSignature.v),
ecSignature.r,
ecSignature.s,
ethUtil.toBuffer(`0x${SignatureType.Trezor}`),
]);
const signatureHex = ethUtil.bufferToHex(signature);
// Validate signature.
// This will fail because `signerAddress` signed the message, but we're passing in `notSignerAddress`
const isValidSignature = await signatureValidator.publicIsValidSignature.callAsync(
orderHashHex,
notSignerAddress,
signatureHex,
);
expect(isValidSignature).to.be.false();
});
it('should return true when SignatureType=Presigned and signer has presigned hash', async () => { it('should return true when SignatureType=Presigned and signer has presigned hash', async () => {
// Presign hash // Presign hash
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
@ -468,6 +447,42 @@ describe('MixinSignatureValidator', () => {
); );
expect(isValidSignature).to.be.false(); expect(isValidSignature).to.be.false();
}); });
it('should return true when message was signed by a Trezor One (firmware version 1.6.2)', async () => {
// messageHash translates to 0x2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b
const messageHash = ethUtil.bufferToHex(ethUtil.toBuffer('++++++++++++++++++++++++++++++++'));
const signer = '0xc28b145f10f0bcf0fc000e778615f8fd73490bad';
const v = ethUtil.toBuffer('0x1c');
const r = ethUtil.toBuffer('0x7b888b596ccf87f0bacab0dcb483124973f7420f169b4824d7a12534ac1e9832');
const s = ethUtil.toBuffer('0x0c8e14f7edc01459e13965f1da56e0c23ed11e2cca932571eee1292178f90424');
const trezorSignatureType = ethUtil.toBuffer(`0x${SignatureType.EthSign}`);
const signature = Buffer.concat([v, r, s, trezorSignatureType]);
const signatureHex = ethUtil.bufferToHex(signature);
const isValidSignature = await signatureValidator.publicIsValidSignature.callAsync(
messageHash,
signer,
signatureHex,
);
expect(isValidSignature).to.be.true();
});
it('should return true when message was signed by a Trezor Model T (firmware version 2.0.7)', async () => {
// messageHash translates to 0x2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b
const messageHash = ethUtil.bufferToHex(ethUtil.toBuffer('++++++++++++++++++++++++++++++++'));
const signer = '0x98ce6d9345e8ffa7d99ee0822272fae9d2c0e895';
const v = ethUtil.toBuffer('0x1c');
const r = ethUtil.toBuffer('0x423b71062c327f0ec4fe199b8da0f34185e59b4c1cb4cc23df86cac4a601fb3f');
const s = ethUtil.toBuffer('0x53810d6591b5348b7ee08ee812c874b0fdfb942c9849d59512c90e295221091f');
const trezorSignatureType = ethUtil.toBuffer(`0x${SignatureType.EthSign}`);
const signature = Buffer.concat([v, r, s, trezorSignatureType]);
const signatureHex = ethUtil.bufferToHex(signature);
const isValidSignature = await signatureValidator.publicIsValidSignature.callAsync(
messageHash,
signer,
signatureHex,
);
expect(isValidSignature).to.be.true();
});
}); });
describe('setSignatureValidatorApproval', () => { describe('setSignatureValidatorApproval', () => {

View File

@ -11,6 +11,7 @@ import { DummyERC721TokenContract } from '../../generated_contract_wrappers/dumm
import { ERC20ProxyContract } from '../../generated_contract_wrappers/erc20_proxy'; import { ERC20ProxyContract } from '../../generated_contract_wrappers/erc20_proxy';
import { ERC721ProxyContract } from '../../generated_contract_wrappers/erc721_proxy'; import { ERC721ProxyContract } from '../../generated_contract_wrappers/erc721_proxy';
import { ExchangeContract } from '../../generated_contract_wrappers/exchange'; import { ExchangeContract } from '../../generated_contract_wrappers/exchange';
import { ReentrantERC20TokenContract } from '../../generated_contract_wrappers/reentrant_erc20_token';
import { artifacts } from '../utils/artifacts'; import { artifacts } from '../utils/artifacts';
import { expectTransactionFailedAsync } from '../utils/assertions'; import { expectTransactionFailedAsync } from '../utils/assertions';
import { getLatestBlockTimestampAsync, increaseTimeAndMineBlockAsync } from '../utils/block_timestamp'; import { getLatestBlockTimestampAsync, increaseTimeAndMineBlockAsync } from '../utils/block_timestamp';
@ -40,6 +41,7 @@ describe('Exchange wrappers', () => {
let exchange: ExchangeContract; let exchange: ExchangeContract;
let erc20Proxy: ERC20ProxyContract; let erc20Proxy: ERC20ProxyContract;
let erc721Proxy: ERC721ProxyContract; let erc721Proxy: ERC721ProxyContract;
let reentrantErc20Token: ReentrantERC20TokenContract;
let exchangeWrapper: ExchangeWrapper; let exchangeWrapper: ExchangeWrapper;
let erc20Wrapper: ERC20Wrapper; let erc20Wrapper: ERC20Wrapper;
@ -104,6 +106,13 @@ describe('Exchange wrappers', () => {
constants.AWAIT_TRANSACTION_MINED_MS, constants.AWAIT_TRANSACTION_MINED_MS,
); );
reentrantErc20Token = await ReentrantERC20TokenContract.deployFrom0xArtifactAsync(
artifacts.ReentrantERC20Token,
provider,
txDefaults,
exchange.address,
);
defaultMakerAssetAddress = erc20TokenA.address; defaultMakerAssetAddress = erc20TokenA.address;
defaultTakerAssetAddress = erc20TokenB.address; defaultTakerAssetAddress = erc20TokenB.address;
@ -126,6 +135,26 @@ describe('Exchange wrappers', () => {
await blockchainLifecycle.revertAsync(); await blockchainLifecycle.revertAsync();
}); });
describe('fillOrKillOrder', () => { describe('fillOrKillOrder', () => {
const reentrancyTest = (functionNames: string[]) => {
_.forEach(functionNames, async (functionName: string, functionId: number) => {
const description = `should not allow fillOrKillOrder to reenter the Exchange contract via ${functionName}`;
it(description, async () => {
const signedOrder = await orderFactory.newSignedOrderAsync({
makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address),
});
await web3Wrapper.awaitTransactionSuccessAsync(
await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await expectTransactionFailedAsync(
exchangeWrapper.fillOrKillOrderAsync(signedOrder, takerAddress),
RevertReason.TransferFailed,
);
});
});
};
describe('fillOrKillOrder reentrancy tests', () => reentrancyTest(constants.FUNCTIONS_WITH_MUTEX));
it('should transfer the correct amounts', async () => { it('should transfer the correct amounts', async () => {
const signedOrder = await orderFactory.newSignedOrderAsync({ const signedOrder = await orderFactory.newSignedOrderAsync({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
@ -197,6 +226,25 @@ describe('Exchange wrappers', () => {
}); });
describe('fillOrderNoThrow', () => { describe('fillOrderNoThrow', () => {
const reentrancyTest = (functionNames: string[]) => {
_.forEach(functionNames, async (functionName: string, functionId: number) => {
const description = `should not allow fillOrderNoThrow to reenter the Exchange contract via ${functionName}`;
it(description, async () => {
const signedOrder = await orderFactory.newSignedOrderAsync({
makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address),
});
await web3Wrapper.awaitTransactionSuccessAsync(
await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await exchangeWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress);
const newBalances = await erc20Wrapper.getBalancesAsync();
expect(erc20Balances).to.deep.equal(newBalances);
});
});
};
describe('fillOrderNoThrow reentrancy tests', () => reentrancyTest(constants.FUNCTIONS_WITH_MUTEX));
it('should transfer the correct amounts', async () => { it('should transfer the correct amounts', async () => {
const signedOrder = await orderFactory.newSignedOrderAsync({ const signedOrder = await orderFactory.newSignedOrderAsync({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
@ -397,6 +445,26 @@ describe('Exchange wrappers', () => {
}); });
describe('batchFillOrders', () => { describe('batchFillOrders', () => {
const reentrancyTest = (functionNames: string[]) => {
_.forEach(functionNames, async (functionName: string, functionId: number) => {
const description = `should not allow batchFillOrders to reenter the Exchange contract via ${functionName}`;
it(description, async () => {
const signedOrder = await orderFactory.newSignedOrderAsync({
makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address),
});
await web3Wrapper.awaitTransactionSuccessAsync(
await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await expectTransactionFailedAsync(
exchangeWrapper.batchFillOrdersAsync([signedOrder], takerAddress),
RevertReason.TransferFailed,
);
});
});
};
describe('batchFillOrders reentrancy tests', () => reentrancyTest(constants.FUNCTIONS_WITH_MUTEX));
it('should transfer the correct amounts', async () => { it('should transfer the correct amounts', async () => {
const takerAssetFillAmounts: BigNumber[] = []; const takerAssetFillAmounts: BigNumber[] = [];
const makerAssetAddress = erc20TokenA.address; const makerAssetAddress = erc20TokenA.address;
@ -446,6 +514,26 @@ describe('Exchange wrappers', () => {
}); });
describe('batchFillOrKillOrders', () => { describe('batchFillOrKillOrders', () => {
const reentrancyTest = (functionNames: string[]) => {
_.forEach(functionNames, async (functionName: string, functionId: number) => {
const description = `should not allow batchFillOrKillOrders to reenter the Exchange contract via ${functionName}`;
it(description, async () => {
const signedOrder = await orderFactory.newSignedOrderAsync({
makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address),
});
await web3Wrapper.awaitTransactionSuccessAsync(
await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await expectTransactionFailedAsync(
exchangeWrapper.batchFillOrKillOrdersAsync([signedOrder], takerAddress),
RevertReason.TransferFailed,
);
});
});
};
describe('batchFillOrKillOrders reentrancy tests', () => reentrancyTest(constants.FUNCTIONS_WITH_MUTEX));
it('should transfer the correct amounts', async () => { it('should transfer the correct amounts', async () => {
const takerAssetFillAmounts: BigNumber[] = []; const takerAssetFillAmounts: BigNumber[] = [];
const makerAssetAddress = erc20TokenA.address; const makerAssetAddress = erc20TokenA.address;
@ -512,6 +600,25 @@ describe('Exchange wrappers', () => {
}); });
describe('batchFillOrdersNoThrow', async () => { describe('batchFillOrdersNoThrow', async () => {
const reentrancyTest = (functionNames: string[]) => {
_.forEach(functionNames, async (functionName: string, functionId: number) => {
const description = `should not allow batchFillOrdersNoThrow to reenter the Exchange contract via ${functionName}`;
it(description, async () => {
const signedOrder = await orderFactory.newSignedOrderAsync({
makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address),
});
await web3Wrapper.awaitTransactionSuccessAsync(
await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await exchangeWrapper.batchFillOrdersNoThrowAsync([signedOrder], takerAddress);
const newBalances = await erc20Wrapper.getBalancesAsync();
expect(erc20Balances).to.deep.equal(newBalances);
});
});
};
describe('batchFillOrdersNoThrow reentrancy tests', () => reentrancyTest(constants.FUNCTIONS_WITH_MUTEX));
it('should transfer the correct amounts', async () => { it('should transfer the correct amounts', async () => {
const takerAssetFillAmounts: BigNumber[] = []; const takerAssetFillAmounts: BigNumber[] = [];
const makerAssetAddress = erc20TokenA.address; const makerAssetAddress = erc20TokenA.address;
@ -625,6 +732,28 @@ describe('Exchange wrappers', () => {
}); });
describe('marketSellOrders', () => { describe('marketSellOrders', () => {
const reentrancyTest = (functionNames: string[]) => {
_.forEach(functionNames, async (functionName: string, functionId: number) => {
const description = `should not allow marketSellOrders to reenter the Exchange contract via ${functionName}`;
it(description, async () => {
const signedOrder = await orderFactory.newSignedOrderAsync({
makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address),
});
await web3Wrapper.awaitTransactionSuccessAsync(
await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await expectTransactionFailedAsync(
exchangeWrapper.marketSellOrdersAsync([signedOrder], takerAddress, {
takerAssetFillAmount: signedOrder.takerAssetAmount,
}),
RevertReason.TransferFailed,
);
});
});
};
describe('marketSellOrders reentrancy tests', () => reentrancyTest(constants.FUNCTIONS_WITH_MUTEX));
it('should stop when the entire takerAssetFillAmount is filled', async () => { it('should stop when the entire takerAssetFillAmount is filled', async () => {
const takerAssetFillAmount = signedOrders[0].takerAssetAmount.plus( const takerAssetFillAmount = signedOrders[0].takerAssetAmount.plus(
signedOrders[1].takerAssetAmount.div(2), signedOrders[1].takerAssetAmount.div(2),
@ -717,6 +846,27 @@ describe('Exchange wrappers', () => {
}); });
describe('marketSellOrdersNoThrow', () => { describe('marketSellOrdersNoThrow', () => {
const reentrancyTest = (functionNames: string[]) => {
_.forEach(functionNames, async (functionName: string, functionId: number) => {
const description = `should not allow marketSellOrdersNoThrow to reenter the Exchange contract via ${functionName}`;
it(description, async () => {
const signedOrder = await orderFactory.newSignedOrderAsync({
makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address),
});
await web3Wrapper.awaitTransactionSuccessAsync(
await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await exchangeWrapper.marketSellOrdersNoThrowAsync([signedOrder], takerAddress, {
takerAssetFillAmount: signedOrder.takerAssetAmount,
});
const newBalances = await erc20Wrapper.getBalancesAsync();
expect(erc20Balances).to.deep.equal(newBalances);
});
});
};
describe('marketSellOrdersNoThrow reentrancy tests', () => reentrancyTest(constants.FUNCTIONS_WITH_MUTEX));
it('should stop when the entire takerAssetFillAmount is filled', async () => { it('should stop when the entire takerAssetFillAmount is filled', async () => {
const takerAssetFillAmount = signedOrders[0].takerAssetAmount.plus( const takerAssetFillAmount = signedOrders[0].takerAssetAmount.plus(
signedOrders[1].takerAssetAmount.div(2), signedOrders[1].takerAssetAmount.div(2),
@ -843,6 +993,28 @@ describe('Exchange wrappers', () => {
}); });
describe('marketBuyOrders', () => { describe('marketBuyOrders', () => {
const reentrancyTest = (functionNames: string[]) => {
_.forEach(functionNames, async (functionName: string, functionId: number) => {
const description = `should not allow marketBuyOrders to reenter the Exchange contract via ${functionName}`;
it(description, async () => {
const signedOrder = await orderFactory.newSignedOrderAsync({
makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address),
});
await web3Wrapper.awaitTransactionSuccessAsync(
await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await expectTransactionFailedAsync(
exchangeWrapper.marketBuyOrdersAsync([signedOrder], takerAddress, {
makerAssetFillAmount: signedOrder.makerAssetAmount,
}),
RevertReason.TransferFailed,
);
});
});
};
describe('marketBuyOrders reentrancy tests', () => reentrancyTest(constants.FUNCTIONS_WITH_MUTEX));
it('should stop when the entire makerAssetFillAmount is filled', async () => { it('should stop when the entire makerAssetFillAmount is filled', async () => {
const makerAssetFillAmount = signedOrders[0].makerAssetAmount.plus( const makerAssetFillAmount = signedOrders[0].makerAssetAmount.plus(
signedOrders[1].makerAssetAmount.div(2), signedOrders[1].makerAssetAmount.div(2),
@ -933,6 +1105,27 @@ describe('Exchange wrappers', () => {
}); });
describe('marketBuyOrdersNoThrow', () => { describe('marketBuyOrdersNoThrow', () => {
const reentrancyTest = (functionNames: string[]) => {
_.forEach(functionNames, async (functionName: string, functionId: number) => {
const description = `should not allow marketBuyOrdersNoThrow to reenter the Exchange contract via ${functionName}`;
it(description, async () => {
const signedOrder = await orderFactory.newSignedOrderAsync({
makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address),
});
await web3Wrapper.awaitTransactionSuccessAsync(
await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await exchangeWrapper.marketBuyOrdersNoThrowAsync([signedOrder], takerAddress, {
makerAssetFillAmount: signedOrder.makerAssetAmount,
});
const newBalances = await erc20Wrapper.getBalancesAsync();
expect(erc20Balances).to.deep.equal(newBalances);
});
});
};
describe('marketBuyOrdersNoThrow reentrancy tests', () => reentrancyTest(constants.FUNCTIONS_WITH_MUTEX));
it('should stop when the entire makerAssetFillAmount is filled', async () => { it('should stop when the entire makerAssetFillAmount is filled', async () => {
const makerAssetFillAmount = signedOrders[0].makerAssetAmount.plus( const makerAssetFillAmount = signedOrders[0].makerAssetAmount.plus(
signedOrders[1].makerAssetAmount.div(2), signedOrders[1].makerAssetAmount.div(2),

View File

@ -16,6 +16,7 @@ import * as MixinAuthorizable from '../../artifacts/MixinAuthorizable.json';
import * as MultiSigWallet from '../../artifacts/MultiSigWallet.json'; import * as MultiSigWallet from '../../artifacts/MultiSigWallet.json';
import * as MultiSigWalletWithTimeLock from '../../artifacts/MultiSigWalletWithTimeLock.json'; import * as MultiSigWalletWithTimeLock from '../../artifacts/MultiSigWalletWithTimeLock.json';
import * as OrderValidator from '../../artifacts/OrderValidator.json'; import * as OrderValidator from '../../artifacts/OrderValidator.json';
import * as ReentrantERC20Token from '../../artifacts/ReentrantERC20Token.json';
import * as TestAssetProxyDispatcher from '../../artifacts/TestAssetProxyDispatcher.json'; import * as TestAssetProxyDispatcher from '../../artifacts/TestAssetProxyDispatcher.json';
import * as TestAssetProxyOwner from '../../artifacts/TestAssetProxyOwner.json'; import * as TestAssetProxyOwner from '../../artifacts/TestAssetProxyOwner.json';
import * as TestConstants from '../../artifacts/TestConstants.json'; import * as TestConstants from '../../artifacts/TestConstants.json';
@ -23,6 +24,7 @@ import * as TestExchangeInternals from '../../artifacts/TestExchangeInternals.js
import * as TestLibBytes from '../../artifacts/TestLibBytes.json'; import * as TestLibBytes from '../../artifacts/TestLibBytes.json';
import * as TestLibs from '../../artifacts/TestLibs.json'; import * as TestLibs from '../../artifacts/TestLibs.json';
import * as TestSignatureValidator from '../../artifacts/TestSignatureValidator.json'; import * as TestSignatureValidator from '../../artifacts/TestSignatureValidator.json';
import * as TestStaticCallReceiver from '../../artifacts/TestStaticCallReceiver.json';
import * as TokenRegistry from '../../artifacts/TokenRegistry.json'; import * as TokenRegistry from '../../artifacts/TokenRegistry.json';
import * as Validator from '../../artifacts/Validator.json'; import * as Validator from '../../artifacts/Validator.json';
import * as Wallet from '../../artifacts/Wallet.json'; import * as Wallet from '../../artifacts/Wallet.json';
@ -48,6 +50,7 @@ export const artifacts = {
MultiSigWallet: (MultiSigWallet as any) as ContractArtifact, MultiSigWallet: (MultiSigWallet as any) as ContractArtifact,
MultiSigWalletWithTimeLock: (MultiSigWalletWithTimeLock as any) as ContractArtifact, MultiSigWalletWithTimeLock: (MultiSigWalletWithTimeLock as any) as ContractArtifact,
OrderValidator: (OrderValidator as any) as ContractArtifact, OrderValidator: (OrderValidator as any) as ContractArtifact,
ReentrantERC20Token: (ReentrantERC20Token as any) as ContractArtifact,
TestAssetProxyOwner: (TestAssetProxyOwner as any) as ContractArtifact, TestAssetProxyOwner: (TestAssetProxyOwner as any) as ContractArtifact,
TestAssetProxyDispatcher: (TestAssetProxyDispatcher as any) as ContractArtifact, TestAssetProxyDispatcher: (TestAssetProxyDispatcher as any) as ContractArtifact,
TestConstants: (TestConstants as any) as ContractArtifact, TestConstants: (TestConstants as any) as ContractArtifact,
@ -55,6 +58,7 @@ export const artifacts = {
TestLibs: (TestLibs as any) as ContractArtifact, TestLibs: (TestLibs as any) as ContractArtifact,
TestExchangeInternals: (TestExchangeInternals as any) as ContractArtifact, TestExchangeInternals: (TestExchangeInternals as any) as ContractArtifact,
TestSignatureValidator: (TestSignatureValidator as any) as ContractArtifact, TestSignatureValidator: (TestSignatureValidator as any) as ContractArtifact,
TestStaticCallReceiver: (TestStaticCallReceiver as any) as ContractArtifact,
Validator: (Validator as any) as ContractArtifact, Validator: (Validator as any) as ContractArtifact,
Wallet: (Wallet as any) as ContractArtifact, Wallet: (Wallet as any) as ContractArtifact,
TokenRegistry: (TokenRegistry as any) as ContractArtifact, TokenRegistry: (TokenRegistry as any) as ContractArtifact,

View File

@ -51,4 +51,16 @@ export const constants = {
WORD_LENGTH: 32, WORD_LENGTH: 32,
ZERO_AMOUNT: new BigNumber(0), ZERO_AMOUNT: new BigNumber(0),
PERCENTAGE_DENOMINATOR: new BigNumber(10).pow(18), PERCENTAGE_DENOMINATOR: new BigNumber(10).pow(18),
FUNCTIONS_WITH_MUTEX: [
'FILL_ORDER',
'FILL_OR_KILL_ORDER',
'BATCH_FILL_ORDERS',
'BATCH_FILL_OR_KILL_ORDERS',
'MARKET_BUY_ORDERS',
'MARKET_SELL_ORDERS',
'MATCH_ORDERS',
'CANCEL_ORDER',
'CANCEL_ORDERS_UP_TO',
'SET_SIGNATURE_VALIDATOR_APPROVAL',
],
}; };

View File

@ -467,17 +467,17 @@ export class FillOrderCombinatorialUtils {
? remainingTakerAmountToFill ? remainingTakerAmountToFill
: alreadyFilledTakerAmount.add(takerAssetFillAmount); : alreadyFilledTakerAmount.add(takerAssetFillAmount);
const expFilledMakerAmount = orderUtils.getPartialAmount( const expFilledMakerAmount = orderUtils.getPartialAmountFloor(
expFilledTakerAmount, expFilledTakerAmount,
signedOrder.takerAssetAmount, signedOrder.takerAssetAmount,
signedOrder.makerAssetAmount, signedOrder.makerAssetAmount,
); );
const expMakerFeePaid = orderUtils.getPartialAmount( const expMakerFeePaid = orderUtils.getPartialAmountFloor(
expFilledTakerAmount, expFilledTakerAmount,
signedOrder.takerAssetAmount, signedOrder.takerAssetAmount,
signedOrder.makerFee, signedOrder.makerFee,
); );
const expTakerFeePaid = orderUtils.getPartialAmount( const expTakerFeePaid = orderUtils.getPartialAmountFloor(
expFilledTakerAmount, expFilledTakerAmount,
signedOrder.takerAssetAmount, signedOrder.takerAssetAmount,
signedOrder.takerFee, signedOrder.takerFee,
@ -668,7 +668,7 @@ export class FillOrderCombinatorialUtils {
signedOrder: SignedOrder, signedOrder: SignedOrder,
takerAssetFillAmount: BigNumber, takerAssetFillAmount: BigNumber,
): Promise<void> { ): Promise<void> {
const makerAssetFillAmount = orderUtils.getPartialAmount( const makerAssetFillAmount = orderUtils.getPartialAmountFloor(
takerAssetFillAmount, takerAssetFillAmount,
signedOrder.takerAssetAmount, signedOrder.takerAssetAmount,
signedOrder.makerAssetAmount, signedOrder.makerAssetAmount,
@ -705,7 +705,7 @@ export class FillOrderCombinatorialUtils {
); );
} }
const makerFee = orderUtils.getPartialAmount( const makerFee = orderUtils.getPartialAmountFloor(
takerAssetFillAmount, takerAssetFillAmount,
signedOrder.takerAssetAmount, signedOrder.takerAssetAmount,
signedOrder.makerFee, signedOrder.makerFee,
@ -829,7 +829,7 @@ export class FillOrderCombinatorialUtils {
); );
} }
const takerFee = orderUtils.getPartialAmount( const takerFee = orderUtils.getPartialAmountFloor(
takerAssetFillAmount, takerAssetFillAmount,
signedOrder.takerAssetAmount, signedOrder.takerAssetAmount,
signedOrder.takerFee, signedOrder.takerFee,

View File

@ -4,11 +4,20 @@ import { BigNumber } from '@0xproject/utils';
import * as chai from 'chai'; import * as chai from 'chai';
import * as _ from 'lodash'; import * as _ from 'lodash';
import { TransactionReceiptWithDecodedLogs } from '../../../../node_modules/ethereum-types';
import { chaiSetup } from './chai_setup'; import { chaiSetup } from './chai_setup';
import { ERC20Wrapper } from './erc20_wrapper'; import { ERC20Wrapper } from './erc20_wrapper';
import { ERC721Wrapper } from './erc721_wrapper'; import { ERC721Wrapper } from './erc721_wrapper';
import { ExchangeWrapper } from './exchange_wrapper'; import { ExchangeWrapper } from './exchange_wrapper';
import { ERC20BalancesByOwner, ERC721TokenIdsByOwner, TransferAmountsByMatchOrders as TransferAmounts } from './types'; import {
ERC20BalancesByOwner,
ERC721TokenIdsByOwner,
OrderInfo,
OrderStatus,
TransferAmountsByMatchOrders as TransferAmounts,
TransferAmountsLoggedByMatchOrders as LoggedTransferAmounts,
} from './types';
chaiSetup.configure(); chaiSetup.configure();
const expect = chai.expect; const expect = chai.expect;
@ -18,43 +27,107 @@ export class MatchOrderTester {
private readonly _erc20Wrapper: ERC20Wrapper; private readonly _erc20Wrapper: ERC20Wrapper;
private readonly _erc721Wrapper: ERC721Wrapper; private readonly _erc721Wrapper: ERC721Wrapper;
private readonly _feeTokenAddress: string; private readonly _feeTokenAddress: string;
/// @dev Checks values from the logs produced by Exchange.matchOrders against the expected transfer amounts.
/// @dev Compares a pair of ERC20 balances and a pair of ERC721 token owners. /// Values include the amounts transferred from the left/right makers and taker, along with
/// @param expectedNewERC20BalancesByOwner Expected ERC20 balances. /// the fees paid on each matched order. These are also the return values of MatchOrders.
/// @param realERC20BalancesByOwner Actual ERC20 balances. /// @param signedOrderLeft First matched order.
/// @param expectedNewERC721TokenIdsByOwner Expected ERC721 token owners. /// @param signedOrderRight Second matched order.
/// @param realERC721TokenIdsByOwner Actual ERC20 token owners. /// @param transactionReceipt Transaction receipt and logs produced by Exchange.matchOrders.
/// @return True only if ERC20 balances match and ERC721 token owners match. /// @param takerAddress Address of taker (account that called Exchange.matchOrders)
private static _compareExpectedAndRealBalances( /// @param expectedTransferAmounts Expected amounts transferred as a result of order matching.
expectedNewERC20BalancesByOwner: ERC20BalancesByOwner, private static async _assertLogsAsync(
realERC20BalancesByOwner: ERC20BalancesByOwner, signedOrderLeft: SignedOrder,
expectedNewERC721TokenIdsByOwner: ERC721TokenIdsByOwner, signedOrderRight: SignedOrder,
realERC721TokenIdsByOwner: ERC721TokenIdsByOwner, transactionReceipt: TransactionReceiptWithDecodedLogs,
): boolean { takerAddress: string,
// ERC20 Balances expectedTransferAmounts: TransferAmounts,
const doesErc20BalancesMatch = _.isEqual(expectedNewERC20BalancesByOwner, realERC20BalancesByOwner); ): Promise<void> {
if (!doesErc20BalancesMatch) { // Should have two fill event logs -- one for each order.
return false; const transactionFillLogs = _.filter(transactionReceipt.logs, ['event', 'Fill']);
} expect(transactionFillLogs.length, 'Checking number of logs').to.be.equal(2);
// ERC721 Token Ids // First log is for left fill
const sortedExpectedNewERC721TokenIdsByOwner = _.mapValues( const leftLog = (transactionFillLogs[0] as any).args as LoggedTransferAmounts;
expectedNewERC721TokenIdsByOwner, expect(leftLog.makerAddress, 'Checking logged maker address of left order').to.be.equal(
tokenIdsByOwner => { signedOrderLeft.makerAddress,
_.mapValues(tokenIdsByOwner, tokenIds => {
_.sortBy(tokenIds);
});
},
); );
expect(leftLog.takerAddress, 'Checking logged taker address of right order').to.be.equal(takerAddress);
const amountBoughtByLeftMaker = new BigNumber(leftLog.takerAssetFilledAmount);
const amountSoldByLeftMaker = new BigNumber(leftLog.makerAssetFilledAmount);
const feePaidByLeftMaker = new BigNumber(leftLog.makerFeePaid);
const feePaidByTakerLeft = new BigNumber(leftLog.takerFeePaid);
// Second log is for right fill
const rightLog = (transactionFillLogs[1] as any).args as LoggedTransferAmounts;
expect(rightLog.makerAddress, 'Checking logged maker address of right order').to.be.equal(
signedOrderRight.makerAddress,
);
expect(rightLog.takerAddress, 'Checking loggerd taker address of right order').to.be.equal(takerAddress);
const amountBoughtByRightMaker = new BigNumber(rightLog.takerAssetFilledAmount);
const amountSoldByRightMaker = new BigNumber(rightLog.makerAssetFilledAmount);
const feePaidByRightMaker = new BigNumber(rightLog.makerFeePaid);
const feePaidByTakerRight = new BigNumber(rightLog.takerFeePaid);
// Derive amount received by taker
const amountReceivedByTaker = amountSoldByLeftMaker.sub(amountBoughtByRightMaker);
// Assert log values - left order
expect(amountBoughtByLeftMaker, 'Checking logged amount bought by left maker').to.be.bignumber.equal(
expectedTransferAmounts.amountBoughtByLeftMaker,
);
expect(amountSoldByLeftMaker, 'Checking logged amount sold by left maker').to.be.bignumber.equal(
expectedTransferAmounts.amountSoldByLeftMaker,
);
expect(feePaidByLeftMaker, 'Checking logged fee paid by left maker').to.be.bignumber.equal(
expectedTransferAmounts.feePaidByLeftMaker,
);
expect(feePaidByTakerLeft, 'Checking logged fee paid on left order by taker').to.be.bignumber.equal(
expectedTransferAmounts.feePaidByTakerLeft,
);
// Assert log values - right order
expect(amountBoughtByRightMaker, 'Checking logged amount bought by right maker').to.be.bignumber.equal(
expectedTransferAmounts.amountBoughtByRightMaker,
);
expect(amountSoldByRightMaker, 'Checking logged amount sold by right maker').to.be.bignumber.equal(
expectedTransferAmounts.amountSoldByRightMaker,
);
expect(feePaidByRightMaker, 'Checking logged fee paid by right maker').to.be.bignumber.equal(
expectedTransferAmounts.feePaidByRightMaker,
);
expect(feePaidByTakerRight, 'Checking logged fee paid on right order by taker').to.be.bignumber.equal(
expectedTransferAmounts.feePaidByTakerRight,
);
// Assert derived amount received by taker
expect(amountReceivedByTaker, 'Checking logged amount received by taker').to.be.bignumber.equal(
expectedTransferAmounts.amountReceivedByTaker,
);
}
/// @dev Asserts all expected ERC20 and ERC721 account holdings match the real holdings.
/// @param expectedERC20BalancesByOwner Expected ERC20 balances.
/// @param realERC20BalancesByOwner Real ERC20 balances.
/// @param expectedERC721TokenIdsByOwner Expected ERC721 token owners.
/// @param realERC721TokenIdsByOwner Real ERC20 token owners.
private static async _assertAllKnownBalancesAsync(
expectedERC20BalancesByOwner: ERC20BalancesByOwner,
realERC20BalancesByOwner: ERC20BalancesByOwner,
expectedERC721TokenIdsByOwner: ERC721TokenIdsByOwner,
realERC721TokenIdsByOwner: ERC721TokenIdsByOwner,
): Promise<void> {
// ERC20 Balances
const areERC20BalancesEqual = _.isEqual(expectedERC20BalancesByOwner, realERC20BalancesByOwner);
expect(areERC20BalancesEqual, 'Checking all known ERC20 account balances').to.be.true();
// ERC721 Token Ids
const sortedExpectedNewERC721TokenIdsByOwner = _.mapValues(expectedERC721TokenIdsByOwner, tokenIdsByOwner => {
_.mapValues(tokenIdsByOwner, tokenIds => {
_.sortBy(tokenIds);
});
});
const sortedNewERC721TokenIdsByOwner = _.mapValues(realERC721TokenIdsByOwner, tokenIdsByOwner => { const sortedNewERC721TokenIdsByOwner = _.mapValues(realERC721TokenIdsByOwner, tokenIdsByOwner => {
_.mapValues(tokenIdsByOwner, tokenIds => { _.mapValues(tokenIdsByOwner, tokenIds => {
_.sortBy(tokenIds); _.sortBy(tokenIds);
}); });
}); });
const doesErc721TokenIdsMatch = _.isEqual( const areERC721TokenIdsEqual = _.isEqual(
sortedExpectedNewERC721TokenIdsByOwner, sortedExpectedNewERC721TokenIdsByOwner,
sortedNewERC721TokenIdsByOwner, sortedNewERC721TokenIdsByOwner,
); );
return doesErc721TokenIdsMatch; expect(areERC721TokenIdsEqual, 'Checking all known ERC721 account balances').to.be.true();
} }
/// @dev Constructs new MatchOrderTester. /// @dev Constructs new MatchOrderTester.
/// @param exchangeWrapper Used to call to the Exchange. /// @param exchangeWrapper Used to call to the Exchange.
@ -72,150 +145,199 @@ export class MatchOrderTester {
this._erc721Wrapper = erc721Wrapper; this._erc721Wrapper = erc721Wrapper;
this._feeTokenAddress = feeTokenAddress; this._feeTokenAddress = feeTokenAddress;
} }
/// @dev Matches two complementary orders and validates results. /// @dev Matches two complementary orders and asserts results.
/// Validation either succeeds or throws.
/// @param signedOrderLeft First matched order. /// @param signedOrderLeft First matched order.
/// @param signedOrderRight Second matched order. /// @param signedOrderRight Second matched order.
/// @param takerAddress Address of taker (the address who matched the two orders) /// @param takerAddress Address of taker (the address who matched the two orders)
/// @param erc20BalancesByOwner Current ERC20 balances. /// @param erc20BalancesByOwner Current ERC20 balances.
/// @param erc721TokenIdsByOwner Current ERC721 token owners. /// @param erc721TokenIdsByOwner Current ERC721 token owners.
/// @param initialTakerAssetFilledAmountLeft Current amount the left order has been filled. /// @param expectedTransferAmounts Expected amounts transferred as a result of order matching.
/// @param initialTakerAssetFilledAmountRight Current amount the right order has been filled. /// @param initialLeftOrderFilledAmount How much left order has been filled, prior to matching orders.
/// @param initialRightOrderFilledAmount How much the right order has been filled, prior to matching orders.
/// @return New ERC20 balances & ERC721 token owners. /// @return New ERC20 balances & ERC721 token owners.
public async matchOrdersAndVerifyBalancesAsync( public async matchOrdersAndAssertEffectsAsync(
signedOrderLeft: SignedOrder, signedOrderLeft: SignedOrder,
signedOrderRight: SignedOrder, signedOrderRight: SignedOrder,
takerAddress: string, takerAddress: string,
erc20BalancesByOwner: ERC20BalancesByOwner, erc20BalancesByOwner: ERC20BalancesByOwner,
erc721TokenIdsByOwner: ERC721TokenIdsByOwner, erc721TokenIdsByOwner: ERC721TokenIdsByOwner,
initialTakerAssetFilledAmountLeft?: BigNumber, expectedTransferAmounts: TransferAmounts,
initialTakerAssetFilledAmountRight?: BigNumber, initialLeftOrderFilledAmount: BigNumber = new BigNumber(0),
initialRightOrderFilledAmount: BigNumber = new BigNumber(0),
): Promise<[ERC20BalancesByOwner, ERC721TokenIdsByOwner]> { ): Promise<[ERC20BalancesByOwner, ERC721TokenIdsByOwner]> {
// Verify Left order preconditions // Assert initial order states
await this._assertInitialOrderStatesAsync(
signedOrderLeft,
signedOrderRight,
initialLeftOrderFilledAmount,
initialRightOrderFilledAmount,
);
// Match left & right orders
const transactionReceipt = await this._exchangeWrapper.matchOrdersAsync(
signedOrderLeft,
signedOrderRight,
takerAddress,
);
const newERC20BalancesByOwner = await this._erc20Wrapper.getBalancesAsync();
const newERC721TokenIdsByOwner = await this._erc721Wrapper.getBalancesAsync();
// Assert logs
await MatchOrderTester._assertLogsAsync(
signedOrderLeft,
signedOrderRight,
transactionReceipt,
takerAddress,
expectedTransferAmounts,
);
// Assert exchange state
await this._assertExchangeStateAsync(
signedOrderLeft,
signedOrderRight,
initialLeftOrderFilledAmount,
initialRightOrderFilledAmount,
expectedTransferAmounts,
);
// Assert balances of makers, taker, and fee recipients
await this._assertBalancesAsync(
signedOrderLeft,
signedOrderRight,
erc20BalancesByOwner,
erc721TokenIdsByOwner,
newERC20BalancesByOwner,
newERC721TokenIdsByOwner,
expectedTransferAmounts,
takerAddress,
);
return [newERC20BalancesByOwner, newERC721TokenIdsByOwner];
}
/// @dev Asserts initial exchange state for the left and right orders.
/// @param signedOrderLeft First matched order.
/// @param signedOrderRight Second matched order.
/// @param expectedOrderFilledAmountLeft How much left order has been filled, prior to matching orders.
/// @param expectedOrderFilledAmountRight How much the right order has been filled, prior to matching orders.
private async _assertInitialOrderStatesAsync(
signedOrderLeft: SignedOrder,
signedOrderRight: SignedOrder,
expectedOrderFilledAmountLeft: BigNumber,
expectedOrderFilledAmountRight: BigNumber,
): Promise<void> {
// Assert left order initial state
const orderTakerAssetFilledAmountLeft = await this._exchangeWrapper.getTakerAssetFilledAmountAsync( const orderTakerAssetFilledAmountLeft = await this._exchangeWrapper.getTakerAssetFilledAmountAsync(
orderHashUtils.getOrderHashHex(signedOrderLeft), orderHashUtils.getOrderHashHex(signedOrderLeft),
); );
const expectedOrderFilledAmountLeft = initialTakerAssetFilledAmountLeft expect(orderTakerAssetFilledAmountLeft, 'Checking inital state of left order').to.be.bignumber.equal(
? initialTakerAssetFilledAmountLeft expectedOrderFilledAmountLeft,
: new BigNumber(0); );
expect(expectedOrderFilledAmountLeft).to.be.bignumber.equal(orderTakerAssetFilledAmountLeft); // Assert right order initial state
// Verify Right order preconditions
const orderTakerAssetFilledAmountRight = await this._exchangeWrapper.getTakerAssetFilledAmountAsync( const orderTakerAssetFilledAmountRight = await this._exchangeWrapper.getTakerAssetFilledAmountAsync(
orderHashUtils.getOrderHashHex(signedOrderRight), orderHashUtils.getOrderHashHex(signedOrderRight),
); );
const expectedOrderFilledAmountRight = initialTakerAssetFilledAmountRight expect(orderTakerAssetFilledAmountRight, 'Checking inital state of right order').to.be.bignumber.equal(
? initialTakerAssetFilledAmountRight expectedOrderFilledAmountRight,
: new BigNumber(0);
expect(expectedOrderFilledAmountRight).to.be.bignumber.equal(orderTakerAssetFilledAmountRight);
// Match left & right orders
await this._exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress);
const newERC20BalancesByOwner = await this._erc20Wrapper.getBalancesAsync();
const newERC721TokenIdsByOwner = await this._erc721Wrapper.getBalancesAsync();
// Calculate expected balance changes
const expectedTransferAmounts = await this._calculateExpectedTransferAmountsAsync(
signedOrderLeft,
signedOrderRight,
orderTakerAssetFilledAmountLeft,
orderTakerAssetFilledAmountRight,
); );
}
/// @dev Asserts the exchange state against the expected amounts transferred by from matching orders.
/// @param signedOrderLeft First matched order.
/// @param signedOrderRight Second matched order.
/// @param initialLeftOrderFilledAmount How much left order has been filled, prior to matching orders.
/// @param initialRightOrderFilledAmount How much the right order has been filled, prior to matching orders.
/// @return TransferAmounts A struct containing the expected transfer amounts.
private async _assertExchangeStateAsync(
signedOrderLeft: SignedOrder,
signedOrderRight: SignedOrder,
initialLeftOrderFilledAmount: BigNumber,
initialRightOrderFilledAmount: BigNumber,
expectedTransferAmounts: TransferAmounts,
): Promise<void> {
// Assert state for left order: amount bought by left maker
let amountBoughtByLeftMaker = await this._exchangeWrapper.getTakerAssetFilledAmountAsync(
orderHashUtils.getOrderHashHex(signedOrderLeft),
);
amountBoughtByLeftMaker = amountBoughtByLeftMaker.minus(initialLeftOrderFilledAmount);
expect(amountBoughtByLeftMaker, 'Checking exchange state for left order').to.be.bignumber.equal(
expectedTransferAmounts.amountBoughtByLeftMaker,
);
// Assert state for right order: amount bought by right maker
let amountBoughtByRightMaker = await this._exchangeWrapper.getTakerAssetFilledAmountAsync(
orderHashUtils.getOrderHashHex(signedOrderRight),
);
amountBoughtByRightMaker = amountBoughtByRightMaker.minus(initialRightOrderFilledAmount);
expect(amountBoughtByRightMaker, 'Checking exchange state for right order').to.be.bignumber.equal(
expectedTransferAmounts.amountBoughtByRightMaker,
);
// Assert left order status
const maxAmountBoughtByLeftMaker = signedOrderLeft.takerAssetAmount.minus(initialLeftOrderFilledAmount);
const leftOrderInfo: OrderInfo = await this._exchangeWrapper.getOrderInfoAsync(signedOrderLeft);
const leftExpectedStatus = expectedTransferAmounts.amountBoughtByLeftMaker.equals(maxAmountBoughtByLeftMaker)
? OrderStatus.FULLY_FILLED
: OrderStatus.FILLABLE;
expect(leftOrderInfo.orderStatus as OrderStatus, 'Checking exchange status for left order').to.be.equal(
leftExpectedStatus,
);
// Assert right order status
const maxAmountBoughtByRightMaker = signedOrderRight.takerAssetAmount.minus(initialRightOrderFilledAmount);
const rightOrderInfo: OrderInfo = await this._exchangeWrapper.getOrderInfoAsync(signedOrderRight);
const rightExpectedStatus = expectedTransferAmounts.amountBoughtByRightMaker.equals(maxAmountBoughtByRightMaker)
? OrderStatus.FULLY_FILLED
: OrderStatus.FILLABLE;
expect(rightOrderInfo.orderStatus as OrderStatus, 'Checking exchange status for right order').to.be.equal(
rightExpectedStatus,
);
}
/// @dev Asserts account balances after matching orders.
/// @param signedOrderLeft First matched order.
/// @param signedOrderRight Second matched order.
/// @param initialERC20BalancesByOwner ERC20 balances prior to order matching.
/// @param initialERC721TokenIdsByOwner ERC721 token owners prior to order matching.
/// @param finalERC20BalancesByOwner ERC20 balances after order matching.
/// @param finalERC721TokenIdsByOwner ERC721 token owners after order matching.
/// @param expectedTransferAmounts Expected amounts transferred as a result of order matching.
/// @param takerAddress Address of taker (account that called Exchange.matchOrders).
private async _assertBalancesAsync(
signedOrderLeft: SignedOrder,
signedOrderRight: SignedOrder,
initialERC20BalancesByOwner: ERC20BalancesByOwner,
initialERC721TokenIdsByOwner: ERC721TokenIdsByOwner,
finalERC20BalancesByOwner: ERC20BalancesByOwner,
finalERC721TokenIdsByOwner: ERC721TokenIdsByOwner,
expectedTransferAmounts: TransferAmounts,
takerAddress: string,
): Promise<void> {
let expectedERC20BalancesByOwner: ERC20BalancesByOwner; let expectedERC20BalancesByOwner: ERC20BalancesByOwner;
let expectedERC721TokenIdsByOwner: ERC721TokenIdsByOwner; let expectedERC721TokenIdsByOwner: ERC721TokenIdsByOwner;
[expectedERC20BalancesByOwner, expectedERC721TokenIdsByOwner] = this._calculateExpectedBalances( [expectedERC20BalancesByOwner, expectedERC721TokenIdsByOwner] = this._calculateExpectedBalances(
signedOrderLeft, signedOrderLeft,
signedOrderRight, signedOrderRight,
takerAddress, takerAddress,
erc20BalancesByOwner, initialERC20BalancesByOwner,
erc721TokenIdsByOwner, initialERC721TokenIdsByOwner,
expectedTransferAmounts, expectedTransferAmounts,
); );
// Assert our expected balances are equal to the actual balances // Assert balances of makers, taker, and fee recipients
const didExpectedBalancesMatchRealBalances = MatchOrderTester._compareExpectedAndRealBalances( await this._assertMakerTakerAndFeeRecipientBalancesAsync(
signedOrderLeft,
signedOrderRight,
expectedERC20BalancesByOwner, expectedERC20BalancesByOwner,
newERC20BalancesByOwner, finalERC20BalancesByOwner,
expectedERC721TokenIdsByOwner, expectedERC721TokenIdsByOwner,
newERC721TokenIdsByOwner, finalERC721TokenIdsByOwner,
takerAddress,
); );
expect(didExpectedBalancesMatchRealBalances).to.be.true(); // Assert balances for all known accounts
return [newERC20BalancesByOwner, newERC721TokenIdsByOwner]; await MatchOrderTester._assertAllKnownBalancesAsync(
} expectedERC20BalancesByOwner,
/// @dev Calculates expected transfer amounts between order makers, fee recipients, and finalERC20BalancesByOwner,
/// the taker when two orders are matched. expectedERC721TokenIdsByOwner,
/// @param signedOrderLeft First matched order. finalERC721TokenIdsByOwner,
/// @param signedOrderRight Second matched order.
/// @param orderTakerAssetFilledAmountLeft How much left order has been filled, prior to matching orders.
/// @param orderTakerAssetFilledAmountRight How much the right order has been filled, prior to matching orders.
/// @return TransferAmounts A struct containing the expected transfer amounts.
private async _calculateExpectedTransferAmountsAsync(
signedOrderLeft: SignedOrder,
signedOrderRight: SignedOrder,
orderTakerAssetFilledAmountLeft: BigNumber,
orderTakerAssetFilledAmountRight: BigNumber,
): Promise<TransferAmounts> {
let amountBoughtByLeftMaker = await this._exchangeWrapper.getTakerAssetFilledAmountAsync(
orderHashUtils.getOrderHashHex(signedOrderLeft),
); );
amountBoughtByLeftMaker = amountBoughtByLeftMaker.minus(orderTakerAssetFilledAmountLeft);
const amountSoldByLeftMaker = amountBoughtByLeftMaker
.times(signedOrderLeft.makerAssetAmount)
.dividedToIntegerBy(signedOrderLeft.takerAssetAmount);
const amountReceivedByRightMaker = amountBoughtByLeftMaker
.times(signedOrderRight.takerAssetAmount)
.dividedToIntegerBy(signedOrderRight.makerAssetAmount);
const amountReceivedByTaker = amountSoldByLeftMaker.minus(amountReceivedByRightMaker);
let amountBoughtByRightMaker = await this._exchangeWrapper.getTakerAssetFilledAmountAsync(
orderHashUtils.getOrderHashHex(signedOrderRight),
);
amountBoughtByRightMaker = amountBoughtByRightMaker.minus(orderTakerAssetFilledAmountRight);
const amountSoldByRightMaker = amountBoughtByRightMaker
.times(signedOrderRight.makerAssetAmount)
.dividedToIntegerBy(signedOrderRight.takerAssetAmount);
const amountReceivedByLeftMaker = amountSoldByRightMaker;
const feePaidByLeftMaker = signedOrderLeft.makerFee
.times(amountSoldByLeftMaker)
.dividedToIntegerBy(signedOrderLeft.makerAssetAmount);
const feePaidByRightMaker = signedOrderRight.makerFee
.times(amountSoldByRightMaker)
.dividedToIntegerBy(signedOrderRight.makerAssetAmount);
const feePaidByTakerLeft = signedOrderLeft.takerFee
.times(amountSoldByLeftMaker)
.dividedToIntegerBy(signedOrderLeft.makerAssetAmount);
const feePaidByTakerRight = signedOrderRight.takerFee
.times(amountSoldByRightMaker)
.dividedToIntegerBy(signedOrderRight.makerAssetAmount);
const totalFeePaidByTaker = feePaidByTakerLeft.add(feePaidByTakerRight);
const feeReceivedLeft = feePaidByLeftMaker.add(feePaidByTakerLeft);
const feeReceivedRight = feePaidByRightMaker.add(feePaidByTakerRight);
// Return values
const expectedTransferAmounts = {
// Left Maker
amountBoughtByLeftMaker,
amountSoldByLeftMaker,
amountReceivedByLeftMaker,
feePaidByLeftMaker,
// Right Maker
amountBoughtByRightMaker,
amountSoldByRightMaker,
amountReceivedByRightMaker,
feePaidByRightMaker,
// Taker
amountReceivedByTaker,
feePaidByTakerLeft,
feePaidByTakerRight,
totalFeePaidByTaker,
// Fee Recipients
feeReceivedLeft,
feeReceivedRight,
};
return expectedTransferAmounts;
} }
/// @dev Calculates the expected balances of order makers, fee recipients, and the taker, /// @dev Calculates the expected balances of order makers, fee recipients, and the taker,
/// as a result of matching two orders. /// as a result of matching two orders.
/// @param signedOrderLeft First matched order. /// @param signedOrderRight First matched order.
/// @param signedOrderRight Second matched order. /// @param signedOrderRight Second matched order.
/// @param takerAddress Address of taker (the address who matched the two orders) /// @param takerAddress Address of taker (the address who matched the two orders)
/// @param erc20BalancesByOwner Current ERC20 balances. /// @param erc20BalancesByOwner Current ERC20 balances.
/// @param erc721TokenIdsByOwner Current ERC721 token owners. /// @param erc721TokenIdsByOwner Current ERC721 token owners.
/// @param expectedTransferAmounts A struct containing the expected transfer amounts. /// @param expectedTransferAmounts Expected amounts transferred as a result of order matching.
/// @return Expected ERC20 balances & ERC721 token owners after orders have been matched. /// @return Expected ERC20 balances & ERC721 token owners after orders have been matched.
private _calculateExpectedBalances( private _calculateExpectedBalances(
signedOrderLeft: SignedOrder, signedOrderLeft: SignedOrder,
@ -247,7 +369,7 @@ export class MatchOrderTester {
expectedNewERC20BalancesByOwner[makerAddressRight][ expectedNewERC20BalancesByOwner[makerAddressRight][
takerAssetAddressRight takerAssetAddressRight
] = expectedNewERC20BalancesByOwner[makerAddressRight][takerAssetAddressRight].add( ] = expectedNewERC20BalancesByOwner[makerAddressRight][takerAssetAddressRight].add(
expectedTransferAmounts.amountReceivedByRightMaker, expectedTransferAmounts.amountBoughtByRightMaker,
); );
// Taker // Taker
expectedNewERC20BalancesByOwner[takerAddress][makerAssetAddressLeft] = expectedNewERC20BalancesByOwner[ expectedNewERC20BalancesByOwner[takerAddress][makerAssetAddressLeft] = expectedNewERC20BalancesByOwner[
@ -277,7 +399,7 @@ export class MatchOrderTester {
// Left Maker // Left Maker
expectedNewERC20BalancesByOwner[makerAddressLeft][takerAssetAddressLeft] = expectedNewERC20BalancesByOwner[ expectedNewERC20BalancesByOwner[makerAddressLeft][takerAssetAddressLeft] = expectedNewERC20BalancesByOwner[
makerAddressLeft makerAddressLeft
][takerAssetAddressLeft].add(expectedTransferAmounts.amountReceivedByLeftMaker); ][takerAssetAddressLeft].add(expectedTransferAmounts.amountBoughtByLeftMaker);
// Right Maker // Right Maker
expectedNewERC20BalancesByOwner[makerAddressRight][ expectedNewERC20BalancesByOwner[makerAddressRight][
makerAssetAddressRight makerAssetAddressRight
@ -307,20 +429,138 @@ export class MatchOrderTester {
// Taker Fees // Taker Fees
expectedNewERC20BalancesByOwner[takerAddress][this._feeTokenAddress] = expectedNewERC20BalancesByOwner[ expectedNewERC20BalancesByOwner[takerAddress][this._feeTokenAddress] = expectedNewERC20BalancesByOwner[
takerAddress takerAddress
][this._feeTokenAddress].minus(expectedTransferAmounts.totalFeePaidByTaker); ][this._feeTokenAddress].minus(
expectedTransferAmounts.feePaidByTakerLeft.add(expectedTransferAmounts.feePaidByTakerRight),
);
// Left Fee Recipient Fees // Left Fee Recipient Fees
expectedNewERC20BalancesByOwner[feeRecipientAddressLeft][ expectedNewERC20BalancesByOwner[feeRecipientAddressLeft][
this._feeTokenAddress this._feeTokenAddress
] = expectedNewERC20BalancesByOwner[feeRecipientAddressLeft][this._feeTokenAddress].add( ] = expectedNewERC20BalancesByOwner[feeRecipientAddressLeft][this._feeTokenAddress].add(
expectedTransferAmounts.feeReceivedLeft, expectedTransferAmounts.feePaidByLeftMaker.add(expectedTransferAmounts.feePaidByTakerLeft),
); );
// Right Fee Recipient Fees // Right Fee Recipient Fees
expectedNewERC20BalancesByOwner[feeRecipientAddressRight][ expectedNewERC20BalancesByOwner[feeRecipientAddressRight][
this._feeTokenAddress this._feeTokenAddress
] = expectedNewERC20BalancesByOwner[feeRecipientAddressRight][this._feeTokenAddress].add( ] = expectedNewERC20BalancesByOwner[feeRecipientAddressRight][this._feeTokenAddress].add(
expectedTransferAmounts.feeReceivedRight, expectedTransferAmounts.feePaidByRightMaker.add(expectedTransferAmounts.feePaidByTakerRight),
); );
return [expectedNewERC20BalancesByOwner, expectedNewERC721TokenIdsByOwner]; return [expectedNewERC20BalancesByOwner, expectedNewERC721TokenIdsByOwner];
} }
} /// @dev Asserts ERC20 account balances and ERC721 token holdings that result from order matching.
/// Specifically checks balances of makers, taker and fee recipients.
/// @param signedOrderLeft First matched order.
/// @param signedOrderRight Second matched order.
/// @param expectedERC20BalancesByOwner Expected ERC20 balances.
/// @param realERC20BalancesByOwner Real ERC20 balances.
/// @param expectedERC721TokenIdsByOwner Expected ERC721 token owners.
/// @param realERC721TokenIdsByOwner Real ERC20 token owners.
/// @param takerAddress Address of taker (account that called Exchange.matchOrders).
private async _assertMakerTakerAndFeeRecipientBalancesAsync(
signedOrderLeft: SignedOrder,
signedOrderRight: SignedOrder,
expectedERC20BalancesByOwner: ERC20BalancesByOwner,
realERC20BalancesByOwner: ERC20BalancesByOwner,
expectedERC721TokenIdsByOwner: ERC721TokenIdsByOwner,
realERC721TokenIdsByOwner: ERC721TokenIdsByOwner,
takerAddress: string,
): Promise<void> {
// Individual balance comparisons
const makerAssetProxyIdLeft = assetDataUtils.decodeAssetProxyId(signedOrderLeft.makerAssetData);
const makerERC20AssetDataLeft =
makerAssetProxyIdLeft === AssetProxyId.ERC20
? assetDataUtils.decodeERC20AssetData(signedOrderLeft.makerAssetData)
: assetDataUtils.decodeERC721AssetData(signedOrderLeft.makerAssetData);
const makerAssetAddressLeft = makerERC20AssetDataLeft.tokenAddress;
const makerAssetProxyIdRight = assetDataUtils.decodeAssetProxyId(signedOrderRight.makerAssetData);
const makerERC20AssetDataRight =
makerAssetProxyIdRight === AssetProxyId.ERC20
? assetDataUtils.decodeERC20AssetData(signedOrderRight.makerAssetData)
: assetDataUtils.decodeERC721AssetData(signedOrderRight.makerAssetData);
const makerAssetAddressRight = makerERC20AssetDataRight.tokenAddress;
if (makerAssetProxyIdLeft === AssetProxyId.ERC20) {
expect(
realERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft],
'Checking left maker egress ERC20 account balance',
).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft]);
expect(
realERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft],
'Checking right maker ingress ERC20 account balance',
).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft]);
expect(
realERC20BalancesByOwner[takerAddress][makerAssetAddressLeft],
'Checking taker ingress ERC20 account balance',
).to.be.bignumber.equal(expectedERC20BalancesByOwner[takerAddress][makerAssetAddressLeft]);
} else if (makerAssetProxyIdLeft === AssetProxyId.ERC721) {
expect(
realERC721TokenIdsByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft].sort(),
'Checking left maker egress ERC721 account holdings',
).to.be.deep.equal(
expectedERC721TokenIdsByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft].sort(),
);
expect(
realERC721TokenIdsByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft].sort(),
'Checking right maker ERC721 account holdings',
).to.be.deep.equal(
expectedERC721TokenIdsByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft].sort(),
);
expect(
realERC721TokenIdsByOwner[takerAddress][makerAssetAddressLeft].sort(),
'Checking taker ingress ERC721 account holdings',
).to.be.deep.equal(expectedERC721TokenIdsByOwner[takerAddress][makerAssetAddressLeft].sort());
} else {
throw new Error(`Unhandled Asset Proxy ID: ${makerAssetProxyIdLeft}`);
}
if (makerAssetProxyIdRight === AssetProxyId.ERC20) {
expect(
realERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight],
'Checking left maker ingress ERC20 account balance',
).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight]);
expect(
realERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressRight],
'Checking right maker egress ERC20 account balance',
).to.be.bignumber.equal(
expectedERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressRight],
);
} else if (makerAssetProxyIdRight === AssetProxyId.ERC721) {
expect(
realERC721TokenIdsByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight].sort(),
'Checking left maker ingress ERC721 account holdings',
).to.be.deep.equal(
expectedERC721TokenIdsByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight].sort(),
);
expect(
realERC721TokenIdsByOwner[signedOrderRight.makerAddress][makerAssetAddressRight],
'Checking right maker agress ERC721 account holdings',
).to.be.deep.equal(expectedERC721TokenIdsByOwner[signedOrderRight.makerAddress][makerAssetAddressRight]);
} else {
throw new Error(`Unhandled Asset Proxy ID: ${makerAssetProxyIdRight}`);
}
// Paid fees
expect(
realERC20BalancesByOwner[signedOrderLeft.makerAddress][this._feeTokenAddress],
'Checking left maker egress ERC20 account fees',
).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderLeft.makerAddress][this._feeTokenAddress]);
expect(
realERC20BalancesByOwner[signedOrderRight.makerAddress][this._feeTokenAddress],
'Checking right maker egress ERC20 account fees',
).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderRight.makerAddress][this._feeTokenAddress]);
expect(
realERC20BalancesByOwner[takerAddress][this._feeTokenAddress],
'Checking taker egress ERC20 account fees',
).to.be.bignumber.equal(expectedERC20BalancesByOwner[takerAddress][this._feeTokenAddress]);
// Received fees
expect(
realERC20BalancesByOwner[signedOrderLeft.feeRecipientAddress][this._feeTokenAddress],
'Checking left fee recipient ingress ERC20 account fees',
).to.be.bignumber.equal(
expectedERC20BalancesByOwner[signedOrderLeft.feeRecipientAddress][this._feeTokenAddress],
);
expect(
realERC20BalancesByOwner[signedOrderRight.feeRecipientAddress][this._feeTokenAddress],
'Checking right fee receipient ingress ERC20 account fees',
).to.be.bignumber.equal(
expectedERC20BalancesByOwner[signedOrderRight.feeRecipientAddress][this._feeTokenAddress],
);
}
} // tslint:disable-line:max-file-line-count

View File

@ -5,7 +5,7 @@ import { constants } from './constants';
import { CancelOrder, MatchOrder } from './types'; import { CancelOrder, MatchOrder } from './types';
export const orderUtils = { export const orderUtils = {
getPartialAmount(numerator: BigNumber, denominator: BigNumber, target: BigNumber): BigNumber { getPartialAmountFloor(numerator: BigNumber, denominator: BigNumber, target: BigNumber): BigNumber {
const partialAmount = numerator const partialAmount = numerator
.mul(target) .mul(target)
.div(denominator) .div(denominator)

View File

@ -117,21 +117,24 @@ export interface TransferAmountsByMatchOrders {
// Left Maker // Left Maker
amountBoughtByLeftMaker: BigNumber; amountBoughtByLeftMaker: BigNumber;
amountSoldByLeftMaker: BigNumber; amountSoldByLeftMaker: BigNumber;
amountReceivedByLeftMaker: BigNumber;
feePaidByLeftMaker: BigNumber; feePaidByLeftMaker: BigNumber;
// Right Maker // Right Maker
amountBoughtByRightMaker: BigNumber; amountBoughtByRightMaker: BigNumber;
amountSoldByRightMaker: BigNumber; amountSoldByRightMaker: BigNumber;
amountReceivedByRightMaker: BigNumber;
feePaidByRightMaker: BigNumber; feePaidByRightMaker: BigNumber;
// Taker // Taker
amountReceivedByTaker: BigNumber; amountReceivedByTaker: BigNumber;
feePaidByTakerLeft: BigNumber; feePaidByTakerLeft: BigNumber;
feePaidByTakerRight: BigNumber; feePaidByTakerRight: BigNumber;
totalFeePaidByTaker: BigNumber; }
// Fee Recipients
feeReceivedLeft: BigNumber; export interface TransferAmountsLoggedByMatchOrders {
feeReceivedRight: BigNumber; makerAddress: string;
takerAddress: string;
makerAssetFilledAmount: string;
takerAssetFilledAmount: string;
makerFeePaid: string;
takerFeePaid: string;
} }
export interface OrderInfo { export interface OrderInfo {

View File

@ -1,4 +1,22 @@
[ [
{
"timestamp": 1535377027,
"version": "1.0.6",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1535133899,
"version": "1.0.5",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{ {
"timestamp": 1534210131, "timestamp": 1534210131,
"version": "1.0.4", "version": "1.0.4",

View File

@ -5,7 +5,15 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG CHANGELOG
## v1.0.4 - _August 13, 2018_ ## v1.0.6 - _August 27, 2018_
* Dependencies updated
## v1.0.5 - _August 24, 2018_
* Dependencies updated
## v1.0.4 - _August 14, 2018_
* Dependencies updated * Dependencies updated
@ -21,7 +29,7 @@ CHANGELOG
* Dependencies updated * Dependencies updated
## v1.0.0 - _July 19, 2018_ ## v1.0.0 - _July 20, 2018_
* Dependencies updated * Dependencies updated
@ -47,7 +55,7 @@ CHANGELOG
* Pass SolCompilerArtifactAdapter to CoverageSubprovider (#589) * Pass SolCompilerArtifactAdapter to CoverageSubprovider (#589)
* Move callbackErrorReporter over from 0x.js (#579) * Move callbackErrorReporter over from 0x.js (#579)
## v0.4.1 - _May 4, 2018_ ## v0.4.1 - _May 5, 2018_
* Dependencies updated * Dependencies updated

View File

@ -1,6 +1,6 @@
{ {
"name": "@0xproject/dev-utils", "name": "@0xproject/dev-utils",
"version": "1.0.4", "version": "1.0.6",
"engines": { "engines": {
"node": ">=6.12" "node": ">=6.12"
}, },
@ -29,7 +29,7 @@
}, },
"homepage": "https://github.com/0xProject/0x-monorepo/packages/dev-utils/README.md", "homepage": "https://github.com/0xProject/0x-monorepo/packages/dev-utils/README.md",
"devDependencies": { "devDependencies": {
"@0xproject/tslint-config": "^1.0.5", "@0xproject/tslint-config": "^1.0.6",
"@types/lodash": "4.14.104", "@types/lodash": "4.14.104",
"@types/mocha": "^2.2.42", "@types/mocha": "^2.2.42",
"chai": "^4.0.1", "chai": "^4.0.1",
@ -43,12 +43,12 @@
"typescript": "3.0.1" "typescript": "3.0.1"
}, },
"dependencies": { "dependencies": {
"@0xproject/subproviders": "^1.0.5", "@0xproject/subproviders": "^2.0.1",
"@0xproject/types": "^1.0.1-rc.4", "@0xproject/types": "^1.0.1-rc.6",
"@0xproject/typescript-typings": "^1.0.4", "@0xproject/typescript-typings": "^1.0.5",
"@0xproject/utils": "^1.0.5", "@0xproject/utils": "^1.0.7",
"@0xproject/web3-wrapper": "^1.2.0", "@0xproject/web3-wrapper": "^2.0.1",
"ethereum-types": "^1.0.4", "ethereum-types": "^1.0.5",
"lodash": "^4.17.5" "lodash": "^4.17.5"
}, },
"publishConfig": { "publishConfig": {

View File

@ -1,4 +1,13 @@
[ [
{
"timestamp": 1535133899,
"version": "1.0.5",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{ {
"timestamp": 1534210131, "timestamp": 1534210131,
"version": "1.0.4", "version": "1.0.4",

View File

@ -5,7 +5,11 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG CHANGELOG
## v1.0.4 - _August 13, 2018_ ## v1.0.5 - _August 24, 2018_
* Dependencies updated
## v1.0.4 - _August 14, 2018_
* Dependencies updated * Dependencies updated
@ -21,11 +25,12 @@ CHANGELOG
* Dependencies updated * Dependencies updated
## v1.0.0 - _July 19, 2018_ ## v1.0.0 - _July 20, 2018_
* Add `TraceParams` interface for `debug_traceTransaction` parameters (#675) * Add `TraceParams` interface for `debug_traceTransaction` parameters (#675)
* Add `TransactionReceiptStatus` type (#812) * Add `TransactionReceiptStatus` type (#812)
* Add Artifact types: `CompilerSettings`, `CompilerOptions`, `OutputField` (#924)
## v0.0.2 - _May 31, 2018_ ## v0.0.2 - _June 1, 2018_
* Initial publish (#642) * Initial publish (#642)

View File

@ -1,6 +1,6 @@
{ {
"name": "ethereum-types", "name": "ethereum-types",
"version": "1.0.4", "version": "1.0.5",
"engines": { "engines": {
"node": ">=6.12" "node": ">=6.12"
}, },
@ -29,7 +29,7 @@
}, },
"homepage": "https://github.com/0xProject/0x-monorepo/packages/ethereum-types/README.md", "homepage": "https://github.com/0xProject/0x-monorepo/packages/ethereum-types/README.md",
"devDependencies": { "devDependencies": {
"@0xproject/tslint-config": "^1.0.5", "@0xproject/tslint-config": "^1.0.6",
"copyfiles": "^2.0.0", "copyfiles": "^2.0.0",
"make-promises-safe": "^1.1.0", "make-promises-safe": "^1.1.0",
"shx": "^0.2.2", "shx": "^0.2.2",

View File

@ -1,4 +1,22 @@
[ [
{
"version": "1.0.1-rc.5",
"changes": [
{
"note": "Dependencies updated"
}
],
"timestamp": 1535377027
},
{
"version": "1.0.1-rc.4",
"changes": [
{
"note": "Dependencies updated"
}
],
"timestamp": 1535133899
},
{ {
"version": "1.0.1-rc.3", "version": "1.0.1-rc.3",
"changes": [ "changes": [

View File

@ -5,7 +5,15 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG CHANGELOG
## v1.0.1-rc.3 - _August 13, 2018_ ## v1.0.1-rc.5 - _August 27, 2018_
* Dependencies updated
## v1.0.1-rc.4 - _August 24, 2018_
* Dependencies updated
## v1.0.1-rc.3 - _August 14, 2018_
* Updated to use latest orderFactory interface, fixed `feeRecipient` spelling error in public interface (#936) * Updated to use latest orderFactory interface, fixed `feeRecipient` spelling error in public interface (#936)
* Dependencies updated * Dependencies updated
@ -22,7 +30,7 @@ CHANGELOG
* Dependencies updated * Dependencies updated
## v1.0.0-rc.1 - _July 19, 2018_ ## v1.0.0-rc.1 - _July 20, 2018_
* Make fill-scenarios compatible with V2 of 0x protocol (#656) * Make fill-scenarios compatible with V2 of 0x protocol (#656)

View File

@ -1,6 +1,6 @@
{ {
"name": "@0xproject/fill-scenarios", "name": "@0xproject/fill-scenarios",
"version": "1.0.1-rc.3", "version": "1.0.1-rc.5",
"description": "0x order fill scenario generator", "description": "0x order fill scenario generator",
"main": "lib/index.js", "main": "lib/index.js",
"types": "lib/index.d.ts", "types": "lib/index.d.ts",
@ -27,8 +27,8 @@
}, },
"homepage": "https://github.com/0xProject/0x-monorepo/packages/fill-scenarios/README.md", "homepage": "https://github.com/0xProject/0x-monorepo/packages/fill-scenarios/README.md",
"devDependencies": { "devDependencies": {
"@0xproject/abi-gen": "^1.0.5", "@0xproject/abi-gen": "^1.0.7",
"@0xproject/tslint-config": "^1.0.5", "@0xproject/tslint-config": "^1.0.6",
"@types/lodash": "4.14.104", "@types/lodash": "4.14.104",
"copyfiles": "^2.0.0", "copyfiles": "^2.0.0",
"make-promises-safe": "^1.1.0", "make-promises-safe": "^1.1.0",
@ -38,13 +38,13 @@
"typescript": "3.0.1" "typescript": "3.0.1"
}, },
"dependencies": { "dependencies": {
"@0xproject/base-contract": "^2.0.0-rc.1", "@0xproject/base-contract": "^2.0.1",
"@0xproject/order-utils": "^1.0.1-rc.3", "@0xproject/order-utils": "^1.0.1-rc.6",
"@0xproject/types": "^1.0.1-rc.4", "@0xproject/types": "^1.0.1-rc.6",
"@0xproject/typescript-typings": "^1.0.4", "@0xproject/typescript-typings": "^1.0.5",
"@0xproject/utils": "^1.0.5", "@0xproject/utils": "^1.0.7",
"@0xproject/web3-wrapper": "^1.2.0", "@0xproject/web3-wrapper": "^2.0.1",
"ethereum-types": "^1.0.4", "ethereum-types": "^1.0.5",
"ethers": "3.0.22", "ethers": "3.0.22",
"lodash": "^4.17.5" "lodash": "^4.17.5"
}, },

View File

@ -1,4 +1,13 @@
[ [
{
"version": "1.0.1-rc.2",
"changes": [
{
"note": "Dependencies updated"
}
],
"timestamp": 1535377027
},
{ {
"version": "1.0.1-rc.1", "version": "1.0.1-rc.1",
"changes": [ "changes": [
@ -6,6 +15,7 @@
"note": "Add initial forwarderHelperFactory", "note": "Add initial forwarderHelperFactory",
"pr": 997 "pr": 997
} }
] ],
"timestamp": 1535133899
} }
] ]

View File

@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG CHANGELOG
## v1.0.1-rc.4 - _August 13, 2018_ ## v1.0.1-rc.2 - _August 27, 2018_
* Add inital spec for SRA v2 (#916) * Dependencies updated
## v1.0.1-rc.1 - _August 24, 2018_
* Add initial forwarderHelperFactory (#997)

View File

@ -1,6 +1,6 @@
{ {
"name": "@0xproject/forwarder-helper", "name": "@0xproject/forwarder-helper",
"version": "1.0.0-rc.1", "version": "1.0.1-rc.2",
"engines": { "engines": {
"node": ">=6.12" "node": ">=6.12"
}, },
@ -15,15 +15,13 @@
"test:coverage": "nyc npm run test --all && yarn coverage:report:lcov", "test:coverage": "nyc npm run test --all && yarn coverage:report:lcov",
"coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info", "coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info",
"test:circleci": "yarn test:coverage", "test:circleci": "yarn test:coverage",
"run_mocha": "run_mocha": "mocha --require source-map-support/register --require make-promises-safe lib/test/**/*_test.js --exit",
"mocha --require source-map-support/register --require make-promises-safe lib/test/**/*_test.js --exit",
"clean": "shx rm -rf lib test_temp scripts", "clean": "shx rm -rf lib test_temp scripts",
"build": "tsc && copyfiles -u 3 './lib/src/monorepo_scripts/**/*' ./scripts", "build": "tsc && copyfiles -u 3 './lib/src/monorepo_scripts/**/*' ./scripts",
"manual:postpublish": "yarn build; node ./scripts/postpublish.js", "manual:postpublish": "yarn build; node ./scripts/postpublish.js",
"docs:stage": "node scripts/stage_docs.js", "docs:stage": "node scripts/stage_docs.js",
"docs:json": "typedoc --excludePrivate --excludeExternals --target ES5 --json $JSON_FILE_PATH $PROJECT_FILES", "docs:json": "typedoc --excludePrivate --excludeExternals --target ES5 --json $JSON_FILE_PATH $PROJECT_FILES",
"upload_docs_json": "upload_docs_json": "aws s3 cp generated_docs/index.json $S3_URL --profile 0xproject --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers --content-type application/json"
"aws s3 cp generated_docs/index.json $S3_URL --profile 0xproject --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers --content-type application/json"
}, },
"config": { "config": {
"postpublish": { "postpublish": {
@ -41,17 +39,17 @@
}, },
"homepage": "https://github.com/0xProject/0x-monorepo/packages/forwarder-helper/README.md", "homepage": "https://github.com/0xProject/0x-monorepo/packages/forwarder-helper/README.md",
"dependencies": { "dependencies": {
"@0xproject/assert": "^1.0.5", "@0xproject/assert": "^1.0.7",
"@0xproject/json-schemas": "^1.0.1-rc.4", "@0xproject/json-schemas": "^1.0.1-rc.6",
"@0xproject/order-utils": "^1.0.1-rc.3", "@0xproject/order-utils": "^1.0.1-rc.6",
"@0xproject/types": "^1.0.1-rc.4", "@0xproject/types": "^1.0.1-rc.6",
"@0xproject/typescript-typings": "^1.0.4", "@0xproject/typescript-typings": "^1.0.5",
"@0xproject/utils": "^1.0.5", "@0xproject/utils": "^1.0.7",
"@types/node": "^8.0.53", "@types/node": "^8.0.53",
"lodash": "^4.17.10" "lodash": "^4.17.10"
}, },
"devDependencies": { "devDependencies": {
"@0xproject/tslint-config": "^1.0.5", "@0xproject/tslint-config": "^1.0.6",
"@types/lodash": "^4.14.116", "@types/lodash": "^4.14.116",
"@types/mocha": "^2.2.42", "@types/mocha": "^2.2.42",
"chai": "^4.0.1", "chai": "^4.0.1",

View File

@ -1,4 +1,13 @@
[ [
{
"version": "1.0.1-rc.6",
"changes": [
{
"note": "Dependencies updated"
}
],
"timestamp": 1535377027
},
{ {
"version": "1.0.1-rc.5", "version": "1.0.1-rc.5",
"changes": [ "changes": [
@ -6,7 +15,8 @@
"note": "Update incorrect relayer api fee recipients response schema", "note": "Update incorrect relayer api fee recipients response schema",
"pr": 974 "pr": 974
} }
] ],
"timestamp": 1535133899
}, },
{ {
"version": "1.0.1-rc.4", "version": "1.0.1-rc.4",

View File

@ -5,7 +5,15 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG CHANGELOG
## v1.0.1-rc.4 - _August 13, 2018_ ## v1.0.1-rc.6 - _August 27, 2018_
* Dependencies updated
## v1.0.1-rc.5 - _August 24, 2018_
* Update incorrect relayer api fee recipients response schema (#974)
## v1.0.1-rc.4 - _August 14, 2018_
* Allow for additional properties in txData schema (#938) * Allow for additional properties in txData schema (#938)
* Change hexSchema to match `0x` (#937) * Change hexSchema to match `0x` (#937)
@ -27,7 +35,7 @@ CHANGELOG
* Dependencies updated * Dependencies updated
## v1.0.0-rc.1 - _July 19, 2018_ ## v1.0.0-rc.1 - _July 20, 2018_
* Update schemas for V2 or 0x Protocol (#615) * Update schemas for V2 or 0x Protocol (#615)
* Added CallData schema (#821) * Added CallData schema (#821)
@ -53,7 +61,7 @@ CHANGELOG
* Dependencies updated * Dependencies updated
## v0.7.23 - _May 4, 2018_ ## v0.7.23 - _May 5, 2018_
* Dependencies updated * Dependencies updated

View File

@ -1,6 +1,6 @@
{ {
"name": "@0xproject/json-schemas", "name": "@0xproject/json-schemas",
"version": "1.0.1-rc.4", "version": "1.0.1-rc.6",
"engines": { "engines": {
"node": ">=6.12" "node": ">=6.12"
}, },
@ -23,7 +23,9 @@
"config": { "config": {
"postpublish": { "postpublish": {
"assets": [], "assets": [],
"docOmitExports": ["schemas"] "docOmitExports": [
"schemas"
]
} }
}, },
"repository": { "repository": {
@ -37,14 +39,14 @@
}, },
"homepage": "https://github.com/0xProject/0x-monorepo/packages/json-schemas/README.md", "homepage": "https://github.com/0xProject/0x-monorepo/packages/json-schemas/README.md",
"dependencies": { "dependencies": {
"@0xproject/typescript-typings": "^1.0.4", "@0xproject/typescript-typings": "^1.0.5",
"@types/node": "^8.0.53", "@types/node": "^8.0.53",
"jsonschema": "^1.2.0", "jsonschema": "^1.2.0",
"lodash.values": "^4.3.0" "lodash.values": "^4.3.0"
}, },
"devDependencies": { "devDependencies": {
"@0xproject/tslint-config": "^1.0.5", "@0xproject/tslint-config": "^1.0.6",
"@0xproject/utils": "^1.0.5", "@0xproject/utils": "^1.0.7",
"@types/lodash.foreach": "^4.5.3", "@types/lodash.foreach": "^4.5.3",
"@types/lodash.values": "^4.3.3", "@types/lodash.values": "^4.3.3",
"@types/mocha": "^2.2.42", "@types/mocha": "^2.2.42",

View File

@ -6,7 +6,6 @@ export const relayerApiFeeRecipientsResponseSchema = {
{ {
properties: { properties: {
records: { records: {
id: '/relayerApiFeeRecipientsSchema',
type: 'array', type: 'array',
items: { $ref: '/addressSchema' }, items: { $ref: '/addressSchema' },
}, },

View File

@ -1,6 +1,6 @@
{ {
"name": "@0xproject/metacoin", "name": "@0xproject/metacoin",
"version": "0.0.15", "version": "0.0.17",
"engines": { "engines": {
"node": ">=6.12" "node": ">=6.12"
}, },
@ -29,25 +29,25 @@
"author": "", "author": "",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@0xproject/abi-gen": "^1.0.5", "@0xproject/abi-gen": "^1.0.7",
"@0xproject/base-contract": "^2.0.0-rc.1", "@0xproject/base-contract": "^2.0.1",
"@0xproject/sol-cov": "^2.0.0", "@0xproject/sol-cov": "^2.1.1",
"@0xproject/subproviders": "^1.0.5", "@0xproject/subproviders": "^2.0.1",
"@0xproject/tslint-config": "^1.0.5", "@0xproject/tslint-config": "^1.0.6",
"@0xproject/types": "^1.0.1-rc.4", "@0xproject/types": "^1.0.1-rc.6",
"@0xproject/typescript-typings": "^1.0.4", "@0xproject/typescript-typings": "^1.0.5",
"@0xproject/utils": "^1.0.5", "@0xproject/utils": "^1.0.7",
"@0xproject/web3-wrapper": "^1.2.0", "@0xproject/web3-wrapper": "^2.0.1",
"@types/mocha": "^5.2.2", "@types/mocha": "^5.2.2",
"copyfiles": "^2.0.0", "copyfiles": "^2.0.0",
"ethereum-types": "^1.0.4", "ethereum-types": "^1.0.5",
"ethers": "3.0.22", "ethers": "3.0.22",
"lodash": "^4.17.5", "lodash": "^4.17.5",
"run-s": "^0.0.0" "run-s": "^0.0.0"
}, },
"devDependencies": { "devDependencies": {
"@0xproject/dev-utils": "^1.0.4", "@0xproject/dev-utils": "^1.0.6",
"@0xproject/sol-compiler": "^1.0.5", "@0xproject/sol-compiler": "^1.1.1",
"chai": "^4.0.1", "chai": "^4.0.1",
"chai-as-promised": "^7.1.0", "chai-as-promised": "^7.1.0",
"chai-bignumber": "^2.0.1", "chai-bignumber": "^2.0.1",

View File

@ -1,4 +1,22 @@
[ [
{
"timestamp": 1535377027,
"version": "1.0.6",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1535133899,
"version": "1.0.5",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{ {
"timestamp": 1534210131, "timestamp": 1534210131,
"version": "1.0.4", "version": "1.0.4",

View File

@ -5,7 +5,15 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG CHANGELOG
## v1.0.4 - _August 13, 2018_ ## v1.0.6 - _August 27, 2018_
* Dependencies updated
## v1.0.5 - _August 24, 2018_
* Dependencies updated
## v1.0.4 - _August 14, 2018_
* Dependencies updated * Dependencies updated
@ -21,7 +29,7 @@ CHANGELOG
* Dependencies updated * Dependencies updated
## v1.0.0 - _July 19, 2018_ ## v1.0.0 - _July 20, 2018_
* Added migrations for 0x Protocol v2 * Added migrations for 0x Protocol v2
@ -45,7 +53,7 @@ CHANGELOG
* Dependencies updated * Dependencies updated
## v0.0.5 - _May 4, 2018_ ## v0.0.5 - _May 5, 2018_
* Dependencies updated * Dependencies updated

View File

@ -782,5 +782,11 @@
} }
} }
}, },
"networks": {} "networks": {
"50": {
"address": "0xe86bb98fcf9bff3512c74589b78fb168200cc546",
"links": {},
"constructorArgs": "[\"0x48bacb9266a570d521063ef5dd96e61686dbe788\",\"0xf47261b0000000000000000000000000871dd7c2b4b25e1aa18728e9d5f2af4c4e431f5c\"]"
}
}
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "@0xproject/migrations", "name": "@0xproject/migrations",
"version": "1.0.4", "version": "1.0.6",
"engines": { "engines": {
"node": ">=6.12" "node": ">=6.12"
}, },
@ -35,10 +35,10 @@
}, },
"license": "Apache-2.0", "license": "Apache-2.0",
"devDependencies": { "devDependencies": {
"@0xproject/abi-gen": "^1.0.5", "@0xproject/abi-gen": "^1.0.7",
"@0xproject/dev-utils": "^1.0.4", "@0xproject/dev-utils": "^1.0.6",
"@0xproject/tslint-config": "^1.0.5", "@0xproject/tslint-config": "^1.0.6",
"@0xproject/types": "^1.0.1-rc.4", "@0xproject/types": "^1.0.1-rc.6",
"@types/yargs": "^10.0.0", "@types/yargs": "^10.0.0",
"copyfiles": "^2.0.0", "copyfiles": "^2.0.0",
"make-promises-safe": "^1.1.0", "make-promises-safe": "^1.1.0",
@ -49,15 +49,15 @@
"yargs": "^10.0.3" "yargs": "^10.0.3"
}, },
"dependencies": { "dependencies": {
"@0xproject/base-contract": "^2.0.0-rc.1", "@0xproject/base-contract": "^2.0.1",
"@0xproject/order-utils": "^1.0.1-rc.3", "@0xproject/order-utils": "^1.0.1-rc.6",
"@0xproject/sol-compiler": "^1.0.5", "@0xproject/sol-compiler": "^1.1.1",
"@0xproject/subproviders": "^1.0.5", "@0xproject/subproviders": "^2.0.1",
"@0xproject/typescript-typings": "^1.0.4", "@0xproject/typescript-typings": "^1.0.5",
"@0xproject/utils": "^1.0.5", "@0xproject/utils": "^1.0.7",
"@0xproject/web3-wrapper": "^1.2.0", "@0xproject/web3-wrapper": "^2.0.1",
"@ledgerhq/hw-app-eth": "^4.3.0", "@ledgerhq/hw-app-eth": "^4.3.0",
"ethereum-types": "^1.0.4", "ethereum-types": "^1.0.5",
"ethers": "3.0.22", "ethers": "3.0.22",
"lodash": "^4.17.5" "lodash": "^4.17.5"
}, },

View File

@ -1,7 +1,7 @@
{ {
"private": true, "private": true,
"name": "@0xproject/monorepo-scripts", "name": "@0xproject/monorepo-scripts",
"version": "1.0.5", "version": "1.0.7",
"engines": { "engines": {
"node": ">=6.12" "node": ">=6.12"
}, },

View File

@ -18,7 +18,6 @@ import { DocGenerateAndUploadUtils } from './utils/doc_generate_and_upload_utils
import { publishReleaseNotesAsync } from './utils/github_release_utils'; import { publishReleaseNotesAsync } from './utils/github_release_utils';
import { utils } from './utils/utils'; import { utils } from './utils/utils';
const DOC_GEN_COMMAND = 'docs:json';
const NPM_NAMESPACE = '@0xproject/'; const NPM_NAMESPACE = '@0xproject/';
const TODAYS_TIMESTAMP = moment().unix(); const TODAYS_TIMESTAMP = moment().unix();
@ -75,9 +74,11 @@ async function confirmAsync(message: string): Promise<void> {
}); });
utils.log(`Calling 'lerna publish'...`); utils.log(`Calling 'lerna publish'...`);
await lernaPublishAsync(packageToNextVersion); await lernaPublishAsync(packageToNextVersion);
const isStaging = false; if (!configs.IS_LOCAL_PUBLISH) {
const shouldUploadDocs = !configs.IS_LOCAL_PUBLISH; const isStaging = false;
await generateAndUploadDocJsonsAsync(packagesWithDocs, isStaging, shouldUploadDocs); const shouldUploadDocs = true;
await generateAndUploadDocJsonsAsync(packagesWithDocs, isStaging, shouldUploadDocs);
}
const isDryRun = configs.IS_LOCAL_PUBLISH; const isDryRun = configs.IS_LOCAL_PUBLISH;
await publishReleaseNotesAsync(updatedPublicPackages, isDryRun); await publishReleaseNotesAsync(updatedPublicPackages, isDryRun);
})().catch(err => { })().catch(err => {
@ -88,7 +89,7 @@ async function confirmAsync(message: string): Promise<void> {
function getPackagesWithDocs(allUpdatedPackages: Package[]): Package[] { function getPackagesWithDocs(allUpdatedPackages: Package[]): Package[] {
const rootPackageJsonPath = `${constants.monorepoRootPath}/package.json`; const rootPackageJsonPath = `${constants.monorepoRootPath}/package.json`;
const rootPackageJson = JSON.parse(fs.readFileSync(rootPackageJsonPath).toString()); const rootPackageJson = JSON.parse(fs.readFileSync(rootPackageJsonPath).toString());
const packagesWithDocPagesStringIfExist = _.get(rootPackageJson, 'configs.packagesWithDocPages', undefined); const packagesWithDocPagesStringIfExist = _.get(rootPackageJson, 'config.packagesWithDocPages', undefined);
if (_.isUndefined(packagesWithDocPagesStringIfExist)) { if (_.isUndefined(packagesWithDocPagesStringIfExist)) {
return []; // None to generate & publish return []; // None to generate & publish
} }

View File

@ -17,6 +17,7 @@ const args = yargs
const allUpdatedPackages = await utils.getUpdatedPackagesAsync(shouldIncludePrivate); const allUpdatedPackages = await utils.getUpdatedPackagesAsync(shouldIncludePrivate);
await publishReleaseNotesAsync(allUpdatedPackages, isDryRun); await publishReleaseNotesAsync(allUpdatedPackages, isDryRun);
process.exit(0);
})().catch(err => { })().catch(err => {
utils.log(err); utils.log(err);
process.exit(1); process.exit(1);

View File

@ -85,11 +85,13 @@ function logIfDefined(x: any): void {
logIfDefined(packageError.error.stdout); logIfDefined(packageError.error.stdout);
logIfDefined(packageError.error.stack); logIfDefined(packageError.error.stack);
}); });
process.exit(1);
} else {
process.exit(0); process.exit(0);
} }
})().catch(err => { })().catch(err => {
utils.log(`Unexpected error: ${err.message}`); utils.log(`Unexpected error: ${err.message}`);
process.exit(0); process.exit(1);
}); });
async function testInstallPackageAsync( async function testInstallPackageAsync(
@ -144,7 +146,7 @@ async function testInstallPackageAsync(
const transpiledIndexFilePath = path.join(testDirectory, 'index.js'); const transpiledIndexFilePath = path.join(testDirectory, 'index.js');
utils.log(`Running test script with ${packageName} imported`); utils.log(`Running test script with ${packageName} imported`);
await execAsync(`node ${transpiledIndexFilePath}`); await execAsync(`node ${transpiledIndexFilePath}`);
utils.log(`Successfilly ran test script with ${packageName} imported`); utils.log(`Successfully ran test script with ${packageName} imported`);
} }
await rimrafAsync(testDirectory); await rimrafAsync(testDirectory);
} }

View File

@ -19,11 +19,6 @@ CHANGELOG
export const changelogUtils = { export const changelogUtils = {
getChangelogMdTitle(versionChangelog: VersionChangelog): string { getChangelogMdTitle(versionChangelog: VersionChangelog): string {
if (_.isUndefined(versionChangelog.timestamp)) {
throw new Error(
'All CHANGELOG.json entries must be updated to include a timestamp before generating their MD version',
);
}
const date = moment(`${versionChangelog.timestamp}`, 'X').format('MMMM D, YYYY'); const date = moment(`${versionChangelog.timestamp}`, 'X').format('MMMM D, YYYY');
const title = `\n## v${versionChangelog.version} - _${date}_\n\n`; const title = `\n## v${versionChangelog.version} - _${date}_\n\n`;
return title; return title;

View File

@ -1,4 +1,22 @@
[ [
{
"version": "1.0.1-rc.6",
"changes": [
{
"note": "Fix missing `BlockParamLiteral` type import issue"
}
],
"timestamp": 1535377027
},
{
"version": "1.0.1-rc.5",
"changes": [
{
"note": "Remove Caller and Trezor SignatureTypes",
"pr": 1015
}
]
},
{ {
"version": "1.0.1-rc.4", "version": "1.0.1-rc.4",
"changes": [ "changes": [
@ -39,7 +57,8 @@
"Update `findFeeOrdersThatCoverFeesForTargetOrders` to round the the nearest integer when calculating required fees", "Update `findFeeOrdersThatCoverFeesForTargetOrders` to round the the nearest integer when calculating required fees",
"pr": 997 "pr": 997
} }
] ],
"timestamp": 1535133899
}, },
{ {
"version": "1.0.1-rc.3", "version": "1.0.1-rc.3",

View File

@ -5,7 +5,26 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG CHANGELOG
## v1.0.1-rc.3 - _August 13, 2018_ ## v1.0.1-rc.6 - _August 27, 2018_
* Fix missing `BlockParamLiteral` type import issue
## v1.0.1-rc.5 - _Invalid date_
* Remove Caller and Trezor SignatureTypes (#1015)
## v1.0.1-rc.4 - _August 24, 2018_
* Remove rounding error being thrown when maker amount is very small (#959)
* Added rateUtils and sortingUtils (#953)
* Update marketUtils api such that all optional parameters are bundled into one optional param and more defaults are provided (#954)
* Instead of exporting signature util methods individually, they are now exported as `signatureUtils` (#924)
* Export types: `SignedOrder`, `Order`, `OrderRelevantState`, `OrderState`, `ECSignature`, `ERC20AssetData`, `ERC721AssetData`, `AssetProxyId`, `SignerType`, `SignatureType`, `OrderStateValid`, `OrderStateInvalid`, `ExchangeContractErrs`, `TradeSide`, `TransferType`, `FindFeeOrdersThatCoverFeesForTargetOrdersOpts`, `FindOrdersThatCoverMakerAssetFillAmountOpts`, `FeeOrdersAndRemainingFeeAmount`, `OrdersAndRemainingFillAmount`, `Provider`, `JSONRPCRequestPayload`, `JSONRPCErrorCallback` and `JSONRPCResponsePayload` (#924)
* Rename `resultOrders` to `resultFeeOrders` for object returned by `findFeeOrdersThatCoverFeesForTargetOrders` in `marketUtils` api (#997)
* Make `sortFeeOrdersByFeeAdjustedRate` in `sortingUtils` generic (#997)
* Update `findFeeOrdersThatCoverFeesForTargetOrders` to round the the nearest integer when calculating required fees (#997)
## v1.0.1-rc.3 - _August 14, 2018_
* Update ecSignOrderHashAsync to return signature string with signature type byte. Removes messagePrefixOpts. (#914) * Update ecSignOrderHashAsync to return signature string with signature type byte. Removes messagePrefixOpts. (#914)
* Added a synchronous `createOrder` method in `orderFactory`, updated public interfaces to support some optional parameters (#936) * Added a synchronous `createOrder` method in `orderFactory`, updated public interfaces to support some optional parameters (#936)
@ -28,7 +47,7 @@ CHANGELOG
* Upgrade ethereumjs-abi dep including a fix so that addresses starting with 0 are properly decoded by `decodeERC20AssetData` * Upgrade ethereumjs-abi dep including a fix so that addresses starting with 0 are properly decoded by `decodeERC20AssetData`
## v1.0.0-rc.1 - _July 19, 2018_ ## v1.0.0-rc.1 - _July 20, 2018_
* Refactor to work with V2 of 0x protocol (#636) * Refactor to work with V2 of 0x protocol (#636)
* Export parseECSignature method (#684) * Export parseECSignature method (#684)
@ -54,7 +73,7 @@ CHANGELOG
* Add orderStateUtils, a module for computing order state needed to decide if an order is still valid * Add orderStateUtils, a module for computing order state needed to decide if an order is still valid
## v0.0.4 - _May 4, 2018_ ## v0.0.4 - _May 5, 2018_
* Dependencies updated * Dependencies updated

View File

@ -1,6 +1,6 @@
{ {
"name": "@0xproject/order-utils", "name": "@0xproject/order-utils",
"version": "1.0.1-rc.3", "version": "1.0.1-rc.6",
"engines": { "engines": {
"node": ">=6.12" "node": ">=6.12"
}, },
@ -40,8 +40,8 @@
}, },
"homepage": "https://github.com/0xProject/0x-monorepo/packages/order-utils/README.md", "homepage": "https://github.com/0xProject/0x-monorepo/packages/order-utils/README.md",
"devDependencies": { "devDependencies": {
"@0xproject/dev-utils": "^1.0.4", "@0xproject/dev-utils": "^1.0.6",
"@0xproject/tslint-config": "^1.0.5", "@0xproject/tslint-config": "^1.0.6",
"@types/bn.js": "^4.11.0", "@types/bn.js": "^4.11.0",
"@types/lodash": "4.14.104", "@types/lodash": "4.14.104",
"chai": "^4.0.1", "chai": "^4.0.1",
@ -59,16 +59,16 @@
"typescript": "3.0.1" "typescript": "3.0.1"
}, },
"dependencies": { "dependencies": {
"@0xproject/assert": "^1.0.5", "@0xproject/assert": "^1.0.7",
"@0xproject/base-contract": "^2.0.0-rc.1", "@0xproject/base-contract": "^2.0.1",
"@0xproject/json-schemas": "^1.0.1-rc.4", "@0xproject/json-schemas": "^1.0.1-rc.6",
"@0xproject/types": "^1.0.1-rc.4", "@0xproject/types": "^1.0.1-rc.6",
"@0xproject/typescript-typings": "^1.0.4", "@0xproject/typescript-typings": "^1.0.5",
"@0xproject/utils": "^1.0.5", "@0xproject/utils": "^1.0.7",
"@0xproject/web3-wrapper": "^1.2.0", "@0xproject/web3-wrapper": "^2.0.1",
"@types/node": "^8.0.53", "@types/node": "^8.0.53",
"bn.js": "^4.11.8", "bn.js": "^4.11.8",
"ethereum-types": "^1.0.4", "ethereum-types": "^1.0.5",
"ethereumjs-abi": "0.6.5", "ethereumjs-abi": "0.6.5",
"ethereumjs-util": "^5.1.1", "ethereumjs-util": "^5.1.1",
"ethers": "3.0.22", "ethers": "3.0.22",

View File

@ -81,7 +81,7 @@ export class OrderStateUtils {
const remainingTakerAssetAmount = signedOrder.takerAssetAmount.minus( const remainingTakerAssetAmount = signedOrder.takerAssetAmount.minus(
sidedOrderRelevantState.filledTakerAssetAmount, sidedOrderRelevantState.filledTakerAssetAmount,
); );
const isRoundingError = OrderValidationUtils.isRoundingError( const isRoundingError = OrderValidationUtils.isRoundingErrorFloor(
remainingTakerAssetAmount, remainingTakerAssetAmount,
signedOrder.takerAssetAmount, signedOrder.takerAssetAmount,
signedOrder.makerAssetAmount, signedOrder.makerAssetAmount,
@ -191,7 +191,7 @@ export class OrderStateUtils {
); );
const remainingFillableTakerAssetAmountGivenMakersStatus = signedOrder.makerAssetAmount.eq(0) const remainingFillableTakerAssetAmountGivenMakersStatus = signedOrder.makerAssetAmount.eq(0)
? new BigNumber(0) ? new BigNumber(0)
: utils.getPartialAmount( : utils.getPartialAmountFloor(
orderRelevantMakerState.remainingFillableAssetAmount, orderRelevantMakerState.remainingFillableAssetAmount,
signedOrder.makerAssetAmount, signedOrder.makerAssetAmount,
signedOrder.takerAssetAmount, signedOrder.takerAssetAmount,

View File

@ -24,7 +24,7 @@ export class OrderValidationUtils {
* @param denominator Denominator value. When used to check an order, pass in `order.takerAssetAmount` * @param denominator Denominator value. When used to check an order, pass in `order.takerAssetAmount`
* @param target Target value. When used to check an order, pass in `order.makerAssetAmount` * @param target Target value. When used to check an order, pass in `order.makerAssetAmount`
*/ */
public static isRoundingError(numerator: BigNumber, denominator: BigNumber, target: BigNumber): boolean { public static isRoundingErrorFloor(numerator: BigNumber, denominator: BigNumber, target: BigNumber): boolean {
// Solidity's mulmod() in JS // Solidity's mulmod() in JS
// Source: https://solidity.readthedocs.io/en/latest/units-and-global-variables.html#mathematical-and-cryptographic-functions // Source: https://solidity.readthedocs.io/en/latest/units-and-global-variables.html#mathematical-and-cryptographic-functions
if (denominator.eq(0)) { if (denominator.eq(0)) {
@ -58,7 +58,7 @@ export class OrderValidationUtils {
zrxAssetData: string, zrxAssetData: string,
): Promise<void> { ): Promise<void> {
try { try {
const fillMakerTokenAmount = utils.getPartialAmount( const fillMakerTokenAmount = utils.getPartialAmountFloor(
fillTakerAssetAmount, fillTakerAssetAmount,
signedOrder.takerAssetAmount, signedOrder.takerAssetAmount,
signedOrder.makerAssetAmount, signedOrder.makerAssetAmount,
@ -79,7 +79,7 @@ export class OrderValidationUtils {
TradeSide.Taker, TradeSide.Taker,
TransferType.Trade, TransferType.Trade,
); );
const makerFeeAmount = utils.getPartialAmount( const makerFeeAmount = utils.getPartialAmountFloor(
fillTakerAssetAmount, fillTakerAssetAmount,
signedOrder.takerAssetAmount, signedOrder.takerAssetAmount,
signedOrder.makerFee, signedOrder.makerFee,
@ -92,7 +92,7 @@ export class OrderValidationUtils {
TradeSide.Maker, TradeSide.Maker,
TransferType.Fee, TransferType.Fee,
); );
const takerFeeAmount = utils.getPartialAmount( const takerFeeAmount = utils.getPartialAmountFloor(
fillTakerAssetAmount, fillTakerAssetAmount,
signedOrder.takerAssetAmount, signedOrder.takerAssetAmount,
signedOrder.takerFee, signedOrder.takerFee,
@ -218,7 +218,7 @@ export class OrderValidationUtils {
zrxAssetData, zrxAssetData,
); );
const wouldRoundingErrorOccur = OrderValidationUtils.isRoundingError( const wouldRoundingErrorOccur = OrderValidationUtils.isRoundingErrorFloor(
desiredFillTakerTokenAmount, desiredFillTakerTokenAmount,
signedOrder.takerAssetAmount, signedOrder.takerAssetAmount,
signedOrder.makerAssetAmount, signedOrder.makerAssetAmount,

View File

@ -53,11 +53,6 @@ export const signatureUtils = {
return signatureUtils.isValidECSignature(prefixedMessageHex, ecSignature, signerAddress); return signatureUtils.isValidECSignature(prefixedMessageHex, ecSignature, signerAddress);
} }
case SignatureType.Caller:
// HACK: We currently do not "validate" the caller signature type.
// It can only be validated during Exchange contract execution.
throw new Error('Caller signature type cannot be validated off-chain');
case SignatureType.Wallet: { case SignatureType.Wallet: {
const isValid = await signatureUtils.isValidWalletSignatureAsync( const isValid = await signatureUtils.isValidWalletSignatureAsync(
provider, provider,
@ -82,12 +77,6 @@ export const signatureUtils = {
return signatureUtils.isValidPresignedSignatureAsync(provider, data, signerAddress); return signatureUtils.isValidPresignedSignatureAsync(provider, data, signerAddress);
} }
case SignatureType.Trezor: {
const prefixedMessageHex = signatureUtils.addSignedMessagePrefix(data, SignerType.Trezor);
const ecSignature = signatureUtils.parseECSignature(signature);
return signatureUtils.isValidECSignature(prefixedMessageHex, ecSignature, signerAddress);
}
default: default:
throw new Error(`Unhandled SignatureType: ${signatureTypeIndexIfExists}`); throw new Error(`Unhandled SignatureType: ${signatureTypeIndexIfExists}`);
} }
@ -293,10 +282,6 @@ export const signatureUtils = {
signatureType = SignatureType.EthSign; signatureType = SignatureType.EthSign;
break; break;
} }
case SignerType.Trezor: {
signatureType = SignatureType.Trezor;
break;
}
default: default:
throw new Error(`Unrecognized SignerType: ${signerType}`); throw new Error(`Unrecognized SignerType: ${signerType}`);
} }
@ -306,7 +291,7 @@ export const signatureUtils = {
/** /**
* Combines the signature proof and the Signature Type. * Combines the signature proof and the Signature Type.
* @param signature The hex encoded signature proof * @param signature The hex encoded signature proof
* @param signatureType The signature type, i.e EthSign, Trezor, Wallet etc. * @param signatureType The signature type, i.e EthSign, Wallet etc.
* @return Hex encoded string of signature proof with Signature Type * @return Hex encoded string of signature proof with Signature Type
*/ */
convertToSignatureWithType(signature: string, signatureType: SignatureType): string { convertToSignatureWithType(signature: string, signatureType: SignatureType): string {
@ -333,12 +318,6 @@ export const signatureUtils = {
const prefixedMsgHex = ethUtil.bufferToHex(prefixedMsgBuff); const prefixedMsgHex = ethUtil.bufferToHex(prefixedMsgBuff);
return prefixedMsgHex; return prefixedMsgHex;
} }
case SignerType.Trezor: {
const msgBuff = ethUtil.toBuffer(message);
const prefixedMsgBuff = hashTrezorPersonalMessage(msgBuff);
const prefixedMsgHex = ethUtil.bufferToHex(prefixedMsgBuff);
return prefixedMsgHex;
}
default: default:
throw new Error(`Unrecognized SignerType: ${signerType}`); throw new Error(`Unrecognized SignerType: ${signerType}`);
} }
@ -350,7 +329,7 @@ export const signatureUtils = {
*/ */
parseECSignature(signature: string): ECSignature { parseECSignature(signature: string): ECSignature {
assert.isHexString('signature', signature); assert.isHexString('signature', signature);
const ecSignatureTypes = [SignatureType.EthSign, SignatureType.EIP712, SignatureType.Trezor]; const ecSignatureTypes = [SignatureType.EthSign, SignatureType.EIP712];
assert.isOneOfExpectedSignatureTypes(signature, ecSignatureTypes); assert.isOneOfExpectedSignatureTypes(signature, ecSignatureTypes);
// tslint:disable-next-line:custom-no-magic-numbers // tslint:disable-next-line:custom-no-magic-numbers
@ -361,11 +340,6 @@ export const signatureUtils = {
}, },
}; };
function hashTrezorPersonalMessage(message: Buffer): Buffer {
const prefix = ethUtil.toBuffer('\x19Ethereum Signed Message:\n' + String.fromCharCode(message.byteLength));
return ethUtil.sha3(Buffer.concat([prefix, message]));
}
function parseValidatorSignature(signature: string): ValidatorSignature { function parseValidatorSignature(signature: string): ValidatorSignature {
assert.isOneOfExpectedSignatureTypes(signature, [SignatureType.Validator]); assert.isOneOfExpectedSignatureTypes(signature, [SignatureType.Validator]);
// tslint:disable:custom-no-magic-numbers // tslint:disable:custom-no-magic-numbers

View File

@ -12,7 +12,7 @@ export const utils = {
const milisecondsInSecond = 1000; const milisecondsInSecond = 1000;
return new BigNumber(Date.now() / milisecondsInSecond).round(); return new BigNumber(Date.now() / milisecondsInSecond).round();
}, },
getPartialAmount(numerator: BigNumber, denominator: BigNumber, target: BigNumber): BigNumber { getPartialAmountFloor(numerator: BigNumber, denominator: BigNumber, target: BigNumber): BigNumber {
const fillMakerTokenAmount = numerator const fillMakerTokenAmount = numerator
.mul(target) .mul(target)
.div(denominator) .div(denominator)

View File

@ -16,7 +16,7 @@ describe('OrderValidationUtils', () => {
const denominator = new BigNumber(999); const denominator = new BigNumber(999);
const target = new BigNumber(50); const target = new BigNumber(50);
// rounding error = ((20*50/999) - floor(20*50/999)) / (20*50/999) = 0.1% // rounding error = ((20*50/999) - floor(20*50/999)) / (20*50/999) = 0.1%
const isRoundingError = OrderValidationUtils.isRoundingError(numerator, denominator, target); const isRoundingError = OrderValidationUtils.isRoundingErrorFloor(numerator, denominator, target);
expect(isRoundingError).to.be.false(); expect(isRoundingError).to.be.false();
}); });
@ -25,7 +25,7 @@ describe('OrderValidationUtils', () => {
const denominator = new BigNumber(9991); const denominator = new BigNumber(9991);
const target = new BigNumber(500); const target = new BigNumber(500);
// rounding error = ((20*500/9991) - floor(20*500/9991)) / (20*500/9991) = 0.09% // rounding error = ((20*500/9991) - floor(20*500/9991)) / (20*500/9991) = 0.09%
const isRoundingError = OrderValidationUtils.isRoundingError(numerator, denominator, target); const isRoundingError = OrderValidationUtils.isRoundingErrorFloor(numerator, denominator, target);
expect(isRoundingError).to.be.false(); expect(isRoundingError).to.be.false();
}); });
@ -34,7 +34,7 @@ describe('OrderValidationUtils', () => {
const denominator = new BigNumber(9989); const denominator = new BigNumber(9989);
const target = new BigNumber(500); const target = new BigNumber(500);
// rounding error = ((20*500/9989) - floor(20*500/9989)) / (20*500/9989) = 0.011% // rounding error = ((20*500/9989) - floor(20*500/9989)) / (20*500/9989) = 0.011%
const isRoundingError = OrderValidationUtils.isRoundingError(numerator, denominator, target); const isRoundingError = OrderValidationUtils.isRoundingErrorFloor(numerator, denominator, target);
expect(isRoundingError).to.be.true(); expect(isRoundingError).to.be.true();
}); });
@ -43,7 +43,7 @@ describe('OrderValidationUtils', () => {
const denominator = new BigNumber(7); const denominator = new BigNumber(7);
const target = new BigNumber(10); const target = new BigNumber(10);
// rounding error = ((3*10/7) - floor(3*10/7)) / (3*10/7) = 6.67% // rounding error = ((3*10/7) - floor(3*10/7)) / (3*10/7) = 6.67%
const isRoundingError = OrderValidationUtils.isRoundingError(numerator, denominator, target); const isRoundingError = OrderValidationUtils.isRoundingErrorFloor(numerator, denominator, target);
expect(isRoundingError).to.be.true(); expect(isRoundingError).to.be.true();
}); });
@ -52,7 +52,7 @@ describe('OrderValidationUtils', () => {
const denominator = new BigNumber(2); const denominator = new BigNumber(2);
const target = new BigNumber(10); const target = new BigNumber(10);
const isRoundingError = OrderValidationUtils.isRoundingError(numerator, denominator, target); const isRoundingError = OrderValidationUtils.isRoundingErrorFloor(numerator, denominator, target);
expect(isRoundingError).to.be.false(); expect(isRoundingError).to.be.false();
}); });
@ -63,7 +63,7 @@ describe('OrderValidationUtils', () => {
const target = new BigNumber(105762562); const target = new BigNumber(105762562);
// rounding error = ((76564*105762562/676373677) - floor(76564*105762562/676373677)) / // rounding error = ((76564*105762562/676373677) - floor(76564*105762562/676373677)) /
// (76564*105762562/676373677) = 0.0007% // (76564*105762562/676373677) = 0.0007%
const isRoundingError = OrderValidationUtils.isRoundingError(numerator, denominator, target); const isRoundingError = OrderValidationUtils.isRoundingErrorFloor(numerator, denominator, target);
expect(isRoundingError).to.be.false(); expect(isRoundingError).to.be.false();
}); });
}); });

View File

@ -76,20 +76,6 @@ describe('Signature utils', () => {
); );
expect(isValidSignatureLocal).to.be.true(); expect(isValidSignatureLocal).to.be.true();
}); });
it('should return true for a valid Trezor signature', async () => {
dataHex = '0xd0d994e31c88f33fd8a572552a70ed339de579e5ba49ee1d17cc978bbe1cdd21';
address = '0x6ecbe1db9ef729cbe972c83fb886247691fb6beb';
const trezorSignature =
'0x1ce4760660e6495b5ae6723087bea073b3a99ce98ea81fdf00c240279c010e63d05b87bc34c4d67d4776e8d5aeb023a67484f4eaf0fd353b40893e5101e845cd9908';
const isValidSignatureLocal = await signatureUtils.isValidSignatureAsync(
provider,
dataHex,
trezorSignature,
address,
);
expect(isValidSignatureLocal).to.be.true();
});
}); });
describe('#isValidECSignature', () => { describe('#isValidECSignature', () => {
const signature = { const signature = {
@ -270,15 +256,6 @@ describe('Signature utils', () => {
r: '0xaca7da997ad177f040240cdccf6905b71ab16b74434388c3a72f34fd25d64393', r: '0xaca7da997ad177f040240cdccf6905b71ab16b74434388c3a72f34fd25d64393',
s: '0x46b2bac274ff29b48b3ea6e2d04c1336eaceafda3c53ab483fc3ff12fac3ebf2', s: '0x46b2bac274ff29b48b3ea6e2d04c1336eaceafda3c53ab483fc3ff12fac3ebf2',
}; };
it('should concatenate v,r,s and append the Trezor signature type', async () => {
const expectedSignatureWithSignatureType =
'0x1baca7da997ad177f040240cdccf6905b71ab16b74434388c3a72f34fd25d6439346b2bac274ff29b48b3ea6e2d04c1336eaceafda3c53ab483fc3ff12fac3ebf208';
const signatureWithSignatureType = signatureUtils.convertECSignatureToSignatureHex(
ecSignature,
SignerType.Trezor,
);
expect(signatureWithSignatureType).to.equal(expectedSignatureWithSignatureType);
});
it('should concatenate v,r,s and append the EthSign signature type when SignerType is Default', async () => { it('should concatenate v,r,s and append the EthSign signature type when SignerType is Default', async () => {
const expectedSignatureWithSignatureType = const expectedSignatureWithSignatureType =
'0x1baca7da997ad177f040240cdccf6905b71ab16b74434388c3a72f34fd25d6439346b2bac274ff29b48b3ea6e2d04c1336eaceafda3c53ab483fc3ff12fac3ebf203'; '0x1baca7da997ad177f040240cdccf6905b71ab16b74434388c3a72f34fd25d6439346b2bac274ff29b48b3ea6e2d04c1336eaceafda3c53ab483fc3ff12fac3ebf203';

View File

@ -1,4 +1,13 @@
[ [
{
"version": "1.0.1-rc.5",
"changes": [
{
"note": "Fix missing `BlockParamLiteral` type import issue"
}
],
"timestamp": 1535377027
},
{ {
"version": "1.0.1-rc.4", "version": "1.0.1-rc.4",
"changes": [ "changes": [
@ -11,7 +20,8 @@
"note": "Remove exporting types: `BlockParamLiteral`, `BlockParam`, `Order`", "note": "Remove exporting types: `BlockParamLiteral`, `BlockParam`, `Order`",
"pr": 924 "pr": 924
} }
] ],
"timestamp": 1535133899
}, },
{ {
"version": "1.0.1-rc.3", "version": "1.0.1-rc.3",

View File

@ -5,7 +5,16 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG CHANGELOG
## v1.0.1-rc.3 - _August 13, 2018_ ## v1.0.1-rc.5 - _August 27, 2018_
* Fix missing `BlockParamLiteral` type import issue
## v1.0.1-rc.4 - _August 24, 2018_
* Export types: `ExchangeContractErrs`, `OrderRelevantState`, `JSONRPCRequestPayload`, `JSONRPCErrorCallback` and `JSONRPCResponsePayload` (#924)
* Remove exporting types: `BlockParamLiteral`, `BlockParam`, `Order` (#924)
## v1.0.1-rc.3 - _August 14, 2018_
* Dependencies updated * Dependencies updated
@ -21,7 +30,7 @@ CHANGELOG
* Dependencies updated * Dependencies updated
## v1.0.0-rc.1 - _July 19, 2018_ ## v1.0.0-rc.1 - _July 20, 2018_
* Add support for ERC721 event watching and Exchange V2 events (#887) * Add support for ERC721 event watching and Exchange V2 events (#887)

View File

@ -1,6 +1,6 @@
{ {
"name": "@0xproject/order-watcher", "name": "@0xproject/order-watcher",
"version": "1.0.1-rc.3", "version": "1.0.1-rc.5",
"description": "An order watcher daemon that watches for order validity", "description": "An order watcher daemon that watches for order validity",
"keywords": [ "keywords": [
"0x", "0x",
@ -43,10 +43,10 @@
"node": ">=6.0.0" "node": ">=6.0.0"
}, },
"devDependencies": { "devDependencies": {
"@0xproject/abi-gen": "^1.0.5", "@0xproject/abi-gen": "^1.0.7",
"@0xproject/dev-utils": "^1.0.4", "@0xproject/dev-utils": "^1.0.6",
"@0xproject/migrations": "^1.0.4", "@0xproject/migrations": "^1.0.6",
"@0xproject/tslint-config": "^1.0.5", "@0xproject/tslint-config": "^1.0.6",
"@types/bintrees": "^1.0.2", "@types/bintrees": "^1.0.2",
"@types/lodash": "4.14.104", "@types/lodash": "4.14.104",
"@types/mocha": "^2.2.42", "@types/mocha": "^2.2.42",
@ -71,18 +71,18 @@
"typescript": "3.0.1" "typescript": "3.0.1"
}, },
"dependencies": { "dependencies": {
"@0xproject/assert": "^1.0.5", "@0xproject/assert": "^1.0.7",
"@0xproject/base-contract": "^2.0.0-rc.1", "@0xproject/base-contract": "^2.0.1",
"@0xproject/contract-wrappers": "^1.0.1-rc.3", "@0xproject/contract-wrappers": "^1.0.1-rc.5",
"@0xproject/fill-scenarios": "^1.0.1-rc.3", "@0xproject/fill-scenarios": "^1.0.1-rc.5",
"@0xproject/json-schemas": "^1.0.1-rc.4", "@0xproject/json-schemas": "^1.0.1-rc.6",
"@0xproject/order-utils": "^1.0.1-rc.3", "@0xproject/order-utils": "^1.0.1-rc.6",
"@0xproject/types": "^1.0.1-rc.4", "@0xproject/types": "^1.0.1-rc.6",
"@0xproject/typescript-typings": "^1.0.4", "@0xproject/typescript-typings": "^1.0.5",
"@0xproject/utils": "^1.0.5", "@0xproject/utils": "^1.0.7",
"@0xproject/web3-wrapper": "^1.2.0", "@0xproject/web3-wrapper": "^2.0.1",
"bintrees": "^1.0.2", "bintrees": "^1.0.2",
"ethereum-types": "^1.0.4", "ethereum-types": "^1.0.5",
"ethereumjs-blockstream": "5.0.0", "ethereumjs-blockstream": "5.0.0",
"ethers": "3.0.22", "ethers": "3.0.22",
"lodash": "^4.17.5" "lodash": "^4.17.5"

View File

@ -1,4 +1,22 @@
[ [
{
"timestamp": 1535377027,
"version": "1.0.7",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1535133899,
"version": "1.0.6",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{ {
"timestamp": 1534210131, "timestamp": 1534210131,
"version": "1.0.5", "version": "1.0.5",

View File

@ -5,7 +5,15 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG CHANGELOG
## v1.0.5 - _August 13, 2018_ ## v1.0.7 - _August 27, 2018_
* Dependencies updated
## v1.0.6 - _August 24, 2018_
* Dependencies updated
## v1.0.5 - _August 14, 2018_
* Dependencies updated * Dependencies updated
@ -25,7 +33,7 @@ CHANGELOG
* Dependencies updated * Dependencies updated
## v1.0.0 - _July 19, 2018_ ## v1.0.0 - _July 20, 2018_
* Dependencies updated * Dependencies updated
@ -45,7 +53,7 @@ CHANGELOG
* Dependencies updated * Dependencies updated
## v0.0.13 - _May 31, 2018_ ## v0.0.13 - _June 1, 2018_
* Incorrect publish that was unpublished * Incorrect publish that was unpublished
@ -53,7 +61,7 @@ CHANGELOG
* Dependencies updated * Dependencies updated
## v0.0.11 - _May 4, 2018_ ## v0.0.11 - _May 5, 2018_
* Dependencies updated * Dependencies updated

View File

@ -1,6 +1,6 @@
{ {
"name": "@0xproject/react-docs", "name": "@0xproject/react-docs",
"version": "1.0.5", "version": "1.0.7",
"engines": { "engines": {
"node": ">=6.12" "node": ">=6.12"
}, },
@ -24,8 +24,8 @@
"url": "https://github.com/0xProject/0x-monorepo.git" "url": "https://github.com/0xProject/0x-monorepo.git"
}, },
"devDependencies": { "devDependencies": {
"@0xproject/dev-utils": "^1.0.4", "@0xproject/dev-utils": "^1.0.6",
"@0xproject/tslint-config": "^1.0.5", "@0xproject/tslint-config": "^1.0.6",
"@types/compare-versions": "^3.0.0", "@types/compare-versions": "^3.0.0",
"copyfiles": "^2.0.0", "copyfiles": "^2.0.0",
"make-promises-safe": "^1.1.0", "make-promises-safe": "^1.1.0",
@ -34,8 +34,8 @@
"typescript": "3.0.1" "typescript": "3.0.1"
}, },
"dependencies": { "dependencies": {
"@0xproject/react-shared": "^1.0.6", "@0xproject/react-shared": "^1.0.8",
"@0xproject/utils": "^1.0.5", "@0xproject/utils": "^1.0.7",
"@types/lodash": "4.14.104", "@types/lodash": "4.14.104",
"@types/material-ui": "0.18.0", "@types/material-ui": "0.18.0",
"@types/node": "^8.0.53", "@types/node": "^8.0.53",

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