Compare commits

..

130 Commits

Author SHA1 Message Date
Fabio Berger
00a4fa5f7c Publish
- 0x.js@1.0.1-rc.6
 - @0xproject/abi-gen@1.0.7
 - @0xproject/assert@1.0.7
 - @0xproject/base-contract@2.0.1
 - @0xproject/connect@2.0.0-rc.2
 - @0xproject/contract-wrappers@1.0.1-rc.5
 - contracts@2.1.42
 - @0xproject/dev-utils@1.0.6
 - @0xproject/fill-scenarios@1.0.1-rc.5
 - @0xproject/forwarder-helper@1.0.1-rc.2
 - @0xproject/json-schemas@1.0.1-rc.6
 - @0xproject/metacoin@0.0.17
 - @0xproject/migrations@1.0.6
 - @0xproject/monorepo-scripts@1.0.7
 - @0xproject/order-utils@1.0.1-rc.6
 - @0xproject/order-watcher@1.0.1-rc.5
 - @0xproject/react-docs@1.0.7
 - @0xproject/react-shared@1.0.8
 - @0xproject/sol-compiler@1.1.1
 - @0xproject/sol-cov@2.1.1
 - @0xproject/sol-resolver@1.0.7
 - @0xproject/sra-report@1.0.7
 - @0xproject/sra-spec@1.0.1-rc.6
 - @0xproject/subproviders@2.0.1
 - @0xproject/testnet-faucets@1.0.43
 - @0xproject/types@1.0.1-rc.6
 - @0xproject/utils@1.0.7
 - @0xproject/web3-wrapper@2.0.1
 - @0xproject/website@0.0.46
2018-08-27 14:48:24 +01:00
Fabio Berger
4475fefd07 Updated CHANGELOGS 2018-08-27 14:47:56 +01:00
Fabio Berger
cd08a9c121 Fix prettier 2018-08-27 13:43:29 +01:00
Fabio Berger
b0c4eb8333 Update changelog files for RC packages 2018-08-27 13:16:31 +01:00
Fabio Berger
368dbda8f0 Merge pull request #1028 from 0xProject/fix-ci
Fix `test_publish` CI Test
2018-08-27 13:09:11 +01:00
Fabio Berger
bc4149683e Skip doc generation for local publishes since we test this in a separate CI test 2018-08-27 12:39:10 +01:00
Fabio Berger
6174d9ebb7 Fix typo 2018-08-27 12:34:34 +01:00
Fabio Berger
e4fc8a8414 Use absolute path 2018-08-27 12:05:55 +01:00
Fabio Berger
907972c466 Merge branch 'development' into fix-ci
* development:
  Run yarn a second time if the first fails
2018-08-27 11:43:35 +01:00
Fabio Berger
49f5fe635f Merge branch 'development' of github.com:0xProject/0x-monorepo into development
* 'development' of github.com:0xProject/0x-monorepo:
  Run yarn a second time if the first fails
2018-08-27 11:43:22 +01:00
Fabio Berger
77290c1efa Run yarn a second time if the first fails 2018-08-27 10:51:38 +01:00
Fabio Berger
4ac43a9fd2 Try relative to root dir 2018-08-27 10:38:06 +01:00
Fabio Berger
cc77d1dd49 merge development 2018-08-27 10:35:16 +01:00
Fabio Berger
51161784e8 Merge branch 'development' of github.com:0xProject/0x-monorepo into development
* 'development' of github.com:0xProject/0x-monorepo:
  Fix command
  Move md files to lib folder during build
  Change exit code to failure
  Fix sra-spec `main` and `types` in package.json
  Issue #1025 BlockParam unroll
  fix: Use yarn version 1.9.4 on CI
2018-08-27 10:34:45 +01:00
Fabio Berger
cb7660fbe7 Merge pull request #1009 from 0xProject/fix/ci-yarn-1.9.4
fix: Use yarn version 1.9.4 on CI
2018-08-27 10:33:02 +01:00
Fabio Berger
82e51b8787 Fix command 2018-08-27 10:15:27 +01:00
Fabio Berger
fffa96bba7 Move md files to lib folder during build 2018-08-27 10:15:13 +01:00
Fabio Berger
e6cb2e0fcd Change exit code to failure 2018-08-27 10:14:43 +01:00
Fabio Berger
38abeaed9c Fix sra-spec main and types in package.json 2018-08-27 10:13:17 +01:00
Fabio Berger
90c9e3496a Actual relative path 2018-08-27 10:10:07 +01:00
Fabio Berger
9fc8a6e214 Try relative path 2018-08-27 10:09:51 +01:00
Fabio Berger
9df87a199a Merge pull request #1026 from 0xProject/fix/contract-wrappers/block-param-literal
[contract_templates] Issue #1025 BlockParam unroll
2018-08-27 10:07:06 +01:00
Jacob Evans
7e9ba50502 Issue #1025 BlockParam unroll
BlockParam unrolls into number | BlockParamLiteral, though BlockParamLiteral does not get imported. This results in type build errors in downstream projects where tslint checks libs
2018-08-27 15:59:43 +10:00
Fabio Berger
41559c39b9 Fix command 2018-08-26 23:43:19 +01:00
Fabio Berger
6a6b424c86 Move md files to lib folder during build 2018-08-26 23:35:47 +01:00
Fabio Berger
3a5c6ed00f Fix sra-spec main and types in package.json 2018-08-26 23:05:04 +01:00
Fabio Berger
db54588d05 Add BlockParamLiteral to template 2018-08-26 22:00:51 +01:00
Fabio Berger
52fde551e4 Remove check since this method is now used in multiple places 2018-08-26 21:16:32 +01:00
Fabio Berger
40cf805e5e Also use failure exit code if unexpected error occurs 2018-08-26 20:54:15 +01:00
Fabio Berger
09d6496135 Change exit code to failure 2018-08-26 20:53:21 +01:00
Fabio Berger
c4dadf4bfd Combine imports 2018-08-26 17:49:06 +01:00
Fabio Berger
35ba3e6f7c Bumop 0x.js version 2018-08-26 17:35:17 +01:00
Fabio Berger
3ac182ee91 Fix file path to main and types in package.json 2018-08-26 17:12:13 +01:00
Amir Bandeali
00e7c70b4d Merge pull request #986 from 0xProject/feature/contracts/assertions
Add more assertions to assertValidFill
2018-08-24 20:03:28 -07:00
Amir Bandeali
0aa9ed3839 Merge pull request #1008 from 0xProject/fix/contracts/robustMatching
Robustness in Order Matching
2018-08-24 19:00:53 -07:00
Remco Bloemen
d652deea23 Merge branch 'fix/contracts/robustMatching' of github.com:0xProject/0x.js into fix/contracts/robustMatching 2018-08-24 18:54:15 -07:00
Greg Hysen
878db3b849 Added comments to order matching 2018-08-24 18:48:29 -07:00
Greg Hysen
ec2e726be0 Rephrased some of the math in MixinMatchOrders to improve readability 2018-08-24 18:20:26 -07:00
Greg Hysen
287830d6e0 Run all tests 2018-08-24 18:20:26 -07:00
Greg Hysen
c7a7ae7e18 Give right maker better price when correct value is not integral 2018-08-24 18:20:26 -07:00
Greg Hysen
1c7ba6a315 Extract only fill event logs 2018-08-24 18:20:26 -07:00
Greg Hysen
0a6f107243 Added temporary @todo to MixinMatchOrders 2018-08-24 18:20:25 -07:00
Greg Hysen
a93f95c55e Wording in MixinMatchOrders 2018-08-24 18:20:25 -07:00
Greg Hysen
6833e243b7 Addressed linter errors in match order tesster 2018-08-24 18:20:25 -07:00
Greg Hysen
81dc893d1d Removed a redundant comment from matchOrders 2018-08-24 18:20:25 -07:00
Greg Hysen
f8e565bc06 Tests for matchOrders edge cases 2018-08-24 18:20:25 -07:00
Greg Hysen
ba15fb6a06 Swapped direction of expect values to match output in failure cases 2018-08-24 18:20:25 -07:00
Greg Hysen
1e6b83719a Renaming verify -> assert in order matching 2018-08-24 18:20:25 -07:00
Greg Hysen
9fcb2dda73 Fixed a function comment 2018-08-24 18:20:25 -07:00
Greg Hysen
9a5ec8d030 Added function signature comments 2018-08-24 18:20:25 -07:00
Greg Hysen
ac872e5181 Added expect messages for checking left/right order states 2018-08-24 18:20:25 -07:00
Greg Hysen
70863cca08 Ran prettier and linter 2018-08-24 18:20:25 -07:00
Greg Hysen
5a1dce15be Updated all existing match order tests to use new format 2018-08-24 18:19:43 -07:00
Greg Hysen
d291256158 Passes comprehensive test 2018-08-24 18:18:30 -07:00
Greg Hysen
8c706ac639 Verify logs 2018-08-24 18:18:30 -07:00
Greg Hysen
f697814849 First balance test with intentional values 2018-08-24 18:18:30 -07:00
Greg Hysen
ca5c9e77c0 Ironing out the new set of test cases for order matchubng 2018-08-24 18:17:27 -07:00
Greg Hysen
a32b201afe Rounding for fees in match orders addressed, plus example 2018-08-24 18:17:27 -07:00
Greg Hysen
0ecdf1e213 All existing tests pass. 2018-08-24 18:17:26 -07:00
Greg Hysen
057891b342 Added fees to matchOrders (previously in calculateFillResults 2018-08-24 18:17:26 -07:00
Greg Hysen
407f63ef20 Removed calculateFillResults from matchOrders workflow. Eliminates compounded rounding errors. 2018-08-24 18:17:26 -07:00
Amir Bandeali
f938c989e3 Merge pull request #1002 from 0xProject/feature/contracts/mutex
[contracts] Add mutexes and reentrancy guards
2018-08-24 18:15:22 -07:00
Amir Bandeali
c8500cab10 Fix build 2018-08-24 17:30:56 -07:00
Amir Bandeali
c28c3db63f Only use one nonReentrant modifier, remove modifier from fillOrderNoThrow variations 2018-08-24 17:30:56 -07:00
Amir Bandeali
a09ee90739 Add tests for matchOrders 2018-08-24 17:30:56 -07:00
Amir Bandeali
7d5a23969d Add reentrancy tests for fillOrder and wrapper functions 2018-08-24 17:30:56 -07:00
Amir Bandeali
56c3c29feb Update ReentrantERC20Token with new functions and check that revert is occuring for correct reason 2018-08-24 17:30:56 -07:00
Amir Bandeali
c75212bef0 Add nonReentrant modifiers on functions that use getCurrentContextAddress only, add lockMutex modifier on functions that make external calls 2018-08-24 17:30:56 -07:00
Amir Bandeali
6d0dedc62c Split modifiers into check only and check, lock, unlock 2018-08-24 17:30:56 -07:00
Amir Bandeali
cf12daea2f Add ReentrantToken 2018-08-24 17:30:56 -07:00
Amir Bandeali
6f88e9bdbd Add internal fill functions, add reentrancy guard to public functions that make external calls 2018-08-24 17:30:56 -07:00
Amir Bandeali
d8cb56caa3 Add ReentrancyGuard contract 2018-08-24 17:30:56 -07:00
Amir Bandeali
044415e23d Remove redundant sload from getCurrentContextAddress 2018-08-24 17:30:56 -07:00
Remco Bloemen
6b866d6053 Revert maker not equal taker check 2018-08-24 17:29:52 -07:00
Amir Bandeali
74ce893f52 Merge pull request #1003 from 0xProject/feature/contracts/roundup
[contracts] Add getPartialAmountCeil and isRoundingErrorCeil
2018-08-24 17:29:09 -07:00
Amir Bandeali
cc1fac9bbe Fix linting errors 2018-08-24 17:02:42 -07:00
Amir Bandeali
94e01be9ed Merge pull request #1021 from 0xProject/feature/contracts/skipselftransfer
[Contracts] Skip self transfers
2018-08-24 16:58:46 -07:00
Remco Bloemen
e215992859 Fix mixin api 2018-08-24 16:46:24 -07:00
Remco Bloemen
e6f5cac878 Fix double definition of error 2018-08-24 16:46:24 -07:00
Remco Bloemen
29971f36cf Split into assertFillable and assertValidFill 2018-08-24 16:46:24 -07:00
Remco Bloemen
3e4493b389 Disallow self filling 2018-08-24 16:46:24 -07:00
Remco Bloemen
749c6ecc30 Add revert reasons to types 2018-08-24 16:46:24 -07:00
Remco Bloemen
e6e7bae445 Remove BUG_ from revert reasons 2018-08-24 16:46:23 -07:00
Remco Bloemen
a1d8943552 Document accetable price check 2018-08-24 16:46:23 -07:00
Remco Bloemen
07e56b3cc7 Fix taker overpay check 2018-08-24 16:46:23 -07:00
Remco Bloemen
b16f5f55fb Check fillable early 2018-08-24 16:46:23 -07:00
Remco Bloemen
d92fd43791 Update for new assertValidFill signature 2018-08-24 16:46:23 -07:00
Remco Bloemen
e706fa76ac Add overfill and price assertion to assertValidFill 2018-08-24 16:46:23 -07:00
Remco Bloemen
11328bd93d Skip self-transfers 2018-08-24 16:26:48 -07:00
Remco Bloemen
bc686fcbf3 Stylistic fixes 2018-08-24 16:17:02 -07:00
Remco Bloemen
80291caf7c Append -Floor to getPartialAmount and isRoundingError 2018-08-24 16:16:44 -07:00
Brandon Millman
cd5e9a5115 Merge pull request #1018 from 0xProject/fix/migrations/order-validator-testnet
[migrations] Add testnet network info to OrderValidator artifact
2018-08-24 16:06:10 -07:00
Amir Bandeali
82b51db17e Merge pull request #1015 from 0xProject/feature/contracts/removeCallerSigType
Remove SignatureType.Caller
2018-08-24 15:09:39 -07:00
Brandon Millman
3557cd93fc Add testnet network info to OrderValidator artifact 2018-08-24 14:42:07 -07:00
Amir Bandeali
0629a7d143 Remove remaining Trezor references 2018-08-24 14:40:00 -07:00
Greg Hysen
a27112cbef SignatureType.Trezor -> SignatureType.EthSign in Signature Validator tests 2018-08-24 14:40:00 -07:00
Greg Hysen
d039a1adda Fixed linter in signatureUtils 2018-08-24 14:40:00 -07:00
Greg Hysen
bb4d449e92 Test case for Trezor Model T signature 2018-08-24 14:40:00 -07:00
Greg Hysen
241534a63d Fixed trezor personal message in client+contracts; added a test using message signed by Trezor One (firmware v1.6.2) 2018-08-24 14:40:00 -07:00
Amir Bandeali
1932aff35c Remove Trezor SignatureType 2018-08-24 14:40:00 -07:00
Amir Bandeali
4f27991959 Remove SigntureType.Caller from signingUtils 2018-08-24 14:39:08 -07:00
Amir Bandeali
8ce4f9c784 Remove SignatureType.Caller 2018-08-24 14:39:08 -07:00
Amir Bandeali
7351bf0b14 Merge pull request #1012 from 0xProject/feature/contracts/staticcall
Use staticcall for external function calls in MixinSignatureValidator
2018-08-24 14:38:54 -07:00
Remco Bloemen
f6080367fe Disambiguate the operator precedence 2018-08-24 14:11:45 -07:00
Remco Bloemen
7f78d7da9d Add tests 2018-08-24 14:09:51 -07:00
Remco Bloemen
6734f2f1bc Add docs 2018-08-24 14:09:51 -07:00
Remco Bloemen
0fb7617a78 Fix incorect modulus 2018-08-24 14:09:51 -07:00
Remco Bloemen
4219af1430 Add DIVISION_BY_ZERO to getPartialAmount for consistency 2018-08-24 14:09:51 -07:00
Remco Bloemen
c109d1f545 Remove .only 2018-08-24 14:09:51 -07:00
Remco Bloemen
50fab9feb3 Improve getPartialAmountCeil docs 2018-08-24 14:09:51 -07:00
Remco Bloemen
3dad6ee55e Add tests for getPartialAmountCeil 2018-08-24 14:09:51 -07:00
Remco Bloemen
5d70df771b Add isRoundingErrorCeil 2018-08-24 14:09:50 -07:00
Remco Bloemen
ab5df342e1 Add getPartialAmountCeil 2018-08-24 14:09:50 -07:00
Amir Bandeali
6a9669a409 Rethrow Wallet and Validator errors 2018-08-24 14:06:46 -07:00
Remco Bloemen
e68942ee78 Handle zero case 2018-08-24 13:45:10 -07:00
Remco Bloemen
4159e45aff Update tests 2018-08-24 13:45:10 -07:00
Remco Bloemen
92497d7df4 Fix isRoundingError 2018-08-24 13:45:10 -07:00
Amir Bandeali
070eff6f3a Rename TestStaticCall => TestStaticCallReceiver 2018-08-24 13:32:04 -07:00
Amir Bandeali
681ed822ec Revert if undefined function called in AssetProxies 2018-08-24 13:19:07 -07:00
Amir Bandeali
0a1ae2c311 Remove pragma experimental v0.5.0 and use staticcall is assembly 2018-08-24 13:19:07 -07:00
Amir Bandeali
c5f8b9c2d2 Add pragma experimental v0.5.0 to SignatureValidator and add tests 2018-08-24 13:19:07 -07:00
Francesco Agosti
7f36574a57 Merge pull request #1011 from 0xProject/sra-api/rename/sra-spec
[sra-spec] Rename the sra-api package to sra-spec
2018-08-24 12:00:39 -07:00
fragosti
b637ca105a Merge branch 'development' of https://github.com/0xProject/0x-monorepo into sra-api/rename/sra-spec 2018-08-24 11:16:28 -07:00
fragosti
9ffddb47b8 Merge branch 'development' of https://github.com/0xProject/0x-monorepo into sra-api/rename/sra-spec 2018-08-24 11:14:34 -07:00
fragosti
7bcaac4e10 Ignore api.json in public 2018-08-24 11:11:56 -07:00
fragosti
d4592c0a60 Run prettier 2018-08-24 10:20:17 -07:00
fragosti
1d6699585e Add sra-spec to prettier ignore 2018-08-23 17:53:21 -07:00
fragosti
a75c298de0 Point to new s3 bucket 2018-08-23 17:31:11 -07:00
fragosti
d603d8da47 Rename sra-api to sra-spec 2018-08-23 17:30:20 -07:00
Alex Browne
a551d0a6dd fix: Use yarn version 1.9.4 on CI 2018-08-23 16:45:18 -07:00
157 changed files with 3017 additions and 997 deletions

View File

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

View File

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

View File

@@ -33,7 +33,7 @@ If you're developing on 0x now or are interested in using 0x infrastructure in t
| [`@0xproject/monorepo-scripts`](/packages/monorepo-scripts) | [![npm](https://img.shields.io/npm/v/@0xproject/monorepo-scripts.svg)](https://www.npmjs.com/package/@0xproject/monorepo-scripts) | Monorepo scripts |
| [`@0xproject/react-docs`](/packages/react-docs) | [![npm](https://img.shields.io/npm/v/@0xproject/react-docs.svg)](https://www.npmjs.com/package/@0xproject/react-docs) | React documentation component for rendering TypeDoc & Doxity generated JSON |
| [`@0xproject/react-shared`](/packages/react-shared) | [![npm](https://img.shields.io/npm/v/@0xproject/react-shared.svg)](https://www.npmjs.com/package/@0xproject/react-shared) | 0x shared react components |
| [`@0xproject/sra-api`](/packages/sra-api) | [![npm](https://img.shields.io/npm/v/@0xproject/sra-api.svg)](https://www.npmjs.com/package/@0xproject/sra-api) | OpenAPI specification for the standard relayer API |
| [`@0xproject/sra-spec`](/packages/sra-spec) | [![npm](https://img.shields.io/npm/v/@0xproject/sra-spec.svg)](https://www.npmjs.com/package/@0xproject/sra-spec) | OpenAPI specification for the standard relayer API |
| [`@0xproject/sra-report`](/packages/sra-report) | [![npm](https://img.shields.io/npm/v/@0xproject/sra-report.svg)](https://www.npmjs.com/package/@0xproject/sra-report) | Generate reports for standard relayer API compliance |
| [`@0xproject/sol-cov`](/packages/sol-cov) | [![npm](https://img.shields.io/npm/v/@0xproject/sol-cov.svg)](https://www.npmjs.com/package/@0xproject/sol-cov) | Solidity test coverage tool |
| [`@0xproject/subproviders`](/packages/subproviders) | [![npm](https://img.shields.io/npm/v/@0xproject/subproviders.svg)](https://www.npmjs.com/package/@0xproject/subproviders) | Useful web3 subproviders (e.g LedgerSubprovider) |

View File

@@ -1,4 +1,22 @@
[
{
"version": "1.0.1-rc.6",
"changes": [
{
"note": "Fix missing `BlockParamLiteral` type import issue"
}
],
"timestamp": 1535377027
},
{
"version": "1.0.1-rc.5",
"changes": [
{
"note":
"Fix `main` and `types` package.json entries so that they point to the new location of index.d.ts and index.js"
}
]
},
{
"version": "1.0.1-rc.4",
"changes": [

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,13 @@
[
{
"version": "1.0.1-rc.5",
"changes": [
{
"note": "Fix missing `BlockParamLiteral` type import issue"
}
],
"timestamp": 1535377027
},
{
"version": "1.0.1-rc.4",
"changes": [

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -59,6 +59,19 @@ contract MExchangeCore is
uint256 orderEpoch // Orders with specified makerAddress and senderAddress with a salt less than this value are considered cancelled.
);
/// @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;

View File

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

View File

@@ -0,0 +1,40 @@
/*
Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity 0.4.24;
pragma experimental ABIEncoderV2;
import "../libs/LibOrder.sol";
import "../libs/LibFillResults.sol";
import "../interfaces/IWrapperFunctions.sol";
contract MWrapperFunctions {
/// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled.
/// @param order LibOrder.Order struct containing order specifications.
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
/// @param signature Proof that order has been created by maker.
function fillOrKillOrderInternal(
LibOrder.Order memory order,
uint256 takerAssetFillAmount,
bytes memory signature
)
internal
returns (LibFillResults.FillResults memory fillResults);
}

View File

@@ -0,0 +1,182 @@
/*
Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity 0.4.24;
pragma experimental ABIEncoderV2;
import "../../utils/LibBytes/LibBytes.sol";
import "../../tokens/ERC20Token/ERC20Token.sol";
import "../../protocol/Exchange/interfaces/IExchange.sol";
import "../../protocol/Exchange/libs/LibOrder.sol";
contract ReentrantERC20Token is
ERC20Token
{
using LibBytes for bytes;
// solhint-disable-next-line var-name-mixedcase
IExchange internal EXCHANGE;
bytes internal constant REENTRANCY_ILLEGAL_REVERT_REASON = abi.encodeWithSelector(
bytes4(keccak256("Error(string)")),
"REENTRANCY_ILLEGAL"
);
// All of these functions are potentially vulnerable to reentrancy
// We do not test any "noThrow" functions because `fillOrderNoThrow` makes a delegatecall to `fillOrder`
enum ExchangeFunction {
FILL_ORDER,
FILL_OR_KILL_ORDER,
BATCH_FILL_ORDERS,
BATCH_FILL_OR_KILL_ORDERS,
MARKET_BUY_ORDERS,
MARKET_SELL_ORDERS,
MATCH_ORDERS,
CANCEL_ORDER,
CANCEL_ORDERS_UP_TO,
SET_SIGNATURE_VALIDATOR_APPROVAL
}
uint8 internal currentFunctionId = 0;
constructor (address _exchange)
public
{
EXCHANGE = IExchange(_exchange);
}
/// @dev Set the current function that will be called when `transferFrom` is called.
/// @param _currentFunctionId Id that corresponds to function name.
function setCurrentFunction(uint8 _currentFunctionId)
external
{
currentFunctionId = _currentFunctionId;
}
/// @dev A version of `transferFrom` that attempts to reenter the Exchange contract.
/// @param _from The address of the sender
/// @param _to The address of the recipient
/// @param _value The amount of token to be transferred
function transferFrom(
address _from,
address _to,
uint256 _value
)
external
returns (bool)
{
// This order would normally be invalid, but it will be used strictly for testing reentrnacy.
// Any reentrancy checks will happen before any other checks that invalidate the order.
LibOrder.Order memory order;
// Initialize remaining null parameters
bytes memory signature;
LibOrder.Order[] memory orders;
uint256[] memory takerAssetFillAmounts;
bytes[] memory signatures;
bytes memory calldata;
// Create calldata for function that corresponds to currentFunctionId
if (currentFunctionId == uint8(ExchangeFunction.FILL_ORDER)) {
calldata = abi.encodeWithSelector(
EXCHANGE.fillOrder.selector,
order,
0,
signature
);
} else if (currentFunctionId == uint8(ExchangeFunction.FILL_OR_KILL_ORDER)) {
calldata = abi.encodeWithSelector(
EXCHANGE.fillOrKillOrder.selector,
order,
0,
signature
);
} else if (currentFunctionId == uint8(ExchangeFunction.BATCH_FILL_ORDERS)) {
calldata = abi.encodeWithSelector(
EXCHANGE.batchFillOrders.selector,
orders,
takerAssetFillAmounts,
signatures
);
} else if (currentFunctionId == uint8(ExchangeFunction.BATCH_FILL_OR_KILL_ORDERS)) {
calldata = abi.encodeWithSelector(
EXCHANGE.batchFillOrKillOrders.selector,
orders,
takerAssetFillAmounts,
signatures
);
} else if (currentFunctionId == uint8(ExchangeFunction.MARKET_BUY_ORDERS)) {
calldata = abi.encodeWithSelector(
EXCHANGE.marketBuyOrders.selector,
orders,
0,
signatures
);
} else if (currentFunctionId == uint8(ExchangeFunction.MARKET_SELL_ORDERS)) {
calldata = abi.encodeWithSelector(
EXCHANGE.marketSellOrders.selector,
orders,
0,
signatures
);
} else if (currentFunctionId == uint8(ExchangeFunction.MATCH_ORDERS)) {
calldata = abi.encodeWithSelector(
EXCHANGE.matchOrders.selector,
order,
order,
signature,
signature
);
} else if (currentFunctionId == uint8(ExchangeFunction.CANCEL_ORDER)) {
calldata = abi.encodeWithSelector(
EXCHANGE.cancelOrder.selector,
order
);
} else if (currentFunctionId == uint8(ExchangeFunction.CANCEL_ORDERS_UP_TO)) {
calldata = abi.encodeWithSelector(
EXCHANGE.cancelOrdersUpTo.selector,
0
);
} else if (currentFunctionId == uint8(ExchangeFunction.SET_SIGNATURE_VALIDATOR_APPROVAL)) {
calldata = abi.encodeWithSelector(
EXCHANGE.setSignatureValidatorApproval.selector,
address(0),
false
);
}
// Call Exchange function, swallow error
address(EXCHANGE).call(calldata);
// Revert reason is 100 bytes
bytes memory returnData = new bytes(100);
// Copy return data
assembly {
returndatacopy(add(returnData, 32), 0, 100)
}
// Revert if function reverted with REENTRANCY_ILLEGAL error
require(!REENTRANCY_ILLEGAL_REVERT_REASON.equals(returnData));
// Transfer will return true if function failed for any other reason
return true;
}
}

View File

@@ -67,7 +67,7 @@ contract TestExchangeInternals is
/// @param denominator Denominator.
/// @param 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.

View File

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

View File

@@ -0,0 +1,81 @@
/*
Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity 0.4.24;
import "../../tokens/ERC20Token/IERC20Token.sol";
// solhint-disable no-unused-vars
contract TestStaticCallReceiver {
uint256 internal state = 1;
/// @dev Updates state and returns true. Intended to be used with `Validator` signature type.
/// @param hash Message hash that is signed.
/// @param signerAddress Address that should have signed the given hash.
/// @param signature Proof of signing.
/// @return Validity of order signature.
function isValidSignature(
bytes32 hash,
address signerAddress,
bytes signature
)
external
returns (bool isValid)
{
updateState();
return true;
}
/// @dev Updates state and returns true. Intended to be used with `Wallet` signature type.
/// @param hash Message hash that is signed.
/// @param signature Proof of signing.
/// @return Validity of order signature.
function isValidSignature(
bytes32 hash,
bytes signature
)
external
returns (bool isValid)
{
updateState();
return true;
}
/// @dev Approves an ERC20 token to spend tokens from this address.
/// @param token Address of ERC20 token.
/// @param spender Address that will spend tokens.
/// @param value Amount of tokens spender is approved to spend.
function approveERC20(
address token,
address spender,
uint256 value
)
external
{
IERC20Token(token).approve(spender, value);
}
/// @dev Increments state variable.
function updateState()
internal
{
state++;
}
}

View File

@@ -0,0 +1,44 @@
/*
Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity 0.4.24;
contract ReentrancyGuard {
// Locked state of mutex
bool private locked = false;
/// @dev Functions with this modifer cannot be reentered. The mutex will be locked
/// before function execution and unlocked after.
modifier nonReentrant() {
// Ensure mutex is unlocked
require(
!locked,
"REENTRANCY_ILLEGAL"
);
// Lock mutex before function call
locked = true;
// Perform function call
_;
// Unlock mutex after function call
locked = false;
}
}

View File

@@ -12,7 +12,7 @@ import { ERC20ProxyContract } from '../../generated_contract_wrappers/erc20_prox
import { ERC721ProxyContract } from '../../generated_contract_wrappers/erc721_proxy';
import { 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

View File

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

View File

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

View File

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

View File

@@ -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', () => {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,13 @@
[
{
"version": "1.0.1-rc.2",
"changes": [
{
"note": "Dependencies updated"
}
],
"timestamp": 1535377027
},
{
"version": "1.0.1-rc.1",
"changes": [

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,22 @@
[
{
"version": "1.0.1-rc.6",
"changes": [
{
"note": "Fix missing `BlockParamLiteral` type import issue"
}
],
"timestamp": 1535377027
},
{
"version": "1.0.1-rc.5",
"changes": [
{
"note": "Remove Caller and Trezor SignatureTypes",
"pr": 1015
}
]
},
{
"version": "1.0.1-rc.4",
"changes": [

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,13 @@
[
{
"version": "1.0.1-rc.5",
"changes": [
{
"note": "Fix missing `BlockParamLiteral` type import issue"
}
],
"timestamp": 1535377027
},
{
"version": "1.0.1-rc.4",
"changes": [

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1535377027,
"version": "1.0.8",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1535133899,
"version": "1.0.7",

View File

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

View File

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

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1535377027,
"version": "1.1.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "1.1.0",
"changes": [

View File

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