aboutsummaryrefslogtreecommitdiffstats
path: root/packages/order-utils
diff options
context:
space:
mode:
Diffstat (limited to 'packages/order-utils')
-rw-r--r--packages/order-utils/CHANGELOG.json134
-rw-r--r--packages/order-utils/CHANGELOG.md56
-rw-r--r--packages/order-utils/README.md10
-rw-r--r--packages/order-utils/package.json63
-rw-r--r--packages/order-utils/src/abstract/abstract_balance_and_proxy_allowance_fetcher.ts19
-rw-r--r--packages/order-utils/src/abstract/abstract_balance_and_proxy_allowance_lazy_store.ts2
-rw-r--r--packages/order-utils/src/abstract/abstract_order_filled_cancelled_fetcher.ts17
-rw-r--r--packages/order-utils/src/abstract/abstract_order_filled_cancelled_lazy_store.ts2
-rw-r--r--packages/order-utils/src/artifacts.ts14
-rw-r--r--packages/order-utils/src/assert.ts10
-rw-r--r--packages/order-utils/src/asset_data_utils.ts6
-rw-r--r--packages/order-utils/src/constants.ts37
-rw-r--r--packages/order-utils/src/eip712_utils.ts152
-rw-r--r--packages/order-utils/src/exchange_transfer_simulator.ts13
-rw-r--r--packages/order-utils/src/globals.d.ts6
-rw-r--r--packages/order-utils/src/index.ts77
-rw-r--r--packages/order-utils/src/market_utils.ts60
-rw-r--r--packages/order-utils/src/monorepo_scripts/postpublish.ts8
-rw-r--r--packages/order-utils/src/monorepo_scripts/stage_docs.ts8
-rw-r--r--packages/order-utils/src/order_factory.ts47
-rw-r--r--packages/order-utils/src/order_hash.ts39
-rw-r--r--packages/order-utils/src/order_state_utils.ts65
-rw-r--r--packages/order-utils/src/order_validation_utils.ts68
-rw-r--r--packages/order-utils/src/parsing_utils.ts27
-rw-r--r--packages/order-utils/src/rate_utils.ts6
-rw-r--r--packages/order-utils/src/remaining_fillable_calculator.ts2
-rw-r--r--packages/order-utils/src/salt.ts2
-rw-r--r--packages/order-utils/src/signature_utils.ts659
-rw-r--r--packages/order-utils/src/sorting_utils.ts8
-rw-r--r--packages/order-utils/src/store/balance_and_proxy_allowance_lazy_store.ts47
-rw-r--r--packages/order-utils/src/store/order_filled_cancelled_lazy_store.ts47
-rw-r--r--packages/order-utils/src/types.ts39
-rw-r--r--packages/order-utils/src/utils.ts4
-rw-r--r--packages/order-utils/test/asset_data_utils_test.ts50
-rw-r--r--packages/order-utils/test/eip712_utils_test.ts44
-rw-r--r--packages/order-utils/test/exchange_transfer_simulator_test.ts12
-rw-r--r--packages/order-utils/test/market_utils_test.ts29
-rw-r--r--packages/order-utils/test/order_hash_test.ts22
-rw-r--r--packages/order-utils/test/order_state_utils_test.ts22
-rw-r--r--packages/order-utils/test/order_validation_utils_test.ts14
-rw-r--r--packages/order-utils/test/rate_utils_test.ts2
-rw-r--r--packages/order-utils/test/remaining_fillable_calculator_test.ts6
-rw-r--r--packages/order-utils/test/signature_utils_test.ts241
-rw-r--r--packages/order-utils/test/sorting_utils_test.ts2
-rw-r--r--packages/order-utils/test/utils/simple_erc20_balance_and_proxy_allowance_fetcher.ts5
-rw-r--r--packages/order-utils/test/utils/test_order_factory.ts5
-rw-r--r--packages/order-utils/test/utils/web3_wrapper.ts4
-rw-r--r--packages/order-utils/tsconfig.json3
-rw-r--r--packages/order-utils/tslint.json2
-rw-r--r--packages/order-utils/typedoc-tsconfig.json7
50 files changed, 1443 insertions, 781 deletions
diff --git a/packages/order-utils/CHANGELOG.json b/packages/order-utils/CHANGELOG.json
index 86f0da65a..22af2412b 100644
--- a/packages/order-utils/CHANGELOG.json
+++ b/packages/order-utils/CHANGELOG.json
@@ -1,5 +1,112 @@
[
{
+ "version": "2.0.0",
+ "changes": [
+ {
+ "note":
+ "Added `ecSignOrderAsync` to first sign an order using `eth_signTypedData` and fallback to `eth_sign`.",
+ "pr": 1102
+ },
+ {
+ "note": "Added `ecSignTypedDataOrderAsync` to sign an order exclusively using `eth_signTypedData`.",
+ "pr": 1102
+ },
+ {
+ "note": "Rename `ecSignOrderHashAsync` to `ecSignHashAsync` removing `SignerType` parameter.",
+ "pr": 1102
+ },
+ {
+ "note": "Use `AssetData` union type for function return values.",
+ "pr": 1131
+ }
+ ],
+ "timestamp": 1539871071
+ },
+ {
+ "version": "1.0.7",
+ "changes": [
+ {
+ "note": "Dependencies updated"
+ }
+ ],
+ "timestamp": 1538693146
+ },
+ {
+ "version": "1.0.6",
+ "changes": [
+ {
+ "note":
+ "Add signerAddress normalization to `isValidECSignature` to avoid `invalid address recovery` error if caller supplies a checksummed address",
+ "pr": 1096
+ }
+ ],
+ "timestamp": 1538157789
+ },
+ {
+ "timestamp": 1537907159,
+ "version": "1.0.5",
+ "changes": [
+ {
+ "note": "Dependencies updated"
+ }
+ ]
+ },
+ {
+ "timestamp": 1537875740,
+ "version": "1.0.4",
+ "changes": [
+ {
+ "note": "Dependencies updated"
+ }
+ ]
+ },
+ {
+ "timestamp": 1537541580,
+ "version": "1.0.3",
+ "changes": [
+ {
+ "note": "Dependencies updated"
+ }
+ ]
+ },
+ {
+ "version": "1.0.2",
+ "changes": [
+ {
+ "note": "Drastically reduce the bundle size by removing unused parts of included contract artifacts."
+ }
+ ],
+ "timestamp": 1537369748
+ },
+ {
+ "version": "1.0.1",
+ "changes": [
+ {
+ "note": "Export `orderParsingUtils`",
+ "pr": 1044
+ }
+ ],
+ "timestamp": 1536142250
+ },
+ {
+ "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": [
{
@@ -14,8 +121,33 @@
"note":
"Update marketUtils api such that all optional parameters are bundled into one optional param and more defaults are provided",
"pr": 954
+ },
+ {
+ "note":
+ "Instead of exporting signature util methods individually, they are now exported as `signatureUtils`",
+ "pr": 924
+ },
+ {
+ "note":
+ "Export types: `SignedOrder`, `Order`, `OrderRelevantState`, `OrderState`, `ECSignature`, `ERC20AssetData`, `ERC721AssetData`, `AssetProxyId`, `SignerType`, `SignatureType`, `OrderStateValid`, `OrderStateInvalid`, `ExchangeContractErrs`, `TradeSide`, `TransferType`, `FindFeeOrdersThatCoverFeesForTargetOrdersOpts`, `FindOrdersThatCoverMakerAssetFillAmountOpts`, `FeeOrdersAndRemainingFeeAmount`, `OrdersAndRemainingFillAmount`, `Provider`, `JSONRPCRequestPayload`, `JSONRPCErrorCallback` and `JSONRPCResponsePayload`",
+ "pr": 924
+ },
+ {
+ "note":
+ "Rename `resultOrders` to `resultFeeOrders` for object returned by `findFeeOrdersThatCoverFeesForTargetOrders` in `marketUtils` api",
+ "pr": 997
+ },
+ {
+ "note": "Make `sortFeeOrdersByFeeAdjustedRate` in `sortingUtils` generic",
+ "pr": 997
+ },
+ {
+ "note":
+ "Update `findFeeOrdersThatCoverFeesForTargetOrders` to round the the nearest integer when calculating required fees",
+ "pr": 997
}
- ]
+ ],
+ "timestamp": 1535133899
},
{
"version": "1.0.1-rc.3",
diff --git a/packages/order-utils/CHANGELOG.md b/packages/order-utils/CHANGELOG.md
index 0df2a6a75..afd5c4adc 100644
--- a/packages/order-utils/CHANGELOG.md
+++ b/packages/order-utils/CHANGELOG.md
@@ -5,7 +5,61 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
-## v1.0.1-rc.3 - _August 13, 2018_
+## v2.0.0 - _October 18, 2018_
+
+ * Added `ecSignOrderAsync` to first sign an order using `eth_signTypedData` and fallback to `eth_sign`. (#1102)
+ * Added `ecSignTypedDataOrderAsync` to sign an order exclusively using `eth_signTypedData`. (#1102)
+ * Rename `ecSignOrderHashAsync` to `ecSignHashAsync` removing `SignerType` parameter. (#1102)
+ * Use `AssetData` union type for function return values. (#1131)
+
+## v1.0.7 - _October 4, 2018_
+
+ * Dependencies updated
+
+## v1.0.6 - _September 28, 2018_
+
+ * Add signerAddress normalization to `isValidECSignature` to avoid `invalid address recovery` error if caller supplies a checksummed address (#1096)
+
+## v1.0.5 - _September 25, 2018_
+
+ * Dependencies updated
+
+## v1.0.4 - _September 25, 2018_
+
+ * Dependencies updated
+
+## v1.0.3 - _September 21, 2018_
+
+ * Dependencies updated
+
+## v1.0.2 - _September 19, 2018_
+
+ * Drastically reduce the bundle size by removing unused parts of included contract artifacts.
+
+## v1.0.1 - _September 5, 2018_
+
+ * Export `orderParsingUtils` (#1044)
+
+## v1.0.1-rc.6 - _August 27, 2018_
+
+ * Fix missing `BlockParamLiteral` type import issue
+
+## v1.0.1-rc.5 - _Invalid date_
+
+ * Remove Caller and Trezor SignatureTypes (#1015)
+
+## v1.0.1-rc.4 - _August 24, 2018_
+
+ * Remove rounding error being thrown when maker amount is very small (#959)
+ * Added rateUtils and sortingUtils (#953)
+ * Update marketUtils api such that all optional parameters are bundled into one optional param and more defaults are provided (#954)
+ * Instead of exporting signature util methods individually, they are now exported as `signatureUtils` (#924)
+ * Export types: `SignedOrder`, `Order`, `OrderRelevantState`, `OrderState`, `ECSignature`, `ERC20AssetData`, `ERC721AssetData`, `AssetProxyId`, `SignerType`, `SignatureType`, `OrderStateValid`, `OrderStateInvalid`, `ExchangeContractErrs`, `TradeSide`, `TransferType`, `FindFeeOrdersThatCoverFeesForTargetOrdersOpts`, `FindOrdersThatCoverMakerAssetFillAmountOpts`, `FeeOrdersAndRemainingFeeAmount`, `OrdersAndRemainingFillAmount`, `Provider`, `JSONRPCRequestPayload`, `JSONRPCErrorCallback` and `JSONRPCResponsePayload` (#924)
+ * Rename `resultOrders` to `resultFeeOrders` for object returned by `findFeeOrdersThatCoverFeesForTargetOrders` in `marketUtils` api (#997)
+ * Make `sortFeeOrdersByFeeAdjustedRate` in `sortingUtils` generic (#997)
+ * Update `findFeeOrdersThatCoverFeesForTargetOrders` to round the the nearest integer when calculating required fees (#997)
+
+## v1.0.1-rc.3 - _August 14, 2018_
* Update ecSignOrderHashAsync to return signature string with signature type byte. Removes messagePrefixOpts. (#914)
* Added a synchronous `createOrder` method in `orderFactory`, updated public interfaces to support some optional parameters (#936)
diff --git a/packages/order-utils/README.md b/packages/order-utils/README.md
index 33df1d4b0..4a8287098 100644
--- a/packages/order-utils/README.md
+++ b/packages/order-utils/README.md
@@ -1,4 +1,4 @@
-## @0xproject/order-utils
+## @0x/order-utils
0x order-related utilities for those developing on top of 0x protocol.
@@ -7,14 +7,14 @@
## Installation
```bash
-yarn add @0xproject/order-utils
+yarn add @0x/order-utils
```
If your project is in [TypeScript](https://www.typescriptlang.org/), add the following to your `tsconfig.json`:
```json
"compilerOptions": {
- "typeRoots": ["node_modules/@0xproject/typescript-typings/types", "node_modules/@types"],
+ "typeRoots": ["node_modules/@0x/typescript-typings/types", "node_modules/@types"],
}
```
@@ -43,13 +43,13 @@ yarn install
To build this package and all other monorepo packages that it depends on, run the following from the monorepo root directory:
```bash
-PKG=@0xproject/order-utils yarn build
+PKG=@0x/order-utils yarn build
```
Or continuously rebuild on change:
```bash
-PKG=@0xproject/order-utils yarn watch
+PKG=@0x/order-utils yarn watch
```
### Clean
diff --git a/packages/order-utils/package.json b/packages/order-utils/package.json
index b94f59e29..a6a84b940 100644
--- a/packages/order-utils/package.json
+++ b/packages/order-utils/package.json
@@ -1,6 +1,6 @@
{
- "name": "@0xproject/order-utils",
- "version": "1.0.1-rc.3",
+ "name": "@0x/order-utils",
+ "version": "2.0.0",
"engines": {
"node": ">=6.12"
},
@@ -8,37 +8,21 @@
"main": "lib/src/index.js",
"types": "lib/src/index.d.ts",
"scripts": {
- "watch_without_deps": "yarn pre_build && tsc -w",
- "build": "run-s pre_build transpile copy_monorepo_scripts",
- "pre_build": "run-s update_artifacts_v2_beta generate_contract_wrappers",
- "transpile": "tsc",
- "copy_monorepo_scripts": "copyfiles -u 3 './lib/src/monorepo_scripts/**/*' ./scripts",
- "generate_contract_wrappers": "abi-gen --abis 'lib/src/artifacts/@(Exchange|IWallet|IValidator|DummyERC20Token|ERC20Proxy|ERC20Token).json' --template ../contract_templates/contract.handlebars --partials '../contract_templates/partials/**/*.handlebars' --output src/generated_contract_wrappers --backend ethers",
- "update_artifacts_v2_beta": "for i in ${npm_package_config_contracts_v2_beta}; do copyfiles -u 4 ../migrations/artifacts/2.0.0-beta-testnet/$i.json lib/src/artifacts; done;",
+ "build": "yarn tsc -b",
+ "build:ci": "yarn build",
"test": "yarn run_mocha",
"rebuild_and_test": "run-s build test",
"test:circleci": "yarn test:coverage",
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe lib/test/**/*_test.js --bail --exit",
"test:coverage": "nyc npm run test --all && yarn coverage:report:lcov",
"coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info",
- "clean": "shx rm -rf lib scripts lib/src/artifacts src/generated_contract_wrappers",
- "lint": "tslint --project . --exclude **/src/generated_contract_wrappers/**/*",
- "manual:postpublish": "yarn build; node ./scripts/postpublish.js",
- "docs:stage": "node scripts/stage_docs.js",
- "docs:json": "typedoc --excludePrivate --excludeExternals --target ES5 --json $JSON_FILE_PATH $PROJECT_FILES",
- "upload_docs_json": "aws s3 cp generated_docs/index.json $S3_URL --profile 0xproject --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers --content-type application/json"
+ "clean": "shx rm -rf lib generated_docs",
+ "lint": "tslint --format stylish --project .",
+ "docs:json": "typedoc --excludePrivate --excludeExternals --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES"
},
"config": {
- "contracts_v2_beta": "IWallet IValidator Exchange ERC20Proxy ERC20Token DummyERC20Token",
"postpublish": {
- "docPublishConfigs": {
- "extraFileIncludes": [
- "../types/src/index.ts",
- "../ethereum-types/src/index.ts"
- ],
- "s3BucketPath": "s3://doc-jsons/order-utils/",
- "s3StagingBucketPath": "s3://staging-doc-jsons/order-utils/"
- }
+ "assets": []
}
},
"license": "Apache-2.0",
@@ -51,15 +35,13 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/packages/order-utils/README.md",
"devDependencies": {
- "@0xproject/dev-utils": "^1.0.4",
- "@0xproject/monorepo-scripts": "^1.0.5",
- "@0xproject/tslint-config": "^1.0.5",
+ "@0x/dev-utils": "^1.0.13",
+ "@0x/tslint-config": "^1.0.9",
"@types/bn.js": "^4.11.0",
"@types/lodash": "4.14.104",
"chai": "^4.0.1",
"chai-as-promised": "^7.1.0",
"chai-bignumber": "^2.0.1",
- "copyfiles": "^2.0.0",
"dirty-chai": "^2.0.1",
"make-promises-safe": "^1.1.0",
"mocha": "^4.1.0",
@@ -67,24 +49,25 @@
"shx": "^0.2.2",
"sinon": "^4.0.0",
"tslint": "5.11.0",
- "typedoc": "0xProject/typedoc",
+ "typedoc": "0.13.0",
"typescript": "3.0.1"
},
"dependencies": {
- "@0xproject/assert": "^1.0.5",
- "@0xproject/base-contract": "^2.0.0-rc.1",
- "@0xproject/json-schemas": "^1.0.1-rc.4",
- "@0xproject/sol-compiler": "^1.0.5",
- "@0xproject/types": "^1.0.1-rc.4",
- "@0xproject/typescript-typings": "^1.0.4",
- "@0xproject/utils": "^1.0.5",
- "@0xproject/web3-wrapper": "^1.2.0",
- "@types/node": "^8.0.53",
+ "@0x/abi-gen-wrappers": "^1.0.1",
+ "@0x/assert": "^1.0.14",
+ "@0x/base-contract": "^3.0.2",
+ "@0x/contract-artifacts": "^1.0.1",
+ "@0x/json-schemas": "^2.0.0",
+ "@0x/types": "^1.2.0",
+ "@0x/typescript-typings": "^3.0.3",
+ "@0x/utils": "^2.0.3",
+ "@0x/web3-wrapper": "^3.1.0",
+ "@types/node": "*",
"bn.js": "^4.11.8",
- "ethereum-types": "^1.0.4",
+ "ethereum-types": "^1.1.1",
"ethereumjs-abi": "0.6.5",
"ethereumjs-util": "^5.1.1",
- "ethers": "3.0.22",
+ "ethers": "~4.0.4",
"lodash": "^4.17.5"
},
"publishConfig": {
diff --git a/packages/order-utils/src/abstract/abstract_balance_and_proxy_allowance_fetcher.ts b/packages/order-utils/src/abstract/abstract_balance_and_proxy_allowance_fetcher.ts
index b2760d98e..13fbf1736 100644
--- a/packages/order-utils/src/abstract/abstract_balance_and_proxy_allowance_fetcher.ts
+++ b/packages/order-utils/src/abstract/abstract_balance_and_proxy_allowance_fetcher.ts
@@ -1,6 +1,23 @@
-import { BigNumber } from '@0xproject/utils';
+import { BigNumber } from '@0x/utils';
+/**
+ * An abstract class to be implemented in order to use OrderStateUtils. The class that
+ * implements this interface must be capable of fetching the balance and proxyAllowance
+ * for an Ethereum address and assetData
+ */
export abstract class AbstractBalanceAndProxyAllowanceFetcher {
+ /**
+ * Get balance of assetData for userAddress
+ * @param assetData AssetData for which to fetch the balance
+ * @param userAddress Ethereum address for which to fetch the balance
+ * @return Balance amount in base units
+ */
public abstract async getBalanceAsync(assetData: string, userAddress: string): Promise<BigNumber>;
+ /**
+ * Get the 0x asset proxy allowance of assetData for userAddress
+ * @param assetData AssetData for which to fetch the allowance
+ * @param userAddress Ethereum address for which to fetch the allowance
+ * @return Allowance amount in base units
+ */
public abstract async getProxyAllowanceAsync(assetData: string, userAddress: string): Promise<BigNumber>;
}
diff --git a/packages/order-utils/src/abstract/abstract_balance_and_proxy_allowance_lazy_store.ts b/packages/order-utils/src/abstract/abstract_balance_and_proxy_allowance_lazy_store.ts
index 38e08b7fe..0a73e92bd 100644
--- a/packages/order-utils/src/abstract/abstract_balance_and_proxy_allowance_lazy_store.ts
+++ b/packages/order-utils/src/abstract/abstract_balance_and_proxy_allowance_lazy_store.ts
@@ -1,4 +1,4 @@
-import { BigNumber } from '@0xproject/utils';
+import { BigNumber } from '@0x/utils';
export abstract class AbstractBalanceAndProxyAllowanceLazyStore {
public abstract async getBalanceAsync(assetData: string, userAddress: string): Promise<BigNumber>;
diff --git a/packages/order-utils/src/abstract/abstract_order_filled_cancelled_fetcher.ts b/packages/order-utils/src/abstract/abstract_order_filled_cancelled_fetcher.ts
index 865ea4e43..de096b7d9 100644
--- a/packages/order-utils/src/abstract/abstract_order_filled_cancelled_fetcher.ts
+++ b/packages/order-utils/src/abstract/abstract_order_filled_cancelled_fetcher.ts
@@ -1,7 +1,22 @@
-import { BigNumber } from '@0xproject/utils';
+import { BigNumber } from '@0x/utils';
+/**
+ * An abstract class to be implemented in order to use OrderStateUtils. The class that
+ * implements this interface must be capable of fetching the amount filled of an order
+ * and whether it's been cancelled.
+ */
export abstract class AbstractOrderFilledCancelledFetcher {
+ /**
+ * Get the amount of the order's takerToken amount already filled
+ * @param orderHash OrderHash of order we are interested in
+ * @return FilledTakerAmount
+ */
public abstract async getFilledTakerAmountAsync(orderHash: string): Promise<BigNumber>;
+ /**
+ * Whether an order is cancelled
+ * @param orderHash OrderHash of order we are interested in
+ * @return Whether or not the order is cancelled
+ */
public abstract async isOrderCancelledAsync(orderHash: string): Promise<boolean>;
public abstract getZRXAssetData(): string;
}
diff --git a/packages/order-utils/src/abstract/abstract_order_filled_cancelled_lazy_store.ts b/packages/order-utils/src/abstract/abstract_order_filled_cancelled_lazy_store.ts
index 617bcb224..d9e66db06 100644
--- a/packages/order-utils/src/abstract/abstract_order_filled_cancelled_lazy_store.ts
+++ b/packages/order-utils/src/abstract/abstract_order_filled_cancelled_lazy_store.ts
@@ -1,4 +1,4 @@
-import { BigNumber } from '@0xproject/utils';
+import { BigNumber } from '@0x/utils';
export abstract class AbstractOrderFilledCancelledLazyStore {
public abstract async getFilledTakerAmountAsync(orderHash: string): Promise<BigNumber>;
diff --git a/packages/order-utils/src/artifacts.ts b/packages/order-utils/src/artifacts.ts
deleted file mode 100644
index 3d2d1e953..000000000
--- a/packages/order-utils/src/artifacts.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { ContractArtifact } from '@0xproject/sol-compiler';
-
-import * as DummyERC20Token from './artifacts/DummyERC20Token.json';
-import * as ERC20Proxy from './artifacts/ERC20Proxy.json';
-import * as Exchange from './artifacts/Exchange.json';
-import * as IValidator from './artifacts/IValidator.json';
-import * as IWallet from './artifacts/IWallet.json';
-export const artifacts = {
- ERC20Proxy: (ERC20Proxy as any) as ContractArtifact,
- DummyERC20Token: (DummyERC20Token as any) as ContractArtifact,
- Exchange: (Exchange as any) as ContractArtifact,
- IWallet: (IWallet as any) as ContractArtifact,
- IValidator: (IValidator as any) as ContractArtifact,
-};
diff --git a/packages/order-utils/src/assert.ts b/packages/order-utils/src/assert.ts
index f8db7ac63..2f73f58c4 100644
--- a/packages/order-utils/src/assert.ts
+++ b/packages/order-utils/src/assert.ts
@@ -1,10 +1,10 @@
-import { assert as sharedAssert } from '@0xproject/assert';
+import { assert as sharedAssert } from '@0x/assert';
// HACK: We need those two unused imports because they're actually used by sharedAssert which gets injected here
// tslint:disable:no-unused-variable
-import { Schema } from '@0xproject/json-schemas';
-import { ECSignature, SignatureType } from '@0xproject/types';
-import { BigNumber } from '@0xproject/utils';
-import { Web3Wrapper } from '@0xproject/web3-wrapper';
+import { Schema } from '@0x/json-schemas';
+import { ECSignature, SignatureType } from '@0x/types';
+import { BigNumber } from '@0x/utils';
+import { Web3Wrapper } from '@0x/web3-wrapper';
// tslint:enable:no-unused-variable
import * as _ from 'lodash';
diff --git a/packages/order-utils/src/asset_data_utils.ts b/packages/order-utils/src/asset_data_utils.ts
index 0c0b59548..9bbef3a23 100644
--- a/packages/order-utils/src/asset_data_utils.ts
+++ b/packages/order-utils/src/asset_data_utils.ts
@@ -1,5 +1,5 @@
-import { AssetProxyId, ERC20AssetData, ERC721AssetData } from '@0xproject/types';
-import { BigNumber } from '@0xproject/utils';
+import { AssetData, AssetProxyId, ERC20AssetData, ERC721AssetData } from '@0x/types';
+import { BigNumber } from '@0x/utils';
import ethAbi = require('ethereumjs-abi');
import ethUtil = require('ethereumjs-util');
@@ -112,7 +112,7 @@ export const assetDataUtils = {
* @param assetData Hex encoded assetData string to decode
* @return Either a ERC20 or ERC721 assetData object
*/
- decodeAssetDataOrThrow(assetData: string): ERC20AssetData | ERC721AssetData {
+ decodeAssetDataOrThrow(assetData: string): AssetData {
const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData);
switch (assetProxyId) {
case AssetProxyId.ERC20:
diff --git a/packages/order-utils/src/constants.ts b/packages/order-utils/src/constants.ts
index c23578c20..10029dcc3 100644
--- a/packages/order-utils/src/constants.ts
+++ b/packages/order-utils/src/constants.ts
@@ -1,4 +1,4 @@
-import { BigNumber } from '@0xproject/utils';
+import { BigNumber } from '@0x/utils';
export const constants = {
NULL_ADDRESS: '0x0000000000000000000000000000000000000000',
@@ -13,4 +13,39 @@ export const constants = {
BASE_16: 16,
INFINITE_TIMESTAMP_SEC: new BigNumber(2524604400), // Close to infinite
ZERO_AMOUNT: new BigNumber(0),
+ EIP712_DOMAIN_NAME: '0x Protocol',
+ EIP712_DOMAIN_VERSION: '2',
+ EIP712_DOMAIN_SCHEMA: {
+ name: 'EIP712Domain',
+ parameters: [
+ { name: 'name', type: 'string' },
+ { name: 'version', type: 'string' },
+ { name: 'verifyingContract', type: 'address' },
+ ],
+ },
+ EIP712_ORDER_SCHEMA: {
+ name: 'Order',
+ parameters: [
+ { name: 'makerAddress', type: 'address' },
+ { name: 'takerAddress', type: 'address' },
+ { name: 'feeRecipientAddress', type: 'address' },
+ { name: 'senderAddress', type: 'address' },
+ { name: 'makerAssetAmount', type: 'uint256' },
+ { name: 'takerAssetAmount', type: 'uint256' },
+ { name: 'makerFee', type: 'uint256' },
+ { name: 'takerFee', type: 'uint256' },
+ { name: 'expirationTimeSeconds', type: 'uint256' },
+ { name: 'salt', type: 'uint256' },
+ { name: 'makerAssetData', type: 'bytes' },
+ { name: 'takerAssetData', type: 'bytes' },
+ ],
+ },
+ EIP712_ZEROEX_TRANSACTION_SCHEMA: {
+ name: 'ZeroExTransaction',
+ parameters: [
+ { name: 'salt', type: 'uint256' },
+ { name: 'signerAddress', type: 'address' },
+ { name: 'data', type: 'bytes' },
+ ],
+ },
};
diff --git a/packages/order-utils/src/eip712_utils.ts b/packages/order-utils/src/eip712_utils.ts
index 2594e6d6d..385fda989 100644
--- a/packages/order-utils/src/eip712_utils.ts
+++ b/packages/order-utils/src/eip712_utils.ts
@@ -1,93 +1,83 @@
-import ethUtil = require('ethereumjs-util');
+import { assert } from '@0x/assert';
+import { schemas } from '@0x/json-schemas';
+import { EIP712Object, EIP712TypedData, EIP712Types, Order, ZeroExTransaction } from '@0x/types';
import * as _ from 'lodash';
-import { crypto } from './crypto';
-import { EIP712Schema, EIP712Types } from './types';
+import { constants } from './constants';
-const EIP191_PREFIX = '\x19\x01';
-const EIP712_DOMAIN_NAME = '0x Protocol';
-const EIP712_DOMAIN_VERSION = '2';
-const EIP712_VALUE_LENGTH = 32;
-
-const EIP712_DOMAIN_SCHEMA: EIP712Schema = {
- name: 'EIP712Domain',
- parameters: [
- { name: 'name', type: EIP712Types.String },
- { name: 'version', type: EIP712Types.String },
- { name: 'verifyingContract', type: EIP712Types.Address },
- ],
-};
-
-export const EIP712Utils = {
+export const eip712Utils = {
/**
- * Compiles the EIP712Schema and returns the hash of the schema.
- * @param schema The EIP712 schema.
- * @return The hash of the compiled schema
+ * Creates a EIP712TypedData object specific to the 0x protocol for use with signTypedData.
+ * @param primaryType The primary type found in message
+ * @param types The additional types for the data in message
+ * @param message The contents of the message
+ * @param exchangeAddress The address of the exchange contract
+ * @return A typed data object
*/
- compileSchema(schema: EIP712Schema): Buffer {
- const eip712Schema = EIP712Utils._encodeType(schema);
- const eip712SchemaHashBuffer = crypto.solSHA3([eip712Schema]);
- return eip712SchemaHashBuffer;
+ createTypedData: (
+ primaryType: string,
+ types: EIP712Types,
+ message: EIP712Object,
+ exchangeAddress: string,
+ ): EIP712TypedData => {
+ assert.isETHAddressHex('exchangeAddress', exchangeAddress);
+ assert.isString('primaryType', primaryType);
+ const typedData = {
+ types: {
+ EIP712Domain: constants.EIP712_DOMAIN_SCHEMA.parameters,
+ ...types,
+ },
+ domain: {
+ name: constants.EIP712_DOMAIN_NAME,
+ version: constants.EIP712_DOMAIN_VERSION,
+ verifyingContract: exchangeAddress,
+ },
+ message,
+ primaryType,
+ };
+ assert.doesConformToSchema('typedData', typedData, schemas.eip712TypedDataSchema);
+ return typedData;
},
/**
- * Merges the EIP712 hash of a struct with the DomainSeparator for 0x v2.
- * @param hashStruct the EIP712 hash of a struct
- * @param contractAddress the exchange contract address
- * @return The hash of an EIP712 message with domain separator prefixed
+ * Creates an Order EIP712TypedData object for use with signTypedData.
+ * @param Order the order
+ * @return A typed data object
*/
- createEIP712Message(hashStruct: Buffer, contractAddress: string): Buffer {
- const domainSeparatorHashBuffer = EIP712Utils._getDomainSeparatorHashBuffer(contractAddress);
- const messageBuff = crypto.solSHA3([EIP191_PREFIX, domainSeparatorHashBuffer, hashStruct]);
- return messageBuff;
- },
- pad32Address(address: string): Buffer {
- const addressBuffer = ethUtil.toBuffer(address);
- const addressPadded = EIP712Utils.pad32Buffer(addressBuffer);
- return addressPadded;
- },
- pad32Buffer(buffer: Buffer): Buffer {
- const bufferPadded = ethUtil.setLengthLeft(buffer, EIP712_VALUE_LENGTH);
- return bufferPadded;
- },
- _getDomainSeparatorSchemaBuffer(): Buffer {
- return EIP712Utils.compileSchema(EIP712_DOMAIN_SCHEMA);
- },
- _getDomainSeparatorHashBuffer(exchangeAddress: string): Buffer {
- const domainSeparatorSchemaBuffer = EIP712Utils._getDomainSeparatorSchemaBuffer();
- const encodedData = EIP712Utils._encodeData(EIP712_DOMAIN_SCHEMA, {
- name: EIP712_DOMAIN_NAME,
- version: EIP712_DOMAIN_VERSION,
- verifyingContract: exchangeAddress,
+ createOrderTypedData: (order: Order): EIP712TypedData => {
+ assert.doesConformToSchema('order', order, schemas.orderSchema, [schemas.hexSchema]);
+ const normalizedOrder = _.mapValues(order, value => {
+ return !_.isString(value) ? value.toString() : value;
});
- const domainSeparatorHashBuff2 = crypto.solSHA3([domainSeparatorSchemaBuffer, ...encodedData]);
- return domainSeparatorHashBuff2;
- },
- _encodeType(schema: EIP712Schema): string {
- const namedTypes = _.map(schema.parameters, ({ name, type }) => `${type} ${name}`);
- const namedTypesJoined = namedTypes.join(',');
- const encodedType = `${schema.name}(${namedTypesJoined})`;
- return encodedType;
- },
- _encodeData(schema: EIP712Schema, data: { [key: string]: any }): any {
- const encodedValues = [];
- for (const parameter of schema.parameters) {
- const value = data[parameter.name];
- if (parameter.type === EIP712Types.String || parameter.type === EIP712Types.Bytes) {
- encodedValues.push(crypto.solSHA3([ethUtil.toBuffer(value)]));
- } else if (parameter.type === EIP712Types.Uint256) {
- encodedValues.push(value);
- } else if (parameter.type === EIP712Types.Address) {
- encodedValues.push(EIP712Utils.pad32Address(value));
- } else {
- throw new Error(`Unable to encode ${parameter.type}`);
- }
- }
- return encodedValues;
+ const typedData = eip712Utils.createTypedData(
+ constants.EIP712_ORDER_SCHEMA.name,
+ { Order: constants.EIP712_ORDER_SCHEMA.parameters },
+ normalizedOrder,
+ order.exchangeAddress,
+ );
+ return typedData;
},
- structHash(schema: EIP712Schema, data: { [key: string]: any }): Buffer {
- const encodedData = EIP712Utils._encodeData(schema, data);
- const schemaHash = EIP712Utils.compileSchema(schema);
- const hashBuffer = crypto.solSHA3([schemaHash, ...encodedData]);
- return hashBuffer;
+ /**
+ * Creates an ExecuteTransaction EIP712TypedData object for use with signTypedData and
+ * 0x Exchange executeTransaction.
+ * @param ZeroExTransaction the 0x transaction
+ * @param exchangeAddress The address of the exchange contract
+ * @return A typed data object
+ */
+ createZeroExTransactionTypedData: (
+ zeroExTransaction: ZeroExTransaction,
+ exchangeAddress: string,
+ ): EIP712TypedData => {
+ assert.isETHAddressHex('exchangeAddress', exchangeAddress);
+ assert.doesConformToSchema('zeroExTransaction', zeroExTransaction, schemas.zeroExTransactionSchema);
+ const normalizedTransaction = _.mapValues(zeroExTransaction, value => {
+ return !_.isString(value) ? value.toString() : value;
+ });
+ const typedData = eip712Utils.createTypedData(
+ constants.EIP712_ZEROEX_TRANSACTION_SCHEMA.name,
+ { ZeroExTransaction: constants.EIP712_ZEROEX_TRANSACTION_SCHEMA.parameters },
+ normalizedTransaction,
+ exchangeAddress,
+ );
+ return typedData;
},
};
diff --git a/packages/order-utils/src/exchange_transfer_simulator.ts b/packages/order-utils/src/exchange_transfer_simulator.ts
index c3a4f9c2a..7a38b35df 100644
--- a/packages/order-utils/src/exchange_transfer_simulator.ts
+++ b/packages/order-utils/src/exchange_transfer_simulator.ts
@@ -1,5 +1,5 @@
-import { ExchangeContractErrs } from '@0xproject/types';
-import { BigNumber } from '@0xproject/utils';
+import { ExchangeContractErrs } from '@0x/types';
+import { BigNumber } from '@0x/utils';
import { AbstractBalanceAndProxyAllowanceLazyStore } from './abstract/abstract_balance_and_proxy_allowance_lazy_store';
import { constants } from './constants';
@@ -33,6 +33,10 @@ const ERR_MSG_MAPPING = {
},
};
+/**
+ * An exchange transfer simulator which simulates asset transfers exactly how the
+ * 0x exchange contract would do them.
+ */
export class ExchangeTransferSimulator {
private readonly _store: AbstractBalanceAndProxyAllowanceLazyStore;
private static _throwValidationError(
@@ -43,6 +47,11 @@ export class ExchangeTransferSimulator {
const errMsg = ERR_MSG_MAPPING[failureReason][tradeSide][transferType];
throw new Error(errMsg);
}
+ /**
+ * Instantiate a ExchangeTransferSimulator
+ * @param store A class that implements AbstractBalanceAndProxyAllowanceLazyStore
+ * @return an instance of ExchangeTransferSimulator
+ */
constructor(store: AbstractBalanceAndProxyAllowanceLazyStore) {
this._store = store;
}
diff --git a/packages/order-utils/src/globals.d.ts b/packages/order-utils/src/globals.d.ts
deleted file mode 100644
index 94e63a32d..000000000
--- a/packages/order-utils/src/globals.d.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-declare module '*.json' {
- const json: any;
- /* tslint:disable */
- export default json;
- /* tslint:enable */
-}
diff --git a/packages/order-utils/src/index.ts b/packages/order-utils/src/index.ts
index 2b1c92973..e70d43efb 100644
--- a/packages/order-utils/src/index.ts
+++ b/packages/order-utils/src/index.ts
@@ -1,29 +1,60 @@
export { orderHashUtils } from './order_hash';
-export {
- isValidSignatureAsync,
- isValidPresignedSignatureAsync,
- isValidWalletSignatureAsync,
- isValidValidatorSignatureAsync,
- isValidECSignature,
- ecSignOrderHashAsync,
- addSignedMessagePrefix,
- parseECSignature,
-} from './signature_utils';
-export { orderFactory } from './order_factory';
-export { constants } from './constants';
-export { crypto } from './crypto';
+export { signatureUtils } from './signature_utils';
export { generatePseudoRandomSalt } from './salt';
-export { CreateOrderOpts, OrderError, EIP712Parameter, EIP712Schema, EIP712Types } from './types';
-export { AbstractBalanceAndProxyAllowanceFetcher } from './abstract/abstract_balance_and_proxy_allowance_fetcher';
-export { AbstractOrderFilledCancelledFetcher } from './abstract/abstract_order_filled_cancelled_fetcher';
-export { BalanceAndProxyAllowanceLazyStore } from './store/balance_and_proxy_allowance_lazy_store';
-export { OrderFilledCancelledLazyStore } from './store/order_filled_cancelled_lazy_store';
-export { RemainingFillableCalculator } from './remaining_fillable_calculator';
-export { OrderStateUtils } from './order_state_utils';
export { assetDataUtils } from './asset_data_utils';
-export { EIP712Utils } from './eip712_utils';
-export { OrderValidationUtils } from './order_validation_utils';
-export { ExchangeTransferSimulator } from './exchange_transfer_simulator';
export { marketUtils } from './market_utils';
export { rateUtils } from './rate_utils';
export { sortingUtils } from './sorting_utils';
+export { orderParsingUtils } from './parsing_utils';
+
+export { OrderStateUtils } from './order_state_utils';
+export { AbstractBalanceAndProxyAllowanceFetcher } from './abstract/abstract_balance_and_proxy_allowance_fetcher';
+export { AbstractBalanceAndProxyAllowanceLazyStore } from './abstract/abstract_balance_and_proxy_allowance_lazy_store';
+export { AbstractOrderFilledCancelledFetcher } from './abstract/abstract_order_filled_cancelled_fetcher';
+export { AbstractOrderFilledCancelledLazyStore } from './abstract/abstract_order_filled_cancelled_lazy_store';
+
+export { OrderValidationUtils } from './order_validation_utils';
+export { ExchangeTransferSimulator } from './exchange_transfer_simulator';
+export { BalanceAndProxyAllowanceLazyStore } from './store/balance_and_proxy_allowance_lazy_store';
+export { OrderFilledCancelledLazyStore } from './store/order_filled_cancelled_lazy_store';
+
+export { eip712Utils } from './eip712_utils';
+
+export {
+ Provider,
+ JSONRPCRequestPayload,
+ JSONRPCErrorCallback,
+ JSONRPCResponsePayload,
+ JSONRPCResponseError,
+} from 'ethereum-types';
+
+export {
+ SignedOrder,
+ Order,
+ OrderRelevantState,
+ OrderState,
+ ECSignature,
+ AssetData,
+ ERC20AssetData,
+ ERC721AssetData,
+ AssetProxyId,
+ SignatureType,
+ OrderStateValid,
+ OrderStateInvalid,
+ ExchangeContractErrs,
+ EIP712Parameter,
+ EIP712TypedData,
+ EIP712Types,
+ EIP712Object,
+ EIP712ObjectValue,
+ ZeroExTransaction,
+} from '@0x/types';
+export {
+ OrderError,
+ TradeSide,
+ TransferType,
+ FindFeeOrdersThatCoverFeesForTargetOrdersOpts,
+ FindOrdersThatCoverMakerAssetFillAmountOpts,
+ FeeOrdersAndRemainingFeeAmount,
+ OrdersAndRemainingFillAmount,
+} from './types';
diff --git a/packages/order-utils/src/market_utils.ts b/packages/order-utils/src/market_utils.ts
index a0a827546..fa32f1413 100644
--- a/packages/order-utils/src/market_utils.ts
+++ b/packages/order-utils/src/market_utils.ts
@@ -1,11 +1,16 @@
-import { schemas } from '@0xproject/json-schemas';
-import { Order } from '@0xproject/types';
-import { BigNumber } from '@0xproject/utils';
+import { schemas } from '@0x/json-schemas';
+import { Order } from '@0x/types';
+import { BigNumber } from '@0x/utils';
import * as _ from 'lodash';
import { assert } from './assert';
import { constants } from './constants';
-import { FindFeeOrdersThatCoverFeesForTargetOrdersOpts, FindOrdersThatCoverMakerAssetFillAmountOpts } from './types';
+import {
+ FeeOrdersAndRemainingFeeAmount,
+ FindFeeOrdersThatCoverFeesForTargetOrdersOpts,
+ FindOrdersThatCoverMakerAssetFillAmountOpts,
+ OrdersAndRemainingFillAmount,
+} from './types';
export const marketUtils = {
/**
@@ -22,7 +27,7 @@ export const marketUtils = {
orders: T[],
makerAssetFillAmount: BigNumber,
opts?: FindOrdersThatCoverMakerAssetFillAmountOpts,
- ): { resultOrders: T[]; remainingFillAmount: BigNumber } {
+ ): OrdersAndRemainingFillAmount<T> {
assert.doesConformToSchema('orders', orders, schemas.ordersSchema);
assert.isValidBaseUnitAmount('makerAssetFillAmount', makerAssetFillAmount);
// try to get remainingFillableMakerAssetAmounts from opts, if it's not there, use makerAssetAmount values from orders
@@ -46,17 +51,23 @@ export const marketUtils = {
// iterate through the orders input from left to right until we have enough makerAsset to fill totalFillAmount
const result = _.reduce(
orders,
- ({ resultOrders, remainingFillAmount }, order, index) => {
+ ({ resultOrders, remainingFillAmount, ordersRemainingFillableMakerAssetAmounts }, order, index) => {
if (remainingFillAmount.lessThanOrEqualTo(constants.ZERO_AMOUNT)) {
- return { resultOrders, remainingFillAmount: constants.ZERO_AMOUNT };
+ return {
+ resultOrders,
+ remainingFillAmount: constants.ZERO_AMOUNT,
+ ordersRemainingFillableMakerAssetAmounts,
+ };
} else {
const makerAssetAmountAvailable = remainingFillableMakerAssetAmounts[index];
+ const shouldIncludeOrder = makerAssetAmountAvailable.gt(constants.ZERO_AMOUNT);
// if there is no makerAssetAmountAvailable do not append order to resultOrders
// if we have exceeded the total amount we want to fill set remainingFillAmount to 0
return {
- resultOrders: makerAssetAmountAvailable.gt(constants.ZERO_AMOUNT)
- ? _.concat(resultOrders, order)
- : resultOrders,
+ resultOrders: shouldIncludeOrder ? _.concat(resultOrders, order) : resultOrders,
+ ordersRemainingFillableMakerAssetAmounts: shouldIncludeOrder
+ ? _.concat(ordersRemainingFillableMakerAssetAmounts, makerAssetAmountAvailable)
+ : ordersRemainingFillableMakerAssetAmounts,
remainingFillAmount: BigNumber.max(
constants.ZERO_AMOUNT,
remainingFillAmount.minus(makerAssetAmountAvailable),
@@ -64,7 +75,11 @@ export const marketUtils = {
};
}
},
- { resultOrders: [] as T[], remainingFillAmount: totalFillAmount },
+ {
+ resultOrders: [] as T[],
+ remainingFillAmount: totalFillAmount,
+ ordersRemainingFillableMakerAssetAmounts: [] as BigNumber[],
+ },
);
return result;
},
@@ -84,7 +99,7 @@ export const marketUtils = {
orders: T[],
feeOrders: T[],
opts?: FindFeeOrdersThatCoverFeesForTargetOrdersOpts,
- ): { resultOrders: T[]; remainingFeeAmount: BigNumber } {
+ ): FeeOrdersAndRemainingFeeAmount<T> {
assert.doesConformToSchema('orders', orders, schemas.ordersSchema);
assert.doesConformToSchema('feeOrders', feeOrders, schemas.ordersSchema);
// try to get remainingFillableMakerAssetAmounts from opts, if it's not there, use makerAssetAmount values from orders
@@ -123,22 +138,23 @@ export const marketUtils = {
const makerAssetAmountAvailable = remainingFillableMakerAssetAmounts[index];
const feeToFillMakerAssetAmountAvailable = makerAssetAmountAvailable
.mul(order.takerFee)
- .div(order.makerAssetAmount);
+ .dividedToIntegerBy(order.makerAssetAmount);
return accFees.plus(feeToFillMakerAssetAmountAvailable);
},
constants.ZERO_AMOUNT,
);
- const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverMakerAssetFillAmount(
- feeOrders,
- totalFeeAmount,
- {
- remainingFillableMakerAssetAmounts: remainingFillableFeeAmounts,
- slippageBufferAmount,
- },
- );
- return {
+ const {
resultOrders,
+ remainingFillAmount,
+ ordersRemainingFillableMakerAssetAmounts,
+ } = marketUtils.findOrdersThatCoverMakerAssetFillAmount(feeOrders, totalFeeAmount, {
+ remainingFillableMakerAssetAmounts: remainingFillableFeeAmounts,
+ slippageBufferAmount,
+ });
+ return {
+ resultFeeOrders: resultOrders,
remainingFeeAmount: remainingFillAmount,
+ feeOrdersRemainingFillableMakerAssetAmounts: ordersRemainingFillableMakerAssetAmounts,
};
// TODO: add more orders here to cover rounding
// https://github.com/0xProject/0x-protocol-specification/blob/master/v2/forwarding-contract-specification.md#over-buying-zrx
diff --git a/packages/order-utils/src/monorepo_scripts/postpublish.ts b/packages/order-utils/src/monorepo_scripts/postpublish.ts
deleted file mode 100644
index dcb99d0f7..000000000
--- a/packages/order-utils/src/monorepo_scripts/postpublish.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { postpublishUtils } from '@0xproject/monorepo-scripts';
-
-import * as packageJSON from '../package.json';
-import * as tsConfigJSON from '../tsconfig.json';
-
-const cwd = `${__dirname}/..`;
-// tslint:disable-next-line:no-floating-promises
-postpublishUtils.runAsync(packageJSON, tsConfigJSON, cwd);
diff --git a/packages/order-utils/src/monorepo_scripts/stage_docs.ts b/packages/order-utils/src/monorepo_scripts/stage_docs.ts
deleted file mode 100644
index e732ac8eb..000000000
--- a/packages/order-utils/src/monorepo_scripts/stage_docs.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { postpublishUtils } from '@0xproject/monorepo-scripts';
-
-import * as packageJSON from '../package.json';
-import * as tsConfigJSON from '../tsconfig.json';
-
-const cwd = `${__dirname}/..`;
-// tslint:disable-next-line:no-floating-promises
-postpublishUtils.publishDocsToStagingAsync(packageJSON, tsConfigJSON, cwd);
diff --git a/packages/order-utils/src/order_factory.ts b/packages/order-utils/src/order_factory.ts
index 4a6f3924b..f7b855bfb 100644
--- a/packages/order-utils/src/order_factory.ts
+++ b/packages/order-utils/src/order_factory.ts
@@ -1,15 +1,28 @@
-import { Order, SignedOrder, SignerType } from '@0xproject/types';
-import { BigNumber } from '@0xproject/utils';
+import { Order, SignedOrder } from '@0x/types';
+import { BigNumber } from '@0x/utils';
import { Provider } from 'ethereum-types';
import * as _ from 'lodash';
import { constants } from './constants';
import { orderHashUtils } from './order_hash';
import { generatePseudoRandomSalt } from './salt';
-import { ecSignOrderHashAsync } from './signature_utils';
+import { signatureUtils } from './signature_utils';
import { CreateOrderOpts } from './types';
-
export const orderFactory = {
+ createOrderFromPartial(partialOrder: Partial<Order>): Order {
+ const defaultOrder = generateEmptyOrder();
+ return {
+ ...defaultOrder,
+ ...partialOrder,
+ };
+ },
+ createSignedOrderFromPartial(partialSignedOrder: Partial<SignedOrder>): SignedOrder {
+ const defaultOrder = generateEmptySignedOrder();
+ return {
+ ...defaultOrder,
+ ...partialSignedOrder,
+ };
+ },
createOrder(
makerAddress: string,
makerAssetAmount: BigNumber,
@@ -58,12 +71,36 @@ export const orderFactory = {
createOrderOpts,
);
const orderHash = orderHashUtils.getOrderHashHex(order);
- const signature = await ecSignOrderHashAsync(provider, orderHash, makerAddress, SignerType.Default);
+ const signature = await signatureUtils.ecSignHashAsync(provider, orderHash, makerAddress);
const signedOrder: SignedOrder = _.assign(order, { signature });
return signedOrder;
},
};
+function generateEmptySignedOrder(): SignedOrder {
+ return {
+ ...generateEmptyOrder(),
+ signature: constants.NULL_BYTES,
+ };
+}
+function generateEmptyOrder(): Order {
+ return {
+ senderAddress: constants.NULL_ADDRESS,
+ makerAddress: constants.NULL_ADDRESS,
+ takerAddress: constants.NULL_ADDRESS,
+ makerFee: constants.ZERO_AMOUNT,
+ takerFee: constants.ZERO_AMOUNT,
+ makerAssetAmount: constants.ZERO_AMOUNT,
+ takerAssetAmount: constants.ZERO_AMOUNT,
+ makerAssetData: constants.NULL_BYTES,
+ takerAssetData: constants.NULL_BYTES,
+ salt: generatePseudoRandomSalt(),
+ exchangeAddress: constants.NULL_ADDRESS,
+ feeRecipientAddress: constants.NULL_ADDRESS,
+ expirationTimeSeconds: constants.INFINITE_TIMESTAMP_SEC,
+ };
+}
+
function generateDefaultCreateOrderOpts(): {
takerAddress: string;
senderAddress: string;
diff --git a/packages/order-utils/src/order_hash.ts b/packages/order-utils/src/order_hash.ts
index 54c500653..c8e9be71e 100644
--- a/packages/order-utils/src/order_hash.ts
+++ b/packages/order-utils/src/order_hash.ts
@@ -1,31 +1,13 @@
-import { schemas, SchemaValidator } from '@0xproject/json-schemas';
-import { Order, SignedOrder } from '@0xproject/types';
+import { schemas, SchemaValidator } from '@0x/json-schemas';
+import { Order, SignedOrder } from '@0x/types';
+import { signTypedDataUtils } from '@0x/utils';
import * as _ from 'lodash';
import { assert } from './assert';
-import { EIP712Utils } from './eip712_utils';
-import { EIP712Schema, EIP712Types } from './types';
+import { eip712Utils } from './eip712_utils';
const INVALID_TAKER_FORMAT = 'instance.takerAddress is not of a type(s) string';
-const EIP712_ORDER_SCHEMA: EIP712Schema = {
- name: 'Order',
- parameters: [
- { name: 'makerAddress', type: EIP712Types.Address },
- { name: 'takerAddress', type: EIP712Types.Address },
- { name: 'feeRecipientAddress', type: EIP712Types.Address },
- { name: 'senderAddress', type: EIP712Types.Address },
- { name: 'makerAssetAmount', type: EIP712Types.Uint256 },
- { name: 'takerAssetAmount', type: EIP712Types.Uint256 },
- { name: 'makerFee', type: EIP712Types.Uint256 },
- { name: 'takerFee', type: EIP712Types.Uint256 },
- { name: 'expirationTimeSeconds', type: EIP712Types.Uint256 },
- { name: 'salt', type: EIP712Types.Uint256 },
- { name: 'makerAssetData', type: EIP712Types.Bytes },
- { name: 'takerAssetData', type: EIP712Types.Bytes },
- ],
-};
-
export const orderHashUtils = {
/**
* Checks if the supplied hex encoded order hash is valid.
@@ -45,7 +27,7 @@ export const orderHashUtils = {
/**
* Computes the orderHash for a supplied order.
* @param order An object that conforms to the Order or SignedOrder interface definitions.
- * @return The resulting orderHash from hashing the supplied order.
+ * @return Hex encoded string orderHash from hashing the supplied order.
*/
getOrderHashHex(order: SignedOrder | Order): string {
try {
@@ -64,16 +46,13 @@ export const orderHashUtils = {
return orderHashHex;
},
/**
- * Computes the orderHash for a supplied order and returns it as a Buffer
+ * Computes the orderHash for a supplied order
* @param order An object that conforms to the Order or SignedOrder interface definitions.
- * @return The resulting orderHash from hashing the supplied order as a Buffer
+ * @return A Buffer containing the resulting orderHash from hashing the supplied order
*/
getOrderHashBuffer(order: SignedOrder | Order): Buffer {
- const orderParamsHashBuff = EIP712Utils.structHash(EIP712_ORDER_SCHEMA, order);
- const orderHashBuff = EIP712Utils.createEIP712Message(orderParamsHashBuff, order.exchangeAddress);
+ const typedData = eip712Utils.createOrderTypedData(order);
+ const orderHashBuff = signTypedDataUtils.generateTypedDataHash(typedData);
return orderHashBuff;
},
- _getOrderSchemaBuffer(): Buffer {
- return EIP712Utils.compileSchema(EIP712_ORDER_SCHEMA);
- },
};
diff --git a/packages/order-utils/src/order_state_utils.ts b/packages/order-utils/src/order_state_utils.ts
index 18fc18bf6..159aeeb09 100644
--- a/packages/order-utils/src/order_state_utils.ts
+++ b/packages/order-utils/src/order_state_utils.ts
@@ -5,8 +5,8 @@ import {
OrderStateInvalid,
OrderStateValid,
SignedOrder,
-} from '@0xproject/types';
-import { BigNumber } from '@0xproject/utils';
+} from '@0x/types';
+import { BigNumber } from '@0x/utils';
import { AbstractBalanceAndProxyAllowanceFetcher } from './abstract/abstract_balance_and_proxy_allowance_fetcher';
import { AbstractOrderFilledCancelledFetcher } from './abstract/abstract_order_filled_cancelled_fetcher';
@@ -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,
@@ -91,6 +91,14 @@ export class OrderStateUtils {
}
return { isValid: true };
}
+ /**
+ * Instantiate OrderStateUtils
+ * @param balanceAndProxyAllowanceFetcher A class that is capable of fetching balances
+ * and proxyAllowances for Ethereum addresses. It must implement AbstractBalanceAndProxyAllowanceFetcher
+ * @param orderFilledCancelledFetcher A class that is capable of fetching whether an order
+ * is cancelled and how much of it has been filled. It must implement AbstractOrderFilledCancelledFetcher
+ * @return Instance of OrderStateUtils
+ */
constructor(
balanceAndProxyAllowanceFetcher: AbstractBalanceAndProxyAllowanceFetcher,
orderFilledCancelledFetcher: AbstractOrderFilledCancelledFetcher,
@@ -98,7 +106,15 @@ export class OrderStateUtils {
this._balanceAndProxyAllowanceFetcher = balanceAndProxyAllowanceFetcher;
this._orderFilledCancelledFetcher = orderFilledCancelledFetcher;
}
- public async getOpenOrderStateAsync(signedOrder: SignedOrder): Promise<OrderState> {
+ /**
+ * Get the orderState for an "open" order (i.e where takerAddress=NULL_ADDRESS)
+ * This method will only check the maker's balance/allowance to calculate the
+ * OrderState.
+ * @param signedOrder The order of interest
+ * @return State relevant to the signedOrder, as well as whether the signedOrder is "valid".
+ * Validity is defined as a non-zero amount of the order can still be filled.
+ */
+ public async getOpenOrderStateAsync(signedOrder: SignedOrder, transactionHash?: string): Promise<OrderState> {
const orderRelevantState = await this.getOpenOrderRelevantStateAsync(signedOrder);
const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
const isOrderCancelled = await this._orderFilledCancelledFetcher.isOrderCancelledAsync(orderHash);
@@ -118,6 +134,7 @@ export class OrderStateUtils {
isValid: true,
orderHash,
orderRelevantState,
+ transactionHash,
};
return orderState;
} else {
@@ -125,10 +142,16 @@ export class OrderStateUtils {
isValid: false,
orderHash,
error: orderValidationResult.error,
+ transactionHash,
};
return orderState;
}
}
+ /**
+ * Get state relevant to an order (i.e makerBalance, makerAllowance, filledTakerAssetAmount, etc...
+ * @param signedOrder Order of interest
+ * @return An instance of OrderRelevantState
+ */
public async getOpenOrderRelevantStateAsync(signedOrder: SignedOrder): Promise<OrderRelevantState> {
const isMaker = true;
const sidedOrderRelevantState = await this._getSidedOrderRelevantStateAsync(
@@ -151,6 +174,12 @@ export class OrderStateUtils {
};
return orderRelevantState;
}
+ /**
+ * Get the max amount of the supplied order's takerAmount that could still be filled
+ * @param signedOrder Order of interest
+ * @param takerAddress Hypothetical taker of the order
+ * @return fillableTakerAssetAmount
+ */
public async getMaxFillableTakerAssetAmountAsync(
signedOrder: SignedOrder,
takerAddress: string,
@@ -164,7 +193,7 @@ export class OrderStateUtils {
);
const remainingFillableTakerAssetAmountGivenMakersStatus = signedOrder.makerAssetAmount.eq(0)
? new BigNumber(0)
- : utils.getPartialAmount(
+ : utils.getPartialAmountFloor(
orderRelevantMakerState.remainingFillableAssetAmount,
signedOrder.makerAssetAmount,
signedOrder.takerAssetAmount,
@@ -183,32 +212,6 @@ export class OrderStateUtils {
return fillableTakerAssetAmount;
}
- public async getMaxFillableTakerAssetAmountForFailingOrderAsync(
- signedOrder: SignedOrder,
- takerAddress: string,
- ): Promise<BigNumber> {
- // Get min of taker balance & allowance
- const takerAssetBalanceOfTaker = await this._balanceAndProxyAllowanceFetcher.getBalanceAsync(
- signedOrder.takerAssetData,
- takerAddress,
- );
- const takerAssetAllowanceOfTaker = await this._balanceAndProxyAllowanceFetcher.getProxyAllowanceAsync(
- signedOrder.takerAssetData,
- takerAddress,
- );
- const minTakerAssetAmount = BigNumber.min([takerAssetBalanceOfTaker, takerAssetAllowanceOfTaker]);
-
- // get remainingFillAmount
- const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
- const filledTakerAssetAmount = await this._orderFilledCancelledFetcher.getFilledTakerAmountAsync(orderHash);
- const remainingFillTakerAssetAmount = signedOrder.takerAssetAmount.minus(filledTakerAssetAmount);
-
- if (minTakerAssetAmount.gte(remainingFillTakerAssetAmount)) {
- return remainingFillTakerAssetAmount;
- } else {
- return minTakerAssetAmount;
- }
- }
private async _getSidedOrderRelevantStateAsync(
isMakerSide: boolean,
signedOrder: SignedOrder,
diff --git a/packages/order-utils/src/order_validation_utils.ts b/packages/order-utils/src/order_validation_utils.ts
index 67d747081..a40069f63 100644
--- a/packages/order-utils/src/order_validation_utils.ts
+++ b/packages/order-utils/src/order_validation_utils.ts
@@ -1,5 +1,5 @@
-import { RevertReason, SignedOrder } from '@0xproject/types';
-import { BigNumber } from '@0xproject/utils';
+import { RevertReason, SignedOrder } from '@0x/types';
+import { BigNumber } from '@0x/utils';
import { Provider } from 'ethereum-types';
import * as _ from 'lodash';
@@ -9,12 +9,22 @@ import { AbstractOrderFilledCancelledFetcher } from './abstract/abstract_order_f
import { constants } from './constants';
import { ExchangeTransferSimulator } from './exchange_transfer_simulator';
import { orderHashUtils } from './order_hash';
-import { isValidSignatureAsync } from './signature_utils';
+import { signatureUtils } from './signature_utils';
import { utils } from './utils';
+/**
+ * A utility class for validating orders
+ */
export class OrderValidationUtils {
private readonly _orderFilledCancelledFetcher: AbstractOrderFilledCancelledFetcher;
- public static isRoundingError(numerator: BigNumber, denominator: BigNumber, target: BigNumber): boolean {
+ /**
+ * A Typescript implementation mirroring the implementation of isRoundingError in the
+ * Exchange smart contract
+ * @param numerator Numerator value. When used to check an order, pass in `takerAssetFilledAmount`
+ * @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 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)) {
@@ -31,6 +41,15 @@ export class OrderValidationUtils {
const isError = errPercentageTimes1000000.gt(1000);
return isError;
}
+ /**
+ * Validate that the maker & taker have sufficient balances/allowances
+ * to fill the supplied order to the fillTakerAssetAmount amount
+ * @param exchangeTradeEmulator ExchangeTradeEmulator to use
+ * @param signedOrder SignedOrder to test
+ * @param fillTakerAssetAmount Amount of takerAsset to fill the signedOrder
+ * @param senderAddress Sender of the fillOrder tx
+ * @param zrxAssetData AssetData for the ZRX token
+ */
public static async validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
exchangeTradeEmulator: ExchangeTransferSimulator,
signedOrder: SignedOrder,
@@ -39,7 +58,7 @@ export class OrderValidationUtils {
zrxAssetData: string,
): Promise<void> {
try {
- const fillMakerTokenAmount = utils.getPartialAmount(
+ const fillMakerTokenAmount = utils.getPartialAmountFloor(
fillTakerAssetAmount,
signedOrder.takerAssetAmount,
signedOrder.makerAssetAmount,
@@ -60,7 +79,7 @@ export class OrderValidationUtils {
TradeSide.Taker,
TransferType.Trade,
);
- const makerFeeAmount = utils.getPartialAmount(
+ const makerFeeAmount = utils.getPartialAmountFloor(
fillTakerAssetAmount,
signedOrder.takerAssetAmount,
signedOrder.makerFee,
@@ -73,7 +92,7 @@ export class OrderValidationUtils {
TradeSide.Maker,
TransferType.Fee,
);
- const takerFeeAmount = utils.getPartialAmount(
+ const takerFeeAmount = utils.getPartialAmountFloor(
fillTakerAssetAmount,
signedOrder.takerAssetAmount,
signedOrder.takerFee,
@@ -104,9 +123,22 @@ export class OrderValidationUtils {
throw new Error(RevertReason.OrderUnfillable);
}
}
+ /**
+ * Instantiate OrderValidationUtils
+ * @param orderFilledCancelledFetcher A module that implements the AbstractOrderFilledCancelledFetcher
+ * @return An instance of OrderValidationUtils
+ */
constructor(orderFilledCancelledFetcher: AbstractOrderFilledCancelledFetcher) {
this._orderFilledCancelledFetcher = orderFilledCancelledFetcher;
}
+ /**
+ * Validate if the supplied order is fillable, and throw if it isn't
+ * @param exchangeTradeEmulator ExchangeTradeEmulator instance
+ * @param signedOrder SignedOrder of interest
+ * @param zrxAssetData ZRX assetData
+ * @param expectedFillTakerTokenAmount If supplied, this call will make sure this amount is fillable.
+ * If it isn't supplied, we check if the order is fillable for a non-zero amount
+ */
public async validateOrderFillableOrThrowAsync(
exchangeTradeEmulator: ExchangeTransferSimulator,
signedOrder: SignedOrder,
@@ -132,6 +164,15 @@ export class OrderValidationUtils {
zrxAssetData,
);
}
+ /**
+ * Validate a call to FillOrder and throw if it wouldn't succeed
+ * @param exchangeTradeEmulator ExchangeTradeEmulator to use
+ * @param provider Web3 provider to use for JSON RPC requests
+ * @param signedOrder SignedOrder of interest
+ * @param fillTakerAssetAmount Amount we'd like to fill the order for
+ * @param takerAddress The taker of the order
+ * @param zrxAssetData ZRX asset data
+ */
public async validateFillOrderThrowIfInvalidAsync(
exchangeTradeEmulator: ExchangeTransferSimulator,
provider: Provider,
@@ -147,7 +188,7 @@ export class OrderValidationUtils {
throw new Error(RevertReason.InvalidTakerAmount);
}
const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
- const isValid = await isValidSignatureAsync(
+ const isValid = await signatureUtils.isValidSignatureAsync(
provider,
orderHash,
signedOrder.signature,
@@ -177,7 +218,7 @@ export class OrderValidationUtils {
zrxAssetData,
);
- const wouldRoundingErrorOccur = OrderValidationUtils.isRoundingError(
+ const wouldRoundingErrorOccur = OrderValidationUtils.isRoundingErrorFloor(
desiredFillTakerTokenAmount,
signedOrder.takerAssetAmount,
signedOrder.makerAssetAmount,
@@ -187,6 +228,15 @@ export class OrderValidationUtils {
}
return filledTakerTokenAmount;
}
+ /**
+ * Validate a call to fillOrKillOrder and throw if it would fail
+ * @param exchangeTradeEmulator ExchangeTradeEmulator to use
+ * @param provider Web3 provider to use for JSON RPC requests
+ * @param signedOrder SignedOrder of interest
+ * @param fillTakerAssetAmount Amount we'd like to fill the order for
+ * @param takerAddress The taker of the order
+ * @param zrxAssetData ZRX asset data
+ */
public async validateFillOrKillOrderThrowIfInvalidAsync(
exchangeTradeEmulator: ExchangeTransferSimulator,
provider: Provider,
diff --git a/packages/order-utils/src/parsing_utils.ts b/packages/order-utils/src/parsing_utils.ts
new file mode 100644
index 000000000..98c6899fe
--- /dev/null
+++ b/packages/order-utils/src/parsing_utils.ts
@@ -0,0 +1,27 @@
+import { BigNumber } from '@0x/utils';
+import * as _ from 'lodash';
+
+export const orderParsingUtils = {
+ convertStringsFieldsToBigNumbers(obj: any, fields: string[]): any {
+ const result = _.assign({}, obj);
+ _.each(fields, field => {
+ _.update(result, field, (value: string) => {
+ if (_.isUndefined(value)) {
+ throw new Error(`Could not find field '${field}' while converting string fields to BigNumber.`);
+ }
+ return new BigNumber(value);
+ });
+ });
+ return result;
+ },
+ convertOrderStringFieldsToBigNumber(order: any): any {
+ return orderParsingUtils.convertStringsFieldsToBigNumbers(order, [
+ 'makerAssetAmount',
+ 'takerAssetAmount',
+ 'makerFee',
+ 'takerFee',
+ 'expirationTimeSeconds',
+ 'salt',
+ ]);
+ },
+};
diff --git a/packages/order-utils/src/rate_utils.ts b/packages/order-utils/src/rate_utils.ts
index c9ca72c59..416e00c67 100644
--- a/packages/order-utils/src/rate_utils.ts
+++ b/packages/order-utils/src/rate_utils.ts
@@ -1,6 +1,6 @@
-import { schemas } from '@0xproject/json-schemas';
-import { Order } from '@0xproject/types';
-import { BigNumber } from '@0xproject/utils';
+import { schemas } from '@0x/json-schemas';
+import { Order } from '@0x/types';
+import { BigNumber } from '@0x/utils';
import { assert } from './assert';
import { constants } from './constants';
diff --git a/packages/order-utils/src/remaining_fillable_calculator.ts b/packages/order-utils/src/remaining_fillable_calculator.ts
index 7022aa979..052eafa1d 100644
--- a/packages/order-utils/src/remaining_fillable_calculator.ts
+++ b/packages/order-utils/src/remaining_fillable_calculator.ts
@@ -1,4 +1,4 @@
-import { BigNumber } from '@0xproject/utils';
+import { BigNumber } from '@0x/utils';
export class RemainingFillableCalculator {
private readonly _isTraderAssetZRX: boolean;
diff --git a/packages/order-utils/src/salt.ts b/packages/order-utils/src/salt.ts
index 90a4197c0..ff47ab5d2 100644
--- a/packages/order-utils/src/salt.ts
+++ b/packages/order-utils/src/salt.ts
@@ -1,4 +1,4 @@
-import { BigNumber } from '@0xproject/utils';
+import { BigNumber } from '@0x/utils';
const MAX_DIGITS_IN_UNSIGNED_256_INT = 78;
diff --git a/packages/order-utils/src/signature_utils.ts b/packages/order-utils/src/signature_utils.ts
index 870aef2ed..96d90e21a 100644
--- a/packages/order-utils/src/signature_utils.ts
+++ b/packages/order-utils/src/signature_utils.ts
@@ -1,346 +1,375 @@
-import { schemas } from '@0xproject/json-schemas';
-import { ECSignature, SignatureType, SignerType, ValidatorSignature } from '@0xproject/types';
-import { Web3Wrapper } from '@0xproject/web3-wrapper';
+import { ExchangeContract, IValidatorContract, IWalletContract } from '@0x/abi-gen-wrappers';
+import * as artifacts from '@0x/contract-artifacts';
+import { schemas } from '@0x/json-schemas';
+import { ECSignature, Order, SignatureType, SignedOrder, ValidatorSignature } from '@0x/types';
+import { Web3Wrapper } from '@0x/web3-wrapper';
import { Provider } from 'ethereum-types';
import * as ethUtil from 'ethereumjs-util';
import * as _ from 'lodash';
-import { artifacts } from './artifacts';
import { assert } from './assert';
-import { ExchangeContract } from './generated_contract_wrappers/exchange';
-import { IValidatorContract } from './generated_contract_wrappers/i_validator';
-import { IWalletContract } from './generated_contract_wrappers/i_wallet';
+import { eip712Utils } from './eip712_utils';
+import { orderHashUtils } from './order_hash';
import { OrderError } from './types';
import { utils } from './utils';
-/**
- * Verifies that the provided signature is valid according to the 0x Protocol smart contracts
- * @param data The hex encoded data signed by the supplied signature.
- * @param signature A hex encoded 0x Protocol signature made up of: [TypeSpecificData][SignatureType].
- * E.g [vrs][SignatureType.EIP712]
- * @param signerAddress The hex encoded address that signed the data, producing the supplied signature.
- * @return Whether the signature is valid for the supplied signerAddress and data.
- */
-export async function isValidSignatureAsync(
- provider: Provider,
- data: string,
- signature: string,
- signerAddress: string,
-): Promise<boolean> {
- assert.isWeb3Provider('provider', provider);
- assert.isHexString('data', data);
- assert.isHexString('signature', signature);
- assert.isETHAddressHex('signerAddress', signerAddress);
- const signatureTypeIndexIfExists = utils.getSignatureTypeIndexIfExists(signature);
- if (_.isUndefined(signatureTypeIndexIfExists)) {
- throw new Error(`Unrecognized signatureType in signature: ${signature}`);
- }
+export const signatureUtils = {
+ /**
+ * Verifies that the provided signature is valid according to the 0x Protocol smart contracts
+ * @param data The hex encoded data signed by the supplied signature.
+ * @param signature A hex encoded 0x Protocol signature made up of: [TypeSpecificData][SignatureType].
+ * E.g [vrs][SignatureType.EIP712]
+ * @param signerAddress The hex encoded address that signed the data, producing the supplied signature.
+ * @return Whether the signature is valid for the supplied signerAddress and data.
+ */
+ async isValidSignatureAsync(
+ provider: Provider,
+ data: string,
+ signature: string,
+ signerAddress: string,
+ ): Promise<boolean> {
+ assert.isWeb3Provider('provider', provider);
+ assert.isHexString('data', data);
+ assert.isHexString('signature', signature);
+ assert.isETHAddressHex('signerAddress', signerAddress);
+ const signatureTypeIndexIfExists = utils.getSignatureTypeIndexIfExists(signature);
+ if (_.isUndefined(signatureTypeIndexIfExists)) {
+ throw new Error(`Unrecognized signatureType in signature: ${signature}`);
+ }
- switch (signatureTypeIndexIfExists) {
- case SignatureType.Illegal:
- case SignatureType.Invalid:
- return false;
+ switch (signatureTypeIndexIfExists) {
+ case SignatureType.Illegal:
+ case SignatureType.Invalid:
+ return false;
- case SignatureType.EIP712: {
- const ecSignature = parseECSignature(signature);
- return isValidECSignature(data, ecSignature, signerAddress);
- }
+ case SignatureType.EIP712: {
+ const ecSignature = signatureUtils.parseECSignature(signature);
+ return signatureUtils.isValidECSignature(data, ecSignature, signerAddress);
+ }
- case SignatureType.EthSign: {
- const ecSignature = parseECSignature(signature);
- const prefixedMessageHex = addSignedMessagePrefix(data, SignerType.Default);
- return isValidECSignature(prefixedMessageHex, ecSignature, signerAddress);
- }
+ case SignatureType.EthSign: {
+ const ecSignature = signatureUtils.parseECSignature(signature);
+ const prefixedMessageHex = signatureUtils.addSignedMessagePrefix(data);
+ 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,
+ data,
+ signature,
+ signerAddress,
+ );
+ return isValid;
+ }
- case SignatureType.Wallet: {
- const isValid = await isValidWalletSignatureAsync(provider, data, signature, signerAddress);
- return isValid;
- }
+ case SignatureType.Validator: {
+ const isValid = await signatureUtils.isValidValidatorSignatureAsync(
+ provider,
+ data,
+ signature,
+ signerAddress,
+ );
+ return isValid;
+ }
- case SignatureType.Validator: {
- const isValid = await isValidValidatorSignatureAsync(provider, data, signature, signerAddress);
- return isValid;
- }
+ case SignatureType.PreSigned: {
+ return signatureUtils.isValidPresignedSignatureAsync(provider, data, signerAddress);
+ }
- case SignatureType.PreSigned: {
- return isValidPresignedSignatureAsync(provider, data, signerAddress);
+ default:
+ throw new Error(`Unhandled SignatureType: ${signatureTypeIndexIfExists}`);
}
-
- case SignatureType.Trezor: {
- const prefixedMessageHex = addSignedMessagePrefix(data, SignerType.Trezor);
- const ecSignature = parseECSignature(signature);
- return isValidECSignature(prefixedMessageHex, ecSignature, signerAddress);
+ },
+ /**
+ * Verifies that the provided presigned signature is valid according to the 0x Protocol smart contracts
+ * @param provider Web3 provider to use for all JSON RPC requests
+ * @param data The hex encoded data signed by the supplied signature
+ * @param signerAddress The hex encoded address that signed the data, producing the supplied signature.
+ * @return Whether the data was preSigned by the supplied signerAddress
+ */
+ async isValidPresignedSignatureAsync(provider: Provider, data: string, signerAddress: string): Promise<boolean> {
+ assert.isWeb3Provider('provider', provider);
+ assert.isHexString('data', data);
+ assert.isETHAddressHex('signerAddress', signerAddress);
+ const exchangeContract = new ExchangeContract(artifacts.Exchange.compilerOutput.abi, signerAddress, provider);
+ const isValid = await exchangeContract.preSigned.callAsync(data, signerAddress);
+ return isValid;
+ },
+ /**
+ * Verifies that the provided wallet signature is valid according to the 0x Protocol smart contracts
+ * @param provider Web3 provider to use for all JSON RPC requests
+ * @param data The hex encoded data signed by the supplied signature.
+ * @param signature A hex encoded presigned 0x Protocol signature made up of: [SignatureType.Presigned]
+ * @param signerAddress The hex encoded address that signed the data, producing the supplied signature.
+ * @return Whether the data was preSigned by the supplied signerAddress.
+ */
+ async isValidWalletSignatureAsync(
+ provider: Provider,
+ data: string,
+ signature: string,
+ signerAddress: string,
+ ): Promise<boolean> {
+ assert.isWeb3Provider('provider', provider);
+ assert.isHexString('data', data);
+ assert.isHexString('signature', signature);
+ assert.isETHAddressHex('signerAddress', signerAddress);
+ // tslint:disable-next-line:custom-no-magic-numbers
+ const signatureWithoutType = signature.slice(-2);
+ const walletContract = new IWalletContract(artifacts.IWallet.compilerOutput.abi, signerAddress, provider);
+ const isValid = await walletContract.isValidSignature.callAsync(data, signatureWithoutType);
+ return isValid;
+ },
+ /**
+ * Verifies that the provided validator signature is valid according to the 0x Protocol smart contracts
+ * @param provider Web3 provider to use for all JSON RPC requests
+ * @param data The hex encoded data signed by the supplied signature.
+ * @param signature A hex encoded presigned 0x Protocol signature made up of: [SignatureType.Presigned]
+ * @param signerAddress The hex encoded address that signed the data, producing the supplied signature.
+ * @return Whether the data was preSigned by the supplied signerAddress.
+ */
+ async isValidValidatorSignatureAsync(
+ provider: Provider,
+ data: string,
+ signature: string,
+ signerAddress: string,
+ ): Promise<boolean> {
+ assert.isWeb3Provider('provider', provider);
+ assert.isHexString('data', data);
+ assert.isHexString('signature', signature);
+ assert.isETHAddressHex('signerAddress', signerAddress);
+ const validatorSignature = parseValidatorSignature(signature);
+ const exchangeContract = new ExchangeContract(artifacts.Exchange.compilerOutput.abi, signerAddress, provider);
+ const isValidatorApproved = await exchangeContract.allowedValidators.callAsync(
+ signerAddress,
+ validatorSignature.validatorAddress,
+ );
+ if (!isValidatorApproved) {
+ throw new Error(
+ `Validator ${validatorSignature.validatorAddress} was not pre-approved by ${signerAddress}.`,
+ );
}
- default:
- throw new Error(`Unhandled SignatureType: ${signatureTypeIndexIfExists}`);
- }
-}
-
-/**
- * Verifies that the provided presigned signature is valid according to the 0x Protocol smart contracts
- * @param data The hex encoded data signed by the supplied signature.
- * @param signature A hex encoded presigned 0x Protocol signature made up of: [SignatureType.Presigned]
- * @param signerAddress The hex encoded address that signed the data, producing the supplied signature.
- * @return Whether the data was preSigned by the supplied signerAddress.
- */
-export async function isValidPresignedSignatureAsync(
- provider: Provider,
- data: string,
- signerAddress: string,
-): Promise<boolean> {
- assert.isWeb3Provider('provider', provider);
- assert.isHexString('data', data);
- assert.isETHAddressHex('signerAddress', signerAddress);
- const exchangeContract = new ExchangeContract(artifacts.Exchange.compilerOutput.abi, signerAddress, provider);
- const isValid = await exchangeContract.preSigned.callAsync(data, signerAddress);
- return isValid;
-}
-
-/**
- * Verifies that the provided wallet signature is valid according to the 0x Protocol smart contracts
- * @param data The hex encoded data signed by the supplied signature.
- * @param signature A hex encoded presigned 0x Protocol signature made up of: [SignatureType.Presigned]
- * @param signerAddress The hex encoded address that signed the data, producing the supplied signature.
- * @return Whether the data was preSigned by the supplied signerAddress.
- */
-export async function isValidWalletSignatureAsync(
- provider: Provider,
- data: string,
- signature: string,
- signerAddress: string,
-): Promise<boolean> {
- assert.isWeb3Provider('provider', provider);
- assert.isHexString('data', data);
- assert.isHexString('signature', signature);
- assert.isETHAddressHex('signerAddress', signerAddress);
- // tslint:disable-next-line:custom-no-magic-numbers
- const signatureWithoutType = signature.slice(-2);
- const walletContract = new IWalletContract(artifacts.IWallet.compilerOutput.abi, signerAddress, provider);
- const isValid = await walletContract.isValidSignature.callAsync(data, signatureWithoutType);
- return isValid;
-}
-
-/**
- * Verifies that the provided validator signature is valid according to the 0x Protocol smart contracts
- * @param data The hex encoded data signed by the supplied signature.
- * @param signature A hex encoded presigned 0x Protocol signature made up of: [SignatureType.Presigned]
- * @param signerAddress The hex encoded address that signed the data, producing the supplied signature.
- * @return Whether the data was preSigned by the supplied signerAddress.
- */
-export async function isValidValidatorSignatureAsync(
- provider: Provider,
- data: string,
- signature: string,
- signerAddress: string,
-): Promise<boolean> {
- assert.isWeb3Provider('provider', provider);
- assert.isHexString('data', data);
- assert.isHexString('signature', signature);
- assert.isETHAddressHex('signerAddress', signerAddress);
- const validatorSignature = parseValidatorSignature(signature);
- const exchangeContract = new ExchangeContract(artifacts.Exchange.compilerOutput.abi, signerAddress, provider);
- const isValidatorApproved = await exchangeContract.allowedValidators.callAsync(
- signerAddress,
- validatorSignature.validatorAddress,
- );
- if (!isValidatorApproved) {
- throw new Error(`Validator ${validatorSignature.validatorAddress} was not pre-approved by ${signerAddress}.`);
- }
-
- const validatorContract = new IValidatorContract(artifacts.IValidator.compilerOutput.abi, signerAddress, provider);
- const isValid = await validatorContract.isValidSignature.callAsync(
- data,
- signerAddress,
- validatorSignature.signature,
- );
- return isValid;
-}
-
-/**
- * Checks if the supplied elliptic curve signature corresponds to signing `data` with
- * the private key corresponding to `signerAddress`
- * @param data The hex encoded data signed by the supplied signature.
- * @param signature An object containing the elliptic curve signature parameters.
- * @param signerAddress The hex encoded address that signed the data, producing the supplied signature.
- * @return Whether the ECSignature is valid.
- */
-export function isValidECSignature(data: string, signature: ECSignature, signerAddress: string): boolean {
- assert.isHexString('data', data);
- assert.doesConformToSchema('signature', signature, schemas.ecSignatureSchema);
- assert.isETHAddressHex('signerAddress', signerAddress);
-
- const msgHashBuff = ethUtil.toBuffer(data);
- try {
- const pubKey = ethUtil.ecrecover(
- msgHashBuff,
- signature.v,
- ethUtil.toBuffer(signature.r),
- ethUtil.toBuffer(signature.s),
+ const validatorContract = new IValidatorContract(
+ artifacts.IValidator.compilerOutput.abi,
+ signerAddress,
+ provider,
);
- const retrievedAddress = ethUtil.bufferToHex(ethUtil.pubToAddress(pubKey));
- return retrievedAddress === signerAddress;
- } catch (err) {
- return false;
- }
-}
-
-/**
- * Signs an orderHash and returns it's elliptic curve signature and signature type.
- * This method currently supports TestRPC, Geth and Parity above and below V1.6.6
- * @param orderHash Hex encoded orderHash to sign.
- * @param signerAddress The hex encoded Ethereum address you wish to sign it with. This address
- * must be available via the Provider supplied to 0x.js.
- * @param signerType Different signers add/require different prefixes to be prepended to the message being signed.
- * Since we cannot know ahead of time which signer you are using, you must supply a SignerType.
- * @return A hex encoded string containing the Elliptic curve signature generated by signing the orderHash and the Signature Type.
- */
-export async function ecSignOrderHashAsync(
- provider: Provider,
- orderHash: string,
- signerAddress: string,
- signerType: SignerType,
-): Promise<string> {
- assert.isWeb3Provider('provider', provider);
- assert.isHexString('orderHash', orderHash);
- assert.isETHAddressHex('signerAddress', signerAddress);
- const web3Wrapper = new Web3Wrapper(provider);
- await assert.isSenderAddressAsync('signerAddress', signerAddress, web3Wrapper);
- const normalizedSignerAddress = signerAddress.toLowerCase();
-
- let msgHashHex = orderHash;
- const prefixedMsgHashHex = addSignedMessagePrefix(orderHash, signerType);
- // Metamask incorrectly implements eth_sign and does not prefix the message as per the spec
- // Source: https://github.com/MetaMask/metamask-extension/commit/a9d36860bec424dcee8db043d3e7da6a5ff5672e
- if (signerType === SignerType.Metamask) {
- msgHashHex = prefixedMsgHashHex;
- }
- const signature = await web3Wrapper.signMessageAsync(normalizedSignerAddress, msgHashHex);
+ const isValid = await validatorContract.isValidSignature.callAsync(
+ data,
+ signerAddress,
+ validatorSignature.signature,
+ );
+ return isValid;
+ },
+ /**
+ * Checks if the supplied elliptic curve signature corresponds to signing `data` with
+ * the private key corresponding to `signerAddress`
+ * @param data The hex encoded data signed by the supplied signature.
+ * @param signature An object containing the elliptic curve signature parameters.
+ * @param signerAddress The hex encoded address that signed the data, producing the supplied signature.
+ * @return Whether the ECSignature is valid.
+ */
+ isValidECSignature(data: string, signature: ECSignature, signerAddress: string): boolean {
+ assert.isHexString('data', data);
+ assert.doesConformToSchema('signature', signature, schemas.ecSignatureSchema);
+ assert.isETHAddressHex('signerAddress', signerAddress);
+ const normalizedSignerAddress = signerAddress.toLowerCase();
- // HACK: There is no consensus on whether the signatureHex string should be formatted as
- // v + r + s OR r + s + v, and different clients (even different versions of the same client)
- // return the signature params in different orders. In order to support all client implementations,
- // we parse the signature in both ways, and evaluate if either one is a valid signature.
- // r + s + v is the most prevalent format from eth_sign, so we attempt this first.
- // tslint:disable-next-line:custom-no-magic-numbers
- const validVParamValues = [27, 28];
- const ecSignatureRSV = parseSignatureHexAsRSV(signature);
- if (_.includes(validVParamValues, ecSignatureRSV.v)) {
- const isValidRSVSignature = isValidECSignature(prefixedMsgHashHex, ecSignatureRSV, normalizedSignerAddress);
- if (isValidRSVSignature) {
- const convertedSignatureHex = convertECSignatureToSignatureHex(ecSignatureRSV, signerType);
- return convertedSignatureHex;
+ const msgHashBuff = ethUtil.toBuffer(data);
+ try {
+ const pubKey = ethUtil.ecrecover(
+ msgHashBuff,
+ signature.v,
+ ethUtil.toBuffer(signature.r),
+ ethUtil.toBuffer(signature.s),
+ );
+ const retrievedAddress = ethUtil.bufferToHex(ethUtil.pubToAddress(pubKey));
+ const normalizedRetrievedAddress = retrievedAddress.toLowerCase();
+ return normalizedRetrievedAddress === normalizedSignerAddress;
+ } catch (err) {
+ return false;
}
- }
- const ecSignatureVRS = parseSignatureHexAsVRS(signature);
- if (_.includes(validVParamValues, ecSignatureVRS.v)) {
- const isValidVRSSignature = isValidECSignature(prefixedMsgHashHex, ecSignatureVRS, normalizedSignerAddress);
- if (isValidVRSSignature) {
- const convertedSignatureHex = convertECSignatureToSignatureHex(ecSignatureVRS, signerType);
- return convertedSignatureHex;
+ },
+ /**
+ * Signs an order and returns a SignedOrder. First `eth_signTypedData` is requested
+ * then a fallback to `eth_sign` if not available on the supplied provider.
+ * @param order The Order to sign.
+ * @param signerAddress The hex encoded Ethereum address you wish to sign it with. This address
+ * must be available via the supplied Provider.
+ * @return A SignedOrder containing the order and Elliptic curve signature with Signature Type.
+ */
+ async ecSignOrderAsync(provider: Provider, order: Order, signerAddress: string): Promise<SignedOrder> {
+ assert.doesConformToSchema('order', order, schemas.orderSchema, [schemas.hexSchema]);
+ try {
+ const signedOrder = await signatureUtils.ecSignTypedDataOrderAsync(provider, order, signerAddress);
+ return signedOrder;
+ } catch (err) {
+ // HACK: We are unable to handle specific errors thrown since provider is not an object
+ // under our control. It could be Metamask Web3, Ethers, or any general RPC provider.
+ // We check for a user denying the signature request in a way that supports Metamask and
+ // Coinbase Wallet. Unfortunately for signers with a different error message,
+ // they will receive two signature requests.
+ if (err.message.includes('User denied message signature')) {
+ throw err;
+ }
+ const orderHash = orderHashUtils.getOrderHashHex(order);
+ const signatureHex = await signatureUtils.ecSignHashAsync(provider, orderHash, signerAddress);
+ const signedOrder = {
+ ...order,
+ signature: signatureHex,
+ };
+ return signedOrder;
}
- }
+ },
+ /**
+ * Signs an order using `eth_signTypedData` and returns a SignedOrder.
+ * @param order The Order to sign.
+ * @param signerAddress The hex encoded Ethereum address you wish to sign it with. This address
+ * must be available via the supplied Provider.
+ * @return A SignedOrder containing the order and Elliptic curve signature with Signature Type.
+ */
+ async ecSignTypedDataOrderAsync(provider: Provider, order: Order, signerAddress: string): Promise<SignedOrder> {
+ assert.isWeb3Provider('provider', provider);
+ assert.isETHAddressHex('signerAddress', signerAddress);
+ assert.doesConformToSchema('order', order, schemas.orderSchema, [schemas.hexSchema]);
+ const web3Wrapper = new Web3Wrapper(provider);
+ await assert.isSenderAddressAsync('signerAddress', signerAddress, web3Wrapper);
+ const normalizedSignerAddress = signerAddress.toLowerCase();
+ const typedData = eip712Utils.createOrderTypedData(order);
+ try {
+ const signature = await web3Wrapper.signTypedDataAsync(normalizedSignerAddress, typedData);
+ const ecSignatureRSV = parseSignatureHexAsRSV(signature);
+ const signatureBuffer = Buffer.concat([
+ ethUtil.toBuffer(ecSignatureRSV.v),
+ ethUtil.toBuffer(ecSignatureRSV.r),
+ ethUtil.toBuffer(ecSignatureRSV.s),
+ ethUtil.toBuffer(SignatureType.EIP712),
+ ]);
+ const signatureHex = `0x${signatureBuffer.toString('hex')}`;
+ return {
+ ...order,
+ signature: signatureHex,
+ };
+ } catch (err) {
+ // Detect if Metamask to transition users to the MetamaskSubprovider
+ if ((provider as any).isMetaMask) {
+ throw new Error(OrderError.InvalidMetamaskSigner);
+ } else {
+ throw err;
+ }
+ }
+ },
+ /**
+ * Signs a hash using `eth_sign` and returns its elliptic curve signature and signature type.
+ * @param msgHash Hex encoded message to sign.
+ * @param signerAddress The hex encoded Ethereum address you wish to sign it with. This address
+ * must be available via the supplied Provider.
+ * @return A hex encoded string containing the Elliptic curve signature generated by signing the msgHash and the Signature Type.
+ */
+ async ecSignHashAsync(provider: Provider, msgHash: string, signerAddress: string): Promise<string> {
+ assert.isWeb3Provider('provider', provider);
+ assert.isHexString('msgHash', msgHash);
+ assert.isETHAddressHex('signerAddress', signerAddress);
+ const web3Wrapper = new Web3Wrapper(provider);
+ await assert.isSenderAddressAsync('signerAddress', signerAddress, web3Wrapper);
+ const normalizedSignerAddress = signerAddress.toLowerCase();
+ const signature = await web3Wrapper.signMessageAsync(normalizedSignerAddress, msgHash);
+ const prefixedMsgHashHex = signatureUtils.addSignedMessagePrefix(msgHash);
- throw new Error(OrderError.InvalidSignature);
-}
-/**
- * Combines ECSignature with V,R,S and the relevant signature type for use in 0x protocol
- * @param ecSignature The ECSignature of the signed data
- * @param signerType The SignerType of the signed data
- * @return Hex encoded string of signature (v,r,s) with Signature Type
- */
-export function convertECSignatureToSignatureHex(ecSignature: ECSignature, signerType: SignerType): string {
- const signatureBuffer = Buffer.concat([
- ethUtil.toBuffer(ecSignature.v),
- ethUtil.toBuffer(ecSignature.r),
- ethUtil.toBuffer(ecSignature.s),
- ]);
- const signatureHex = `0x${signatureBuffer.toString('hex')}`;
- let signatureType;
- switch (signerType) {
- case SignerType.Metamask:
- case SignerType.Ledger:
- case SignerType.Default: {
- signatureType = SignatureType.EthSign;
- break;
+ // HACK: There is no consensus on whether the signatureHex string should be formatted as
+ // v + r + s OR r + s + v, and different clients (even different versions of the same client)
+ // return the signature params in different orders. In order to support all client implementations,
+ // we parse the signature in both ways, and evaluate if either one is a valid signature.
+ // r + s + v is the most prevalent format from eth_sign, so we attempt this first.
+ // tslint:disable-next-line:custom-no-magic-numbers
+ const validVParamValues = [27, 28];
+ const ecSignatureRSV = parseSignatureHexAsRSV(signature);
+ if (_.includes(validVParamValues, ecSignatureRSV.v)) {
+ const isValidRSVSignature = signatureUtils.isValidECSignature(
+ prefixedMsgHashHex,
+ ecSignatureRSV,
+ normalizedSignerAddress,
+ );
+ if (isValidRSVSignature) {
+ const convertedSignatureHex = signatureUtils.convertECSignatureToSignatureHex(ecSignatureRSV);
+ return convertedSignatureHex;
+ }
}
- case SignerType.Trezor: {
- signatureType = SignatureType.Trezor;
- break;
+ const ecSignatureVRS = parseSignatureHexAsVRS(signature);
+ if (_.includes(validVParamValues, ecSignatureVRS.v)) {
+ const isValidVRSSignature = signatureUtils.isValidECSignature(
+ prefixedMsgHashHex,
+ ecSignatureVRS,
+ normalizedSignerAddress,
+ );
+ if (isValidVRSSignature) {
+ const convertedSignatureHex = signatureUtils.convertECSignatureToSignatureHex(ecSignatureVRS);
+ return convertedSignatureHex;
+ }
}
- default:
- throw new Error(`Unrecognized SignerType: ${signerType}`);
- }
- const signatureWithType = convertToSignatureWithType(signatureHex, signatureType);
- return signatureWithType;
-}
-/**
- * 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.
- * @return Hex encoded string of signature proof with Signature Type
- */
-export function convertToSignatureWithType(signature: string, signatureType: SignatureType): string {
- const signatureBuffer = Buffer.concat([ethUtil.toBuffer(signature), ethUtil.toBuffer(signatureType)]);
- const signatureHex = `0x${signatureBuffer.toString('hex')}`;
- return signatureHex;
-}
-/**
- * Adds the relevant prefix to the message being signed.
- * @param message Message to sign
- * @param signerType The type of message prefix to add for a given SignerType. Different signers expect
- * specific message prefixes.
- * @return Prefixed message
- */
-export function addSignedMessagePrefix(message: string, signerType: SignerType = SignerType.Default): string {
- assert.isString('message', message);
- assert.doesBelongToStringEnum('signerType', signerType, SignerType);
- switch (signerType) {
- case SignerType.Metamask:
- case SignerType.Ledger:
- case SignerType.Default: {
- const msgBuff = ethUtil.toBuffer(message);
- const prefixedMsgBuff = ethUtil.hashPersonalMessage(msgBuff);
- 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;
+ // Detect if Metamask to transition users to the MetamaskSubprovider
+ if ((provider as any).isMetaMask) {
+ throw new Error(OrderError.InvalidMetamaskSigner);
+ } else {
+ throw new Error(OrderError.InvalidSignature);
}
- default:
- throw new Error(`Unrecognized SignerType: ${signerType}`);
- }
-}
+ },
+ /**
+ * Combines ECSignature with V,R,S and the EthSign signature type for use in 0x protocol
+ * @param ecSignature The ECSignature of the signed data
+ * @return Hex encoded string of signature (v,r,s) with Signature Type
+ */
+ convertECSignatureToSignatureHex(ecSignature: ECSignature): string {
+ const signatureBuffer = Buffer.concat([
+ ethUtil.toBuffer(ecSignature.v),
+ ethUtil.toBuffer(ecSignature.r),
+ ethUtil.toBuffer(ecSignature.s),
+ ]);
+ const signatureHex = `0x${signatureBuffer.toString('hex')}`;
+ const signatureWithType = signatureUtils.convertToSignatureWithType(signatureHex, SignatureType.EthSign);
+ return signatureWithType;
+ },
+ /**
+ * Combines the signature proof and the Signature Type.
+ * @param signature The hex encoded signature proof
+ * @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 {
+ const signatureBuffer = Buffer.concat([ethUtil.toBuffer(signature), ethUtil.toBuffer(signatureType)]);
+ const signatureHex = `0x${signatureBuffer.toString('hex')}`;
+ return signatureHex;
+ },
+ /**
+ * Adds the relevant prefix to the message being signed.
+ * @param message Message to sign
+ * @return Prefixed message
+ */
+ addSignedMessagePrefix(message: string): string {
+ assert.isString('message', message);
+ const msgBuff = ethUtil.toBuffer(message);
+ const prefixedMsgBuff = ethUtil.hashPersonalMessage(msgBuff);
+ const prefixedMsgHex = ethUtil.bufferToHex(prefixedMsgBuff);
+ return prefixedMsgHex;
+ },
+ /**
+ * Parse a 0x protocol hex-encoded signature string into its ECSignature components
+ * @param signature A hex encoded ecSignature 0x Protocol signature
+ * @return An ECSignature object with r,s,v parameters
+ */
+ parseECSignature(signature: string): ECSignature {
+ assert.isHexString('signature', signature);
+ const ecSignatureTypes = [SignatureType.EthSign, SignatureType.EIP712];
+ assert.isOneOfExpectedSignatureTypes(signature, ecSignatureTypes);
-/**
- * Parse a 0x protocol hex-encoded signature string into it's ECSignature components
- * @param signature A hex encoded ecSignature 0x Protocol signature
- * @return An ECSignature object with r,s,v parameters
- */
-export function parseECSignature(signature: string): ECSignature {
- assert.isHexString('signature', signature);
- const ecSignatureTypes = [SignatureType.EthSign, SignatureType.EIP712, SignatureType.Trezor];
- assert.isOneOfExpectedSignatureTypes(signature, ecSignatureTypes);
+ // tslint:disable-next-line:custom-no-magic-numbers
+ const vrsHex = signature.slice(0, -2);
+ const ecSignature = parseSignatureHexAsVRS(vrsHex);
- // tslint:disable-next-line:custom-no-magic-numbers
- const vrsHex = signature.slice(0, -2);
- const ecSignature = parseSignatureHexAsVRS(vrsHex);
-
- return ecSignature;
-}
-
-function hashTrezorPersonalMessage(message: Buffer): Buffer {
- const prefix = ethUtil.toBuffer('\x19Ethereum Signed Message:\n' + String.fromCharCode(message.byteLength));
- return ethUtil.sha3(Buffer.concat([prefix, message]));
-}
+ return ecSignature;
+ },
+};
function parseValidatorSignature(signature: string): ValidatorSignature {
assert.isOneOfExpectedSignatureTypes(signature, [SignatureType.Validator]);
diff --git a/packages/order-utils/src/sorting_utils.ts b/packages/order-utils/src/sorting_utils.ts
index 8811bcaf8..1de24264f 100644
--- a/packages/order-utils/src/sorting_utils.ts
+++ b/packages/order-utils/src/sorting_utils.ts
@@ -1,6 +1,6 @@
-import { schemas } from '@0xproject/json-schemas';
-import { Order } from '@0xproject/types';
-import { BigNumber } from '@0xproject/utils';
+import { schemas } from '@0x/json-schemas';
+import { Order } from '@0x/types';
+import { BigNumber } from '@0x/utils';
import * as _ from 'lodash';
import { assert } from './assert';
@@ -32,7 +32,7 @@ export const sortingUtils = {
* the makerAsset and WETH as the takerAsset.
* @return The input orders sorted by rate in ascending order
*/
- sortFeeOrdersByFeeAdjustedRate(feeOrders: Order[]): Order[] {
+ sortFeeOrdersByFeeAdjustedRate<T extends Order>(feeOrders: T[]): T[] {
assert.doesConformToSchema('feeOrders', feeOrders, schemas.ordersSchema);
const rateCalculator = rateUtils.getFeeAdjustedRateOfFeeOrder.bind(rateUtils);
const sortedOrders = sortOrders(feeOrders, rateCalculator);
diff --git a/packages/order-utils/src/store/balance_and_proxy_allowance_lazy_store.ts b/packages/order-utils/src/store/balance_and_proxy_allowance_lazy_store.ts
index 5a2c1d7ff..f42a76d0c 100644
--- a/packages/order-utils/src/store/balance_and_proxy_allowance_lazy_store.ts
+++ b/packages/order-utils/src/store/balance_and_proxy_allowance_lazy_store.ts
@@ -1,5 +1,5 @@
-import { AssetProxyId } from '@0xproject/types';
-import { BigNumber } from '@0xproject/utils';
+import { AssetProxyId } from '@0x/types';
+import { BigNumber } from '@0x/utils';
import * as _ from 'lodash';
import { AbstractBalanceAndProxyAllowanceFetcher } from '../abstract/abstract_balance_and_proxy_allowance_fetcher';
@@ -21,11 +21,21 @@ export class BalanceAndProxyAllowanceLazyStore implements AbstractBalanceAndProx
[userAddress: string]: BigNumber;
};
};
+ /**
+ * Instantiates a BalanceAndProxyAllowanceLazyStore
+ * @param balanceAndProxyAllowanceFetcher Class the implements the AbstractBalanceAndProxyAllowanceFetcher
+ * @return Instance of BalanceAndProxyAllowanceLazyStore
+ */
constructor(balanceAndProxyAllowanceFetcher: AbstractBalanceAndProxyAllowanceFetcher) {
this._balanceAndProxyAllowanceFetcher = balanceAndProxyAllowanceFetcher;
this._balance = {};
this._proxyAllowance = {};
}
+ /**
+ * Get a users balance of an asset
+ * @param assetData AssetData of interest
+ * @param userAddress Ethereum address of interest
+ */
public async getBalanceAsync(assetData: string, userAddress: string): Promise<BigNumber> {
if (_.isUndefined(this._balance[assetData]) || _.isUndefined(this._balance[assetData][userAddress])) {
const balance = await this._balanceAndProxyAllowanceFetcher.getBalanceAsync(assetData, userAddress);
@@ -34,12 +44,22 @@ export class BalanceAndProxyAllowanceLazyStore implements AbstractBalanceAndProx
const cachedBalance = this._balance[assetData][userAddress];
return cachedBalance;
}
+ /**
+ * Set the balance of an asset for a user
+ * @param assetData AssetData of interest
+ * @param userAddress Ethereum address of interest
+ */
public setBalance(assetData: string, userAddress: string, balance: BigNumber): void {
if (_.isUndefined(this._balance[assetData])) {
this._balance[assetData] = {};
}
this._balance[assetData][userAddress] = balance;
}
+ /**
+ * Clear the balance of an asset for a user
+ * @param assetData AssetData of interest
+ * @param userAddress Ethereum address of interest
+ */
public deleteBalance(assetData: string, userAddress: string): void {
if (!_.isUndefined(this._balance[assetData])) {
delete this._balance[assetData][userAddress];
@@ -48,6 +68,11 @@ export class BalanceAndProxyAllowanceLazyStore implements AbstractBalanceAndProx
}
}
}
+ /**
+ * Get the 0x asset proxy allowance
+ * @param assetData AssetData of interest
+ * @param userAddress Ethereum address of interest
+ */
public async getProxyAllowanceAsync(assetData: string, userAddress: string): Promise<BigNumber> {
if (
_.isUndefined(this._proxyAllowance[assetData]) ||
@@ -62,12 +87,22 @@ export class BalanceAndProxyAllowanceLazyStore implements AbstractBalanceAndProx
const cachedProxyAllowance = this._proxyAllowance[assetData][userAddress];
return cachedProxyAllowance;
}
+ /**
+ * Set the 0x asset proxy allowance
+ * @param assetData AssetData of interest
+ * @param userAddress Ethereum address of interest
+ */
public setProxyAllowance(assetData: string, userAddress: string, proxyAllowance: BigNumber): void {
if (_.isUndefined(this._proxyAllowance[assetData])) {
this._proxyAllowance[assetData] = {};
}
this._proxyAllowance[assetData][userAddress] = proxyAllowance;
}
+ /**
+ * Clear the 0x asset proxy allowance
+ * @param assetData AssetData of interest
+ * @param userAddress Ethereum address of interest
+ */
public deleteProxyAllowance(assetData: string, userAddress: string): void {
if (!_.isUndefined(this._proxyAllowance[assetData])) {
delete this._proxyAllowance[assetData][userAddress];
@@ -76,6 +111,11 @@ export class BalanceAndProxyAllowanceLazyStore implements AbstractBalanceAndProx
}
}
}
+ /**
+ * Clear all ERC721 0x proxy allowances a user has on all items of a specific ERC721 contract
+ * @param tokenAddress ERc721 token address
+ * @param userAddress Owner Ethereum address
+ */
public deleteAllERC721ProxyAllowance(tokenAddress: string, userAddress: string): void {
for (const assetData in this._proxyAllowance) {
if (this._proxyAllowance.hasOwnProperty(assetData)) {
@@ -90,6 +130,9 @@ export class BalanceAndProxyAllowanceLazyStore implements AbstractBalanceAndProx
}
}
}
+ /**
+ * Delete all balances & allowances
+ */
public deleteAll(): void {
this._balance = {};
this._proxyAllowance = {};
diff --git a/packages/order-utils/src/store/order_filled_cancelled_lazy_store.ts b/packages/order-utils/src/store/order_filled_cancelled_lazy_store.ts
index 336c6d0ba..1d84ffdaa 100644
--- a/packages/order-utils/src/store/order_filled_cancelled_lazy_store.ts
+++ b/packages/order-utils/src/store/order_filled_cancelled_lazy_store.ts
@@ -1,4 +1,4 @@
-import { BigNumber } from '@0xproject/utils';
+import { BigNumber } from '@0x/utils';
import * as _ from 'lodash';
import { AbstractOrderFilledCancelledFetcher } from '../abstract/abstract_order_filled_cancelled_fetcher';
@@ -15,11 +15,21 @@ export class OrderFilledCancelledLazyStore implements AbstractOrderFilledCancell
private _isCancelled: {
[orderHash: string]: boolean;
};
+ /**
+ * Instantiate a OrderFilledCancelledLazyStore
+ * @param orderFilledCancelledFetcher Class instance that implements the AbstractOrderFilledCancelledFetcher
+ * @returns An instance of OrderFilledCancelledLazyStore
+ */
constructor(orderFilledCancelledFetcher: AbstractOrderFilledCancelledFetcher) {
this._orderFilledCancelledFetcher = orderFilledCancelledFetcher;
this._filledTakerAmount = {};
this._isCancelled = {};
}
+ /**
+ * Get the filledTakerAssetAmount of an order
+ * @param orderHash OrderHash from order of interest
+ * @return filledTakerAssetAmount
+ */
public async getFilledTakerAmountAsync(orderHash: string): Promise<BigNumber> {
if (_.isUndefined(this._filledTakerAmount[orderHash])) {
const filledTakerAmount = await this._orderFilledCancelledFetcher.getFilledTakerAmountAsync(orderHash);
@@ -28,12 +38,26 @@ export class OrderFilledCancelledLazyStore implements AbstractOrderFilledCancell
const cachedFilledTakerAmount = this._filledTakerAmount[orderHash];
return cachedFilledTakerAmount;
}
+ /**
+ * Set the filledTakerAssetAmount of an order
+ * @param orderHash OrderHash from order of interest
+ * @param filledTakerAmount Desired filledTakerAssetAmount
+ */
public setFilledTakerAmount(orderHash: string, filledTakerAmount: BigNumber): void {
this._filledTakerAmount[orderHash] = filledTakerAmount;
}
+ /**
+ * Clear the filledTakerAssetAmount of an order
+ * @param orderHash OrderHash from order of interest
+ */
public deleteFilledTakerAmount(orderHash: string): void {
delete this._filledTakerAmount[orderHash];
}
+ /**
+ * Check if an order has been cancelled
+ * @param orderHash OrderHash from order of interest
+ * @return Whether the order has been cancelled
+ */
public async getIsCancelledAsync(orderHash: string): Promise<boolean> {
if (_.isUndefined(this._isCancelled[orderHash])) {
const isCancelled = await this._orderFilledCancelledFetcher.isOrderCancelledAsync(orderHash);
@@ -42,22 +66,43 @@ export class OrderFilledCancelledLazyStore implements AbstractOrderFilledCancell
const cachedIsCancelled = this._isCancelled[orderHash]; // tslint:disable-line:boolean-naming
return cachedIsCancelled;
}
+ /**
+ * Set whether an order has been cancelled or not
+ * @param orderHash OrderHash from order of interest
+ * @param isCancelled Whether this order should be cancelled or not
+ */
public setIsCancelled(orderHash: string, isCancelled: boolean): void {
this._isCancelled[orderHash] = isCancelled;
}
+ /**
+ * Clear whether the order has been cancelled if already set
+ * @param orderHash OrderHash from order of interest
+ */
public deleteIsCancelled(orderHash: string): void {
delete this._isCancelled[orderHash];
}
+ /**
+ * Clear all filled/cancelled state
+ */
public deleteAll(): void {
this.deleteAllFilled();
this.deleteAllIsCancelled();
}
+ /**
+ * Clear all cancelled state
+ */
public deleteAllIsCancelled(): void {
this._isCancelled = {};
}
+ /**
+ * Clear all filled state
+ */
public deleteAllFilled(): void {
this._filledTakerAmount = {};
}
+ /**
+ * Get the ZRX assetData
+ */
public getZRXAssetData(): string {
const zrxAssetData = this._orderFilledCancelledFetcher.getZRXAssetData();
return zrxAssetData;
diff --git a/packages/order-utils/src/types.ts b/packages/order-utils/src/types.ts
index 2e9c79d80..55ec553db 100644
--- a/packages/order-utils/src/types.ts
+++ b/packages/order-utils/src/types.ts
@@ -1,7 +1,8 @@
-import { BigNumber } from '@0xproject/utils';
+import { BigNumber } from '@0x/utils';
export enum OrderError {
InvalidSignature = 'INVALID_SIGNATURE',
+ InvalidMetamaskSigner = "MetaMask provider must be wrapped in a MetamaskSubprovider (from the '@0x/subproviders' package) in order to work with this method.",
}
export enum TradeSide {
@@ -14,24 +15,6 @@ export enum TransferType {
Fee = 'fee',
}
-export interface EIP712Parameter {
- name: string;
- type: EIP712Types;
-}
-
-export interface EIP712Schema {
- name: string;
- parameters: EIP712Parameter[];
-}
-
-export enum EIP712Types {
- Address = 'address',
- Bytes = 'bytes',
- Bytes32 = 'bytes32',
- String = 'string',
- Uint256 = 'uint256',
-}
-
export interface CreateOrderOpts {
takerAddress?: string;
senderAddress?: string;
@@ -44,7 +27,7 @@ export interface CreateOrderOpts {
/**
* remainingFillableMakerAssetAmount: An array of BigNumbers corresponding to the `orders` parameter.
- * You can use `OrderStateUtils` `@0xproject/order-utils` to perform blockchain lookups for these values.
+ * You can use `OrderStateUtils` `@0x/order-utils` to perform blockchain lookups for these values.
* Defaults to `makerAssetAmount` values from the orders param.
* slippageBufferAmount: An additional amount of makerAsset to be covered by the result in case of trade collisions or partial fills.
* Defaults to 0
@@ -56,10 +39,10 @@ export interface FindOrdersThatCoverMakerAssetFillAmountOpts {
/**
* remainingFillableMakerAssetAmount: An array of BigNumbers corresponding to the `orders` parameter.
- * You can use `OrderStateUtils` `@0xproject/order-utils` to perform blockchain lookups for these values.
+ * You can use `OrderStateUtils` `@0x/order-utils` to perform blockchain lookups for these values.
* Defaults to `makerAssetAmount` values from the orders param.
* remainingFillableFeeAmounts: An array of BigNumbers corresponding to the feeOrders parameter.
- * You can use OrderStateUtils @0xproject/order-utils to perform blockchain lookups for these values.
+ * You can use OrderStateUtils @0x/order-utils to perform blockchain lookups for these values.
* Defaults to `makerAssetAmount` values from the feeOrders param.
* slippageBufferAmount: An additional amount of fee to be covered by the result in case of trade collisions or partial fills.
* Defaults to 0
@@ -69,3 +52,15 @@ export interface FindFeeOrdersThatCoverFeesForTargetOrdersOpts {
remainingFillableFeeAmounts?: BigNumber[];
slippageBufferAmount?: BigNumber;
}
+
+export interface FeeOrdersAndRemainingFeeAmount<T> {
+ resultFeeOrders: T[];
+ feeOrdersRemainingFillableMakerAssetAmounts: BigNumber[];
+ remainingFeeAmount: BigNumber;
+}
+
+export interface OrdersAndRemainingFillAmount<T> {
+ resultOrders: T[];
+ ordersRemainingFillableMakerAssetAmounts: BigNumber[];
+ remainingFillAmount: BigNumber;
+}
diff --git a/packages/order-utils/src/utils.ts b/packages/order-utils/src/utils.ts
index 7aaaf0609..6b2261001 100644
--- a/packages/order-utils/src/utils.ts
+++ b/packages/order-utils/src/utils.ts
@@ -1,4 +1,4 @@
-import { BigNumber } from '@0xproject/utils';
+import { BigNumber } from '@0x/utils';
export const utils = {
getSignatureTypeIndexIfExists(signature: string): number {
@@ -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)
diff --git a/packages/order-utils/test/asset_data_utils_test.ts b/packages/order-utils/test/asset_data_utils_test.ts
new file mode 100644
index 000000000..f175b7a38
--- /dev/null
+++ b/packages/order-utils/test/asset_data_utils_test.ts
@@ -0,0 +1,50 @@
+import * as chai from 'chai';
+
+import { ERC20AssetData, ERC721AssetData } from '@0x/types';
+import { BigNumber } from '@0x/utils';
+
+import { assetDataUtils } from '../src/asset_data_utils';
+
+import { chaiSetup } from './utils/chai_setup';
+
+chaiSetup.configure();
+const expect = chai.expect;
+
+const KNOWN_ENCODINGS = [
+ {
+ address: '0x1dc4c1cefef38a777b15aa20260a54e584b16c48',
+ assetData: '0xf47261b00000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48',
+ },
+ {
+ address: '0x1dc4c1cefef38a777b15aa20260a54e584b16c48',
+ tokenId: new BigNumber(1),
+ assetData:
+ '0x025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c480000000000000000000000000000000000000000000000000000000000000001',
+ },
+];
+
+const ERC20_ASSET_PROXY_ID = '0xf47261b0';
+const ERC721_ASSET_PROXY_ID = '0x02571792';
+
+describe('assetDataUtils', () => {
+ it('should encode ERC20', () => {
+ const assetData = assetDataUtils.encodeERC20AssetData(KNOWN_ENCODINGS[0].address);
+ expect(assetData).to.equal(KNOWN_ENCODINGS[0].assetData);
+ });
+ it('should decode ERC20', () => {
+ const assetData: ERC20AssetData = assetDataUtils.decodeERC20AssetData(KNOWN_ENCODINGS[0].assetData);
+ expect(assetData.tokenAddress).to.equal(KNOWN_ENCODINGS[0].address);
+ expect(assetData.assetProxyId).to.equal(ERC20_ASSET_PROXY_ID);
+ });
+ it('should encode ERC721', () => {
+ const assetData = assetDataUtils.encodeERC721AssetData(KNOWN_ENCODINGS[1].address, KNOWN_ENCODINGS[1]
+ .tokenId as BigNumber);
+ expect(assetData).to.equal(KNOWN_ENCODINGS[1].assetData);
+ });
+ it('should decode ERC721', () => {
+ const assetData: ERC721AssetData = assetDataUtils.decodeERC721AssetData(KNOWN_ENCODINGS[1].assetData);
+ expect(assetData.tokenAddress).to.equal(KNOWN_ENCODINGS[1].address);
+ expect(assetData.assetProxyId).to.equal(ERC721_ASSET_PROXY_ID);
+ expect(assetData.tokenId).to.be.bignumber.equal(KNOWN_ENCODINGS[1].tokenId);
+ });
+});
diff --git a/packages/order-utils/test/eip712_utils_test.ts b/packages/order-utils/test/eip712_utils_test.ts
new file mode 100644
index 000000000..a54e49958
--- /dev/null
+++ b/packages/order-utils/test/eip712_utils_test.ts
@@ -0,0 +1,44 @@
+import { BigNumber } from '@0x/utils';
+import * as chai from 'chai';
+import 'mocha';
+
+import { constants } from '../src/constants';
+import { eip712Utils } from '../src/eip712_utils';
+
+import { chaiSetup } from './utils/chai_setup';
+
+chaiSetup.configure();
+const expect = chai.expect;
+
+describe('EIP712 Utils', () => {
+ describe('createTypedData', () => {
+ it('adds in the EIP712DomainSeparator', () => {
+ const primaryType = 'Test';
+ const typedData = eip712Utils.createTypedData(
+ primaryType,
+ { Test: [{ name: 'testValue', type: 'uint256' }] },
+ { testValue: '1' },
+ constants.NULL_ADDRESS,
+ );
+ expect(typedData.domain).to.not.be.undefined();
+ expect(typedData.types.EIP712Domain).to.not.be.undefined();
+ const domainObject = typedData.domain;
+ expect(domainObject.name).to.eq(constants.EIP712_DOMAIN_NAME);
+ expect(typedData.primaryType).to.eq(primaryType);
+ });
+ });
+ describe('createTypedData', () => {
+ it('adds in the EIP712DomainSeparator', () => {
+ const typedData = eip712Utils.createZeroExTransactionTypedData(
+ {
+ salt: new BigNumber('0'),
+ data: constants.NULL_BYTES,
+ signerAddress: constants.NULL_ADDRESS,
+ },
+ constants.NULL_ADDRESS,
+ );
+ expect(typedData.primaryType).to.eq(constants.EIP712_ZEROEX_TRANSACTION_SCHEMA.name);
+ expect(typedData.types.EIP712Domain).to.not.be.undefined();
+ });
+ });
+});
diff --git a/packages/order-utils/test/exchange_transfer_simulator_test.ts b/packages/order-utils/test/exchange_transfer_simulator_test.ts
index f5c18cdb9..c26eb1907 100644
--- a/packages/order-utils/test/exchange_transfer_simulator_test.ts
+++ b/packages/order-utils/test/exchange_transfer_simulator_test.ts
@@ -1,15 +1,13 @@
-import { BlockchainLifecycle, devConstants } from '@0xproject/dev-utils';
-import { ExchangeContractErrs } from '@0xproject/types';
-import { BigNumber } from '@0xproject/utils';
+import { DummyERC20TokenContract, ERC20ProxyContract, ERC20TokenContract } from '@0x/abi-gen-wrappers';
+import * as artifacts from '@0x/contract-artifacts';
+import { BlockchainLifecycle, devConstants } from '@0x/dev-utils';
+import { ExchangeContractErrs } from '@0x/types';
+import { BigNumber } from '@0x/utils';
import * as chai from 'chai';
-import { artifacts } from '../src/artifacts';
import { assetDataUtils } from '../src/asset_data_utils';
import { constants } from '../src/constants';
import { ExchangeTransferSimulator } from '../src/exchange_transfer_simulator';
-import { DummyERC20TokenContract } from '../src/generated_contract_wrappers/dummy_erc20_token';
-import { ERC20ProxyContract } from '../src/generated_contract_wrappers/erc20_proxy';
-import { ERC20TokenContract } from '../src/generated_contract_wrappers/erc20_token';
import { BalanceAndProxyAllowanceLazyStore } from '../src/store/balance_and_proxy_allowance_lazy_store';
import { TradeSide, TransferType } from '../src/types';
diff --git a/packages/order-utils/test/market_utils_test.ts b/packages/order-utils/test/market_utils_test.ts
index 109420a02..42ea195bb 100644
--- a/packages/order-utils/test/market_utils_test.ts
+++ b/packages/order-utils/test/market_utils_test.ts
@@ -1,8 +1,9 @@
-import { BigNumber } from '@0xproject/utils';
+import { BigNumber } from '@0x/utils';
import * as chai from 'chai';
import 'mocha';
-import { constants, marketUtils } from '../src';
+import { marketUtils } from '../src';
+import { constants } from '../src/constants';
import { chaiSetup } from './utils/chai_setup';
import { testOrderFactory } from './utils/test_order_factory';
@@ -139,11 +140,11 @@ describe('marketUtils', () => {
);
describe('no target orders', () => {
it('returns empty and zero remainingFeeAmount', async () => {
- const { resultOrders, remainingFeeAmount } = marketUtils.findFeeOrdersThatCoverFeesForTargetOrders(
+ const { resultFeeOrders, remainingFeeAmount } = marketUtils.findFeeOrdersThatCoverFeesForTargetOrders(
[],
inputFeeOrders,
);
- expect(resultOrders).to.be.empty;
+ expect(resultFeeOrders).to.be.empty;
expect(remainingFeeAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT);
});
});
@@ -162,14 +163,14 @@ describe('marketUtils', () => {
// generate remainingFillableMakerAssetAmounts that equal the makerAssetAmount
const remainingFillableMakerAssetAmounts = [makerAssetAmount, makerAssetAmount, makerAssetAmount];
it('returns empty and non-zero remainingFeeAmount', async () => {
- const { resultOrders, remainingFeeAmount } = marketUtils.findFeeOrdersThatCoverFeesForTargetOrders(
+ const { resultFeeOrders, remainingFeeAmount } = marketUtils.findFeeOrdersThatCoverFeesForTargetOrders(
inputOrders,
[],
{
remainingFillableMakerAssetAmounts,
},
);
- expect(resultOrders).to.be.empty;
+ expect(resultFeeOrders).to.be.empty;
expect(remainingFeeAmount).to.be.bignumber.equal(new BigNumber(30));
});
});
@@ -183,11 +184,11 @@ describe('marketUtils', () => {
3,
);
it('returns empty and zero remainingFeeAmount', async () => {
- const { resultOrders, remainingFeeAmount } = marketUtils.findFeeOrdersThatCoverFeesForTargetOrders(
+ const { resultFeeOrders, remainingFeeAmount } = marketUtils.findFeeOrdersThatCoverFeesForTargetOrders(
inputOrders,
inputFeeOrders,
);
- expect(resultOrders).to.be.empty;
+ expect(resultFeeOrders).to.be.empty;
expect(remainingFeeAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT);
});
});
@@ -204,11 +205,11 @@ describe('marketUtils', () => {
3,
);
it('returns input fee orders and zero remainingFeeAmount', async () => {
- const { resultOrders, remainingFeeAmount } = marketUtils.findFeeOrdersThatCoverFeesForTargetOrders(
+ const { resultFeeOrders, remainingFeeAmount } = marketUtils.findFeeOrdersThatCoverFeesForTargetOrders(
inputOrders,
inputFeeOrders,
);
- expect(resultOrders).to.be.deep.equal(inputFeeOrders);
+ expect(resultFeeOrders).to.be.deep.equal(inputFeeOrders);
expect(remainingFeeAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT);
});
});
@@ -230,14 +231,14 @@ describe('marketUtils', () => {
// 3. order is completely fillable
const remainingFillableMakerAssetAmounts = [constants.ZERO_AMOUNT, new BigNumber(5), makerAssetAmount];
it('returns first two input fee orders and zero remainingFeeAmount', async () => {
- const { resultOrders, remainingFeeAmount } = marketUtils.findFeeOrdersThatCoverFeesForTargetOrders(
+ const { resultFeeOrders, remainingFeeAmount } = marketUtils.findFeeOrdersThatCoverFeesForTargetOrders(
inputOrders,
inputFeeOrders,
{
remainingFillableMakerAssetAmounts,
},
);
- expect(resultOrders).to.be.deep.equal([inputFeeOrders[0], inputFeeOrders[1]]);
+ expect(resultFeeOrders).to.be.deep.equal([inputFeeOrders[0], inputFeeOrders[1]]);
expect(remainingFeeAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT);
});
});
@@ -254,11 +255,11 @@ describe('marketUtils', () => {
3,
);
it('returns input fee orders and non-zero remainingFeeAmount', async () => {
- const { resultOrders, remainingFeeAmount } = marketUtils.findFeeOrdersThatCoverFeesForTargetOrders(
+ const { resultFeeOrders, remainingFeeAmount } = marketUtils.findFeeOrdersThatCoverFeesForTargetOrders(
inputOrders,
inputFeeOrders,
);
- expect(resultOrders).to.be.deep.equal(inputFeeOrders);
+ expect(resultFeeOrders).to.be.deep.equal(inputFeeOrders);
expect(remainingFeeAmount).to.be.bignumber.equal(new BigNumber(30));
});
});
diff --git a/packages/order-utils/test/order_hash_test.ts b/packages/order-utils/test/order_hash_test.ts
index 0a6be83d0..a85d4c81a 100644
--- a/packages/order-utils/test/order_hash_test.ts
+++ b/packages/order-utils/test/order_hash_test.ts
@@ -1,9 +1,11 @@
-import { Order } from '@0xproject/types';
-import { BigNumber } from '@0xproject/utils';
+import { Order } from '@0x/types';
+import { BigNumber } from '@0x/utils';
import * as chai from 'chai';
import 'mocha';
-import { constants, orderHashUtils } from '../src';
+import { orderHashUtils } from '../src';
+
+import { constants } from '../src/constants';
import { chaiSetup } from './utils/chai_setup';
@@ -33,6 +35,20 @@ describe('Order hashing', () => {
const orderHash = orderHashUtils.getOrderHashHex(order);
expect(orderHash).to.be.equal(expectedOrderHash);
});
+ it('calculates the order hash if amounts are strings', async () => {
+ // It's common for developers using javascript to provide the amounts
+ // as strings. Since we eventually toString() the BigNumber
+ // before encoding we should result in the same orderHash in this scenario
+ // tslint:disable-next-line:no-unnecessary-type-assertion
+ const orderHash = orderHashUtils.getOrderHashHex({
+ ...order,
+ makerAssetAmount: '0',
+ takerAssetAmount: '0',
+ makerFee: '0',
+ takerFee: '0',
+ } as any);
+ expect(orderHash).to.be.equal(expectedOrderHash);
+ });
it('throws a readable error message if taker format is invalid', async () => {
const orderWithInvalidtakerFormat = {
...order,
diff --git a/packages/order-utils/test/order_state_utils_test.ts b/packages/order-utils/test/order_state_utils_test.ts
index 91ef23b69..39c4c362f 100644
--- a/packages/order-utils/test/order_state_utils_test.ts
+++ b/packages/order-utils/test/order_state_utils_test.ts
@@ -1,4 +1,4 @@
-import { BigNumber } from '@0xproject/utils';
+import { BigNumber } from '@0x/utils';
import * as chai from 'chai';
import 'mocha';
@@ -120,5 +120,25 @@ describe('OrderStateUtils', () => {
const orderState = await orderStateUtils.getOpenOrderStateAsync(signedOrder);
expect(orderState.isValid).to.eq(false);
});
+ it('should include the transactionHash in orderState if supplied in method invocation', async () => {
+ const makerAssetAmount = new BigNumber(10);
+ const takerAssetAmount = new BigNumber(10000000000000000);
+ const takerBalance = takerAssetAmount;
+ const orderFilledAmount = new BigNumber(0);
+ const mockBalanceFetcher = buildMockBalanceFetcher(takerBalance);
+ const mockOrderFilledFetcher = buildMockOrderFilledFetcher(orderFilledAmount);
+ const [signedOrder] = testOrderFactory.generateTestSignedOrders(
+ {
+ makerAssetAmount,
+ takerAssetAmount,
+ },
+ 1,
+ );
+
+ const orderStateUtils = new OrderStateUtils(mockBalanceFetcher, mockOrderFilledFetcher);
+ const transactionHash = '0xdeadbeef';
+ const orderState = await orderStateUtils.getOpenOrderStateAsync(signedOrder, transactionHash);
+ expect(orderState.transactionHash).to.eq(transactionHash);
+ });
});
});
diff --git a/packages/order-utils/test/order_validation_utils_test.ts b/packages/order-utils/test/order_validation_utils_test.ts
index d3ff867d7..d4d12a6a7 100644
--- a/packages/order-utils/test/order_validation_utils_test.ts
+++ b/packages/order-utils/test/order_validation_utils_test.ts
@@ -1,4 +1,4 @@
-import { BigNumber } from '@0xproject/utils';
+import { BigNumber } from '@0x/utils';
import * as chai from 'chai';
import 'mocha';
@@ -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();
});
});
diff --git a/packages/order-utils/test/rate_utils_test.ts b/packages/order-utils/test/rate_utils_test.ts
index 2e299c209..b13878bb5 100644
--- a/packages/order-utils/test/rate_utils_test.ts
+++ b/packages/order-utils/test/rate_utils_test.ts
@@ -1,4 +1,4 @@
-import { BigNumber } from '@0xproject/utils';
+import { BigNumber } from '@0x/utils';
import * as chai from 'chai';
import 'mocha';
diff --git a/packages/order-utils/test/remaining_fillable_calculator_test.ts b/packages/order-utils/test/remaining_fillable_calculator_test.ts
index a5a3b7fc6..c55a76def 100644
--- a/packages/order-utils/test/remaining_fillable_calculator_test.ts
+++ b/packages/order-utils/test/remaining_fillable_calculator_test.ts
@@ -1,6 +1,6 @@
-import { SignedOrder } from '@0xproject/types';
-import { BigNumber } from '@0xproject/utils';
-import { Web3Wrapper } from '@0xproject/web3-wrapper';
+import { SignedOrder } from '@0x/types';
+import { BigNumber } from '@0x/utils';
+import { Web3Wrapper } from '@0x/web3-wrapper';
import * as chai from 'chai';
import 'mocha';
diff --git a/packages/order-utils/test/signature_utils_test.ts b/packages/order-utils/test/signature_utils_test.ts
index a25d2afd6..84e5559a9 100644
--- a/packages/order-utils/test/signature_utils_test.ts
+++ b/packages/order-utils/test/signature_utils_test.ts
@@ -1,13 +1,14 @@
-import { SignerType } from '@0xproject/types';
-import { BigNumber } from '@0xproject/utils';
+import { Order, SignatureType } from '@0x/types';
+import { BigNumber } from '@0x/utils';
import * as chai from 'chai';
import { JSONRPCErrorCallback, JSONRPCRequestPayload } from 'ethereum-types';
+import * as ethUtil from 'ethereumjs-util';
import * as _ from 'lodash';
import 'mocha';
-import * as Sinon from 'sinon';
-import { ecSignOrderHashAsync, generatePseudoRandomSalt } from '../src';
-import { convertECSignatureToSignatureHex, isValidECSignature, isValidSignatureAsync } from '../src/signature_utils';
+import { generatePseudoRandomSalt, orderHashUtils } from '../src';
+import { constants } from '../src/constants';
+import { signatureUtils } from '../src/signature_utils';
import { chaiSetup } from './utils/chai_setup';
import { provider, web3Wrapper } from './utils/web3_wrapper';
@@ -16,7 +17,29 @@ chaiSetup.configure();
const expect = chai.expect;
describe('Signature utils', () => {
- describe('#isValidSignature', () => {
+ let makerAddress: string;
+ const fakeExchangeContractAddress = '0x1dc4c1cefef38a777b15aa20260a54e584b16c48';
+ let order: Order;
+ before(async () => {
+ const availableAddreses = await web3Wrapper.getAvailableAddressesAsync();
+ makerAddress = availableAddreses[0];
+ order = {
+ makerAddress,
+ takerAddress: constants.NULL_ADDRESS,
+ senderAddress: constants.NULL_ADDRESS,
+ feeRecipientAddress: constants.NULL_ADDRESS,
+ makerAssetData: constants.NULL_ADDRESS,
+ takerAssetData: constants.NULL_ADDRESS,
+ exchangeAddress: fakeExchangeContractAddress,
+ salt: new BigNumber(0),
+ makerFee: new BigNumber(0),
+ takerFee: new BigNumber(0),
+ makerAssetAmount: new BigNumber(0),
+ takerAssetAmount: new BigNumber(0),
+ expirationTimeSeconds: new BigNumber(0),
+ };
+ });
+ describe('#isValidSignatureAsync', () => {
let dataHex = '0x6927e990021d23b1eb7b8789f6a6feaf98fe104bb0cf8259421b79f9a34222b0';
const ethSignSignature =
'0x1B61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc3340349190569279751135161d22529dc25add4f6069af05be04cacbda2ace225403';
@@ -24,12 +47,14 @@ describe('Signature utils', () => {
it("should return false if the data doesn't pertain to the signature & address", async () => {
const bytes32Zeros = '0x0000000000000000000000000000000000000000000000000000000000000000';
- expect(await isValidSignatureAsync(provider, bytes32Zeros, ethSignSignature, address)).to.be.false();
+ expect(
+ await signatureUtils.isValidSignatureAsync(provider, bytes32Zeros, ethSignSignature, address),
+ ).to.be.false();
});
it("should return false if the address doesn't pertain to the signature & data", async () => {
const validUnrelatedAddress = '0x8b0292b11a196601ed2ce54b665cafeca0347d42';
expect(
- await isValidSignatureAsync(provider, dataHex, ethSignSignature, validUnrelatedAddress),
+ await signatureUtils.isValidSignatureAsync(provider, dataHex, ethSignSignature, validUnrelatedAddress),
).to.be.false();
});
it("should return false if the signature doesn't pertain to the dataHex & address", async () => {
@@ -37,18 +62,27 @@ describe('Signature utils', () => {
// tslint:disable-next-line:custom-no-magic-numbers
signatureArray[5] = 'C'; // V = 28, instead of 27
const wrongSignature = signatureArray.join('');
- expect(await isValidSignatureAsync(provider, dataHex, wrongSignature, address)).to.be.false();
+ expect(
+ await signatureUtils.isValidSignatureAsync(provider, dataHex, wrongSignature, address),
+ ).to.be.false();
});
it('should throw if signatureType is invalid', () => {
const signatureArray = ethSignSignature.split('');
signatureArray[3] = '9'; // SignatureType w/ index 9 doesn't exist
const signatureWithInvalidType = signatureArray.join('');
- expect(isValidSignatureAsync(provider, dataHex, signatureWithInvalidType, address)).to.be.rejected();
+ expect(
+ signatureUtils.isValidSignatureAsync(provider, dataHex, signatureWithInvalidType, address),
+ ).to.be.rejected();
});
it('should return true for a valid Ecrecover (EthSign) signature', async () => {
- const isValidSignatureLocal = await isValidSignatureAsync(provider, dataHex, ethSignSignature, address);
+ const isValidSignatureLocal = await signatureUtils.isValidSignatureAsync(
+ provider,
+ dataHex,
+ ethSignSignature,
+ address,
+ );
expect(isValidSignatureLocal).to.be.true();
});
@@ -57,16 +91,12 @@ describe('Signature utils', () => {
address = '0x6ecbe1db9ef729cbe972c83fb886247691fb6beb';
const eip712Signature =
'0x1bdde07aac4bf12c12ddbb155919c43eba4146a2cfcf904a862950dbebe332554c6674975603eb5a4eaf8fd7f2e06350267e5b36cda9851a89f8bb49fe2fc9afe202';
- const isValidSignatureLocal = await isValidSignatureAsync(provider, dataHex, eip712Signature, address);
- 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 isValidSignatureAsync(provider, dataHex, trezorSignature, address);
+ const isValidSignatureLocal = await signatureUtils.isValidSignatureAsync(
+ provider,
+ dataHex,
+ eip712Signature,
+ address,
+ );
expect(isValidSignatureLocal).to.be.true();
});
});
@@ -80,18 +110,18 @@ describe('Signature utils', () => {
const address = '0x0e5cb767cce09a7f3ca594df118aa519be5e2b5a';
it("should return false if the data doesn't pertain to the signature & address", async () => {
- expect(isValidECSignature('0x0', signature, address)).to.be.false();
+ expect(signatureUtils.isValidECSignature('0x0', signature, address)).to.be.false();
});
it("should return false if the address doesn't pertain to the signature & data", async () => {
const validUnrelatedAddress = '0x8b0292b11a196601ed2ce54b665cafeca0347d42';
- expect(isValidECSignature(data, signature, validUnrelatedAddress)).to.be.false();
+ expect(signatureUtils.isValidECSignature(data, signature, validUnrelatedAddress)).to.be.false();
});
it("should return false if the signature doesn't pertain to the data & address", async () => {
const wrongSignature = _.assign({}, signature, { v: 28 });
- expect(isValidECSignature(data, wrongSignature, address)).to.be.false();
+ expect(signatureUtils.isValidECSignature(data, wrongSignature, address)).to.be.false();
});
it('should return true if the signature does pertain to the data & address', async () => {
- const isValidSignatureLocal = isValidECSignature(data, signature, address);
+ const isValidSignatureLocal = signatureUtils.isValidECSignature(data, signature, address);
expect(isValidSignatureLocal).to.be.true();
});
});
@@ -108,23 +138,64 @@ describe('Signature utils', () => {
expect(salt.lessThan(twoPow256)).to.be.true();
});
});
- describe('#ecSignOrderHashAsync', () => {
- let stubs: Sinon.SinonStub[] = [];
- let makerAddress: string;
+ describe('#ecSignOrderAsync', () => {
+ it('should default to eth_sign if eth_signTypedData is unavailable', async () => {
+ const expectedSignature =
+ '0x1c3582f06356a1314dbf1c0e534c4d8e92e59b056ee607a7ff5a825f5f2cc5e6151c5cc7fdd420f5608e4d5bef108e42ad90c7a4b408caef32e24374cf387b0d7603';
+
+ const fakeProvider = {
+ async sendAsync(payload: JSONRPCRequestPayload, callback: JSONRPCErrorCallback): Promise<void> {
+ if (payload.method === 'eth_signTypedData') {
+ callback(new Error('Internal RPC Error'));
+ } else if (payload.method === 'eth_sign') {
+ const [address, message] = payload.params;
+ const signature = await web3Wrapper.signMessageAsync(address, message);
+ callback(null, {
+ id: 42,
+ jsonrpc: '2.0',
+ result: signature,
+ });
+ } else {
+ callback(null, { id: 42, jsonrpc: '2.0', result: [makerAddress] });
+ }
+ },
+ };
+ const signedOrder = await signatureUtils.ecSignOrderAsync(fakeProvider, order, makerAddress);
+ expect(signedOrder.signature).to.equal(expectedSignature);
+ });
+ it('should throw if the user denies the signing request', async () => {
+ const fakeProvider = {
+ async sendAsync(payload: JSONRPCRequestPayload, callback: JSONRPCErrorCallback): Promise<void> {
+ if (payload.method === 'eth_signTypedData') {
+ callback(new Error('User denied message signature'));
+ } else if (payload.method === 'eth_sign') {
+ const [address, message] = payload.params;
+ const signature = await web3Wrapper.signMessageAsync(address, message);
+ callback(null, {
+ id: 42,
+ jsonrpc: '2.0',
+ result: signature,
+ });
+ } else {
+ callback(null, { id: 42, jsonrpc: '2.0', result: [makerAddress] });
+ }
+ },
+ };
+ expect(signatureUtils.ecSignOrderAsync(fakeProvider, order, makerAddress)).to.to.be.rejectedWith(
+ 'User denied message signature',
+ );
+ });
+ });
+ describe('#ecSignHashAsync', () => {
before(async () => {
const availableAddreses = await web3Wrapper.getAvailableAddressesAsync();
makerAddress = availableAddreses[0];
});
- afterEach(() => {
- // clean up any stubs after the test has completed
- _.each(stubs, s => s.restore());
- stubs = [];
- });
- it('Should return the correct Signature', async () => {
+ it('should return the correct Signature', async () => {
const orderHash = '0x6927e990021d23b1eb7b8789f6a6feaf98fe104bb0cf8259421b79f9a34222b0';
const expectedSignature =
'0x1b61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc3340349190569279751135161d22529dc25add4f6069af05be04cacbda2ace225403';
- const ecSignature = await ecSignOrderHashAsync(provider, orderHash, makerAddress, SignerType.Default);
+ const ecSignature = await signatureUtils.ecSignHashAsync(provider, orderHash, makerAddress);
expect(ecSignature).to.equal(expectedSignature);
});
it('should return the correct Signature for signatureHex concatenated as R + S + V', async () => {
@@ -150,7 +221,7 @@ describe('Signature utils', () => {
}
},
};
- const ecSignature = await ecSignOrderHashAsync(fakeProvider, orderHash, makerAddress, SignerType.Default);
+ const ecSignature = await signatureUtils.ecSignHashAsync(fakeProvider, orderHash, makerAddress);
expect(ecSignature).to.equal(expectedSignature);
});
it('should return the correct Signature for signatureHex concatenated as V + R + S', async () => {
@@ -173,44 +244,68 @@ describe('Signature utils', () => {
},
};
- const ecSignature = await ecSignOrderHashAsync(fakeProvider, orderHash, makerAddress, SignerType.Default);
+ const ecSignature = await signatureUtils.ecSignHashAsync(fakeProvider, orderHash, makerAddress);
expect(ecSignature).to.equal(expectedSignature);
});
- // Note this is due to a bug in Metamask where it does not prefix before signing, this is a known issue and is to be fixed in the future
- // Source: https://github.com/MetaMask/metamask-extension/commit/a9d36860bec424dcee8db043d3e7da6a5ff5672e
- it('should receive a payload modified with a prefix when Metamask is SignerType', async () => {
+ it('should return a valid signature', async () => {
const orderHash = '0x34decbedc118904df65f379a175bb39ca18209d6ce41d5ed549d54e6e0a95004';
- const orderHashPrefixed = '0xae70f31d26096291aa681b26cb7574563956221d0b4213631e1ef9df675d4cba';
+ const ecSignature = await signatureUtils.ecSignHashAsync(provider, orderHash, makerAddress);
+
+ const isValidSignature = await signatureUtils.isValidSignatureAsync(
+ provider,
+ orderHash,
+ ecSignature,
+ makerAddress,
+ );
+ expect(isValidSignature).to.be.true();
+ });
+ });
+ describe('#ecSignTypedDataOrderAsync', () => {
+ it('should result in the same signature as signing the order hash without an ethereum message prefix', async () => {
+ // Note: Since order hash is an EIP712 hash the result of a valid EIP712 signature
+ // of order hash is the same as signing the order without the Ethereum Message prefix.
+ const orderHashHex = orderHashUtils.getOrderHashHex(order);
+ const sig = ethUtil.ecsign(
+ ethUtil.toBuffer(orderHashHex),
+ Buffer.from('F2F48EE19680706196E2E339E5DA3491186E0C4C5030670656B0E0164837257D', 'hex'),
+ );
+ const signatureBuffer = Buffer.concat([
+ ethUtil.toBuffer(sig.v),
+ ethUtil.toBuffer(sig.r),
+ ethUtil.toBuffer(sig.s),
+ ethUtil.toBuffer(SignatureType.EIP712),
+ ]);
+ const signatureHex = `0x${signatureBuffer.toString('hex')}`;
+ const signedOrder = await signatureUtils.ecSignTypedDataOrderAsync(provider, order, makerAddress);
+ const isValidSignature = await signatureUtils.isValidSignatureAsync(
+ provider,
+ orderHashHex,
+ signedOrder.signature,
+ makerAddress,
+ );
+ expect(signatureHex).to.eq(signedOrder.signature);
+ expect(isValidSignature).to.eq(true);
+ });
+ it('should return the correct Signature for signatureHex concatenated as R + S + V', async () => {
const expectedSignature =
- '0x1b117902c86dfb95fe0d1badd983ee166ad259b27acb220174cbb4460d872871137feabdfe76e05924b484789f79af4ee7fa29ec006cedce1bbf369320d034e10b03';
- // Generated from a MM eth_sign request from 0x5409ed021d9299bf6814279a6a1411a7e866a631 signing 0xae70f31d26096291aa681b26cb7574563956221d0b4213631e1ef9df675d4cba
- const metamaskSignature =
- '0x117902c86dfb95fe0d1badd983ee166ad259b27acb220174cbb4460d872871137feabdfe76e05924b484789f79af4ee7fa29ec006cedce1bbf369320d034e10b1b';
+ '0x1cd472c439833774b55d248c31b6585f21aea1b9363ebb4ec58549e46b62eb5a6f696f5781f62de008ee7f77650ef940d99c97ec1dee67b3f5cea1bbfdfeb2eba602';
const fakeProvider = {
async sendAsync(payload: JSONRPCRequestPayload, callback: JSONRPCErrorCallback): Promise<void> {
- if (payload.method === 'eth_sign') {
- const [, message] = payload.params;
- expect(message).to.equal(orderHashPrefixed);
+ if (payload.method === 'eth_signTypedData') {
+ const [address, typedData] = payload.params;
+ const signature = await web3Wrapper.signTypedDataAsync(address, typedData);
callback(null, {
id: 42,
jsonrpc: '2.0',
- result: metamaskSignature,
+ result: signature,
});
} else {
callback(null, { id: 42, jsonrpc: '2.0', result: [makerAddress] });
}
},
};
-
- const ecSignature = await ecSignOrderHashAsync(fakeProvider, orderHash, makerAddress, SignerType.Metamask);
- expect(ecSignature).to.equal(expectedSignature);
- });
- it('should return a valid signature', async () => {
- const orderHash = '0x34decbedc118904df65f379a175bb39ca18209d6ce41d5ed549d54e6e0a95004';
- const ecSignature = await ecSignOrderHashAsync(provider, orderHash, makerAddress, SignerType.Default);
-
- const isValidSignature = await isValidSignatureAsync(provider, orderHash, ecSignature, makerAddress);
- expect(isValidSignature).to.be.true();
+ const signedOrder = await signatureUtils.ecSignTypedDataOrderAsync(fakeProvider, order, makerAddress);
+ expect(signedOrder.signature).to.equal(expectedSignature);
});
});
describe('#convertECSignatureToSignatureHex', () => {
@@ -219,35 +314,11 @@ 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 = convertECSignatureToSignatureHex(ecSignature, SignerType.Trezor);
- expect(signatureWithSignatureType).to.equal(expectedSignatureWithSignatureType);
- });
- it('should concatenate v,r,s and append the EthSign signature type when SignerType is Default', async () => {
+ it('should concatenate v,r,s and append the EthSign signature type', async () => {
const expectedSignatureWithSignatureType =
'0x1baca7da997ad177f040240cdccf6905b71ab16b74434388c3a72f34fd25d6439346b2bac274ff29b48b3ea6e2d04c1336eaceafda3c53ab483fc3ff12fac3ebf203';
- const signatureWithSignatureType = convertECSignatureToSignatureHex(ecSignature, SignerType.Default);
+ const signatureWithSignatureType = signatureUtils.convertECSignatureToSignatureHex(ecSignature);
expect(signatureWithSignatureType).to.equal(expectedSignatureWithSignatureType);
});
- it('should concatenate v,r,s and append the EthSign signature type when SignerType is Ledger', async () => {
- const expectedSignatureWithSignatureType =
- '0x1baca7da997ad177f040240cdccf6905b71ab16b74434388c3a72f34fd25d6439346b2bac274ff29b48b3ea6e2d04c1336eaceafda3c53ab483fc3ff12fac3ebf203';
- const signatureWithSignatureType = convertECSignatureToSignatureHex(ecSignature, SignerType.Ledger);
- expect(signatureWithSignatureType).to.equal(expectedSignatureWithSignatureType);
- });
- it('should concatenate v,r,s and append the EthSign signature type when SignerType is Metamask', async () => {
- const expectedSignatureWithSignatureType =
- '0x1baca7da997ad177f040240cdccf6905b71ab16b74434388c3a72f34fd25d6439346b2bac274ff29b48b3ea6e2d04c1336eaceafda3c53ab483fc3ff12fac3ebf203';
- const signatureWithSignatureType = convertECSignatureToSignatureHex(ecSignature, SignerType.Metamask);
- expect(signatureWithSignatureType).to.equal(expectedSignatureWithSignatureType);
- });
- it('should throw if the SignerType is invalid', async () => {
- const expectedMessage = 'Unrecognized SignerType: INVALID_SIGNER';
- expect(() => convertECSignatureToSignatureHex(ecSignature, 'INVALID_SIGNER' as SignerType)).to.throw(
- expectedMessage,
- );
- });
});
});
diff --git a/packages/order-utils/test/sorting_utils_test.ts b/packages/order-utils/test/sorting_utils_test.ts
index 016432505..0b8757496 100644
--- a/packages/order-utils/test/sorting_utils_test.ts
+++ b/packages/order-utils/test/sorting_utils_test.ts
@@ -1,4 +1,4 @@
-import { BigNumber } from '@0xproject/utils';
+import { BigNumber } from '@0x/utils';
import * as chai from 'chai';
import 'mocha';
diff --git a/packages/order-utils/test/utils/simple_erc20_balance_and_proxy_allowance_fetcher.ts b/packages/order-utils/test/utils/simple_erc20_balance_and_proxy_allowance_fetcher.ts
index 279a5c0db..d3ea8b456 100644
--- a/packages/order-utils/test/utils/simple_erc20_balance_and_proxy_allowance_fetcher.ts
+++ b/packages/order-utils/test/utils/simple_erc20_balance_and_proxy_allowance_fetcher.ts
@@ -1,9 +1,8 @@
-import { BigNumber } from '@0xproject/utils';
+import { ERC20TokenContract } from '@0x/abi-gen-wrappers';
+import { BigNumber } from '@0x/utils';
import { AbstractBalanceAndProxyAllowanceFetcher } from '../../src/abstract/abstract_balance_and_proxy_allowance_fetcher';
-import { ERC20TokenContract } from '../../src/generated_contract_wrappers/erc20_token';
-
export class SimpleERC20BalanceAndProxyAllowanceFetcher implements AbstractBalanceAndProxyAllowanceFetcher {
private readonly _erc20TokenContract: ERC20TokenContract;
private readonly _erc20ProxyAddress: string;
diff --git a/packages/order-utils/test/utils/test_order_factory.ts b/packages/order-utils/test/utils/test_order_factory.ts
index 75dc6f1f2..145332674 100644
--- a/packages/order-utils/test/utils/test_order_factory.ts
+++ b/packages/order-utils/test/utils/test_order_factory.ts
@@ -1,7 +1,8 @@
-import { Order, SignedOrder } from '@0xproject/types';
+import { Order, SignedOrder } from '@0x/types';
import * as _ from 'lodash';
-import { constants, orderFactory } from '../../src';
+import { constants } from '../../src/constants';
+import { orderFactory } from '../../src/order_factory';
const BASE_TEST_ORDER: Order = orderFactory.createOrder(
constants.NULL_ADDRESS,
diff --git a/packages/order-utils/test/utils/web3_wrapper.ts b/packages/order-utils/test/utils/web3_wrapper.ts
index ab801fa7f..accfcb7fe 100644
--- a/packages/order-utils/test/utils/web3_wrapper.ts
+++ b/packages/order-utils/test/utils/web3_wrapper.ts
@@ -1,5 +1,5 @@
-import { web3Factory } from '@0xproject/dev-utils';
-import { Web3Wrapper } from '@0xproject/web3-wrapper';
+import { web3Factory } from '@0x/dev-utils';
+import { Web3Wrapper } from '@0x/web3-wrapper';
import { Provider } from 'ethereum-types';
const provider: Provider = web3Factory.getRpcProvider({ shouldUseInProcessGanache: true });
diff --git a/packages/order-utils/tsconfig.json b/packages/order-utils/tsconfig.json
index 8b4cd47a2..718e623c7 100644
--- a/packages/order-utils/tsconfig.json
+++ b/packages/order-utils/tsconfig.json
@@ -1,7 +1,8 @@
{
"extends": "../../tsconfig",
"compilerOptions": {
- "outDir": "lib"
+ "outDir": "lib",
+ "rootDir": "."
},
"include": ["src/**/*", "test/**/*"]
}
diff --git a/packages/order-utils/tslint.json b/packages/order-utils/tslint.json
index ffaefe83a..dd9053357 100644
--- a/packages/order-utils/tslint.json
+++ b/packages/order-utils/tslint.json
@@ -1,3 +1,3 @@
{
- "extends": ["@0xproject/tslint-config"]
+ "extends": ["@0x/tslint-config"]
}
diff --git a/packages/order-utils/typedoc-tsconfig.json b/packages/order-utils/typedoc-tsconfig.json
new file mode 100644
index 000000000..b9c6b36f3
--- /dev/null
+++ b/packages/order-utils/typedoc-tsconfig.json
@@ -0,0 +1,7 @@
+{
+ "extends": "../../typedoc-tsconfig",
+ "compilerOptions": {
+ "outDir": "lib"
+ },
+ "include": ["src/**/*", "test/**/*"]
+}