Compare commits
130 Commits
@0xproject
...
@0xproject
Author | SHA1 | Date | |
---|---|---|---|
|
00a4fa5f7c | ||
|
4475fefd07 | ||
|
cd08a9c121 | ||
|
b0c4eb8333 | ||
|
368dbda8f0 | ||
|
bc4149683e | ||
|
6174d9ebb7 | ||
|
e4fc8a8414 | ||
|
907972c466 | ||
|
49f5fe635f | ||
|
77290c1efa | ||
|
4ac43a9fd2 | ||
|
cc77d1dd49 | ||
|
51161784e8 | ||
|
cb7660fbe7 | ||
|
82e51b8787 | ||
|
fffa96bba7 | ||
|
e6cb2e0fcd | ||
|
38abeaed9c | ||
|
90c9e3496a | ||
|
9fc8a6e214 | ||
|
9df87a199a | ||
|
7e9ba50502 | ||
|
41559c39b9 | ||
|
6a6b424c86 | ||
|
3a5c6ed00f | ||
|
db54588d05 | ||
|
52fde551e4 | ||
|
40cf805e5e | ||
|
09d6496135 | ||
|
c4dadf4bfd | ||
|
35ba3e6f7c | ||
|
3ac182ee91 | ||
|
00e7c70b4d | ||
|
0aa9ed3839 | ||
|
d652deea23 | ||
|
878db3b849 | ||
|
ec2e726be0 | ||
|
287830d6e0 | ||
|
c7a7ae7e18 | ||
|
1c7ba6a315 | ||
|
0a6f107243 | ||
|
a93f95c55e | ||
|
6833e243b7 | ||
|
81dc893d1d | ||
|
f8e565bc06 | ||
|
ba15fb6a06 | ||
|
1e6b83719a | ||
|
9fcb2dda73 | ||
|
9a5ec8d030 | ||
|
ac872e5181 | ||
|
70863cca08 | ||
|
5a1dce15be | ||
|
d291256158 | ||
|
8c706ac639 | ||
|
f697814849 | ||
|
ca5c9e77c0 | ||
|
a32b201afe | ||
|
0ecdf1e213 | ||
|
057891b342 | ||
|
407f63ef20 | ||
|
f938c989e3 | ||
|
c8500cab10 | ||
|
c28c3db63f | ||
|
a09ee90739 | ||
|
7d5a23969d | ||
|
56c3c29feb | ||
|
c75212bef0 | ||
|
6d0dedc62c | ||
|
cf12daea2f | ||
|
6f88e9bdbd | ||
|
d8cb56caa3 | ||
|
044415e23d | ||
|
6b866d6053 | ||
|
74ce893f52 | ||
|
cc1fac9bbe | ||
|
94e01be9ed | ||
|
e215992859 | ||
|
e6f5cac878 | ||
|
29971f36cf | ||
|
3e4493b389 | ||
|
749c6ecc30 | ||
|
e6e7bae445 | ||
|
a1d8943552 | ||
|
07e56b3cc7 | ||
|
b16f5f55fb | ||
|
d92fd43791 | ||
|
e706fa76ac | ||
|
11328bd93d | ||
|
bc686fcbf3 | ||
|
80291caf7c | ||
|
cd5e9a5115 | ||
|
82b51db17e | ||
|
3557cd93fc | ||
|
0629a7d143 | ||
|
a27112cbef | ||
|
d039a1adda | ||
|
bb4d449e92 | ||
|
241534a63d | ||
|
1932aff35c | ||
|
4f27991959 | ||
|
8ce4f9c784 | ||
|
7351bf0b14 | ||
|
f6080367fe | ||
|
7f78d7da9d | ||
|
6734f2f1bc | ||
|
0fb7617a78 | ||
|
4219af1430 | ||
|
c109d1f545 | ||
|
50fab9feb3 | ||
|
3dad6ee55e | ||
|
5d70df771b | ||
|
ab5df342e1 | ||
|
6a9669a409 | ||
|
e68942ee78 | ||
|
4159e45aff | ||
|
92497d7df4 | ||
|
070eff6f3a | ||
|
681ed822ec | ||
|
0a1ae2c311 | ||
|
c5f8b9c2d2 | ||
|
7f36574a57 | ||
|
b637ca105a | ||
|
9ffddb47b8 | ||
|
7bcaac4e10 | ||
|
d4592c0a60 | ||
|
1d6699585e | ||
|
a75c298de0 | ||
|
d603d8da47 | ||
|
a551d0a6dd |
@@ -18,11 +18,11 @@ jobs:
|
||||
- yarn-packages-master
|
||||
- yarn-packages-
|
||||
- run:
|
||||
name: yarn
|
||||
command: yarn --frozen-lockfile install || true
|
||||
name: install-yarn
|
||||
command: sudo npm install --global yarn@1.9.4
|
||||
- run:
|
||||
name: yarn
|
||||
command: yarn --frozen-lockfile install
|
||||
command: yarn --frozen-lockfile install || yarn --frozen-lockfile install
|
||||
- save_cache:
|
||||
name: Save Yarn Package Cache
|
||||
key: yarn-packages-{{ .Branch }}-{{ checksum "yarn.lock" }}
|
||||
@@ -254,4 +254,4 @@ workflows:
|
||||
- build
|
||||
- submit-coverage:
|
||||
requires:
|
||||
- test-rest
|
||||
- test-rest
|
||||
|
@@ -15,7 +15,7 @@ lib
|
||||
/packages/contract-wrappers/src/artifacts
|
||||
/packages/order-watcher/src/artifacts
|
||||
/packages/metacoin/artifacts
|
||||
/packages/sra-api/public/
|
||||
/packages/sra-spec/public/
|
||||
/packages/contract-wrappers/test/artifacts
|
||||
/packages/order-watcher/test/artifacts
|
||||
/packages/migrations/artifacts/1.0.0
|
||||
|
@@ -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) | [](https://www.npmjs.com/package/@0xproject/monorepo-scripts) | Monorepo scripts |
|
||||
| [`@0xproject/react-docs`](/packages/react-docs) | [](https://www.npmjs.com/package/@0xproject/react-docs) | React documentation component for rendering TypeDoc & Doxity generated JSON |
|
||||
| [`@0xproject/react-shared`](/packages/react-shared) | [](https://www.npmjs.com/package/@0xproject/react-shared) | 0x shared react components |
|
||||
| [`@0xproject/sra-api`](/packages/sra-api) | [](https://www.npmjs.com/package/@0xproject/sra-api) | OpenAPI specification for the standard relayer API |
|
||||
| [`@0xproject/sra-spec`](/packages/sra-spec) | [](https://www.npmjs.com/package/@0xproject/sra-spec) | OpenAPI specification for the standard relayer API |
|
||||
| [`@0xproject/sra-report`](/packages/sra-report) | [](https://www.npmjs.com/package/@0xproject/sra-report) | Generate reports for standard relayer API compliance |
|
||||
| [`@0xproject/sol-cov`](/packages/sol-cov) | [](https://www.npmjs.com/package/@0xproject/sol-cov) | Solidity test coverage tool |
|
||||
| [`@0xproject/subproviders`](/packages/subproviders) | [](https://www.npmjs.com/package/@0xproject/subproviders) | Useful web3 subproviders (e.g LedgerSubprovider) |
|
||||
|
@@ -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",
|
||||
"changes": [
|
||||
|
@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## 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)
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "0x.js",
|
||||
"version": "1.0.1-rc.4",
|
||||
"version": "1.0.1-rc.6",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -12,8 +12,8 @@
|
||||
"tokens",
|
||||
"exchange"
|
||||
],
|
||||
"main": "lib/src/index.js",
|
||||
"types": "lib/src/index.d.ts",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
"scripts": {
|
||||
"watch_without_deps": "tsc -w",
|
||||
"build": "yarn build:all",
|
||||
@@ -42,10 +42,10 @@
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"devDependencies": {
|
||||
"@0xproject/abi-gen": "^1.0.6",
|
||||
"@0xproject/dev-utils": "^1.0.5",
|
||||
"@0xproject/migrations": "^1.0.5",
|
||||
"@0xproject/monorepo-scripts": "^1.0.6",
|
||||
"@0xproject/abi-gen": "^1.0.7",
|
||||
"@0xproject/dev-utils": "^1.0.6",
|
||||
"@0xproject/migrations": "^1.0.6",
|
||||
"@0xproject/monorepo-scripts": "^1.0.7",
|
||||
"@0xproject/tslint-config": "^1.0.6",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^2.2.42",
|
||||
@@ -73,16 +73,16 @@
|
||||
"webpack": "^3.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0xproject/assert": "^1.0.6",
|
||||
"@0xproject/base-contract": "^2.0.0",
|
||||
"@0xproject/contract-wrappers": "^1.0.1-rc.4",
|
||||
"@0xproject/order-utils": "^1.0.1-rc.4",
|
||||
"@0xproject/order-watcher": "^1.0.1-rc.4",
|
||||
"@0xproject/subproviders": "^2.0.0",
|
||||
"@0xproject/types": "^1.0.1-rc.5",
|
||||
"@0xproject/assert": "^1.0.7",
|
||||
"@0xproject/base-contract": "^2.0.1",
|
||||
"@0xproject/contract-wrappers": "^1.0.1-rc.5",
|
||||
"@0xproject/order-utils": "^1.0.1-rc.6",
|
||||
"@0xproject/order-watcher": "^1.0.1-rc.5",
|
||||
"@0xproject/subproviders": "^2.0.1",
|
||||
"@0xproject/types": "^1.0.1-rc.6",
|
||||
"@0xproject/typescript-typings": "^1.0.5",
|
||||
"@0xproject/utils": "^1.0.6",
|
||||
"@0xproject/web3-wrapper": "^2.0.0",
|
||||
"@0xproject/utils": "^1.0.7",
|
||||
"@0xproject/web3-wrapper": "^2.0.1",
|
||||
"ethereum-types": "^1.0.5",
|
||||
"ethers": "3.0.22",
|
||||
"lodash": "^4.17.5",
|
||||
|
@@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1535377027,
|
||||
"version": "1.0.7",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1535133899,
|
||||
"version": "1.0.6",
|
||||
|
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v1.0.7 - _August 27, 2018_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.6 - _August 24, 2018_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0xproject/abi-gen",
|
||||
"version": "1.0.6",
|
||||
"version": "1.0.7",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -32,7 +32,7 @@
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/packages/abi-gen/README.md",
|
||||
"dependencies": {
|
||||
"@0xproject/typescript-typings": "^1.0.5",
|
||||
"@0xproject/utils": "^1.0.6",
|
||||
"@0xproject/utils": "^1.0.7",
|
||||
"chalk": "^2.3.0",
|
||||
"ethereum-types": "^1.0.5",
|
||||
"glob": "^7.1.2",
|
||||
|
@@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1535377027,
|
||||
"version": "1.0.7",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1535133899,
|
||||
"version": "1.0.6",
|
||||
|
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v1.0.7 - _August 27, 2018_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.6 - _August 24, 2018_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0xproject/assert",
|
||||
"version": "1.0.6",
|
||||
"version": "1.0.7",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -45,9 +45,9 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0xproject/json-schemas": "^1.0.1-rc.5",
|
||||
"@0xproject/json-schemas": "^1.0.1-rc.6",
|
||||
"@0xproject/typescript-typings": "^1.0.5",
|
||||
"@0xproject/utils": "^1.0.6",
|
||||
"@0xproject/utils": "^1.0.7",
|
||||
"lodash": "^4.17.5",
|
||||
"valid-url": "^1.0.9"
|
||||
},
|
||||
|
@@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1535377027,
|
||||
"version": "2.0.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1535133899,
|
||||
"version": "2.0.0",
|
||||
|
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v2.0.1 - _August 27, 2018_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.0.0 - _August 24, 2018_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0xproject/base-contract",
|
||||
"version": "2.0.0",
|
||||
"version": "2.0.1",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -42,8 +42,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@0xproject/typescript-typings": "^1.0.5",
|
||||
"@0xproject/utils": "^1.0.6",
|
||||
"@0xproject/web3-wrapper": "^2.0.0",
|
||||
"@0xproject/utils": "^1.0.7",
|
||||
"@0xproject/web3-wrapper": "^2.0.1",
|
||||
"ethereum-types": "^1.0.5",
|
||||
"ethers": "3.0.22",
|
||||
"lodash": "^4.17.5"
|
||||
|
@@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"version": "2.0.0-rc.2",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
],
|
||||
"timestamp": 1535377027
|
||||
},
|
||||
{
|
||||
"version": "2.0.0-rc.1",
|
||||
"changes": [
|
||||
|
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v2.0.0-rc.2 - _August 27, 2018_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.0.0-rc.1 - _August 24, 2018_
|
||||
|
||||
* Updated for SRA v2 (#974)
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0xproject/connect",
|
||||
"version": "2.0.0-rc.1",
|
||||
"version": "2.0.0-rc.2",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -44,11 +44,11 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/packages/connect/README.md",
|
||||
"dependencies": {
|
||||
"@0xproject/assert": "^1.0.6",
|
||||
"@0xproject/json-schemas": "^1.0.1-rc.5",
|
||||
"@0xproject/types": "^1.0.1-rc.5",
|
||||
"@0xproject/assert": "^1.0.7",
|
||||
"@0xproject/json-schemas": "^1.0.1-rc.6",
|
||||
"@0xproject/types": "^1.0.1-rc.6",
|
||||
"@0xproject/typescript-typings": "^1.0.5",
|
||||
"@0xproject/utils": "^1.0.6",
|
||||
"@0xproject/utils": "^1.0.7",
|
||||
"lodash": "^4.17.5",
|
||||
"query-string": "^5.0.1",
|
||||
"sinon": "^4.0.0",
|
||||
|
@@ -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",
|
||||
"changes": [
|
||||
|
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## 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)
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0xproject/contract-wrappers",
|
||||
"version": "1.0.1-rc.4",
|
||||
"version": "1.0.1-rc.5",
|
||||
"description": "Smart TS wrappers for 0x smart contracts",
|
||||
"keywords": [
|
||||
"0xproject",
|
||||
@@ -44,10 +44,10 @@
|
||||
"node": ">=6.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@0xproject/abi-gen": "^1.0.6",
|
||||
"@0xproject/dev-utils": "^1.0.5",
|
||||
"@0xproject/migrations": "^1.0.5",
|
||||
"@0xproject/subproviders": "^2.0.0",
|
||||
"@0xproject/abi-gen": "^1.0.7",
|
||||
"@0xproject/dev-utils": "^1.0.6",
|
||||
"@0xproject/migrations": "^1.0.6",
|
||||
"@0xproject/subproviders": "^2.0.1",
|
||||
"@0xproject/tslint-config": "^1.0.6",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^2.2.42",
|
||||
@@ -74,15 +74,15 @@
|
||||
"web3-provider-engine": "14.0.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0xproject/assert": "^1.0.6",
|
||||
"@0xproject/base-contract": "^2.0.0",
|
||||
"@0xproject/fill-scenarios": "^1.0.1-rc.4",
|
||||
"@0xproject/json-schemas": "^1.0.1-rc.5",
|
||||
"@0xproject/order-utils": "^1.0.1-rc.4",
|
||||
"@0xproject/types": "^1.0.1-rc.5",
|
||||
"@0xproject/assert": "^1.0.7",
|
||||
"@0xproject/base-contract": "^2.0.1",
|
||||
"@0xproject/fill-scenarios": "^1.0.1-rc.5",
|
||||
"@0xproject/json-schemas": "^1.0.1-rc.6",
|
||||
"@0xproject/order-utils": "^1.0.1-rc.6",
|
||||
"@0xproject/types": "^1.0.1-rc.6",
|
||||
"@0xproject/typescript-typings": "^1.0.5",
|
||||
"@0xproject/utils": "^1.0.6",
|
||||
"@0xproject/web3-wrapper": "^2.0.0",
|
||||
"@0xproject/utils": "^1.0.7",
|
||||
"@0xproject/web3-wrapper": "^2.0.1",
|
||||
"ethereum-types": "^1.0.5",
|
||||
"ethereumjs-blockstream": "5.0.0",
|
||||
"ethereumjs-util": "^5.1.1",
|
||||
|
@@ -1,7 +1,14 @@
|
||||
import { AbiDecoder, intervalUtils, logUtils } from '@0xproject/utils';
|
||||
import { Web3Wrapper } from '@0xproject/web3-wrapper';
|
||||
import { ContractArtifact } from 'ethereum-types';
|
||||
import { BlockParamLiteral, ContractAbi, FilterObject, LogEntry, LogWithDecodedArgs, RawLog } from 'ethereum-types';
|
||||
import {
|
||||
BlockParamLiteral,
|
||||
ContractAbi,
|
||||
ContractArtifact,
|
||||
FilterObject,
|
||||
LogEntry,
|
||||
LogWithDecodedArgs,
|
||||
RawLog,
|
||||
} from 'ethereum-types';
|
||||
import { Block, BlockAndLogStreamer, Log } from 'ethereumjs-blockstream';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
|
@@ -2,7 +2,7 @@
|
||||
// tslint:disable:no-unused-variable
|
||||
// tslint:disable:no-unbound-method
|
||||
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 { Web3Wrapper } from '@0xproject/web3-wrapper';
|
||||
import * as ethers from 'ethers';
|
||||
|
@@ -40,6 +40,7 @@
|
||||
"MultiSigWallet",
|
||||
"MultiSigWalletWithTimeLock",
|
||||
"OrderValidator",
|
||||
"ReentrantERC20Token",
|
||||
"TestAssetProxyOwner",
|
||||
"TestAssetProxyDispatcher",
|
||||
"TestConstants",
|
||||
@@ -47,6 +48,7 @@
|
||||
"TestLibs",
|
||||
"TestExchangeInternals",
|
||||
"TestSignatureValidator",
|
||||
"TestStaticCallReceiver",
|
||||
"TokenRegistry",
|
||||
"Validator",
|
||||
"Wallet",
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "contracts",
|
||||
"version": "2.1.41",
|
||||
"version": "2.1.42",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -33,7 +33,7 @@
|
||||
"lint-contracts": "solhint src/2.0.0/**/**/**/**/*.sol"
|
||||
},
|
||||
"config": {
|
||||
"abis": "../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"
|
||||
"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"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -46,11 +46,11 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/packages/contracts/README.md",
|
||||
"devDependencies": {
|
||||
"@0xproject/abi-gen": "^1.0.6",
|
||||
"@0xproject/dev-utils": "^1.0.5",
|
||||
"@0xproject/sol-compiler": "^1.1.0",
|
||||
"@0xproject/sol-cov": "^2.1.0",
|
||||
"@0xproject/subproviders": "^2.0.0",
|
||||
"@0xproject/abi-gen": "^1.0.7",
|
||||
"@0xproject/dev-utils": "^1.0.6",
|
||||
"@0xproject/sol-compiler": "^1.1.1",
|
||||
"@0xproject/sol-cov": "^2.1.1",
|
||||
"@0xproject/subproviders": "^2.0.1",
|
||||
"@0xproject/tslint-config": "^1.0.6",
|
||||
"@types/bn.js": "^4.11.0",
|
||||
"@types/ethereumjs-abi": "^0.6.0",
|
||||
@@ -73,12 +73,12 @@
|
||||
"yargs": "^10.0.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0xproject/base-contract": "^2.0.0",
|
||||
"@0xproject/order-utils": "^1.0.1-rc.4",
|
||||
"@0xproject/types": "^1.0.1-rc.5",
|
||||
"@0xproject/base-contract": "^2.0.1",
|
||||
"@0xproject/order-utils": "^1.0.1-rc.6",
|
||||
"@0xproject/types": "^1.0.1-rc.6",
|
||||
"@0xproject/typescript-typings": "^1.0.5",
|
||||
"@0xproject/utils": "^1.0.6",
|
||||
"@0xproject/web3-wrapper": "^2.0.0",
|
||||
"@0xproject/utils": "^1.0.7",
|
||||
"@0xproject/web3-wrapper": "^2.0.1",
|
||||
"@types/js-combinatorics": "^0.5.29",
|
||||
"bn.js": "^4.11.8",
|
||||
"ethereum-types": "^1.0.5",
|
||||
|
@@ -37,7 +37,7 @@ contract Whitelist is
|
||||
bytes internal TX_ORIGIN_SIGNATURE;
|
||||
// solhint-enable var-name-mixedcase
|
||||
|
||||
byte constant internal VALIDATOR_SIGNATURE_BYTE = "\x06";
|
||||
byte constant internal VALIDATOR_SIGNATURE_BYTE = "\x05";
|
||||
|
||||
constructor (address _exchange)
|
||||
public
|
||||
|
@@ -163,7 +163,7 @@ contract MixinExchangeWrapper is
|
||||
|
||||
// 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
|
||||
uint256 remainingTakerAssetFillAmount = getPartialAmount(
|
||||
uint256 remainingTakerAssetFillAmount = getPartialAmountFloor(
|
||||
orders[i].takerAssetAmount,
|
||||
orders[i].makerAssetAmount,
|
||||
remainingMakerAssetFillAmount
|
||||
@@ -231,7 +231,7 @@ contract MixinExchangeWrapper is
|
||||
|
||||
// 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.
|
||||
uint256 remainingWethSellAmount = getPartialAmount(
|
||||
uint256 remainingWethSellAmount = getPartialAmountFloor(
|
||||
orders[i].takerAssetAmount,
|
||||
safeSub(orders[i].makerAssetAmount, orders[i].takerFee), // our exchange rate after fees
|
||||
remainingZrxBuyAmount
|
||||
|
@@ -87,7 +87,7 @@ contract MixinForwarderCore is
|
||||
uint256 makerAssetAmountPurchased;
|
||||
if (orders[0].makerAssetData.equals(ZRX_ASSET_DATA)) {
|
||||
// Calculate amount of WETH that won't be spent on ETH fees.
|
||||
wethSellAmount = getPartialAmount(
|
||||
wethSellAmount = getPartialAmountFloor(
|
||||
PERCENTAGE_DENOMINATOR,
|
||||
safeAdd(PERCENTAGE_DENOMINATOR, feePercentage),
|
||||
msg.value
|
||||
@@ -103,7 +103,7 @@ contract MixinForwarderCore is
|
||||
makerAssetAmountPurchased = safeSub(orderFillResults.makerAssetFilledAmount, orderFillResults.takerFeePaid);
|
||||
} else {
|
||||
// 5% of WETH is reserved for filling feeOrders and paying feeRecipient.
|
||||
wethSellAmount = getPartialAmount(
|
||||
wethSellAmount = getPartialAmountFloor(
|
||||
MAX_WETH_FILL_PERCENTAGE,
|
||||
PERCENTAGE_DENOMINATOR,
|
||||
msg.value
|
||||
|
@@ -82,7 +82,7 @@ contract MixinWeth is
|
||||
uint256 wethRemaining = safeSub(msg.value, wethSold);
|
||||
|
||||
// Calculate ETH fee to pay to feeRecipient.
|
||||
uint256 ethFee = getPartialAmount(
|
||||
uint256 ethFee = getPartialAmountFloor(
|
||||
feePercentage,
|
||||
PERCENTAGE_DENOMINATOR,
|
||||
wethSoldExcludingFeeOrders
|
||||
|
@@ -118,6 +118,9 @@ contract ERC20Proxy is
|
||||
mstore(96, 0)
|
||||
revert(0, 100)
|
||||
}
|
||||
|
||||
// Revert if undefined function is called
|
||||
revert(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -152,6 +152,9 @@ contract ERC721Proxy is
|
||||
mstore(96, 0)
|
||||
revert(0, 100)
|
||||
}
|
||||
|
||||
// Revert if undefined function is called
|
||||
revert(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -83,7 +83,7 @@ contract MixinAssetProxyDispatcher is
|
||||
internal
|
||||
{
|
||||
// Do nothing if no amount should be transferred.
|
||||
if (amount > 0) {
|
||||
if (amount > 0 && from != to) {
|
||||
// Ensure assetData length is valid
|
||||
require(
|
||||
assetData.length > 3,
|
||||
|
@@ -19,6 +19,7 @@
|
||||
pragma solidity 0.4.24;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "../../utils/ReentrancyGuard/ReentrancyGuard.sol";
|
||||
import "./libs/LibConstants.sol";
|
||||
import "./libs/LibFillResults.sol";
|
||||
import "./libs/LibOrder.sol";
|
||||
@@ -30,6 +31,7 @@ import "./mixins/MAssetProxyDispatcher.sol";
|
||||
|
||||
|
||||
contract MixinExchangeCore is
|
||||
ReentrancyGuard,
|
||||
LibConstants,
|
||||
LibMath,
|
||||
LibOrder,
|
||||
@@ -54,6 +56,7 @@ contract MixinExchangeCore is
|
||||
/// @param targetOrderEpoch Orders created with a salt less or equal to this value will be cancelled.
|
||||
function cancelOrdersUpTo(uint256 targetOrderEpoch)
|
||||
external
|
||||
nonReentrant
|
||||
{
|
||||
address makerAddress = getCurrentContextAddress();
|
||||
// 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
|
||||
)
|
||||
public
|
||||
nonReentrant
|
||||
returns (FillResults memory fillResults)
|
||||
{
|
||||
// Fetch order info
|
||||
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(
|
||||
fillResults = fillOrderInternal(
|
||||
order,
|
||||
orderInfo,
|
||||
takerAddress,
|
||||
takerAssetFillAmount,
|
||||
takerAssetFilledAmount,
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -131,6 +105,7 @@ contract MixinExchangeCore is
|
||||
/// @param order Order to cancel. Order must be OrderStatus.FILLABLE.
|
||||
function cancelOrder(Order memory order)
|
||||
public
|
||||
nonReentrant
|
||||
{
|
||||
// Fetch current order status
|
||||
OrderInfo memory orderInfo = getOrderInfo(order);
|
||||
@@ -203,6 +178,64 @@ contract MixinExchangeCore is
|
||||
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.
|
||||
/// @param order that was filled.
|
||||
/// @param takerAddress Address of taker who filled the order.
|
||||
@@ -259,20 +292,16 @@ contract MixinExchangeCore is
|
||||
order.takerAssetData
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/// @dev Validates context for fillOrder. Succeeds or throws.
|
||||
/// @param order to be filled.
|
||||
/// @param orderInfo OrderStatus, orderHash, and amount already filled of order.
|
||||
/// @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.
|
||||
function assertValidFill(
|
||||
function assertFillableOrder(
|
||||
Order memory order,
|
||||
OrderInfo memory orderInfo,
|
||||
address takerAddress,
|
||||
uint256 takerAssetFillAmount,
|
||||
uint256 takerAssetFilledAmount,
|
||||
bytes memory signature
|
||||
)
|
||||
internal
|
||||
@@ -283,13 +312,7 @@ contract MixinExchangeCore is
|
||||
orderInfo.orderStatus == uint8(OrderStatus.FILLABLE),
|
||||
"ORDER_UNFILLABLE"
|
||||
);
|
||||
|
||||
// Revert if fill amount is invalid
|
||||
require(
|
||||
takerAssetFillAmount != 0,
|
||||
"INVALID_TAKER_AMOUNT"
|
||||
);
|
||||
|
||||
|
||||
// Validate sender is allowed to fill this order
|
||||
if (order.senderAddress != address(0)) {
|
||||
require(
|
||||
@@ -297,7 +320,7 @@ contract MixinExchangeCore is
|
||||
"INVALID_SENDER"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// Validate taker is allowed to fill this order
|
||||
if (order.takerAddress != address(0)) {
|
||||
require(
|
||||
@@ -305,7 +328,7 @@ contract MixinExchangeCore is
|
||||
"INVALID_TAKER"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// Validate Maker signature (check only if first time seen)
|
||||
if (orderInfo.orderTakerAssetFilledAmount == 0) {
|
||||
require(
|
||||
@@ -317,10 +340,74 @@ contract MixinExchangeCore is
|
||||
"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
|
||||
require(
|
||||
!isRoundingError(
|
||||
!isRoundingErrorFloor(
|
||||
takerAssetFilledAmount,
|
||||
order.takerAssetAmount,
|
||||
order.makerAssetAmount
|
||||
@@ -376,17 +463,17 @@ contract MixinExchangeCore is
|
||||
{
|
||||
// Compute proportional transfer amounts
|
||||
fillResults.takerAssetFilledAmount = takerAssetFilledAmount;
|
||||
fillResults.makerAssetFilledAmount = getPartialAmount(
|
||||
fillResults.makerAssetFilledAmount = getPartialAmountFloor(
|
||||
takerAssetFilledAmount,
|
||||
order.takerAssetAmount,
|
||||
order.makerAssetAmount
|
||||
);
|
||||
fillResults.makerFeePaid = getPartialAmount(
|
||||
fillResults.makerFeePaid = getPartialAmountFloor(
|
||||
takerAssetFilledAmount,
|
||||
order.takerAssetAmount,
|
||||
order.makerFee
|
||||
);
|
||||
fillResults.takerFeePaid = getPartialAmount(
|
||||
fillResults.takerFeePaid = getPartialAmountFloor(
|
||||
takerAssetFilledAmount,
|
||||
order.takerAssetAmount,
|
||||
order.takerFee
|
||||
|
@@ -14,6 +14,7 @@
|
||||
pragma solidity 0.4.24;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "../../utils/ReentrancyGuard/ReentrancyGuard.sol";
|
||||
import "./libs/LibConstants.sol";
|
||||
import "./libs/LibMath.sol";
|
||||
import "./libs/LibOrder.sol";
|
||||
@@ -25,6 +26,7 @@ import "./mixins/MAssetProxyDispatcher.sol";
|
||||
|
||||
|
||||
contract MixinMatchOrders is
|
||||
ReentrancyGuard,
|
||||
LibConstants,
|
||||
LibMath,
|
||||
MAssetProxyDispatcher,
|
||||
@@ -48,6 +50,7 @@ contract MixinMatchOrders is
|
||||
bytes memory rightSignature
|
||||
)
|
||||
public
|
||||
nonReentrant
|
||||
returns (LibFillResults.MatchedFillResults memory matchedFillResults)
|
||||
{
|
||||
// We assume that rightOrder.takerAssetData == leftOrder.makerAssetData and rightOrder.makerAssetData == leftOrder.takerAssetData.
|
||||
@@ -61,8 +64,20 @@ contract MixinMatchOrders is
|
||||
|
||||
// Fetch taker address
|
||||
address takerAddress = getCurrentContextAddress();
|
||||
|
||||
|
||||
// Either our context is valid or we revert
|
||||
assertFillableOrder(
|
||||
leftOrder,
|
||||
leftOrderInfo,
|
||||
takerAddress,
|
||||
leftSignature
|
||||
);
|
||||
assertFillableOrder(
|
||||
rightOrder,
|
||||
rightOrderInfo,
|
||||
takerAddress,
|
||||
rightSignature
|
||||
);
|
||||
assertValidMatch(leftOrder, rightOrder);
|
||||
|
||||
// Compute proportional fill amounts
|
||||
@@ -77,20 +92,18 @@ contract MixinMatchOrders is
|
||||
assertValidFill(
|
||||
leftOrder,
|
||||
leftOrderInfo,
|
||||
takerAddress,
|
||||
matchedFillResults.left.takerAssetFilledAmount,
|
||||
matchedFillResults.left.takerAssetFilledAmount,
|
||||
leftSignature
|
||||
matchedFillResults.left.makerAssetFilledAmount
|
||||
);
|
||||
assertValidFill(
|
||||
rightOrder,
|
||||
rightOrderInfo,
|
||||
takerAddress,
|
||||
matchedFillResults.right.takerAssetFilledAmount,
|
||||
matchedFillResults.right.takerAssetFilledAmount,
|
||||
rightSignature
|
||||
matchedFillResults.right.makerAssetFilledAmount
|
||||
);
|
||||
|
||||
|
||||
// Update exchange state
|
||||
updateFilledState(
|
||||
leftOrder,
|
||||
@@ -106,7 +119,7 @@ contract MixinMatchOrders is
|
||||
rightOrderInfo.orderTakerAssetFilledAmount,
|
||||
matchedFillResults.right
|
||||
);
|
||||
|
||||
|
||||
// Settle matched orders. Succeeds or throws.
|
||||
settleMatchedOrders(
|
||||
leftOrder,
|
||||
@@ -162,62 +175,85 @@ contract MixinMatchOrders is
|
||||
pure
|
||||
returns (LibFillResults.MatchedFillResults memory matchedFillResults)
|
||||
{
|
||||
// We settle orders at the exchange rate of the right order.
|
||||
// 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>
|
||||
// Derive maker asset amounts for left & right orders, given store taker assert amounts
|
||||
uint256 leftTakerAssetAmountRemaining = safeSub(leftOrder.takerAssetAmount, leftOrderTakerAssetFilledAmount);
|
||||
uint256 leftMakerAssetAmountRemaining = getPartialAmountFloor(
|
||||
leftOrder.makerAssetAmount,
|
||||
leftOrder.takerAssetAmount,
|
||||
leftTakerAssetAmountRemaining
|
||||
);
|
||||
uint256 rightTakerAssetAmountRemaining = safeSub(rightOrder.takerAssetAmount, rightOrderTakerAssetFilledAmount);
|
||||
uint256 leftTakerAssetFilledAmount;
|
||||
uint256 rightTakerAssetFilledAmount;
|
||||
if (
|
||||
safeMul(leftTakerAssetAmountRemaining, rightOrder.takerAssetAmount) <=
|
||||
safeMul(rightTakerAssetAmountRemaining, rightOrder.makerAssetAmount)
|
||||
) {
|
||||
// Left order will be fully filled: maximally fill left
|
||||
leftTakerAssetFilledAmount = leftTakerAssetAmountRemaining;
|
||||
uint256 rightMakerAssetAmountRemaining = getPartialAmountFloor(
|
||||
rightOrder.makerAssetAmount,
|
||||
rightOrder.takerAssetAmount,
|
||||
rightTakerAssetAmountRemaining
|
||||
);
|
||||
|
||||
// The right order receives an amount proportional to how much was spent.
|
||||
rightTakerAssetFilledAmount = getPartialAmount(
|
||||
rightOrder.takerAssetAmount,
|
||||
rightOrder.makerAssetAmount,
|
||||
leftTakerAssetFilledAmount
|
||||
// Calculate fill results for maker and taker assets: at least one order will be fully filled.
|
||||
// The maximum amount the left maker can buy is `leftTakerAssetAmountRemaining`
|
||||
// The maximum amount the right maker can sell is `rightMakerAssetAmountRemaining`
|
||||
// We have two distinct cases for calculating the fill results:
|
||||
// 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 {
|
||||
// Right order will be fully filled: maximally fill right
|
||||
rightTakerAssetFilledAmount = rightTakerAssetAmountRemaining;
|
||||
|
||||
// The left order receives an amount proportional to how much was spent.
|
||||
leftTakerAssetFilledAmount = getPartialAmount(
|
||||
rightOrder.makerAssetAmount,
|
||||
// Case 2: Left order is fully filled
|
||||
matchedFillResults.left.makerAssetFilledAmount = leftMakerAssetAmountRemaining;
|
||||
matchedFillResults.left.takerAssetFilledAmount = leftTakerAssetAmountRemaining;
|
||||
matchedFillResults.right.makerAssetFilledAmount = matchedFillResults.left.takerAssetFilledAmount;
|
||||
// Round up 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.right.takerAssetFilledAmount = getPartialAmountCeil(
|
||||
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
|
||||
matchedFillResults.leftMakerAssetSpreadAmount = safeSub(
|
||||
matchedFillResults.left.makerAssetFilledAmount,
|
||||
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 matchedFillResults;
|
||||
}
|
||||
|
@@ -19,6 +19,7 @@
|
||||
pragma solidity 0.4.24;
|
||||
|
||||
import "../../utils/LibBytes/LibBytes.sol";
|
||||
import "../../utils/ReentrancyGuard/ReentrancyGuard.sol";
|
||||
import "./mixins/MSignatureValidator.sol";
|
||||
import "./mixins/MTransactions.sol";
|
||||
import "./interfaces/IWallet.sol";
|
||||
@@ -26,6 +27,7 @@ import "./interfaces/IValidator.sol";
|
||||
|
||||
|
||||
contract MixinSignatureValidator is
|
||||
ReentrancyGuard,
|
||||
MSignatureValidator,
|
||||
MTransactions
|
||||
{
|
||||
@@ -48,14 +50,16 @@ contract MixinSignatureValidator is
|
||||
)
|
||||
external
|
||||
{
|
||||
require(
|
||||
isValidSignature(
|
||||
hash,
|
||||
signerAddress,
|
||||
signature
|
||||
),
|
||||
"INVALID_SIGNATURE"
|
||||
);
|
||||
if (signerAddress != msg.sender) {
|
||||
require(
|
||||
isValidSignature(
|
||||
hash,
|
||||
signerAddress,
|
||||
signature
|
||||
),
|
||||
"INVALID_SIGNATURE"
|
||||
);
|
||||
}
|
||||
preSigned[hash][signerAddress] = true;
|
||||
}
|
||||
|
||||
@@ -67,6 +71,7 @@ contract MixinSignatureValidator is
|
||||
bool approval
|
||||
)
|
||||
external
|
||||
nonReentrant
|
||||
{
|
||||
address signerAddress = getCurrentContextAddress();
|
||||
allowedValidators[signerAddress][validatorAddress] = approval;
|
||||
@@ -172,26 +177,14 @@ contract MixinSignatureValidator is
|
||||
isValid = signerAddress == recovered;
|
||||
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.
|
||||
// If used with an order, the maker of the order is the wallet contract.
|
||||
} else if (signatureType == SignatureType.Wallet) {
|
||||
isValid = IWallet(signerAddress).isValidSignature(hash, signature);
|
||||
isValid = isValidWalletSignature(
|
||||
hash,
|
||||
signerAddress,
|
||||
signature
|
||||
);
|
||||
return isValid;
|
||||
|
||||
// Signature verified by validator contract.
|
||||
@@ -209,7 +202,8 @@ contract MixinSignatureValidator is
|
||||
if (!allowedValidators[signerAddress][validatorAddress]) {
|
||||
return false;
|
||||
}
|
||||
isValid = IValidator(validatorAddress).isValidSignature(
|
||||
isValid = isValidValidatorSignature(
|
||||
validatorAddress,
|
||||
hash,
|
||||
signerAddress,
|
||||
signature
|
||||
@@ -220,34 +214,6 @@ contract MixinSignatureValidator is
|
||||
} else if (signatureType == SignatureType.PreSigned) {
|
||||
isValid = preSigned[hash][signerAddress];
|
||||
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
|
||||
@@ -257,4 +223,102 @@ contract MixinSignatureValidator is
|
||||
// signature was invalid.)
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@@ -155,7 +155,8 @@ contract MixinTransactions is
|
||||
view
|
||||
returns (address)
|
||||
{
|
||||
address contextAddress = currentContextAddress == address(0) ? msg.sender : currentContextAddress;
|
||||
address currentContextAddress_ = currentContextAddress;
|
||||
address contextAddress = currentContextAddress_ == address(0) ? msg.sender : currentContextAddress_;
|
||||
return contextAddress;
|
||||
}
|
||||
}
|
||||
|
@@ -19,18 +19,22 @@
|
||||
pragma solidity 0.4.24;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "../../utils/ReentrancyGuard/ReentrancyGuard.sol";
|
||||
import "./libs/LibMath.sol";
|
||||
import "./libs/LibOrder.sol";
|
||||
import "./libs/LibFillResults.sol";
|
||||
import "./libs/LibAbiEncoder.sol";
|
||||
import "./mixins/MExchangeCore.sol";
|
||||
import "./mixins/MWrapperFunctions.sol";
|
||||
|
||||
|
||||
contract MixinWrapperFunctions is
|
||||
ReentrancyGuard,
|
||||
LibMath,
|
||||
LibFillResults,
|
||||
LibAbiEncoder,
|
||||
MExchangeCore
|
||||
MExchangeCore,
|
||||
MWrapperFunctions
|
||||
{
|
||||
|
||||
/// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled.
|
||||
@@ -43,17 +47,14 @@ contract MixinWrapperFunctions is
|
||||
bytes memory signature
|
||||
)
|
||||
public
|
||||
nonReentrant
|
||||
returns (FillResults memory fillResults)
|
||||
{
|
||||
fillResults = fillOrder(
|
||||
fillResults = fillOrKillOrderInternal(
|
||||
order,
|
||||
takerAssetFillAmount,
|
||||
signature
|
||||
);
|
||||
require(
|
||||
fillResults.takerAssetFilledAmount == takerAssetFillAmount,
|
||||
"COMPLETE_FILL_FAILED"
|
||||
);
|
||||
return fillResults;
|
||||
}
|
||||
|
||||
@@ -88,14 +89,7 @@ contract MixinWrapperFunctions is
|
||||
fillOrderCalldata, // write output over input
|
||||
128 // output size is 128 bytes
|
||||
)
|
||||
switch success
|
||||
case 0 {
|
||||
mstore(fillResults, 0)
|
||||
mstore(add(fillResults, 32), 0)
|
||||
mstore(add(fillResults, 64), 0)
|
||||
mstore(add(fillResults, 96), 0)
|
||||
}
|
||||
case 1 {
|
||||
if success {
|
||||
mstore(fillResults, mload(fillOrderCalldata))
|
||||
mstore(add(fillResults, 32), mload(add(fillOrderCalldata, 32)))
|
||||
mstore(add(fillResults, 64), mload(add(fillOrderCalldata, 64)))
|
||||
@@ -117,11 +111,12 @@ contract MixinWrapperFunctions is
|
||||
bytes[] memory signatures
|
||||
)
|
||||
public
|
||||
nonReentrant
|
||||
returns (FillResults memory totalFillResults)
|
||||
{
|
||||
uint256 ordersLength = orders.length;
|
||||
for (uint256 i = 0; i != ordersLength; i++) {
|
||||
FillResults memory singleFillResults = fillOrder(
|
||||
FillResults memory singleFillResults = fillOrderInternal(
|
||||
orders[i],
|
||||
takerAssetFillAmounts[i],
|
||||
signatures[i]
|
||||
@@ -143,11 +138,12 @@ contract MixinWrapperFunctions is
|
||||
bytes[] memory signatures
|
||||
)
|
||||
public
|
||||
nonReentrant
|
||||
returns (FillResults memory totalFillResults)
|
||||
{
|
||||
uint256 ordersLength = orders.length;
|
||||
for (uint256 i = 0; i != ordersLength; i++) {
|
||||
FillResults memory singleFillResults = fillOrKillOrder(
|
||||
FillResults memory singleFillResults = fillOrKillOrderInternal(
|
||||
orders[i],
|
||||
takerAssetFillAmounts[i],
|
||||
signatures[i]
|
||||
@@ -195,6 +191,7 @@ contract MixinWrapperFunctions is
|
||||
bytes[] memory signatures
|
||||
)
|
||||
public
|
||||
nonReentrant
|
||||
returns (FillResults memory totalFillResults)
|
||||
{
|
||||
bytes memory takerAssetData = orders[0].takerAssetData;
|
||||
@@ -210,7 +207,7 @@ contract MixinWrapperFunctions is
|
||||
uint256 remainingTakerAssetFillAmount = safeSub(takerAssetFillAmount, totalFillResults.takerAssetFilledAmount);
|
||||
|
||||
// Attempt to sell the remaining amount of takerAsset
|
||||
FillResults memory singleFillResults = fillOrder(
|
||||
FillResults memory singleFillResults = fillOrderInternal(
|
||||
orders[i],
|
||||
remainingTakerAssetFillAmount,
|
||||
signatures[i]
|
||||
@@ -282,6 +279,7 @@ contract MixinWrapperFunctions is
|
||||
bytes[] memory signatures
|
||||
)
|
||||
public
|
||||
nonReentrant
|
||||
returns (FillResults memory totalFillResults)
|
||||
{
|
||||
bytes memory makerAssetData = orders[0].makerAssetData;
|
||||
@@ -298,14 +296,14 @@ contract MixinWrapperFunctions is
|
||||
|
||||
// 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
|
||||
uint256 remainingTakerAssetFillAmount = getPartialAmount(
|
||||
uint256 remainingTakerAssetFillAmount = getPartialAmountFloor(
|
||||
orders[i].takerAssetAmount,
|
||||
orders[i].makerAssetAmount,
|
||||
remainingMakerAssetFillAmount
|
||||
);
|
||||
|
||||
// Attempt to sell the remaining amount of takerAsset
|
||||
FillResults memory singleFillResults = fillOrder(
|
||||
FillResults memory singleFillResults = fillOrderInternal(
|
||||
orders[i],
|
||||
remainingTakerAssetFillAmount,
|
||||
signatures[i]
|
||||
@@ -350,7 +348,7 @@ contract MixinWrapperFunctions is
|
||||
|
||||
// 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
|
||||
uint256 remainingTakerAssetFillAmount = getPartialAmount(
|
||||
uint256 remainingTakerAssetFillAmount = getPartialAmountFloor(
|
||||
orders[i].takerAssetAmount,
|
||||
orders[i].makerAssetAmount,
|
||||
remainingMakerAssetFillAmount
|
||||
@@ -400,4 +398,28 @@ contract MixinWrapperFunctions is
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@@ -25,12 +25,12 @@ contract LibMath is
|
||||
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 denominator Denominator.
|
||||
/// @param target Value to calculate partial of.
|
||||
/// @return Partial value of target.
|
||||
function getPartialAmount(
|
||||
/// @return Partial value of target rounded down.
|
||||
function getPartialAmountFloor(
|
||||
uint256 numerator,
|
||||
uint256 denominator,
|
||||
uint256 target
|
||||
@@ -39,19 +39,56 @@ contract LibMath is
|
||||
pure
|
||||
returns (uint256 partialAmount)
|
||||
{
|
||||
require(
|
||||
denominator > 0,
|
||||
"DIVISION_BY_ZERO"
|
||||
);
|
||||
|
||||
partialAmount = safeDiv(
|
||||
safeMul(numerator, target),
|
||||
denominator
|
||||
);
|
||||
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 denominator Denominator.
|
||||
/// @param target Value to multiply with numerator/denominator.
|
||||
/// @return Rounding error is present.
|
||||
function isRoundingError(
|
||||
function isRoundingErrorFloor(
|
||||
uint256 numerator,
|
||||
uint256 denominator,
|
||||
uint256 target
|
||||
@@ -60,16 +97,73 @@ contract LibMath is
|
||||
pure
|
||||
returns (bool isError)
|
||||
{
|
||||
uint256 remainder = mulmod(target, numerator, denominator);
|
||||
if (remainder == 0) {
|
||||
return false; // No rounding error.
|
||||
}
|
||||
|
||||
uint256 errPercentageTimes1000000 = safeDiv(
|
||||
safeMul(remainder, 1000000),
|
||||
safeMul(numerator, target)
|
||||
require(
|
||||
denominator > 0,
|
||||
"DIVISION_BY_ZERO"
|
||||
);
|
||||
isError = errPercentageTimes1000000 > 1000;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Otherwise, we want the relative rounding error to be strictly
|
||||
// less than 0.1%.
|
||||
// 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"
|
||||
);
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
@@ -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.
|
||||
);
|
||||
|
||||
/// @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.
|
||||
/// @param order that was filled.
|
||||
/// @param takerAddress Address of taker who filled the order.
|
||||
@@ -83,21 +96,33 @@ contract MExchangeCore is
|
||||
bytes32 orderHash
|
||||
)
|
||||
internal;
|
||||
|
||||
|
||||
/// @dev Validates context for fillOrder. Succeeds or throws.
|
||||
/// @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 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.
|
||||
function assertValidFill(
|
||||
function assertFillableOrder(
|
||||
LibOrder.Order memory order,
|
||||
LibOrder.OrderInfo memory orderInfo,
|
||||
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 takerAssetFilledAmount,
|
||||
bytes memory signature
|
||||
uint256 makerAssetFilledAmount
|
||||
)
|
||||
internal
|
||||
view;
|
||||
|
@@ -36,11 +36,40 @@ contract MSignatureValidator is
|
||||
Invalid, // 0x01
|
||||
EIP712, // 0x02
|
||||
EthSign, // 0x03
|
||||
Caller, // 0x04
|
||||
Wallet, // 0x05
|
||||
Validator, // 0x06
|
||||
PreSigned, // 0x07
|
||||
Trezor, // 0x08
|
||||
NSignatureTypes // 0x09, number of signature types. Always leave at end.
|
||||
Wallet, // 0x04
|
||||
Validator, // 0x05
|
||||
PreSigned, // 0x06
|
||||
NSignatureTypes // 0x07, 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);
|
||||
}
|
||||
|
40
packages/contracts/src/2.0.0/protocol/Exchange/mixins/MWrapperFunctions.sol
vendored
Normal file
40
packages/contracts/src/2.0.0/protocol/Exchange/mixins/MWrapperFunctions.sol
vendored
Normal 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);
|
||||
}
|
182
packages/contracts/src/2.0.0/test/ReentrantERC20Token/ReentrantERC20Token.sol
vendored
Normal file
182
packages/contracts/src/2.0.0/test/ReentrantERC20Token/ReentrantERC20Token.sol
vendored
Normal 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;
|
||||
}
|
||||
}
|
@@ -67,7 +67,7 @@ contract TestExchangeInternals is
|
||||
/// @param denominator Denominator.
|
||||
/// @param target Value to calculate partial of.
|
||||
/// @return Partial value of target.
|
||||
function publicGetPartialAmount(
|
||||
function publicGetPartialAmountFloor(
|
||||
uint256 numerator,
|
||||
uint256 denominator,
|
||||
uint256 target
|
||||
@@ -76,15 +76,32 @@ contract TestExchangeInternals is
|
||||
pure
|
||||
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 denominator Denominator.
|
||||
/// @param target Value to multiply with numerator/denominator.
|
||||
/// @return Rounding error is present.
|
||||
function publicIsRoundingError(
|
||||
function publicIsRoundingErrorFloor(
|
||||
uint256 numerator,
|
||||
uint256 denominator,
|
||||
uint256 target
|
||||
@@ -93,7 +110,24 @@ contract TestExchangeInternals is
|
||||
pure
|
||||
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.
|
||||
|
@@ -49,7 +49,7 @@ contract TestLibs is
|
||||
return fillOrderCalldata;
|
||||
}
|
||||
|
||||
function publicGetPartialAmount(
|
||||
function publicGetPartialAmountFloor(
|
||||
uint256 numerator,
|
||||
uint256 denominator,
|
||||
uint256 target
|
||||
@@ -58,7 +58,7 @@ contract TestLibs is
|
||||
pure
|
||||
returns (uint256 partialAmount)
|
||||
{
|
||||
partialAmount = getPartialAmount(
|
||||
partialAmount = getPartialAmountFloor(
|
||||
numerator,
|
||||
denominator,
|
||||
target
|
||||
@@ -66,7 +66,24 @@ contract TestLibs is
|
||||
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 denominator,
|
||||
uint256 target
|
||||
@@ -75,7 +92,24 @@ contract TestLibs is
|
||||
pure
|
||||
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,
|
||||
denominator,
|
||||
target
|
||||
|
81
packages/contracts/src/2.0.0/test/TestStaticCallReceiver/TestStaticCallReceiver.sol
vendored
Normal file
81
packages/contracts/src/2.0.0/test/TestStaticCallReceiver/TestStaticCallReceiver.sol
vendored
Normal 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++;
|
||||
}
|
||||
}
|
44
packages/contracts/src/2.0.0/utils/ReentrancyGuard/ReentrancyGuard.sol
vendored
Normal file
44
packages/contracts/src/2.0.0/utils/ReentrancyGuard/ReentrancyGuard.sol
vendored
Normal 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;
|
||||
}
|
||||
}
|
@@ -12,7 +12,7 @@ import { ERC20ProxyContract } from '../../generated_contract_wrappers/erc20_prox
|
||||
import { ERC721ProxyContract } from '../../generated_contract_wrappers/erc721_proxy';
|
||||
import { IAssetProxyContract } from '../../generated_contract_wrappers/i_asset_proxy';
|
||||
import { artifacts } from '../utils/artifacts';
|
||||
import { expectTransactionFailedAsync } from '../utils/assertions';
|
||||
import { expectTransactionFailedAsync, expectTransactionFailedWithoutReasonAsync } from '../utils/assertions';
|
||||
import { chaiSetup } from '../utils/chai_setup';
|
||||
import { constants } from '../utils/constants';
|
||||
import { ERC20Wrapper } from '../utils/erc20_wrapper';
|
||||
@@ -99,6 +99,17 @@ describe('Asset Transfer Proxies', () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
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', () => {
|
||||
it('should successfully transfer tokens', async () => {
|
||||
// Construct ERC20 asset data
|
||||
@@ -219,6 +230,17 @@ describe('Asset Transfer Proxies', () => {
|
||||
});
|
||||
|
||||
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', () => {
|
||||
it('should successfully transfer tokens', async () => {
|
||||
// Construct ERC721 asset data
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import { BlockchainLifecycle } from '@0xproject/dev-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 { Web3Wrapper } from '@0xproject/web3-wrapper';
|
||||
import * as chai from 'chai';
|
||||
@@ -14,6 +14,8 @@ import { DummyNoReturnERC20TokenContract } from '../../generated_contract_wrappe
|
||||
import { ERC20ProxyContract } from '../../generated_contract_wrappers/erc20_proxy';
|
||||
import { ERC721ProxyContract } from '../../generated_contract_wrappers/erc721_proxy';
|
||||
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 { expectTransactionFailedAsync } from '../utils/assertions';
|
||||
import { getLatestBlockTimestampAsync, increaseTimeAndMineBlockAsync } from '../utils/block_timestamp';
|
||||
@@ -41,9 +43,12 @@ describe('Exchange core', () => {
|
||||
let zrxToken: DummyERC20TokenContract;
|
||||
let erc721Token: DummyERC721TokenContract;
|
||||
let noReturnErc20Token: DummyNoReturnERC20TokenContract;
|
||||
let reentrantErc20Token: ReentrantERC20TokenContract;
|
||||
let exchange: ExchangeContract;
|
||||
let erc20Proxy: ERC20ProxyContract;
|
||||
let erc721Proxy: ERC721ProxyContract;
|
||||
let maliciousWallet: TestStaticCallReceiverContract;
|
||||
let maliciousValidator: TestStaticCallReceiverContract;
|
||||
|
||||
let signedOrder: SignedOrder;
|
||||
let erc20Balances: ERC20BalancesByOwner;
|
||||
@@ -109,6 +114,18 @@ describe('Exchange core', () => {
|
||||
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;
|
||||
defaultTakerAssetAddress = erc20TokenB.address;
|
||||
|
||||
@@ -135,6 +152,26 @@ describe('Exchange core', () => {
|
||||
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 () => {
|
||||
signedOrder = await orderFactory.newSignedOrderAsync({
|
||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
||||
@@ -161,6 +198,51 @@ describe('Exchange core', () => {
|
||||
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', () => {
|
||||
@@ -448,7 +530,7 @@ describe('Exchange core', () => {
|
||||
// HACK(albrow): We need to hardcode the gas estimate here because
|
||||
// the Geth gas estimator doesn't work with the way we use
|
||||
// delegatecall and swallow errors.
|
||||
gas: 490000,
|
||||
gas: 600000,
|
||||
});
|
||||
|
||||
const newBalances = await erc20Wrapper.getBalancesAsync();
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import { BlockchainLifecycle } from '@0xproject/dev-utils';
|
||||
import { Order, RevertReason, SignedOrder } from '@0xproject/types';
|
||||
import { BigNumber } from '@0xproject/utils';
|
||||
import * as chai from 'chai';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
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';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
|
||||
const MAX_UINT256 = new BigNumber(2).pow(256).minus(1);
|
||||
@@ -43,26 +46,11 @@ const emptySignedOrder: SignedOrder = {
|
||||
|
||||
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', () => {
|
||||
let testExchange: TestExchangeInternalsContract;
|
||||
let invalidOpcodeErrorForCall: Error | undefined;
|
||||
let overflowErrorForSendTransaction: Error | undefined;
|
||||
let divisionByZeroErrorForCall: Error | undefined;
|
||||
|
||||
before(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
@@ -79,11 +67,29 @@ describe('Exchange core internal functions', () => {
|
||||
overflowErrorForSendTransaction = new Error(
|
||||
await getRevertReasonOrErrorMessageForSendTransactionAsync(RevertReason.Uint256Overflow),
|
||||
);
|
||||
divisionByZeroErrorForCall = new Error(
|
||||
await getRevertReasonOrErrorMessageForSendTransactionAsync(RevertReason.DivisionByZero),
|
||||
);
|
||||
invalidOpcodeErrorForCall = new Error(await getInvalidOpcodeErrorMessageForCallAsync());
|
||||
});
|
||||
// Note(albrow): Don't forget to add beforeEach and afterEach calls to reset
|
||||
// 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 () => {
|
||||
function makeFillResults(value: BigNumber): FillResults {
|
||||
return {
|
||||
@@ -159,18 +165,18 @@ describe('Exchange core internal functions', () => {
|
||||
// implementation or the Solidity implementation of
|
||||
// calculateFillResults.
|
||||
return {
|
||||
makerAssetFilledAmount: await referenceGetPartialAmountAsync(
|
||||
makerAssetFilledAmount: await referenceGetPartialAmountFloorAsync(
|
||||
takerAssetFilledAmount,
|
||||
orderTakerAssetAmount,
|
||||
otherAmount,
|
||||
),
|
||||
takerAssetFilledAmount,
|
||||
makerFeePaid: await referenceGetPartialAmountAsync(
|
||||
makerFeePaid: await referenceGetPartialAmountFloorAsync(
|
||||
takerAssetFilledAmount,
|
||||
orderTakerAssetAmount,
|
||||
otherAmount,
|
||||
),
|
||||
takerFeePaid: await referenceGetPartialAmountAsync(
|
||||
takerFeePaid: await referenceGetPartialAmountFloorAsync(
|
||||
takerAssetFilledAmount,
|
||||
orderTakerAssetAmount,
|
||||
otherAmount,
|
||||
@@ -193,18 +199,55 @@ describe('Exchange core internal functions', () => {
|
||||
);
|
||||
});
|
||||
|
||||
describe('getPartialAmount', async () => {
|
||||
async function testGetPartialAmountAsync(
|
||||
describe('getPartialAmountFloor', async () => {
|
||||
async function testGetPartialAmountFloorAsync(
|
||||
numerator: BigNumber,
|
||||
denominator: BigNumber,
|
||||
target: BigNumber,
|
||||
): Promise<BigNumber> {
|
||||
return testExchange.publicGetPartialAmount.callAsync(numerator, denominator, target);
|
||||
return testExchange.publicGetPartialAmountFloor.callAsync(numerator, denominator, target);
|
||||
}
|
||||
await testCombinatoriallyWithReferenceFuncAsync(
|
||||
'getPartialAmount',
|
||||
referenceGetPartialAmountAsync,
|
||||
testGetPartialAmountAsync,
|
||||
referenceGetPartialAmountFloorAsync,
|
||||
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],
|
||||
);
|
||||
});
|
||||
@@ -215,33 +258,33 @@ describe('Exchange core internal functions', () => {
|
||||
denominator: BigNumber,
|
||||
target: BigNumber,
|
||||
): Promise<boolean> {
|
||||
const product = numerator.mul(target);
|
||||
if (denominator.eq(0)) {
|
||||
throw invalidOpcodeErrorForCall;
|
||||
throw divisionByZeroErrorForCall;
|
||||
}
|
||||
const remainder = product.mod(denominator);
|
||||
if (remainder.eq(0)) {
|
||||
if (numerator.eq(0)) {
|
||||
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)) {
|
||||
throw overflowErrorForCall;
|
||||
}
|
||||
if (product.eq(0)) {
|
||||
throw invalidOpcodeErrorForCall;
|
||||
}
|
||||
const remainderTimes1000000 = remainder.mul('1000000');
|
||||
if (remainderTimes1000000.greaterThan(MAX_UINT256)) {
|
||||
if (remainderTimes1000.greaterThan(MAX_UINT256)) {
|
||||
throw overflowErrorForCall;
|
||||
}
|
||||
const errPercentageTimes1000000 = remainderTimes1000000.dividedToIntegerBy(product);
|
||||
return errPercentageTimes1000000.greaterThan('1000');
|
||||
return isError;
|
||||
}
|
||||
async function testIsRoundingErrorAsync(
|
||||
numerator: BigNumber,
|
||||
denominator: BigNumber,
|
||||
target: BigNumber,
|
||||
): Promise<boolean> {
|
||||
return testExchange.publicIsRoundingError.callAsync(numerator, denominator, target);
|
||||
return testExchange.publicIsRoundingErrorFloor.callAsync(numerator, denominator, target);
|
||||
}
|
||||
await testCombinatoriallyWithReferenceFuncAsync(
|
||||
'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 () => {
|
||||
// Note(albrow): Since updateFilledState modifies the state by calling
|
||||
// sendTransaction, we must reset the state after each test.
|
||||
|
@@ -71,29 +71,57 @@ describe('Exchange libs', () => {
|
||||
// combinatorial tests in test/exchange/internal. They test specific edge
|
||||
// cases that are not covered by the combinatorial tests.
|
||||
describe('LibMath', () => {
|
||||
it('should return false if there is a rounding error of 0.1%', async () => {
|
||||
const numerator = new BigNumber(20);
|
||||
const denominator = new BigNumber(999);
|
||||
const target = new BigNumber(50);
|
||||
// rounding error = ((20*50/999) - floor(20*50/999)) / (20*50/999) = 0.1%
|
||||
const isRoundingError = await libs.publicIsRoundingError.callAsync(numerator, denominator, target);
|
||||
expect(isRoundingError).to.be.false();
|
||||
describe('isRoundingError', () => {
|
||||
it('should return true if there is a rounding error of 0.1%', async () => {
|
||||
const numerator = new BigNumber(20);
|
||||
const denominator = new BigNumber(999);
|
||||
const target = new BigNumber(50);
|
||||
// rounding error = ((20*50/999) - floor(20*50/999)) / (20*50/999) = 0.1%
|
||||
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 () => {
|
||||
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.publicIsRoundingError.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.publicIsRoundingError.callAsync(numerator, denominator, target);
|
||||
expect(isRoundingError).to.be.true();
|
||||
describe('isRoundingErrorCeil', () => {
|
||||
it('should return true if there is a rounding error of 0.1%', async () => {
|
||||
const numerator = new BigNumber(20);
|
||||
const denominator = new BigNumber(1001);
|
||||
const target = new BigNumber(50);
|
||||
// rounding error = (ceil(20*50/1001) - (20*50/1001)) / (20*50/1001) = 0.1%
|
||||
const isRoundingError = await libs.publicIsRoundingErrorCeil.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(10009);
|
||||
const target = new BigNumber(500);
|
||||
// rounding error = (ceil(20*500/10009) - (20*500/10009)) / (20*500/10009) = 0.09%
|
||||
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
@@ -9,11 +9,12 @@ import {
|
||||
TestSignatureValidatorContract,
|
||||
TestSignatureValidatorSignatureValidatorApprovalEventArgs,
|
||||
} 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 { WalletContract } from '../../generated_contract_wrappers/wallet';
|
||||
import { addressUtils } from '../utils/address_utils';
|
||||
import { artifacts } from '../utils/artifacts';
|
||||
import { expectContractCallFailed } from '../utils/assertions';
|
||||
import { expectContractCallFailed, expectContractCallFailedWithoutReasonAsync } from '../utils/assertions';
|
||||
import { chaiSetup } from '../utils/chai_setup';
|
||||
import { constants } from '../utils/constants';
|
||||
import { LogDecoder } from '../utils/log_decoder';
|
||||
@@ -31,6 +32,8 @@ describe('MixinSignatureValidator', () => {
|
||||
let signatureValidator: TestSignatureValidatorContract;
|
||||
let testWallet: WalletContract;
|
||||
let testValidator: ValidatorContract;
|
||||
let maliciousWallet: TestStaticCallReceiverContract;
|
||||
let maliciousValidator: TestStaticCallReceiverContract;
|
||||
let signerAddress: string;
|
||||
let signerPrivateKey: Buffer;
|
||||
let notSignerAddress: string;
|
||||
@@ -65,6 +68,11 @@ describe('MixinSignatureValidator', () => {
|
||||
txDefaults,
|
||||
signerAddress,
|
||||
);
|
||||
maliciousWallet = maliciousValidator = await TestStaticCallReceiverContract.deployFrom0xArtifactAsync(
|
||||
artifacts.TestStaticCallReceiver,
|
||||
provider,
|
||||
txDefaults,
|
||||
);
|
||||
signatureValidatorLogDecoder = new LogDecoder(web3Wrapper);
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await signatureValidator.setSignatureValidatorApproval.sendTransactionAsync(testValidator.address, true, {
|
||||
@@ -72,6 +80,16 @@ describe('MixinSignatureValidator', () => {
|
||||
}),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await signatureValidator.setSignatureValidatorApproval.sendTransactionAsync(
|
||||
maliciousValidator.address,
|
||||
true,
|
||||
{
|
||||
from: signerAddress,
|
||||
},
|
||||
),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
|
||||
const defaultOrderParams = {
|
||||
...constants.STATIC_ORDER_PARAMS,
|
||||
@@ -263,32 +281,6 @@ describe('MixinSignatureValidator', () => {
|
||||
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 () => {
|
||||
// Create EIP712 signature
|
||||
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
|
||||
@@ -334,6 +326,29 @@ describe('MixinSignatureValidator', () => {
|
||||
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 () => {
|
||||
const validatorAddress = ethUtil.toBuffer(`${testValidator.address}`);
|
||||
const signatureType = ethUtil.toBuffer(`0x${SignatureType.Validator}`);
|
||||
@@ -364,6 +379,17 @@ describe('MixinSignatureValidator', () => {
|
||||
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 () => {
|
||||
// Set approval of signature validator to false
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
@@ -388,53 +414,6 @@ describe('MixinSignatureValidator', () => {
|
||||
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 () => {
|
||||
// Presign hash
|
||||
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
|
||||
@@ -468,6 +447,42 @@ describe('MixinSignatureValidator', () => {
|
||||
);
|
||||
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', () => {
|
||||
|
@@ -11,6 +11,7 @@ import { DummyERC721TokenContract } from '../../generated_contract_wrappers/dumm
|
||||
import { ERC20ProxyContract } from '../../generated_contract_wrappers/erc20_proxy';
|
||||
import { ERC721ProxyContract } from '../../generated_contract_wrappers/erc721_proxy';
|
||||
import { ExchangeContract } from '../../generated_contract_wrappers/exchange';
|
||||
import { ReentrantERC20TokenContract } from '../../generated_contract_wrappers/reentrant_erc20_token';
|
||||
import { artifacts } from '../utils/artifacts';
|
||||
import { expectTransactionFailedAsync } from '../utils/assertions';
|
||||
import { getLatestBlockTimestampAsync, increaseTimeAndMineBlockAsync } from '../utils/block_timestamp';
|
||||
@@ -40,6 +41,7 @@ describe('Exchange wrappers', () => {
|
||||
let exchange: ExchangeContract;
|
||||
let erc20Proxy: ERC20ProxyContract;
|
||||
let erc721Proxy: ERC721ProxyContract;
|
||||
let reentrantErc20Token: ReentrantERC20TokenContract;
|
||||
|
||||
let exchangeWrapper: ExchangeWrapper;
|
||||
let erc20Wrapper: ERC20Wrapper;
|
||||
@@ -104,6 +106,13 @@ describe('Exchange wrappers', () => {
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
|
||||
reentrantErc20Token = await ReentrantERC20TokenContract.deployFrom0xArtifactAsync(
|
||||
artifacts.ReentrantERC20Token,
|
||||
provider,
|
||||
txDefaults,
|
||||
exchange.address,
|
||||
);
|
||||
|
||||
defaultMakerAssetAddress = erc20TokenA.address;
|
||||
defaultTakerAssetAddress = erc20TokenB.address;
|
||||
|
||||
@@ -126,6 +135,26 @@ describe('Exchange wrappers', () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
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 () => {
|
||||
const signedOrder = await orderFactory.newSignedOrderAsync({
|
||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
|
||||
@@ -197,6 +226,25 @@ describe('Exchange wrappers', () => {
|
||||
});
|
||||
|
||||
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 () => {
|
||||
const signedOrder = await orderFactory.newSignedOrderAsync({
|
||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
|
||||
@@ -397,6 +445,26 @@ describe('Exchange wrappers', () => {
|
||||
});
|
||||
|
||||
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 () => {
|
||||
const takerAssetFillAmounts: BigNumber[] = [];
|
||||
const makerAssetAddress = erc20TokenA.address;
|
||||
@@ -446,6 +514,26 @@ describe('Exchange wrappers', () => {
|
||||
});
|
||||
|
||||
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 () => {
|
||||
const takerAssetFillAmounts: BigNumber[] = [];
|
||||
const makerAssetAddress = erc20TokenA.address;
|
||||
@@ -512,6 +600,25 @@ describe('Exchange wrappers', () => {
|
||||
});
|
||||
|
||||
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 () => {
|
||||
const takerAssetFillAmounts: BigNumber[] = [];
|
||||
const makerAssetAddress = erc20TokenA.address;
|
||||
@@ -625,6 +732,28 @@ describe('Exchange wrappers', () => {
|
||||
});
|
||||
|
||||
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 () => {
|
||||
const takerAssetFillAmount = signedOrders[0].takerAssetAmount.plus(
|
||||
signedOrders[1].takerAssetAmount.div(2),
|
||||
@@ -717,6 +846,27 @@ describe('Exchange wrappers', () => {
|
||||
});
|
||||
|
||||
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 () => {
|
||||
const takerAssetFillAmount = signedOrders[0].takerAssetAmount.plus(
|
||||
signedOrders[1].takerAssetAmount.div(2),
|
||||
@@ -843,6 +993,28 @@ describe('Exchange wrappers', () => {
|
||||
});
|
||||
|
||||
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 () => {
|
||||
const makerAssetFillAmount = signedOrders[0].makerAssetAmount.plus(
|
||||
signedOrders[1].makerAssetAmount.div(2),
|
||||
@@ -933,6 +1105,27 @@ describe('Exchange wrappers', () => {
|
||||
});
|
||||
|
||||
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 () => {
|
||||
const makerAssetFillAmount = signedOrders[0].makerAssetAmount.plus(
|
||||
signedOrders[1].makerAssetAmount.div(2),
|
||||
|
@@ -16,6 +16,7 @@ import * as MixinAuthorizable from '../../artifacts/MixinAuthorizable.json';
|
||||
import * as MultiSigWallet from '../../artifacts/MultiSigWallet.json';
|
||||
import * as MultiSigWalletWithTimeLock from '../../artifacts/MultiSigWalletWithTimeLock.json';
|
||||
import * as OrderValidator from '../../artifacts/OrderValidator.json';
|
||||
import * as ReentrantERC20Token from '../../artifacts/ReentrantERC20Token.json';
|
||||
import * as TestAssetProxyDispatcher from '../../artifacts/TestAssetProxyDispatcher.json';
|
||||
import * as TestAssetProxyOwner from '../../artifacts/TestAssetProxyOwner.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 TestLibs from '../../artifacts/TestLibs.json';
|
||||
import * as TestSignatureValidator from '../../artifacts/TestSignatureValidator.json';
|
||||
import * as TestStaticCallReceiver from '../../artifacts/TestStaticCallReceiver.json';
|
||||
import * as TokenRegistry from '../../artifacts/TokenRegistry.json';
|
||||
import * as Validator from '../../artifacts/Validator.json';
|
||||
import * as Wallet from '../../artifacts/Wallet.json';
|
||||
@@ -48,6 +50,7 @@ export const artifacts = {
|
||||
MultiSigWallet: (MultiSigWallet as any) as ContractArtifact,
|
||||
MultiSigWalletWithTimeLock: (MultiSigWalletWithTimeLock as any) as ContractArtifact,
|
||||
OrderValidator: (OrderValidator as any) as ContractArtifact,
|
||||
ReentrantERC20Token: (ReentrantERC20Token as any) as ContractArtifact,
|
||||
TestAssetProxyOwner: (TestAssetProxyOwner as any) as ContractArtifact,
|
||||
TestAssetProxyDispatcher: (TestAssetProxyDispatcher as any) as ContractArtifact,
|
||||
TestConstants: (TestConstants as any) as ContractArtifact,
|
||||
@@ -55,6 +58,7 @@ export const artifacts = {
|
||||
TestLibs: (TestLibs as any) as ContractArtifact,
|
||||
TestExchangeInternals: (TestExchangeInternals as any) as ContractArtifact,
|
||||
TestSignatureValidator: (TestSignatureValidator as any) as ContractArtifact,
|
||||
TestStaticCallReceiver: (TestStaticCallReceiver as any) as ContractArtifact,
|
||||
Validator: (Validator as any) as ContractArtifact,
|
||||
Wallet: (Wallet as any) as ContractArtifact,
|
||||
TokenRegistry: (TokenRegistry as any) as ContractArtifact,
|
||||
|
@@ -51,4 +51,16 @@ export const constants = {
|
||||
WORD_LENGTH: 32,
|
||||
ZERO_AMOUNT: new BigNumber(0),
|
||||
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',
|
||||
],
|
||||
};
|
||||
|
@@ -467,17 +467,17 @@ export class FillOrderCombinatorialUtils {
|
||||
? remainingTakerAmountToFill
|
||||
: alreadyFilledTakerAmount.add(takerAssetFillAmount);
|
||||
|
||||
const expFilledMakerAmount = orderUtils.getPartialAmount(
|
||||
const expFilledMakerAmount = orderUtils.getPartialAmountFloor(
|
||||
expFilledTakerAmount,
|
||||
signedOrder.takerAssetAmount,
|
||||
signedOrder.makerAssetAmount,
|
||||
);
|
||||
const expMakerFeePaid = orderUtils.getPartialAmount(
|
||||
const expMakerFeePaid = orderUtils.getPartialAmountFloor(
|
||||
expFilledTakerAmount,
|
||||
signedOrder.takerAssetAmount,
|
||||
signedOrder.makerFee,
|
||||
);
|
||||
const expTakerFeePaid = orderUtils.getPartialAmount(
|
||||
const expTakerFeePaid = orderUtils.getPartialAmountFloor(
|
||||
expFilledTakerAmount,
|
||||
signedOrder.takerAssetAmount,
|
||||
signedOrder.takerFee,
|
||||
@@ -668,7 +668,7 @@ export class FillOrderCombinatorialUtils {
|
||||
signedOrder: SignedOrder,
|
||||
takerAssetFillAmount: BigNumber,
|
||||
): Promise<void> {
|
||||
const makerAssetFillAmount = orderUtils.getPartialAmount(
|
||||
const makerAssetFillAmount = orderUtils.getPartialAmountFloor(
|
||||
takerAssetFillAmount,
|
||||
signedOrder.takerAssetAmount,
|
||||
signedOrder.makerAssetAmount,
|
||||
@@ -705,7 +705,7 @@ export class FillOrderCombinatorialUtils {
|
||||
);
|
||||
}
|
||||
|
||||
const makerFee = orderUtils.getPartialAmount(
|
||||
const makerFee = orderUtils.getPartialAmountFloor(
|
||||
takerAssetFillAmount,
|
||||
signedOrder.takerAssetAmount,
|
||||
signedOrder.makerFee,
|
||||
@@ -829,7 +829,7 @@ export class FillOrderCombinatorialUtils {
|
||||
);
|
||||
}
|
||||
|
||||
const takerFee = orderUtils.getPartialAmount(
|
||||
const takerFee = orderUtils.getPartialAmountFloor(
|
||||
takerAssetFillAmount,
|
||||
signedOrder.takerAssetAmount,
|
||||
signedOrder.takerFee,
|
||||
|
@@ -4,11 +4,20 @@ import { BigNumber } from '@0xproject/utils';
|
||||
import * as chai from 'chai';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { TransactionReceiptWithDecodedLogs } from '../../../../node_modules/ethereum-types';
|
||||
|
||||
import { chaiSetup } from './chai_setup';
|
||||
import { ERC20Wrapper } from './erc20_wrapper';
|
||||
import { ERC721Wrapper } from './erc721_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();
|
||||
const expect = chai.expect;
|
||||
@@ -18,43 +27,107 @@ export class MatchOrderTester {
|
||||
private readonly _erc20Wrapper: ERC20Wrapper;
|
||||
private readonly _erc721Wrapper: ERC721Wrapper;
|
||||
private readonly _feeTokenAddress: string;
|
||||
|
||||
/// @dev Compares a pair of ERC20 balances and a pair of ERC721 token owners.
|
||||
/// @param expectedNewERC20BalancesByOwner Expected ERC20 balances.
|
||||
/// @param realERC20BalancesByOwner Actual ERC20 balances.
|
||||
/// @param expectedNewERC721TokenIdsByOwner Expected ERC721 token owners.
|
||||
/// @param realERC721TokenIdsByOwner Actual ERC20 token owners.
|
||||
/// @return True only if ERC20 balances match and ERC721 token owners match.
|
||||
private static _compareExpectedAndRealBalances(
|
||||
expectedNewERC20BalancesByOwner: ERC20BalancesByOwner,
|
||||
realERC20BalancesByOwner: ERC20BalancesByOwner,
|
||||
expectedNewERC721TokenIdsByOwner: ERC721TokenIdsByOwner,
|
||||
realERC721TokenIdsByOwner: ERC721TokenIdsByOwner,
|
||||
): boolean {
|
||||
// ERC20 Balances
|
||||
const doesErc20BalancesMatch = _.isEqual(expectedNewERC20BalancesByOwner, realERC20BalancesByOwner);
|
||||
if (!doesErc20BalancesMatch) {
|
||||
return false;
|
||||
}
|
||||
// ERC721 Token Ids
|
||||
const sortedExpectedNewERC721TokenIdsByOwner = _.mapValues(
|
||||
expectedNewERC721TokenIdsByOwner,
|
||||
tokenIdsByOwner => {
|
||||
_.mapValues(tokenIdsByOwner, tokenIds => {
|
||||
_.sortBy(tokenIds);
|
||||
});
|
||||
},
|
||||
/// @dev Checks values from the logs produced by Exchange.matchOrders against the expected transfer amounts.
|
||||
/// Values include the amounts transferred from the left/right makers and taker, along with
|
||||
/// the fees paid on each matched order. These are also the return values of MatchOrders.
|
||||
/// @param signedOrderLeft First matched order.
|
||||
/// @param signedOrderRight Second matched order.
|
||||
/// @param transactionReceipt Transaction receipt and logs produced by Exchange.matchOrders.
|
||||
/// @param takerAddress Address of taker (account that called Exchange.matchOrders)
|
||||
/// @param expectedTransferAmounts Expected amounts transferred as a result of order matching.
|
||||
private static async _assertLogsAsync(
|
||||
signedOrderLeft: SignedOrder,
|
||||
signedOrderRight: SignedOrder,
|
||||
transactionReceipt: TransactionReceiptWithDecodedLogs,
|
||||
takerAddress: string,
|
||||
expectedTransferAmounts: TransferAmounts,
|
||||
): Promise<void> {
|
||||
// Should have two fill event logs -- one for each order.
|
||||
const transactionFillLogs = _.filter(transactionReceipt.logs, ['event', 'Fill']);
|
||||
expect(transactionFillLogs.length, 'Checking number of logs').to.be.equal(2);
|
||||
// First log is for left fill
|
||||
const leftLog = (transactionFillLogs[0] as any).args as LoggedTransferAmounts;
|
||||
expect(leftLog.makerAddress, 'Checking logged maker address of left order').to.be.equal(
|
||||
signedOrderLeft.makerAddress,
|
||||
);
|
||||
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 => {
|
||||
_.mapValues(tokenIdsByOwner, tokenIds => {
|
||||
_.sortBy(tokenIds);
|
||||
});
|
||||
});
|
||||
const doesErc721TokenIdsMatch = _.isEqual(
|
||||
const areERC721TokenIdsEqual = _.isEqual(
|
||||
sortedExpectedNewERC721TokenIdsByOwner,
|
||||
sortedNewERC721TokenIdsByOwner,
|
||||
);
|
||||
return doesErc721TokenIdsMatch;
|
||||
expect(areERC721TokenIdsEqual, 'Checking all known ERC721 account balances').to.be.true();
|
||||
}
|
||||
/// @dev Constructs new MatchOrderTester.
|
||||
/// @param exchangeWrapper Used to call to the Exchange.
|
||||
@@ -72,150 +145,199 @@ export class MatchOrderTester {
|
||||
this._erc721Wrapper = erc721Wrapper;
|
||||
this._feeTokenAddress = feeTokenAddress;
|
||||
}
|
||||
/// @dev Matches two complementary orders and validates results.
|
||||
/// Validation either succeeds or throws.
|
||||
/// @dev Matches two complementary orders and asserts results.
|
||||
/// @param signedOrderLeft First matched order.
|
||||
/// @param signedOrderRight Second matched order.
|
||||
/// @param takerAddress Address of taker (the address who matched the two orders)
|
||||
/// @param erc20BalancesByOwner Current ERC20 balances.
|
||||
/// @param erc721TokenIdsByOwner Current ERC721 token owners.
|
||||
/// @param initialTakerAssetFilledAmountLeft Current amount the left order has been filled.
|
||||
/// @param initialTakerAssetFilledAmountRight Current amount the right order has been filled.
|
||||
/// @param expectedTransferAmounts Expected amounts transferred as a result of order matching.
|
||||
/// @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.
|
||||
public async matchOrdersAndVerifyBalancesAsync(
|
||||
public async matchOrdersAndAssertEffectsAsync(
|
||||
signedOrderLeft: SignedOrder,
|
||||
signedOrderRight: SignedOrder,
|
||||
takerAddress: string,
|
||||
erc20BalancesByOwner: ERC20BalancesByOwner,
|
||||
erc721TokenIdsByOwner: ERC721TokenIdsByOwner,
|
||||
initialTakerAssetFilledAmountLeft?: BigNumber,
|
||||
initialTakerAssetFilledAmountRight?: BigNumber,
|
||||
expectedTransferAmounts: TransferAmounts,
|
||||
initialLeftOrderFilledAmount: BigNumber = new BigNumber(0),
|
||||
initialRightOrderFilledAmount: BigNumber = new BigNumber(0),
|
||||
): 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(
|
||||
orderHashUtils.getOrderHashHex(signedOrderLeft),
|
||||
);
|
||||
const expectedOrderFilledAmountLeft = initialTakerAssetFilledAmountLeft
|
||||
? initialTakerAssetFilledAmountLeft
|
||||
: new BigNumber(0);
|
||||
expect(expectedOrderFilledAmountLeft).to.be.bignumber.equal(orderTakerAssetFilledAmountLeft);
|
||||
// Verify Right order preconditions
|
||||
expect(orderTakerAssetFilledAmountLeft, 'Checking inital state of left order').to.be.bignumber.equal(
|
||||
expectedOrderFilledAmountLeft,
|
||||
);
|
||||
// Assert right order initial state
|
||||
const orderTakerAssetFilledAmountRight = await this._exchangeWrapper.getTakerAssetFilledAmountAsync(
|
||||
orderHashUtils.getOrderHashHex(signedOrderRight),
|
||||
);
|
||||
const expectedOrderFilledAmountRight = initialTakerAssetFilledAmountRight
|
||||
? initialTakerAssetFilledAmountRight
|
||||
: 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,
|
||||
expect(orderTakerAssetFilledAmountRight, 'Checking inital state of right order').to.be.bignumber.equal(
|
||||
expectedOrderFilledAmountRight,
|
||||
);
|
||||
}
|
||||
/// @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 expectedERC721TokenIdsByOwner: ERC721TokenIdsByOwner;
|
||||
[expectedERC20BalancesByOwner, expectedERC721TokenIdsByOwner] = this._calculateExpectedBalances(
|
||||
signedOrderLeft,
|
||||
signedOrderRight,
|
||||
takerAddress,
|
||||
erc20BalancesByOwner,
|
||||
erc721TokenIdsByOwner,
|
||||
initialERC20BalancesByOwner,
|
||||
initialERC721TokenIdsByOwner,
|
||||
expectedTransferAmounts,
|
||||
);
|
||||
// Assert our expected balances are equal to the actual balances
|
||||
const didExpectedBalancesMatchRealBalances = MatchOrderTester._compareExpectedAndRealBalances(
|
||||
// Assert balances of makers, taker, and fee recipients
|
||||
await this._assertMakerTakerAndFeeRecipientBalancesAsync(
|
||||
signedOrderLeft,
|
||||
signedOrderRight,
|
||||
expectedERC20BalancesByOwner,
|
||||
newERC20BalancesByOwner,
|
||||
finalERC20BalancesByOwner,
|
||||
expectedERC721TokenIdsByOwner,
|
||||
newERC721TokenIdsByOwner,
|
||||
finalERC721TokenIdsByOwner,
|
||||
takerAddress,
|
||||
);
|
||||
expect(didExpectedBalancesMatchRealBalances).to.be.true();
|
||||
return [newERC20BalancesByOwner, newERC721TokenIdsByOwner];
|
||||
}
|
||||
/// @dev Calculates expected transfer amounts between order makers, fee recipients, and
|
||||
/// the taker when two orders are matched.
|
||||
/// @param signedOrderLeft First matched order.
|
||||
/// @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),
|
||||
// Assert balances for all known accounts
|
||||
await MatchOrderTester._assertAllKnownBalancesAsync(
|
||||
expectedERC20BalancesByOwner,
|
||||
finalERC20BalancesByOwner,
|
||||
expectedERC721TokenIdsByOwner,
|
||||
finalERC721TokenIdsByOwner,
|
||||
);
|
||||
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,
|
||||
/// as a result of matching two orders.
|
||||
/// @param signedOrderLeft First matched order.
|
||||
/// @param signedOrderRight First matched order.
|
||||
/// @param signedOrderRight Second matched order.
|
||||
/// @param takerAddress Address of taker (the address who matched the two orders)
|
||||
/// @param erc20BalancesByOwner Current ERC20 balances.
|
||||
/// @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.
|
||||
private _calculateExpectedBalances(
|
||||
signedOrderLeft: SignedOrder,
|
||||
@@ -247,7 +369,7 @@ export class MatchOrderTester {
|
||||
expectedNewERC20BalancesByOwner[makerAddressRight][
|
||||
takerAssetAddressRight
|
||||
] = expectedNewERC20BalancesByOwner[makerAddressRight][takerAssetAddressRight].add(
|
||||
expectedTransferAmounts.amountReceivedByRightMaker,
|
||||
expectedTransferAmounts.amountBoughtByRightMaker,
|
||||
);
|
||||
// Taker
|
||||
expectedNewERC20BalancesByOwner[takerAddress][makerAssetAddressLeft] = expectedNewERC20BalancesByOwner[
|
||||
@@ -277,7 +399,7 @@ export class MatchOrderTester {
|
||||
// Left Maker
|
||||
expectedNewERC20BalancesByOwner[makerAddressLeft][takerAssetAddressLeft] = expectedNewERC20BalancesByOwner[
|
||||
makerAddressLeft
|
||||
][takerAssetAddressLeft].add(expectedTransferAmounts.amountReceivedByLeftMaker);
|
||||
][takerAssetAddressLeft].add(expectedTransferAmounts.amountBoughtByLeftMaker);
|
||||
// Right Maker
|
||||
expectedNewERC20BalancesByOwner[makerAddressRight][
|
||||
makerAssetAddressRight
|
||||
@@ -307,20 +429,138 @@ export class MatchOrderTester {
|
||||
// Taker Fees
|
||||
expectedNewERC20BalancesByOwner[takerAddress][this._feeTokenAddress] = expectedNewERC20BalancesByOwner[
|
||||
takerAddress
|
||||
][this._feeTokenAddress].minus(expectedTransferAmounts.totalFeePaidByTaker);
|
||||
][this._feeTokenAddress].minus(
|
||||
expectedTransferAmounts.feePaidByTakerLeft.add(expectedTransferAmounts.feePaidByTakerRight),
|
||||
);
|
||||
// Left Fee Recipient Fees
|
||||
expectedNewERC20BalancesByOwner[feeRecipientAddressLeft][
|
||||
this._feeTokenAddress
|
||||
] = expectedNewERC20BalancesByOwner[feeRecipientAddressLeft][this._feeTokenAddress].add(
|
||||
expectedTransferAmounts.feeReceivedLeft,
|
||||
expectedTransferAmounts.feePaidByLeftMaker.add(expectedTransferAmounts.feePaidByTakerLeft),
|
||||
);
|
||||
// Right Fee Recipient Fees
|
||||
expectedNewERC20BalancesByOwner[feeRecipientAddressRight][
|
||||
this._feeTokenAddress
|
||||
] = expectedNewERC20BalancesByOwner[feeRecipientAddressRight][this._feeTokenAddress].add(
|
||||
expectedTransferAmounts.feeReceivedRight,
|
||||
expectedTransferAmounts.feePaidByRightMaker.add(expectedTransferAmounts.feePaidByTakerRight),
|
||||
);
|
||||
|
||||
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
|
||||
|
@@ -5,7 +5,7 @@ import { constants } from './constants';
|
||||
import { CancelOrder, MatchOrder } from './types';
|
||||
|
||||
export const orderUtils = {
|
||||
getPartialAmount(numerator: BigNumber, denominator: BigNumber, target: BigNumber): BigNumber {
|
||||
getPartialAmountFloor(numerator: BigNumber, denominator: BigNumber, target: BigNumber): BigNumber {
|
||||
const partialAmount = numerator
|
||||
.mul(target)
|
||||
.div(denominator)
|
||||
|
@@ -117,21 +117,24 @@ export interface TransferAmountsByMatchOrders {
|
||||
// Left Maker
|
||||
amountBoughtByLeftMaker: BigNumber;
|
||||
amountSoldByLeftMaker: BigNumber;
|
||||
amountReceivedByLeftMaker: BigNumber;
|
||||
feePaidByLeftMaker: BigNumber;
|
||||
// Right Maker
|
||||
amountBoughtByRightMaker: BigNumber;
|
||||
amountSoldByRightMaker: BigNumber;
|
||||
amountReceivedByRightMaker: BigNumber;
|
||||
feePaidByRightMaker: BigNumber;
|
||||
// Taker
|
||||
amountReceivedByTaker: BigNumber;
|
||||
feePaidByTakerLeft: BigNumber;
|
||||
feePaidByTakerRight: BigNumber;
|
||||
totalFeePaidByTaker: BigNumber;
|
||||
// Fee Recipients
|
||||
feeReceivedLeft: BigNumber;
|
||||
feeReceivedRight: BigNumber;
|
||||
}
|
||||
|
||||
export interface TransferAmountsLoggedByMatchOrders {
|
||||
makerAddress: string;
|
||||
takerAddress: string;
|
||||
makerAssetFilledAmount: string;
|
||||
takerAssetFilledAmount: string;
|
||||
makerFeePaid: string;
|
||||
takerFeePaid: string;
|
||||
}
|
||||
|
||||
export interface OrderInfo {
|
||||
|
@@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1535377027,
|
||||
"version": "1.0.6",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1535133899,
|
||||
"version": "1.0.5",
|
||||
|
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v1.0.6 - _August 27, 2018_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.5 - _August 24, 2018_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0xproject/dev-utils",
|
||||
"version": "1.0.5",
|
||||
"version": "1.0.6",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -43,11 +43,11 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0xproject/subproviders": "^2.0.0",
|
||||
"@0xproject/types": "^1.0.1-rc.5",
|
||||
"@0xproject/subproviders": "^2.0.1",
|
||||
"@0xproject/types": "^1.0.1-rc.6",
|
||||
"@0xproject/typescript-typings": "^1.0.5",
|
||||
"@0xproject/utils": "^1.0.6",
|
||||
"@0xproject/web3-wrapper": "^2.0.0",
|
||||
"@0xproject/utils": "^1.0.7",
|
||||
"@0xproject/web3-wrapper": "^2.0.1",
|
||||
"ethereum-types": "^1.0.5",
|
||||
"lodash": "^4.17.5"
|
||||
},
|
||||
|
@@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"version": "1.0.1-rc.5",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
],
|
||||
"timestamp": 1535377027
|
||||
},
|
||||
{
|
||||
"version": "1.0.1-rc.4",
|
||||
"changes": [
|
||||
|
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v1.0.1-rc.5 - _August 27, 2018_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.1-rc.4 - _August 24, 2018_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0xproject/fill-scenarios",
|
||||
"version": "1.0.1-rc.4",
|
||||
"version": "1.0.1-rc.5",
|
||||
"description": "0x order fill scenario generator",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
@@ -27,7 +27,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/packages/fill-scenarios/README.md",
|
||||
"devDependencies": {
|
||||
"@0xproject/abi-gen": "^1.0.6",
|
||||
"@0xproject/abi-gen": "^1.0.7",
|
||||
"@0xproject/tslint-config": "^1.0.6",
|
||||
"@types/lodash": "4.14.104",
|
||||
"copyfiles": "^2.0.0",
|
||||
@@ -38,12 +38,12 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0xproject/base-contract": "^2.0.0",
|
||||
"@0xproject/order-utils": "^1.0.1-rc.4",
|
||||
"@0xproject/types": "^1.0.1-rc.5",
|
||||
"@0xproject/base-contract": "^2.0.1",
|
||||
"@0xproject/order-utils": "^1.0.1-rc.6",
|
||||
"@0xproject/types": "^1.0.1-rc.6",
|
||||
"@0xproject/typescript-typings": "^1.0.5",
|
||||
"@0xproject/utils": "^1.0.6",
|
||||
"@0xproject/web3-wrapper": "^2.0.0",
|
||||
"@0xproject/utils": "^1.0.7",
|
||||
"@0xproject/web3-wrapper": "^2.0.1",
|
||||
"ethereum-types": "^1.0.5",
|
||||
"ethers": "3.0.22",
|
||||
"lodash": "^4.17.5"
|
||||
|
@@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"version": "1.0.1-rc.2",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
],
|
||||
"timestamp": 1535377027
|
||||
},
|
||||
{
|
||||
"version": "1.0.1-rc.1",
|
||||
"changes": [
|
||||
|
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v1.0.1-rc.2 - _August 27, 2018_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.1-rc.1 - _August 24, 2018_
|
||||
|
||||
* Add initial forwarderHelperFactory (#997)
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0xproject/forwarder-helper",
|
||||
"version": "1.0.1-rc.1",
|
||||
"version": "1.0.1-rc.2",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -39,12 +39,12 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/packages/forwarder-helper/README.md",
|
||||
"dependencies": {
|
||||
"@0xproject/assert": "^1.0.6",
|
||||
"@0xproject/json-schemas": "^1.0.1-rc.5",
|
||||
"@0xproject/order-utils": "^1.0.1-rc.4",
|
||||
"@0xproject/types": "^1.0.1-rc.5",
|
||||
"@0xproject/assert": "^1.0.7",
|
||||
"@0xproject/json-schemas": "^1.0.1-rc.6",
|
||||
"@0xproject/order-utils": "^1.0.1-rc.6",
|
||||
"@0xproject/types": "^1.0.1-rc.6",
|
||||
"@0xproject/typescript-typings": "^1.0.5",
|
||||
"@0xproject/utils": "^1.0.6",
|
||||
"@0xproject/utils": "^1.0.7",
|
||||
"@types/node": "^8.0.53",
|
||||
"lodash": "^4.17.10"
|
||||
},
|
||||
|
@@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"version": "1.0.1-rc.6",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
],
|
||||
"timestamp": 1535377027
|
||||
},
|
||||
{
|
||||
"version": "1.0.1-rc.5",
|
||||
"changes": [
|
||||
|
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## 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)
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0xproject/json-schemas",
|
||||
"version": "1.0.1-rc.5",
|
||||
"version": "1.0.1-rc.6",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -46,7 +46,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@0xproject/tslint-config": "^1.0.6",
|
||||
"@0xproject/utils": "^1.0.6",
|
||||
"@0xproject/utils": "^1.0.7",
|
||||
"@types/lodash.foreach": "^4.5.3",
|
||||
"@types/lodash.values": "^4.3.3",
|
||||
"@types/mocha": "^2.2.42",
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0xproject/metacoin",
|
||||
"version": "0.0.16",
|
||||
"version": "0.0.17",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -29,15 +29,15 @@
|
||||
"author": "",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@0xproject/abi-gen": "^1.0.6",
|
||||
"@0xproject/base-contract": "^2.0.0",
|
||||
"@0xproject/sol-cov": "^2.1.0",
|
||||
"@0xproject/subproviders": "^2.0.0",
|
||||
"@0xproject/abi-gen": "^1.0.7",
|
||||
"@0xproject/base-contract": "^2.0.1",
|
||||
"@0xproject/sol-cov": "^2.1.1",
|
||||
"@0xproject/subproviders": "^2.0.1",
|
||||
"@0xproject/tslint-config": "^1.0.6",
|
||||
"@0xproject/types": "^1.0.1-rc.5",
|
||||
"@0xproject/types": "^1.0.1-rc.6",
|
||||
"@0xproject/typescript-typings": "^1.0.5",
|
||||
"@0xproject/utils": "^1.0.6",
|
||||
"@0xproject/web3-wrapper": "^2.0.0",
|
||||
"@0xproject/utils": "^1.0.7",
|
||||
"@0xproject/web3-wrapper": "^2.0.1",
|
||||
"@types/mocha": "^5.2.2",
|
||||
"copyfiles": "^2.0.0",
|
||||
"ethereum-types": "^1.0.5",
|
||||
@@ -46,8 +46,8 @@
|
||||
"run-s": "^0.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@0xproject/dev-utils": "^1.0.5",
|
||||
"@0xproject/sol-compiler": "^1.1.0",
|
||||
"@0xproject/dev-utils": "^1.0.6",
|
||||
"@0xproject/sol-compiler": "^1.1.1",
|
||||
"chai": "^4.0.1",
|
||||
"chai-as-promised": "^7.1.0",
|
||||
"chai-bignumber": "^2.0.1",
|
||||
|
@@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1535377027,
|
||||
"version": "1.0.6",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1535133899,
|
||||
"version": "1.0.5",
|
||||
|
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v1.0.6 - _August 27, 2018_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.5 - _August 24, 2018_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -782,5 +782,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"networks": {}
|
||||
"networks": {
|
||||
"50": {
|
||||
"address": "0xe86bb98fcf9bff3512c74589b78fb168200cc546",
|
||||
"links": {},
|
||||
"constructorArgs": "[\"0x48bacb9266a570d521063ef5dd96e61686dbe788\",\"0xf47261b0000000000000000000000000871dd7c2b4b25e1aa18728e9d5f2af4c4e431f5c\"]"
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0xproject/migrations",
|
||||
"version": "1.0.5",
|
||||
"version": "1.0.6",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -35,10 +35,10 @@
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"devDependencies": {
|
||||
"@0xproject/abi-gen": "^1.0.6",
|
||||
"@0xproject/dev-utils": "^1.0.5",
|
||||
"@0xproject/abi-gen": "^1.0.7",
|
||||
"@0xproject/dev-utils": "^1.0.6",
|
||||
"@0xproject/tslint-config": "^1.0.6",
|
||||
"@0xproject/types": "^1.0.1-rc.5",
|
||||
"@0xproject/types": "^1.0.1-rc.6",
|
||||
"@types/yargs": "^10.0.0",
|
||||
"copyfiles": "^2.0.0",
|
||||
"make-promises-safe": "^1.1.0",
|
||||
@@ -49,13 +49,13 @@
|
||||
"yargs": "^10.0.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0xproject/base-contract": "^2.0.0",
|
||||
"@0xproject/order-utils": "^1.0.1-rc.4",
|
||||
"@0xproject/sol-compiler": "^1.1.0",
|
||||
"@0xproject/subproviders": "^2.0.0",
|
||||
"@0xproject/base-contract": "^2.0.1",
|
||||
"@0xproject/order-utils": "^1.0.1-rc.6",
|
||||
"@0xproject/sol-compiler": "^1.1.1",
|
||||
"@0xproject/subproviders": "^2.0.1",
|
||||
"@0xproject/typescript-typings": "^1.0.5",
|
||||
"@0xproject/utils": "^1.0.6",
|
||||
"@0xproject/web3-wrapper": "^2.0.0",
|
||||
"@0xproject/utils": "^1.0.7",
|
||||
"@0xproject/web3-wrapper": "^2.0.1",
|
||||
"@ledgerhq/hw-app-eth": "^4.3.0",
|
||||
"ethereum-types": "^1.0.5",
|
||||
"ethers": "3.0.22",
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "@0xproject/monorepo-scripts",
|
||||
"version": "1.0.6",
|
||||
"version": "1.0.7",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
|
@@ -74,9 +74,11 @@ async function confirmAsync(message: string): Promise<void> {
|
||||
});
|
||||
utils.log(`Calling 'lerna publish'...`);
|
||||
await lernaPublishAsync(packageToNextVersion);
|
||||
const isStaging = false;
|
||||
const shouldUploadDocs = !configs.IS_LOCAL_PUBLISH;
|
||||
await generateAndUploadDocJsonsAsync(packagesWithDocs, isStaging, shouldUploadDocs);
|
||||
if (!configs.IS_LOCAL_PUBLISH) {
|
||||
const isStaging = false;
|
||||
const shouldUploadDocs = true;
|
||||
await generateAndUploadDocJsonsAsync(packagesWithDocs, isStaging, shouldUploadDocs);
|
||||
}
|
||||
const isDryRun = configs.IS_LOCAL_PUBLISH;
|
||||
await publishReleaseNotesAsync(updatedPublicPackages, isDryRun);
|
||||
})().catch(err => {
|
||||
|
@@ -85,11 +85,13 @@ function logIfDefined(x: any): void {
|
||||
logIfDefined(packageError.error.stdout);
|
||||
logIfDefined(packageError.error.stack);
|
||||
});
|
||||
process.exit(1);
|
||||
} else {
|
||||
process.exit(0);
|
||||
}
|
||||
})().catch(err => {
|
||||
utils.log(`Unexpected error: ${err.message}`);
|
||||
process.exit(0);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
async function testInstallPackageAsync(
|
||||
@@ -144,7 +146,7 @@ async function testInstallPackageAsync(
|
||||
const transpiledIndexFilePath = path.join(testDirectory, 'index.js');
|
||||
utils.log(`Running test script with ${packageName} imported`);
|
||||
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);
|
||||
}
|
||||
|
@@ -19,11 +19,6 @@ CHANGELOG
|
||||
|
||||
export const changelogUtils = {
|
||||
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 title = `\n## v${versionChangelog.version} - _${date}_\n\n`;
|
||||
return title;
|
||||
|
@@ -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",
|
||||
"changes": [
|
||||
|
@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## 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)
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0xproject/order-utils",
|
||||
"version": "1.0.1-rc.4",
|
||||
"version": "1.0.1-rc.6",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -40,7 +40,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/packages/order-utils/README.md",
|
||||
"devDependencies": {
|
||||
"@0xproject/dev-utils": "^1.0.5",
|
||||
"@0xproject/dev-utils": "^1.0.6",
|
||||
"@0xproject/tslint-config": "^1.0.6",
|
||||
"@types/bn.js": "^4.11.0",
|
||||
"@types/lodash": "4.14.104",
|
||||
@@ -59,13 +59,13 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0xproject/assert": "^1.0.6",
|
||||
"@0xproject/base-contract": "^2.0.0",
|
||||
"@0xproject/json-schemas": "^1.0.1-rc.5",
|
||||
"@0xproject/types": "^1.0.1-rc.5",
|
||||
"@0xproject/assert": "^1.0.7",
|
||||
"@0xproject/base-contract": "^2.0.1",
|
||||
"@0xproject/json-schemas": "^1.0.1-rc.6",
|
||||
"@0xproject/types": "^1.0.1-rc.6",
|
||||
"@0xproject/typescript-typings": "^1.0.5",
|
||||
"@0xproject/utils": "^1.0.6",
|
||||
"@0xproject/web3-wrapper": "^2.0.0",
|
||||
"@0xproject/utils": "^1.0.7",
|
||||
"@0xproject/web3-wrapper": "^2.0.1",
|
||||
"@types/node": "^8.0.53",
|
||||
"bn.js": "^4.11.8",
|
||||
"ethereum-types": "^1.0.5",
|
||||
|
@@ -81,7 +81,7 @@ export class OrderStateUtils {
|
||||
const remainingTakerAssetAmount = signedOrder.takerAssetAmount.minus(
|
||||
sidedOrderRelevantState.filledTakerAssetAmount,
|
||||
);
|
||||
const isRoundingError = OrderValidationUtils.isRoundingError(
|
||||
const isRoundingError = OrderValidationUtils.isRoundingErrorFloor(
|
||||
remainingTakerAssetAmount,
|
||||
signedOrder.takerAssetAmount,
|
||||
signedOrder.makerAssetAmount,
|
||||
@@ -191,7 +191,7 @@ export class OrderStateUtils {
|
||||
);
|
||||
const remainingFillableTakerAssetAmountGivenMakersStatus = signedOrder.makerAssetAmount.eq(0)
|
||||
? new BigNumber(0)
|
||||
: utils.getPartialAmount(
|
||||
: utils.getPartialAmountFloor(
|
||||
orderRelevantMakerState.remainingFillableAssetAmount,
|
||||
signedOrder.makerAssetAmount,
|
||||
signedOrder.takerAssetAmount,
|
||||
|
@@ -24,7 +24,7 @@ export class OrderValidationUtils {
|
||||
* @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`
|
||||
*/
|
||||
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
|
||||
// Source: https://solidity.readthedocs.io/en/latest/units-and-global-variables.html#mathematical-and-cryptographic-functions
|
||||
if (denominator.eq(0)) {
|
||||
@@ -58,7 +58,7 @@ export class OrderValidationUtils {
|
||||
zrxAssetData: string,
|
||||
): Promise<void> {
|
||||
try {
|
||||
const fillMakerTokenAmount = utils.getPartialAmount(
|
||||
const fillMakerTokenAmount = utils.getPartialAmountFloor(
|
||||
fillTakerAssetAmount,
|
||||
signedOrder.takerAssetAmount,
|
||||
signedOrder.makerAssetAmount,
|
||||
@@ -79,7 +79,7 @@ export class OrderValidationUtils {
|
||||
TradeSide.Taker,
|
||||
TransferType.Trade,
|
||||
);
|
||||
const makerFeeAmount = utils.getPartialAmount(
|
||||
const makerFeeAmount = utils.getPartialAmountFloor(
|
||||
fillTakerAssetAmount,
|
||||
signedOrder.takerAssetAmount,
|
||||
signedOrder.makerFee,
|
||||
@@ -92,7 +92,7 @@ export class OrderValidationUtils {
|
||||
TradeSide.Maker,
|
||||
TransferType.Fee,
|
||||
);
|
||||
const takerFeeAmount = utils.getPartialAmount(
|
||||
const takerFeeAmount = utils.getPartialAmountFloor(
|
||||
fillTakerAssetAmount,
|
||||
signedOrder.takerAssetAmount,
|
||||
signedOrder.takerFee,
|
||||
@@ -218,7 +218,7 @@ export class OrderValidationUtils {
|
||||
zrxAssetData,
|
||||
);
|
||||
|
||||
const wouldRoundingErrorOccur = OrderValidationUtils.isRoundingError(
|
||||
const wouldRoundingErrorOccur = OrderValidationUtils.isRoundingErrorFloor(
|
||||
desiredFillTakerTokenAmount,
|
||||
signedOrder.takerAssetAmount,
|
||||
signedOrder.makerAssetAmount,
|
||||
|
@@ -53,11 +53,6 @@ export const signatureUtils = {
|
||||
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: {
|
||||
const isValid = await signatureUtils.isValidWalletSignatureAsync(
|
||||
provider,
|
||||
@@ -82,12 +77,6 @@ export const signatureUtils = {
|
||||
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:
|
||||
throw new Error(`Unhandled SignatureType: ${signatureTypeIndexIfExists}`);
|
||||
}
|
||||
@@ -293,10 +282,6 @@ export const signatureUtils = {
|
||||
signatureType = SignatureType.EthSign;
|
||||
break;
|
||||
}
|
||||
case SignerType.Trezor: {
|
||||
signatureType = SignatureType.Trezor;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new Error(`Unrecognized SignerType: ${signerType}`);
|
||||
}
|
||||
@@ -306,7 +291,7 @@ export const signatureUtils = {
|
||||
/**
|
||||
* Combines the signature proof and the Signature Type.
|
||||
* @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
|
||||
*/
|
||||
convertToSignatureWithType(signature: string, signatureType: SignatureType): string {
|
||||
@@ -333,12 +318,6 @@ export const signatureUtils = {
|
||||
const prefixedMsgHex = ethUtil.bufferToHex(prefixedMsgBuff);
|
||||
return prefixedMsgHex;
|
||||
}
|
||||
case SignerType.Trezor: {
|
||||
const msgBuff = ethUtil.toBuffer(message);
|
||||
const prefixedMsgBuff = hashTrezorPersonalMessage(msgBuff);
|
||||
const prefixedMsgHex = ethUtil.bufferToHex(prefixedMsgBuff);
|
||||
return prefixedMsgHex;
|
||||
}
|
||||
default:
|
||||
throw new Error(`Unrecognized SignerType: ${signerType}`);
|
||||
}
|
||||
@@ -350,7 +329,7 @@ export const signatureUtils = {
|
||||
*/
|
||||
parseECSignature(signature: string): ECSignature {
|
||||
assert.isHexString('signature', signature);
|
||||
const ecSignatureTypes = [SignatureType.EthSign, SignatureType.EIP712, SignatureType.Trezor];
|
||||
const ecSignatureTypes = [SignatureType.EthSign, SignatureType.EIP712];
|
||||
assert.isOneOfExpectedSignatureTypes(signature, ecSignatureTypes);
|
||||
|
||||
// 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 {
|
||||
assert.isOneOfExpectedSignatureTypes(signature, [SignatureType.Validator]);
|
||||
// tslint:disable:custom-no-magic-numbers
|
||||
|
@@ -12,7 +12,7 @@ export const utils = {
|
||||
const milisecondsInSecond = 1000;
|
||||
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
|
||||
.mul(target)
|
||||
.div(denominator)
|
||||
|
@@ -16,7 +16,7 @@ describe('OrderValidationUtils', () => {
|
||||
const denominator = new BigNumber(999);
|
||||
const target = new BigNumber(50);
|
||||
// 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();
|
||||
});
|
||||
|
||||
@@ -25,7 +25,7 @@ describe('OrderValidationUtils', () => {
|
||||
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 = OrderValidationUtils.isRoundingError(numerator, denominator, target);
|
||||
const isRoundingError = OrderValidationUtils.isRoundingErrorFloor(numerator, denominator, target);
|
||||
expect(isRoundingError).to.be.false();
|
||||
});
|
||||
|
||||
@@ -34,7 +34,7 @@ describe('OrderValidationUtils', () => {
|
||||
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 = OrderValidationUtils.isRoundingError(numerator, denominator, target);
|
||||
const isRoundingError = OrderValidationUtils.isRoundingErrorFloor(numerator, denominator, target);
|
||||
expect(isRoundingError).to.be.true();
|
||||
});
|
||||
|
||||
@@ -43,7 +43,7 @@ describe('OrderValidationUtils', () => {
|
||||
const denominator = new BigNumber(7);
|
||||
const target = new BigNumber(10);
|
||||
// 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();
|
||||
});
|
||||
|
||||
@@ -52,7 +52,7 @@ describe('OrderValidationUtils', () => {
|
||||
const denominator = new BigNumber(2);
|
||||
const target = new BigNumber(10);
|
||||
|
||||
const isRoundingError = OrderValidationUtils.isRoundingError(numerator, denominator, target);
|
||||
const isRoundingError = OrderValidationUtils.isRoundingErrorFloor(numerator, denominator, target);
|
||||
expect(isRoundingError).to.be.false();
|
||||
});
|
||||
|
||||
@@ -63,7 +63,7 @@ describe('OrderValidationUtils', () => {
|
||||
const target = new BigNumber(105762562);
|
||||
// rounding error = ((76564*105762562/676373677) - floor(76564*105762562/676373677)) /
|
||||
// (76564*105762562/676373677) = 0.0007%
|
||||
const isRoundingError = OrderValidationUtils.isRoundingError(numerator, denominator, target);
|
||||
const isRoundingError = OrderValidationUtils.isRoundingErrorFloor(numerator, denominator, target);
|
||||
expect(isRoundingError).to.be.false();
|
||||
});
|
||||
});
|
||||
|
@@ -76,20 +76,6 @@ describe('Signature utils', () => {
|
||||
);
|
||||
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', () => {
|
||||
const signature = {
|
||||
@@ -270,15 +256,6 @@ describe('Signature utils', () => {
|
||||
r: '0xaca7da997ad177f040240cdccf6905b71ab16b74434388c3a72f34fd25d64393',
|
||||
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 () => {
|
||||
const expectedSignatureWithSignatureType =
|
||||
'0x1baca7da997ad177f040240cdccf6905b71ab16b74434388c3a72f34fd25d6439346b2bac274ff29b48b3ea6e2d04c1336eaceafda3c53ab483fc3ff12fac3ebf203';
|
||||
|
@@ -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",
|
||||
"changes": [
|
||||
|
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## 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)
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0xproject/order-watcher",
|
||||
"version": "1.0.1-rc.4",
|
||||
"version": "1.0.1-rc.5",
|
||||
"description": "An order watcher daemon that watches for order validity",
|
||||
"keywords": [
|
||||
"0x",
|
||||
@@ -43,9 +43,9 @@
|
||||
"node": ">=6.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@0xproject/abi-gen": "^1.0.6",
|
||||
"@0xproject/dev-utils": "^1.0.5",
|
||||
"@0xproject/migrations": "^1.0.5",
|
||||
"@0xproject/abi-gen": "^1.0.7",
|
||||
"@0xproject/dev-utils": "^1.0.6",
|
||||
"@0xproject/migrations": "^1.0.6",
|
||||
"@0xproject/tslint-config": "^1.0.6",
|
||||
"@types/bintrees": "^1.0.2",
|
||||
"@types/lodash": "4.14.104",
|
||||
@@ -71,16 +71,16 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0xproject/assert": "^1.0.6",
|
||||
"@0xproject/base-contract": "^2.0.0",
|
||||
"@0xproject/contract-wrappers": "^1.0.1-rc.4",
|
||||
"@0xproject/fill-scenarios": "^1.0.1-rc.4",
|
||||
"@0xproject/json-schemas": "^1.0.1-rc.5",
|
||||
"@0xproject/order-utils": "^1.0.1-rc.4",
|
||||
"@0xproject/types": "^1.0.1-rc.5",
|
||||
"@0xproject/assert": "^1.0.7",
|
||||
"@0xproject/base-contract": "^2.0.1",
|
||||
"@0xproject/contract-wrappers": "^1.0.1-rc.5",
|
||||
"@0xproject/fill-scenarios": "^1.0.1-rc.5",
|
||||
"@0xproject/json-schemas": "^1.0.1-rc.6",
|
||||
"@0xproject/order-utils": "^1.0.1-rc.6",
|
||||
"@0xproject/types": "^1.0.1-rc.6",
|
||||
"@0xproject/typescript-typings": "^1.0.5",
|
||||
"@0xproject/utils": "^1.0.6",
|
||||
"@0xproject/web3-wrapper": "^2.0.0",
|
||||
"@0xproject/utils": "^1.0.7",
|
||||
"@0xproject/web3-wrapper": "^2.0.1",
|
||||
"bintrees": "^1.0.2",
|
||||
"ethereum-types": "^1.0.5",
|
||||
"ethereumjs-blockstream": "5.0.0",
|
||||
|
@@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1535377027,
|
||||
"version": "1.0.7",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1535133899,
|
||||
"version": "1.0.6",
|
||||
|
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v1.0.7 - _August 27, 2018_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.6 - _August 24, 2018_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0xproject/react-docs",
|
||||
"version": "1.0.6",
|
||||
"version": "1.0.7",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -24,7 +24,7 @@
|
||||
"url": "https://github.com/0xProject/0x-monorepo.git"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@0xproject/dev-utils": "^1.0.5",
|
||||
"@0xproject/dev-utils": "^1.0.6",
|
||||
"@0xproject/tslint-config": "^1.0.6",
|
||||
"@types/compare-versions": "^3.0.0",
|
||||
"copyfiles": "^2.0.0",
|
||||
@@ -34,8 +34,8 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0xproject/react-shared": "^1.0.7",
|
||||
"@0xproject/utils": "^1.0.6",
|
||||
"@0xproject/react-shared": "^1.0.8",
|
||||
"@0xproject/utils": "^1.0.7",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/material-ui": "0.18.0",
|
||||
"@types/node": "^8.0.53",
|
||||
|
@@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1535377027,
|
||||
"version": "1.0.8",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1535133899,
|
||||
"version": "1.0.7",
|
||||
|
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v1.0.8 - _August 27, 2018_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.7 - _August 24, 2018_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0xproject/react-shared",
|
||||
"version": "1.0.7",
|
||||
"version": "1.0.8",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -24,7 +24,7 @@
|
||||
"url": "https://github.com/0xProject/0x-monorepo.git"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@0xproject/dev-utils": "^1.0.5",
|
||||
"@0xproject/dev-utils": "^1.0.6",
|
||||
"@0xproject/tslint-config": "^1.0.6",
|
||||
"copyfiles": "^2.0.0",
|
||||
"make-promises-safe": "^1.1.0",
|
||||
|
@@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1535377027,
|
||||
"version": "1.1.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "1.1.0",
|
||||
"changes": [
|
||||
|
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v1.1.1 - _August 27, 2018_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.1.0 - _August 24, 2018_
|
||||
|
||||
* Quicken compilation by sending multiple contracts to the same solcjs invocation, batching them together based on compiler version requirements. (#965)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user