Merge branch 'development' into fix/mutatedInput

* development:
  Changes to abi-gen after code review
  Added constructor ABIs to abi-gen
  Describe #295 in a CHANGELOG
  Add #302 description to changelog
  Fix formatting
  Apply prettier config
  Install prettier
  Remove formatting esilnt rules
  sendTransactionAsync should return txHash string
  Added Event generation to abi-gen
  Publish
  Add dates to CHANGELOG entries
  Update subproviders CHANGELOG
  Support both personal_sign and eth_sign
  Fix Ledger tests given change from `personal_sign` to `eth_sign`
  Update subprovider to catch correct RPC method
  Rename guide
  Update contribution guide
  Fix broken links in the abi-gen README
  Fix typing generation for arrays in which types separated by |s
This commit is contained in:
Brandon Millman 2018-01-08 16:35:12 -08:00
commit 734cf5819a
307 changed files with 6967 additions and 6326 deletions

View File

@ -26,3 +26,4 @@ jobs:
background: true background: true
- run: yarn lerna:run test:circleci - run: yarn lerna:run test:circleci
- run: yarn lerna:run lint - run: yarn lerna:run lint
- run: yarn prettier:ci

2
.prettierignore Normal file
View File

@ -0,0 +1,2 @@
lib
generated

6
.prettierrc Normal file
View File

@ -0,0 +1,6 @@
{
"tabWidth": 4,
"printWidth": 120,
"trailingComma": all,
"singleQuote": true
}

View File

@ -1,62 +1,47 @@
# 0x.js CONTRIBUTING.md 0x Contribution Guide
---------------------
Thank you for your interest in contributing to 0x.js! We welcome contributions from anyone on the internet, and are grateful for even the smallest of fixes! Thank you for your interest in contributing to 0x protocol! We welcome contributions from anyone on the internet, and are grateful for even the smallest of fixes!
## Developer's guide ### How to contribute
## How to contribute If you'd like to contribute to 0x protocol, please fork the repo, fix, commit and send a pull request against the `development` branch for the maintainers to review and merge into the main code base. If you wish to submit more complex changes though, please check with a core dev first on [our RocketChat #dev channel](http://chat.0xproject.com) to ensure those changes are in-line with the general philosophy of the project and/or to get some early feedback which can make both your efforts easier as well as our review and merge procedures quick and simple.
If you'd like to contribute to 0x.js, please fork the repo, fix, commit and send a pull request against the `development` branch for the maintainers to review and merge into the main code base. If you wish to submit more complex changes though, please check up with a core dev first on [our gitter channel](https://gitter.im/0xProject/Lobby) or in the `#dev` channel on our [slack](https://slack.0xproject.com/) to ensure those changes are in line with the general philosophy of the project and/or to get some early feedback which can make both your efforts easier as well as our review and merge procedures quick and simple. We encourage a “PR early” approach so create the PR as early as possible even without the fix/feature ready, so that devs and other contributors know you have picked up the issue. These early PRs should indicate an 'in progress' status by adding the '[WIP]' prefix to the PR title. Please make sure your contributions adhere to our coding guidelines:
We encourage a “PR early” approach so create the PR as early as possible even without the fix/feature ready, so that devs and other volunteers know you have picked up the issue. These early PRs should indicate an 'in progress' status by adding the '[WIP]' prefix to the PR title. Please make sure your contributions adhere to our coding guidelines:
* Pull requests adding features or refactoring should be opened against the `development` branch * Pull requests adding features or refactoring should be opened against the `development` branch
* Pull requests fixing bugs in the latest release version should be opened again the `master` branch * Pull requests fixing bugs in the latest release version should be opened again the `master` branch
* Write [good commit messages](https://chris.beams.io/posts/git-commit/) * Write [good commit messages](https://chris.beams.io/posts/git-commit/)
## Code quality ### Code quality
Because 0x.js is used by multiple relayers in production and their businesses depend on it, we strive for exceptional code quality. Please follow the existing code standards and conventions. `tslint` and `prettier` (described below) will help you.
Because 0x.js is used by multiple relayers in production and their businesses depend on it, we strive for excellent code quality. Please follow the existing code standards and conventions. `tslint` (described below) will help you.
If you're adding functionality, please also add tests and make sure they pass. We have an automatic coverage reporting tool, so we'll see it if they are missing ;) If you're adding functionality, please also add tests and make sure they pass. We have an automatic coverage reporting tool, so we'll see it if they are missing ;)
If you're adding a new public function/member, make sure you document it with Java doc-style comments. We use typedoc to generate [awesome documentation](https://0xproject.com/docs/0xjs) from the comments within our source code. If you're adding a new public function/member, make sure you document it with Java doc-style comments. We use typedoc to generate [awesome documentation](https://0xproject.com/docs/0xjs) from the comments within our source code.
## Running and building If the sub-package you are modifying has a `CHANGELOG.md` file, make sure to add an entry in it for the change made to the package. For published packages, only changes that modify the public interface or behavior of the package need a CHANGELOG entry.
First thing to do with an unknown code base is to run the tests. ### Styleguide
We assume that you have `npm` and `yarn` installed.
To do that: We use [TSLint](https://palantir.github.io/tslint/) with [custom configs](https://github.com/0xProject/0x.js/tree/development/packages/tslint-config) to keep our code style consistent.
* Install dependencies: `yarn`
* Initialize the testrpc state (migrate the contracts) by doing one of the following:
* Manual contracts migration:
* Run testrpc: `yarn testrpc`
* Clone the `[contracts](https://github.com/0xProject/contracts)` repo and run `yarn migrate`
* Use one of the existing testrpc snapshots
* Check out `circle.yml` for an example
* Run tests: `yarn test`
To build run: `yarn build`
We also recommend you read through the tests.
## Styleguide
We use [TSLint](https://palantir.github.io/tslint/) with [custom configs](https://github.com/0xProject/tslint-config-0xproject) to keep our code style consistent.
To lint your code just run: `yarn lint` To lint your code just run: `yarn lint`
We also use [Prettier](https://prettier.io/) to auto-format our code. Be sure to either add a [text editor integration](https://prettier.io/docs/en/editors.html) or a [pre-commit hook](https://prettier.io/docs/en/precommit.html) to properly format your code changes.
If using the Atom text editor, we recommend you install the following packages: If using the Atom text editor, we recommend you install the following packages:
* [atom-typescript](https://atom.io/packages/atom-typescript) * [atom-typescript](https://atom.io/packages/atom-typescript)
* [linter-tslint](https://atom.io/packages/linter-tslint) * [linter-tslint](https://atom.io/packages/linter-tslint)
* [prettier-atom](https://atom.io/packages/prettier-atom)
* [language-ethereum](https://atom.io/packages/language-ethereum)
Our CI will also run it as a part of the test run when you submit your PR. Our CI will also run TSLint and Prettier as a part of the test run when you submit your PR. Make sure that the CI tests pass for your contribution.
### Branch structure & versioning
## Branch structure & versioning We use [semantic versioning](http://semver.org/), but before a package reaches v1.0.0 all breaking changes as well as new features will be minor version bumps.
We use [semantic versioning](http://semver.org/), but before we reach v1.0.0 all breaking changes as well as new features will be minor version bumps.
We have two main branches: `master` and `development`. We have two main branches: `master` and `development`.

View File

@ -1,14 +1,15 @@
{ {
"private": true, "private": true,
"name": "0x.js", "name": "0x.js",
"workspaces": [ "workspaces": ["packages/*"],
"packages/*"
],
"scripts": { "scripts": {
"testrpc": "testrpc -p 8545 --networkId 50 -m \"${npm_package_config_mnemonic}\"", "testrpc": "testrpc -p 8545 --networkId 50 -m \"${npm_package_config_mnemonic}\"",
"prettier": "prettier --write '**/*.{ts,tsx}'",
"prettier:ci": "prettier --list-different '**/*.{ts,tsx}'",
"lerna:run": "lerna run", "lerna:run": "lerna run",
"lerna:rebuild": "lerna run clean; lerna run build;", "lerna:rebuild": "lerna run clean; lerna run build;",
"lerna:publish": "yarn install; lerna run clean; lerna run build; lerna publish --registry=https://registry.npmjs.org/" "lerna:publish":
"yarn install; lerna run clean; lerna run build; lerna publish --registry=https://registry.npmjs.org/"
}, },
"config": { "config": {
"mnemonic": "concert load couple harbor equip island argue ramp clarify fence smart topic" "mnemonic": "concert load couple harbor equip island argue ramp clarify fence smart topic"
@ -18,6 +19,7 @@
"async-child-process": "^1.1.1", "async-child-process": "^1.1.1",
"ethereumjs-testrpc": "6.0.3", "ethereumjs-testrpc": "6.0.3",
"lerna": "^2.5.1", "lerna": "^2.5.1",
"prettier": "1.9.2",
"publish-release": "0xproject/publish-release", "publish-release": "0xproject/publish-release",
"semver-sort": "^0.0.4" "semver-sort": "^0.0.4"
} }

View File

@ -1,6 +1,6 @@
# CHANGELOG # CHANGELOG
vx.x.x - _TBD_ v0.29.0 - _December 28, 2017_
------------------------ ------------------------
* Assert baseUnit amount supplied to `toUnitAmount` is integer amount. (#287) * Assert baseUnit amount supplied to `toUnitAmount` is integer amount. (#287)
* `toBaseUnitAmount` throws if amount supplied has too many decimals (#287) * `toBaseUnitAmount` throws if amount supplied has too many decimals (#287)

View File

@ -1,6 +1,6 @@
{ {
"name": "0x.js", "name": "0x.js",
"version": "0.28.0", "version": "0.29.0",
"description": "A javascript library for interacting with the 0x protocol", "description": "A javascript library for interacting with the 0x protocol",
"keywords": [ "keywords": [
"0x.js", "0x.js",
@ -45,10 +45,10 @@
"node": ">=6.0.0" "node": ">=6.0.0"
}, },
"devDependencies": { "devDependencies": {
"@0xproject/abi-gen": "^0.0.3", "@0xproject/abi-gen": "^0.0.4",
"@0xproject/dev-utils": "^0.0.2", "@0xproject/dev-utils": "^0.0.3",
"@0xproject/tslint-config": "^0.3.0", "@0xproject/tslint-config": "^0.4.0",
"@0xproject/types": "^0.1.1", "@0xproject/types": "^0.1.2",
"@types/bintrees": "^1.0.2", "@types/bintrees": "^1.0.2",
"@types/jsonschema": "^1.1.1", "@types/jsonschema": "^1.1.1",
"@types/lodash": "^4.14.86", "@types/lodash": "^4.14.86",
@ -84,10 +84,10 @@
"webpack": "^3.1.0" "webpack": "^3.1.0"
}, },
"dependencies": { "dependencies": {
"@0xproject/assert": "^0.0.8", "@0xproject/assert": "^0.0.9",
"@0xproject/json-schemas": "^0.7.0", "@0xproject/json-schemas": "^0.7.1",
"@0xproject/utils": "^0.1.1", "@0xproject/utils": "^0.1.2",
"@0xproject/web3-wrapper": "^0.1.1", "@0xproject/web3-wrapper": "^0.1.2",
"bignumber.js": "~4.1.0", "bignumber.js": "~4.1.0",
"bintrees": "^1.0.2", "bintrees": "^1.0.2",
"bn.js": "^4.11.8", "bn.js": "^4.11.8",

View File

@ -188,12 +188,7 @@ export class ZeroEx {
config.networkId, config.networkId,
config.tokenTransferProxyContractAddress, config.tokenTransferProxyContractAddress,
); );
this.token = new TokenWrapper( this.token = new TokenWrapper(this._web3Wrapper, config.networkId, this._abiDecoder, this.proxy);
this._web3Wrapper,
config.networkId,
this._abiDecoder,
this.proxy,
);
this.exchange = new ExchangeWrapper( this.exchange = new ExchangeWrapper(
this._web3Wrapper, this._web3Wrapper,
config.networkId, config.networkId,
@ -202,13 +197,17 @@ export class ZeroEx {
config.exchangeContractAddress, config.exchangeContractAddress,
); );
this.tokenRegistry = new TokenRegistryWrapper( this.tokenRegistry = new TokenRegistryWrapper(
this._web3Wrapper, config.networkId, config.tokenRegistryContractAddress, this._web3Wrapper,
); config.networkId,
this.etherToken = new EtherTokenWrapper( config.tokenRegistryContractAddress,
this._web3Wrapper, config.networkId, this._abiDecoder, this.token,
); );
this.etherToken = new EtherTokenWrapper(this._web3Wrapper, config.networkId, this._abiDecoder, this.token);
this.orderStateWatcher = new OrderStateWatcher( this.orderStateWatcher = new OrderStateWatcher(
this._web3Wrapper, this._abiDecoder, this.token, this.exchange, config.orderWatcherConfig, this._web3Wrapper,
this._abiDecoder,
this.token,
this.exchange,
config.orderWatcherConfig,
); );
} }
/** /**
@ -291,10 +290,13 @@ export class ZeroEx {
* @return Transaction receipt with decoded log args. * @return Transaction receipt with decoded log args.
*/ */
public async awaitTransactionMinedAsync( public async awaitTransactionMinedAsync(
txHash: string, pollingIntervalMs = 1000, timeoutMs?: number): Promise<TransactionReceiptWithDecodedLogs> { txHash: string,
pollingIntervalMs = 1000,
timeoutMs?: number,
): Promise<TransactionReceiptWithDecodedLogs> {
let timeoutExceeded = false; let timeoutExceeded = false;
if (timeoutMs) { if (timeoutMs) {
setTimeout(() => timeoutExceeded = true, timeoutMs); setTimeout(() => (timeoutExceeded = true), timeoutMs);
} }
const txReceiptPromise = new Promise( const txReceiptPromise = new Promise(
@ -319,7 +321,8 @@ export class ZeroEx {
resolve(transactionReceiptWithDecodedLogArgs); resolve(transactionReceiptWithDecodedLogArgs);
} }
}, pollingIntervalMs); }, pollingIntervalMs);
}); },
);
return txReceiptPromise; return txReceiptPromise;
} }

View File

@ -8,11 +8,11 @@ import * as ZRXArtifact from './artifacts/ZRX.json';
import { Artifact } from './types'; import { Artifact } from './types';
export const artifacts = { export const artifacts = {
ZRXArtifact: ZRXArtifact as any as Artifact, ZRXArtifact: (ZRXArtifact as any) as Artifact,
DummyTokenArtifact: DummyTokenArtifact as any as Artifact, DummyTokenArtifact: (DummyTokenArtifact as any) as Artifact,
TokenArtifact: TokenArtifact as any as Artifact, TokenArtifact: (TokenArtifact as any) as Artifact,
ExchangeArtifact: ExchangeArtifact as any as Artifact, ExchangeArtifact: (ExchangeArtifact as any) as Artifact,
EtherTokenArtifact: EtherTokenArtifact as any as Artifact, EtherTokenArtifact: (EtherTokenArtifact as any) as Artifact,
TokenRegistryArtifact: TokenRegistryArtifact as any as Artifact, TokenRegistryArtifact: (TokenRegistryArtifact as any) as Artifact,
TokenTransferProxyArtifact: TokenTransferProxyArtifact as any as Artifact, TokenTransferProxyArtifact: (TokenTransferProxyArtifact as any) as Artifact,
}; };

View File

@ -21,7 +21,9 @@ import {AbiDecoder} from '../utils/abi_decoder';
import { constants } from '../utils/constants'; import { constants } from '../utils/constants';
import { filterUtils } from '../utils/filter_utils'; import { filterUtils } from '../utils/filter_utils';
const CONTRACT_NAME_TO_NOT_FOUND_ERROR: {[contractName: string]: ZeroExError} = { const CONTRACT_NAME_TO_NOT_FOUND_ERROR: {
[contractName: string]: ZeroExError;
} = {
ZRX: ZeroExError.ZRXContractDoesNotExist, ZRX: ZeroExError.ZRXContractDoesNotExist,
EtherToken: ZeroExError.EtherTokenContractDoesNotExist, EtherToken: ZeroExError.EtherTokenContractDoesNotExist,
Token: ZeroExError.TokenContractDoesNotExist, Token: ZeroExError.TokenContractDoesNotExist,
@ -37,7 +39,9 @@ export class ContractWrapper {
private _blockAndLogStreamerIfExists: BlockAndLogStreamer | undefined; private _blockAndLogStreamerIfExists: BlockAndLogStreamer | undefined;
private _blockAndLogStreamInterval: NodeJS.Timer; private _blockAndLogStreamInterval: NodeJS.Timer;
private _filters: { [filterToken: string]: Web3.FilterObject }; private _filters: { [filterToken: string]: Web3.FilterObject };
private _filterCallbacks: {[filterToken: string]: EventCallback<ContractEventArgs>}; private _filterCallbacks: {
[filterToken: string]: EventCallback<ContractEventArgs>;
};
private _onLogAddedSubscriptionToken: string | undefined; private _onLogAddedSubscriptionToken: string | undefined;
private _onLogRemovedSubscriptionToken: string | undefined; private _onLogRemovedSubscriptionToken: string | undefined;
constructor(web3Wrapper: Web3Wrapper, networkId: number, abiDecoder?: AbiDecoder) { constructor(web3Wrapper: Web3Wrapper, networkId: number, abiDecoder?: AbiDecoder) {
@ -71,8 +75,12 @@ export class ContractWrapper {
} }
} }
protected _subscribe<ArgsType extends ContractEventArgs>( protected _subscribe<ArgsType extends ContractEventArgs>(
address: string, eventName: ContractEvents, indexFilterValues: IndexedFilterValues, abi: Web3.ContractAbi, address: string,
callback: EventCallback<ArgsType>): string { eventName: ContractEvents,
indexFilterValues: IndexedFilterValues,
abi: Web3.ContractAbi,
callback: EventCallback<ArgsType>,
): string {
const filter = filterUtils.getFilter(address, eventName, indexFilterValues, abi); const filter = filterUtils.getFilter(address, eventName, indexFilterValues, abi);
if (_.isUndefined(this._blockAndLogStreamerIfExists)) { if (_.isUndefined(this._blockAndLogStreamerIfExists)) {
this._startBlockAndLogStream(); this._startBlockAndLogStream();
@ -83,15 +91,20 @@ export class ContractWrapper {
return filterToken; return filterToken;
} }
protected async _getLogsAsync<ArgsType extends ContractEventArgs>( protected async _getLogsAsync<ArgsType extends ContractEventArgs>(
address: string, eventName: ContractEvents, blockRange: BlockRange, address: string,
indexFilterValues: IndexedFilterValues, abi: Web3.ContractAbi): Promise<Array<LogWithDecodedArgs<ArgsType>>> { eventName: ContractEvents,
blockRange: BlockRange,
indexFilterValues: IndexedFilterValues,
abi: Web3.ContractAbi,
): Promise<Array<LogWithDecodedArgs<ArgsType>>> {
const filter = filterUtils.getFilter(address, eventName, indexFilterValues, abi, blockRange); const filter = filterUtils.getFilter(address, eventName, indexFilterValues, abi, blockRange);
const logs = await this._web3Wrapper.getLogsAsync(filter); const logs = await this._web3Wrapper.getLogsAsync(filter);
const logsWithDecodedArguments = _.map(logs, this._tryToDecodeLogOrNoop.bind(this)); const logsWithDecodedArguments = _.map(logs, this._tryToDecodeLogOrNoop.bind(this));
return logsWithDecodedArguments; return logsWithDecodedArguments;
} }
protected _tryToDecodeLogOrNoop<ArgsType extends ContractEventArgs>( protected _tryToDecodeLogOrNoop<ArgsType extends ContractEventArgs>(
log: Web3.LogEntry): LogWithDecodedArgs<ArgsType>|RawLog { log: Web3.LogEntry,
): LogWithDecodedArgs<ArgsType> | RawLog {
if (_.isUndefined(this._abiDecoder)) { if (_.isUndefined(this._abiDecoder)) {
throw new Error(InternalZeroExError.NoAbiDecoder); throw new Error(InternalZeroExError.NoAbiDecoder);
} }
@ -99,7 +112,8 @@ export class ContractWrapper {
return logWithDecodedArgs; return logWithDecodedArgs;
} }
protected async _instantiateContractIfExistsAsync( protected async _instantiateContractIfExistsAsync(
artifact: Artifact, addressIfExists?: string, artifact: Artifact,
addressIfExists?: string,
): Promise<Web3.ContractInstance> { ): Promise<Web3.ContractInstance> {
let contractAddress: string; let contractAddress: string;
if (_.isUndefined(addressIfExists)) { if (_.isUndefined(addressIfExists)) {
@ -114,9 +128,7 @@ export class ContractWrapper {
if (!doesContractExist) { if (!doesContractExist) {
throw new Error(CONTRACT_NAME_TO_NOT_FOUND_ERROR[artifact.contract_name]); throw new Error(CONTRACT_NAME_TO_NOT_FOUND_ERROR[artifact.contract_name]);
} }
const contractInstance = this._web3Wrapper.getContractInstance( const contractInstance = this._web3Wrapper.getContractInstance(artifact.abi, contractAddress);
artifact.abi, contractAddress,
);
return contractInstance; return contractInstance;
} }
protected _getContractAddress(artifact: Artifact, addressIfExists?: string): string { protected _getContractAddress(artifact: Artifact, addressIfExists?: string): string {
@ -153,7 +165,8 @@ export class ContractWrapper {
const catchAllLogFilter = {}; const catchAllLogFilter = {};
this._blockAndLogStreamerIfExists.addLogFilter(catchAllLogFilter); this._blockAndLogStreamerIfExists.addLogFilter(catchAllLogFilter);
this._blockAndLogStreamInterval = intervalUtils.setAsyncExcludingInterval( this._blockAndLogStreamInterval = intervalUtils.setAsyncExcludingInterval(
this._reconcileBlockAsync.bind(this), constants.DEFAULT_BLOCK_POLLING_INTERVAL, this._reconcileBlockAsync.bind(this),
constants.DEFAULT_BLOCK_POLLING_INTERVAL,
); );
let isRemoved = false; let isRemoved = false;
this._onLogAddedSubscriptionToken = this._blockAndLogStreamerIfExists.subscribeToOnLogAdded( this._onLogAddedSubscriptionToken = this._blockAndLogStreamerIfExists.subscribeToOnLogAdded(
@ -179,7 +192,7 @@ export class ContractWrapper {
// We need to coerce to Block type cause Web3.Block includes types for mempool blocks // We need to coerce to Block type cause Web3.Block includes types for mempool blocks
if (!_.isUndefined(this._blockAndLogStreamerIfExists)) { if (!_.isUndefined(this._blockAndLogStreamerIfExists)) {
// If we clear the interval while fetching the block - this._blockAndLogStreamer will be undefined // If we clear the interval while fetching the block - this._blockAndLogStreamer will be undefined
await this._blockAndLogStreamerIfExists.reconcileNewBlock(latestBlock as any as Block); await this._blockAndLogStreamerIfExists.reconcileNewBlock((latestBlock as any) as Block);
} }
} catch (err) { } catch (err) {
const filterTokens = _.keys(this._filterCallbacks); const filterTokens = _.keys(this._filterCallbacks);

View File

@ -26,7 +26,9 @@ import {TokenWrapper} from './token_wrapper';
* The caller can convert ETH into the equivalent number of wrapped ETH ERC20 tokens and back. * The caller can convert ETH into the equivalent number of wrapped ETH ERC20 tokens and back.
*/ */
export class EtherTokenWrapper extends ContractWrapper { export class EtherTokenWrapper extends ContractWrapper {
private _etherTokenContractsByAddress: {[address: string]: EtherTokenContract} = {}; private _etherTokenContractsByAddress: {
[address: string]: EtherTokenContract;
} = {};
private _tokenWrapper: TokenWrapper; private _tokenWrapper: TokenWrapper;
constructor(web3Wrapper: Web3Wrapper, networkId: number, abiDecoder: AbiDecoder, tokenWrapper: TokenWrapper) { constructor(web3Wrapper: Web3Wrapper, networkId: number, abiDecoder: AbiDecoder, tokenWrapper: TokenWrapper) {
super(web3Wrapper, networkId, abiDecoder); super(web3Wrapper, networkId, abiDecoder);
@ -43,7 +45,10 @@ export class EtherTokenWrapper extends ContractWrapper {
* @return Transaction hash. * @return Transaction hash.
*/ */
public async depositAsync( public async depositAsync(
etherTokenAddress: string, amountInWei: BigNumber, depositor: string, txOpts: TransactionOpts = {}, etherTokenAddress: string,
amountInWei: BigNumber,
depositor: string,
txOpts: TransactionOpts = {},
): Promise<string> { ): Promise<string> {
assert.isValidBaseUnitAmount('amountInWei', amountInWei); assert.isValidBaseUnitAmount('amountInWei', amountInWei);
await assert.isSenderAddressAsync('depositor', depositor, this._web3Wrapper); await assert.isSenderAddressAsync('depositor', depositor, this._web3Wrapper);
@ -70,7 +75,10 @@ export class EtherTokenWrapper extends ContractWrapper {
* @return Transaction hash. * @return Transaction hash.
*/ */
public async withdrawAsync( public async withdrawAsync(
etherTokenAddress: string, amountInWei: BigNumber, withdrawer: string, txOpts: TransactionOpts = {}, etherTokenAddress: string,
amountInWei: BigNumber,
withdrawer: string,
txOpts: TransactionOpts = {},
): Promise<string> { ): Promise<string> {
assert.isValidBaseUnitAmount('amountInWei', amountInWei); assert.isValidBaseUnitAmount('amountInWei', amountInWei);
await assert.isSenderAddressAsync('withdrawer', withdrawer, this._web3Wrapper); await assert.isSenderAddressAsync('withdrawer', withdrawer, this._web3Wrapper);
@ -96,14 +104,21 @@ export class EtherTokenWrapper extends ContractWrapper {
* @return Array of logs that match the parameters * @return Array of logs that match the parameters
*/ */
public async getLogsAsync<ArgsType extends EtherTokenContractEventArgs>( public async getLogsAsync<ArgsType extends EtherTokenContractEventArgs>(
etherTokenAddress: string, eventName: EtherTokenEvents, blockRange: BlockRange, etherTokenAddress: string,
indexFilterValues: IndexedFilterValues): Promise<Array<LogWithDecodedArgs<ArgsType>>> { eventName: EtherTokenEvents,
blockRange: BlockRange,
indexFilterValues: IndexedFilterValues,
): Promise<Array<LogWithDecodedArgs<ArgsType>>> {
assert.isETHAddressHex('etherTokenAddress', etherTokenAddress); assert.isETHAddressHex('etherTokenAddress', etherTokenAddress);
assert.doesBelongToStringEnum('eventName', eventName, EtherTokenEvents); assert.doesBelongToStringEnum('eventName', eventName, EtherTokenEvents);
assert.doesConformToSchema('blockRange', blockRange, schemas.blockRangeSchema); assert.doesConformToSchema('blockRange', blockRange, schemas.blockRangeSchema);
assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema); assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
const logs = await this._getLogsAsync<ArgsType>( const logs = await this._getLogsAsync<ArgsType>(
etherTokenAddress, eventName, blockRange, indexFilterValues, artifacts.EtherTokenArtifact.abi, etherTokenAddress,
eventName,
blockRange,
indexFilterValues,
artifacts.EtherTokenArtifact.abi,
); );
return logs; return logs;
} }
@ -117,14 +132,21 @@ export class EtherTokenWrapper extends ContractWrapper {
* @return Subscription token used later to unsubscribe * @return Subscription token used later to unsubscribe
*/ */
public subscribe<ArgsType extends EtherTokenContractEventArgs>( public subscribe<ArgsType extends EtherTokenContractEventArgs>(
etherTokenAddress: string, eventName: EtherTokenEvents, indexFilterValues: IndexedFilterValues, etherTokenAddress: string,
callback: EventCallback<ArgsType>): string { eventName: EtherTokenEvents,
indexFilterValues: IndexedFilterValues,
callback: EventCallback<ArgsType>,
): string {
assert.isETHAddressHex('etherTokenAddress', etherTokenAddress); assert.isETHAddressHex('etherTokenAddress', etherTokenAddress);
assert.doesBelongToStringEnum('eventName', eventName, EtherTokenEvents); assert.doesBelongToStringEnum('eventName', eventName, EtherTokenEvents);
assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema); assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
assert.isFunction('callback', callback); assert.isFunction('callback', callback);
const subscriptionToken = this._subscribe<ArgsType>( const subscriptionToken = this._subscribe<ArgsType>(
etherTokenAddress, eventName, indexFilterValues, artifacts.EtherTokenArtifact.abi, callback, etherTokenAddress,
eventName,
indexFilterValues,
artifacts.EtherTokenArtifact.abi,
callback,
); );
return subscriptionToken; return subscriptionToken;
} }
@ -151,7 +173,8 @@ export class EtherTokenWrapper extends ContractWrapper {
return etherTokenContract; return etherTokenContract;
} }
const web3ContractInstance = await this._instantiateContractIfExistsAsync( const web3ContractInstance = await this._instantiateContractIfExistsAsync(
artifacts.EtherTokenArtifact, etherTokenAddress, artifacts.EtherTokenArtifact,
etherTokenAddress,
); );
const contractInstance = new EtherTokenContract(web3ContractInstance, this._web3Wrapper.getContractDefaults()); const contractInstance = new EtherTokenContract(web3ContractInstance, this._web3Wrapper.getContractDefaults());
etherTokenContract = contractInstance; etherTokenContract = contractInstance;

View File

@ -81,8 +81,13 @@ export class ExchangeWrapper extends ContractWrapper {
]; ];
return [orderAddresses, orderValues]; return [orderAddresses, orderValues];
} }
constructor(web3Wrapper: Web3Wrapper, networkId: number, abiDecoder: AbiDecoder, constructor(
tokenWrapper: TokenWrapper, contractAddressIfExists?: string) { web3Wrapper: Web3Wrapper,
networkId: number,
abiDecoder: AbiDecoder,
tokenWrapper: TokenWrapper,
contractAddressIfExists?: string,
) {
super(web3Wrapper, networkId, abiDecoder); super(web3Wrapper, networkId, abiDecoder);
this._tokenWrapper = tokenWrapper; this._tokenWrapper = tokenWrapper;
this._orderValidationUtils = new OrderValidationUtils(this); this._orderValidationUtils = new OrderValidationUtils(this);
@ -97,14 +102,14 @@ export class ExchangeWrapper extends ContractWrapper {
* @param methodOpts Optional arguments this method accepts. * @param methodOpts Optional arguments this method accepts.
* @return The amount of the order (in taker tokens) that has either been filled or cancelled. * @return The amount of the order (in taker tokens) that has either been filled or cancelled.
*/ */
public async getUnavailableTakerAmountAsync(orderHash: string, public async getUnavailableTakerAmountAsync(orderHash: string, methodOpts?: MethodOpts): Promise<BigNumber> {
methodOpts?: MethodOpts): Promise<BigNumber> {
assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema); assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema);
const exchangeContract = await this._getExchangeContractAsync(); const exchangeContract = await this._getExchangeContractAsync();
const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock; const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock;
let unavailableTakerTokenAmount = await exchangeContract.getUnavailableTakerTokenAmount.callAsync( let unavailableTakerTokenAmount = await exchangeContract.getUnavailableTakerTokenAmount.callAsync(
orderHash, defaultBlock, orderHash,
defaultBlock,
); );
// Wrap BigNumbers returned from web3 with our own (later) version of BigNumber // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber
unavailableTakerTokenAmount = new BigNumber(unavailableTakerTokenAmount); unavailableTakerTokenAmount = new BigNumber(unavailableTakerTokenAmount);
@ -163,24 +168,32 @@ export class ExchangeWrapper extends ContractWrapper {
* @return Transaction hash. * @return Transaction hash.
*/ */
@decorators.asyncZeroExErrorHandler @decorators.asyncZeroExErrorHandler
public async fillOrderAsync(signedOrder: SignedOrder, fillTakerTokenAmount: BigNumber, public async fillOrderAsync(
signedOrder: SignedOrder,
fillTakerTokenAmount: BigNumber,
shouldThrowOnInsufficientBalanceOrAllowance: boolean, shouldThrowOnInsufficientBalanceOrAllowance: boolean,
takerAddress: string, takerAddress: string,
orderTransactionOpts: OrderTransactionOpts = {}): Promise<string> { orderTransactionOpts: OrderTransactionOpts = {},
): Promise<string> {
assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema); assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount); assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance); assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance);
await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
const exchangeInstance = await this._getExchangeContractAsync(); const exchangeInstance = await this._getExchangeContractAsync();
const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate) ? const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
SHOULD_VALIDATE_BY_DEFAULT : ? SHOULD_VALIDATE_BY_DEFAULT
orderTransactionOpts.shouldValidate; : orderTransactionOpts.shouldValidate;
if (shouldValidate) { if (shouldValidate) {
const zrxTokenAddress = this.getZRXTokenAddress(); const zrxTokenAddress = this.getZRXTokenAddress();
const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest); const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest);
await this._orderValidationUtils.validateFillOrderThrowIfInvalidAsync( await this._orderValidationUtils.validateFillOrderThrowIfInvalidAsync(
exchangeTradeEmulator, signedOrder, fillTakerTokenAmount, takerAddress, zrxTokenAddress); exchangeTradeEmulator,
signedOrder,
fillTakerTokenAmount,
takerAddress,
zrxTokenAddress,
);
} }
const [orderAddresses, orderValues] = ExchangeWrapper._getOrderAddressesAndValues(signedOrder); const [orderAddresses, orderValues] = ExchangeWrapper._getOrderAddressesAndValues(signedOrder);
@ -219,30 +232,42 @@ export class ExchangeWrapper extends ContractWrapper {
* @return Transaction hash. * @return Transaction hash.
*/ */
@decorators.asyncZeroExErrorHandler @decorators.asyncZeroExErrorHandler
public async fillOrdersUpToAsync(signedOrders: SignedOrder[], fillTakerTokenAmount: BigNumber, public async fillOrdersUpToAsync(
signedOrders: SignedOrder[],
fillTakerTokenAmount: BigNumber,
shouldThrowOnInsufficientBalanceOrAllowance: boolean, shouldThrowOnInsufficientBalanceOrAllowance: boolean,
takerAddress: string, takerAddress: string,
orderTransactionOpts: OrderTransactionOpts = {}): Promise<string> { orderTransactionOpts: OrderTransactionOpts = {},
): Promise<string> {
assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema); assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema);
const takerTokenAddresses = _.map(signedOrders, signedOrder => signedOrder.takerTokenAddress); const takerTokenAddresses = _.map(signedOrders, signedOrder => signedOrder.takerTokenAddress);
assert.hasAtMostOneUniqueValue(takerTokenAddresses, assert.hasAtMostOneUniqueValue(
ExchangeContractErrs.MultipleTakerTokensInFillUpToDisallowed); takerTokenAddresses,
ExchangeContractErrs.MultipleTakerTokensInFillUpToDisallowed,
);
const exchangeContractAddresses = _.map(signedOrders, signedOrder => signedOrder.exchangeContractAddress); const exchangeContractAddresses = _.map(signedOrders, signedOrder => signedOrder.exchangeContractAddress);
assert.hasAtMostOneUniqueValue(exchangeContractAddresses, assert.hasAtMostOneUniqueValue(
ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress); exchangeContractAddresses,
ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress,
);
assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount); assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance); assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance);
await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate) ? const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
SHOULD_VALIDATE_BY_DEFAULT : ? SHOULD_VALIDATE_BY_DEFAULT
orderTransactionOpts.shouldValidate; : orderTransactionOpts.shouldValidate;
if (shouldValidate) { if (shouldValidate) {
const zrxTokenAddress = this.getZRXTokenAddress(); const zrxTokenAddress = this.getZRXTokenAddress();
const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest); const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest);
for (const signedOrder of signedOrders) { for (const signedOrder of signedOrders) {
await this._orderValidationUtils.validateFillOrderThrowIfInvalidAsync( await this._orderValidationUtils.validateFillOrderThrowIfInvalidAsync(
exchangeTradeEmulator, signedOrder, fillTakerTokenAmount, takerAddress, zrxTokenAddress); exchangeTradeEmulator,
signedOrder,
fillTakerTokenAmount,
takerAddress,
zrxTokenAddress,
);
} }
} }
@ -300,29 +325,36 @@ export class ExchangeWrapper extends ContractWrapper {
* @return Transaction hash. * @return Transaction hash.
*/ */
@decorators.asyncZeroExErrorHandler @decorators.asyncZeroExErrorHandler
public async batchFillOrdersAsync(orderFillRequests: OrderFillRequest[], public async batchFillOrdersAsync(
orderFillRequests: OrderFillRequest[],
shouldThrowOnInsufficientBalanceOrAllowance: boolean, shouldThrowOnInsufficientBalanceOrAllowance: boolean,
takerAddress: string, takerAddress: string,
orderTransactionOpts: OrderTransactionOpts = {}): Promise<string> { orderTransactionOpts: OrderTransactionOpts = {},
): Promise<string> {
assert.doesConformToSchema('orderFillRequests', orderFillRequests, schemas.orderFillRequestsSchema); assert.doesConformToSchema('orderFillRequests', orderFillRequests, schemas.orderFillRequestsSchema);
const exchangeContractAddresses = _.map( const exchangeContractAddresses = _.map(
orderFillRequests, orderFillRequests,
orderFillRequest => orderFillRequest.signedOrder.exchangeContractAddress, orderFillRequest => orderFillRequest.signedOrder.exchangeContractAddress,
); );
assert.hasAtMostOneUniqueValue(exchangeContractAddresses, assert.hasAtMostOneUniqueValue(
ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress); exchangeContractAddresses,
ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress,
);
assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance); assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance);
await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate) ? const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
SHOULD_VALIDATE_BY_DEFAULT : ? SHOULD_VALIDATE_BY_DEFAULT
orderTransactionOpts.shouldValidate; : orderTransactionOpts.shouldValidate;
if (shouldValidate) { if (shouldValidate) {
const zrxTokenAddress = this.getZRXTokenAddress(); const zrxTokenAddress = this.getZRXTokenAddress();
const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest); const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest);
for (const orderFillRequest of orderFillRequests) { for (const orderFillRequest of orderFillRequests) {
await this._orderValidationUtils.validateFillOrderThrowIfInvalidAsync( await this._orderValidationUtils.validateFillOrderThrowIfInvalidAsync(
exchangeTradeEmulator, orderFillRequest.signedOrder, orderFillRequest.takerTokenFillAmount, exchangeTradeEmulator,
takerAddress, zrxTokenAddress, orderFillRequest.signedOrder,
orderFillRequest.takerTokenFillAmount,
takerAddress,
zrxTokenAddress,
); );
} }
} }
@ -373,23 +405,31 @@ export class ExchangeWrapper extends ContractWrapper {
* @return Transaction hash. * @return Transaction hash.
*/ */
@decorators.asyncZeroExErrorHandler @decorators.asyncZeroExErrorHandler
public async fillOrKillOrderAsync(signedOrder: SignedOrder, fillTakerTokenAmount: BigNumber, public async fillOrKillOrderAsync(
signedOrder: SignedOrder,
fillTakerTokenAmount: BigNumber,
takerAddress: string, takerAddress: string,
orderTransactionOpts: OrderTransactionOpts = {}): Promise<string> { orderTransactionOpts: OrderTransactionOpts = {},
): Promise<string> {
assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema); assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount); assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
const exchangeInstance = await this._getExchangeContractAsync(); const exchangeInstance = await this._getExchangeContractAsync();
const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate) ? const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
SHOULD_VALIDATE_BY_DEFAULT : ? SHOULD_VALIDATE_BY_DEFAULT
orderTransactionOpts.shouldValidate; : orderTransactionOpts.shouldValidate;
if (shouldValidate) { if (shouldValidate) {
const zrxTokenAddress = this.getZRXTokenAddress(); const zrxTokenAddress = this.getZRXTokenAddress();
const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest); const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest);
await this._orderValidationUtils.validateFillOrKillOrderThrowIfInvalidAsync( await this._orderValidationUtils.validateFillOrKillOrderThrowIfInvalidAsync(
exchangeTradeEmulator, signedOrder, fillTakerTokenAmount, takerAddress, zrxTokenAddress); exchangeTradeEmulator,
signedOrder,
fillTakerTokenAmount,
takerAddress,
zrxTokenAddress,
);
} }
const [orderAddresses, orderValues] = ExchangeWrapper._getOrderAddressesAndValues(signedOrder); const [orderAddresses, orderValues] = ExchangeWrapper._getOrderAddressesAndValues(signedOrder);
@ -418,33 +458,39 @@ export class ExchangeWrapper extends ContractWrapper {
* @return Transaction hash. * @return Transaction hash.
*/ */
@decorators.asyncZeroExErrorHandler @decorators.asyncZeroExErrorHandler
public async batchFillOrKillAsync(orderFillRequests: OrderFillRequest[], public async batchFillOrKillAsync(
orderFillRequests: OrderFillRequest[],
takerAddress: string, takerAddress: string,
orderTransactionOpts: OrderTransactionOpts = {}): Promise<string> { orderTransactionOpts: OrderTransactionOpts = {},
assert.doesConformToSchema('orderFillRequests', orderFillRequests, ): Promise<string> {
schemas.orderFillRequestsSchema); assert.doesConformToSchema('orderFillRequests', orderFillRequests, schemas.orderFillRequestsSchema);
const exchangeContractAddresses = _.map( const exchangeContractAddresses = _.map(
orderFillRequests, orderFillRequests,
orderFillRequest => orderFillRequest.signedOrder.exchangeContractAddress, orderFillRequest => orderFillRequest.signedOrder.exchangeContractAddress,
); );
assert.hasAtMostOneUniqueValue(exchangeContractAddresses, assert.hasAtMostOneUniqueValue(
ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress); exchangeContractAddresses,
ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress,
);
await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
if (_.isEmpty(orderFillRequests)) { if (_.isEmpty(orderFillRequests)) {
throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem); throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
} }
const exchangeInstance = await this._getExchangeContractAsync(); const exchangeInstance = await this._getExchangeContractAsync();
const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate) ? const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
SHOULD_VALIDATE_BY_DEFAULT : ? SHOULD_VALIDATE_BY_DEFAULT
orderTransactionOpts.shouldValidate; : orderTransactionOpts.shouldValidate;
if (shouldValidate) { if (shouldValidate) {
const zrxTokenAddress = this.getZRXTokenAddress(); const zrxTokenAddress = this.getZRXTokenAddress();
const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest); const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest);
for (const orderFillRequest of orderFillRequests) { for (const orderFillRequest of orderFillRequests) {
await this._orderValidationUtils.validateFillOrKillOrderThrowIfInvalidAsync( await this._orderValidationUtils.validateFillOrKillOrderThrowIfInvalidAsync(
exchangeTradeEmulator, orderFillRequest.signedOrder, orderFillRequest.takerTokenFillAmount, exchangeTradeEmulator,
takerAddress, zrxTokenAddress, orderFillRequest.signedOrder,
orderFillRequest.takerTokenFillAmount,
takerAddress,
zrxTokenAddress,
); );
} }
} }
@ -460,8 +506,9 @@ export class ExchangeWrapper extends ContractWrapper {
}); });
// We use _.unzip<any> because _.unzip doesn't type check if values have different types :'( // We use _.unzip<any> because _.unzip doesn't type check if values have different types :'(
const [orderAddresses, orderValues, fillTakerTokenAmounts, vParams, rParams, sParams] = const [orderAddresses, orderValues, fillTakerTokenAmounts, vParams, rParams, sParams] = _.unzip<any>(
_.unzip<any>(orderAddressesValuesAndTakerTokenFillAmounts); orderAddressesValuesAndTakerTokenFillAmounts,
);
const txHash = await exchangeInstance.batchFillOrKillOrders.sendTransactionAsync( const txHash = await exchangeInstance.batchFillOrKillOrders.sendTransactionAsync(
orderAddresses, orderAddresses,
orderValues, orderValues,
@ -486,23 +533,28 @@ export class ExchangeWrapper extends ContractWrapper {
* @return Transaction hash. * @return Transaction hash.
*/ */
@decorators.asyncZeroExErrorHandler @decorators.asyncZeroExErrorHandler
public async cancelOrderAsync(order: Order|SignedOrder, public async cancelOrderAsync(
order: Order | SignedOrder,
cancelTakerTokenAmount: BigNumber, cancelTakerTokenAmount: BigNumber,
orderTransactionOpts: OrderTransactionOpts = {}): Promise<string> { orderTransactionOpts: OrderTransactionOpts = {},
): Promise<string> {
assert.doesConformToSchema('order', order, schemas.orderSchema); assert.doesConformToSchema('order', order, schemas.orderSchema);
assert.isValidBaseUnitAmount('takerTokenCancelAmount', cancelTakerTokenAmount); assert.isValidBaseUnitAmount('takerTokenCancelAmount', cancelTakerTokenAmount);
await assert.isSenderAddressAsync('order.maker', order.maker, this._web3Wrapper); await assert.isSenderAddressAsync('order.maker', order.maker, this._web3Wrapper);
const exchangeInstance = await this._getExchangeContractAsync(); const exchangeInstance = await this._getExchangeContractAsync();
const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate) ? const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
SHOULD_VALIDATE_BY_DEFAULT : ? SHOULD_VALIDATE_BY_DEFAULT
orderTransactionOpts.shouldValidate; : orderTransactionOpts.shouldValidate;
if (shouldValidate) { if (shouldValidate) {
const orderHash = utils.getOrderHashHex(order); const orderHash = utils.getOrderHashHex(order);
const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash); const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash);
OrderValidationUtils.validateCancelOrderThrowIfInvalid( OrderValidationUtils.validateCancelOrderThrowIfInvalid(
order, cancelTakerTokenAmount, unavailableTakerTokenAmount); order,
cancelTakerTokenAmount,
unavailableTakerTokenAmount,
);
} }
const [orderAddresses, orderValues] = ExchangeWrapper._getOrderAddressesAndValues(order); const [orderAddresses, orderValues] = ExchangeWrapper._getOrderAddressesAndValues(order);
@ -527,33 +579,40 @@ export class ExchangeWrapper extends ContractWrapper {
* @return Transaction hash. * @return Transaction hash.
*/ */
@decorators.asyncZeroExErrorHandler @decorators.asyncZeroExErrorHandler
public async batchCancelOrdersAsync(orderCancellationRequests: OrderCancellationRequest[], public async batchCancelOrdersAsync(
orderTransactionOpts: OrderTransactionOpts = {}): Promise<string> { orderCancellationRequests: OrderCancellationRequest[],
assert.doesConformToSchema('orderCancellationRequests', orderCancellationRequests, orderTransactionOpts: OrderTransactionOpts = {},
schemas.orderCancellationRequestsSchema); ): Promise<string> {
assert.doesConformToSchema(
'orderCancellationRequests',
orderCancellationRequests,
schemas.orderCancellationRequestsSchema,
);
const exchangeContractAddresses = _.map( const exchangeContractAddresses = _.map(
orderCancellationRequests, orderCancellationRequests,
orderCancellationRequest => orderCancellationRequest.order.exchangeContractAddress, orderCancellationRequest => orderCancellationRequest.order.exchangeContractAddress,
); );
assert.hasAtMostOneUniqueValue(exchangeContractAddresses, assert.hasAtMostOneUniqueValue(
ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress); exchangeContractAddresses,
ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress,
);
const makers = _.map(orderCancellationRequests, cancellationRequest => cancellationRequest.order.maker); const makers = _.map(orderCancellationRequests, cancellationRequest => cancellationRequest.order.maker);
assert.hasAtMostOneUniqueValue(makers, ExchangeContractErrs.MultipleMakersInSingleCancelBatchDisallowed); assert.hasAtMostOneUniqueValue(makers, ExchangeContractErrs.MultipleMakersInSingleCancelBatchDisallowed);
const maker = makers[0]; const maker = makers[0];
await assert.isSenderAddressAsync('maker', maker, this._web3Wrapper); await assert.isSenderAddressAsync('maker', maker, this._web3Wrapper);
const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate) ? const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
SHOULD_VALIDATE_BY_DEFAULT : ? SHOULD_VALIDATE_BY_DEFAULT
orderTransactionOpts.shouldValidate; : orderTransactionOpts.shouldValidate;
if (shouldValidate) { if (shouldValidate) {
for (const orderCancellationRequest of orderCancellationRequests) { for (const orderCancellationRequest of orderCancellationRequests) {
const orderHash = utils.getOrderHashHex(orderCancellationRequest.order); const orderHash = utils.getOrderHashHex(orderCancellationRequest.order);
const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash); const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash);
OrderValidationUtils.validateCancelOrderThrowIfInvalid( OrderValidationUtils.validateCancelOrderThrowIfInvalid(
orderCancellationRequest.order, orderCancellationRequest.takerTokenCancelAmount, orderCancellationRequest.order,
orderCancellationRequest.takerTokenCancelAmount,
unavailableTakerTokenAmount, unavailableTakerTokenAmount,
); );
} }
} }
if (_.isEmpty(orderCancellationRequests)) { if (_.isEmpty(orderCancellationRequests)) {
throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem); throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
@ -566,8 +625,9 @@ export class ExchangeWrapper extends ContractWrapper {
]; ];
}); });
// We use _.unzip<any> because _.unzip doesn't type check if values have different types :'( // We use _.unzip<any> because _.unzip doesn't type check if values have different types :'(
const [orderAddresses, orderValues, cancelTakerTokenAmounts] = const [orderAddresses, orderValues, cancelTakerTokenAmounts] = _.unzip<any>(
_.unzip<any>(orderAddressesValuesAndTakerTokenCancelAmounts); orderAddressesValuesAndTakerTokenCancelAmounts,
);
const txHash = await exchangeInstance.batchCancelOrders.sendTransactionAsync( const txHash = await exchangeInstance.batchCancelOrders.sendTransactionAsync(
orderAddresses, orderAddresses,
orderValues, orderValues,
@ -589,14 +649,20 @@ export class ExchangeWrapper extends ContractWrapper {
* @return Subscription token used later to unsubscribe * @return Subscription token used later to unsubscribe
*/ */
public subscribe<ArgsType extends ExchangeContractEventArgs>( public subscribe<ArgsType extends ExchangeContractEventArgs>(
eventName: ExchangeEvents, indexFilterValues: IndexedFilterValues, eventName: ExchangeEvents,
callback: EventCallback<ArgsType>): string { indexFilterValues: IndexedFilterValues,
callback: EventCallback<ArgsType>,
): string {
assert.doesBelongToStringEnum('eventName', eventName, ExchangeEvents); assert.doesBelongToStringEnum('eventName', eventName, ExchangeEvents);
assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema); assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
assert.isFunction('callback', callback); assert.isFunction('callback', callback);
const exchangeContractAddress = this.getContractAddress(); const exchangeContractAddress = this.getContractAddress();
const subscriptionToken = this._subscribe<ArgsType>( const subscriptionToken = this._subscribe<ArgsType>(
exchangeContractAddress, eventName, indexFilterValues, artifacts.ExchangeArtifact.abi, callback, exchangeContractAddress,
eventName,
indexFilterValues,
artifacts.ExchangeArtifact.abi,
callback,
); );
return subscriptionToken; return subscriptionToken;
} }
@ -622,14 +688,20 @@ export class ExchangeWrapper extends ContractWrapper {
* @return Array of logs that match the parameters * @return Array of logs that match the parameters
*/ */
public async getLogsAsync<ArgsType extends ExchangeContractEventArgs>( public async getLogsAsync<ArgsType extends ExchangeContractEventArgs>(
eventName: ExchangeEvents, blockRange: BlockRange, indexFilterValues: IndexedFilterValues, eventName: ExchangeEvents,
blockRange: BlockRange,
indexFilterValues: IndexedFilterValues,
): Promise<Array<LogWithDecodedArgs<ArgsType>>> { ): Promise<Array<LogWithDecodedArgs<ArgsType>>> {
assert.doesBelongToStringEnum('eventName', eventName, ExchangeEvents); assert.doesBelongToStringEnum('eventName', eventName, ExchangeEvents);
assert.doesConformToSchema('blockRange', blockRange, schemas.blockRangeSchema); assert.doesConformToSchema('blockRange', blockRange, schemas.blockRangeSchema);
assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema); assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
const exchangeContractAddress = this.getContractAddress(); const exchangeContractAddress = this.getContractAddress();
const logs = await this._getLogsAsync<ArgsType>( const logs = await this._getLogsAsync<ArgsType>(
exchangeContractAddress, eventName, blockRange, indexFilterValues, artifacts.ExchangeArtifact.abi, exchangeContractAddress,
eventName,
blockRange,
indexFilterValues,
artifacts.ExchangeArtifact.abi,
); );
return logs; return logs;
} }
@ -652,14 +724,18 @@ export class ExchangeWrapper extends ContractWrapper {
* to validate for. * to validate for.
*/ */
public async validateOrderFillableOrThrowAsync( public async validateOrderFillableOrThrowAsync(
signedOrder: SignedOrder, opts?: ValidateOrderFillableOpts, signedOrder: SignedOrder,
opts?: ValidateOrderFillableOpts,
): Promise<void> { ): Promise<void> {
assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema); assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
const zrxTokenAddress = this.getZRXTokenAddress(); const zrxTokenAddress = this.getZRXTokenAddress();
const expectedFillTakerTokenAmount = !_.isUndefined(opts) ? opts.expectedFillTakerTokenAmount : undefined; const expectedFillTakerTokenAmount = !_.isUndefined(opts) ? opts.expectedFillTakerTokenAmount : undefined;
const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest); const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest);
await this._orderValidationUtils.validateOrderFillableOrThrowAsync( await this._orderValidationUtils.validateOrderFillableOrThrowAsync(
exchangeTradeEmulator, signedOrder, zrxTokenAddress, expectedFillTakerTokenAmount, exchangeTradeEmulator,
signedOrder,
zrxTokenAddress,
expectedFillTakerTokenAmount,
); );
} }
/** /**
@ -670,16 +746,23 @@ export class ExchangeWrapper extends ContractWrapper {
* @param takerAddress The user Ethereum address who would like to fill this order. * @param takerAddress The user Ethereum address who would like to fill this order.
* Must be available via the supplied Web3.Provider passed to 0x.js. * Must be available via the supplied Web3.Provider passed to 0x.js.
*/ */
public async validateFillOrderThrowIfInvalidAsync(signedOrder: SignedOrder, public async validateFillOrderThrowIfInvalidAsync(
signedOrder: SignedOrder,
fillTakerTokenAmount: BigNumber, fillTakerTokenAmount: BigNumber,
takerAddress: string): Promise<void> { takerAddress: string,
): Promise<void> {
assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema); assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount); assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
const zrxTokenAddress = this.getZRXTokenAddress(); const zrxTokenAddress = this.getZRXTokenAddress();
const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest); const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest);
await this._orderValidationUtils.validateFillOrderThrowIfInvalidAsync( await this._orderValidationUtils.validateFillOrderThrowIfInvalidAsync(
exchangeTradeEmulator, signedOrder, fillTakerTokenAmount, takerAddress, zrxTokenAddress); exchangeTradeEmulator,
signedOrder,
fillTakerTokenAmount,
takerAddress,
zrxTokenAddress,
);
} }
/** /**
* Checks if cancelling a given order will succeed and throws an informative error if it won't. * Checks if cancelling a given order will succeed and throws an informative error if it won't.
@ -688,13 +771,18 @@ export class ExchangeWrapper extends ContractWrapper {
* @param cancelTakerTokenAmount The amount (specified in taker tokens) that you would like to cancel. * @param cancelTakerTokenAmount The amount (specified in taker tokens) that you would like to cancel.
*/ */
public async validateCancelOrderThrowIfInvalidAsync( public async validateCancelOrderThrowIfInvalidAsync(
order: Order, cancelTakerTokenAmount: BigNumber): Promise<void> { order: Order,
cancelTakerTokenAmount: BigNumber,
): Promise<void> {
assert.doesConformToSchema('order', order, schemas.orderSchema); assert.doesConformToSchema('order', order, schemas.orderSchema);
assert.isValidBaseUnitAmount('cancelTakerTokenAmount', cancelTakerTokenAmount); assert.isValidBaseUnitAmount('cancelTakerTokenAmount', cancelTakerTokenAmount);
const orderHash = utils.getOrderHashHex(order); const orderHash = utils.getOrderHashHex(order);
const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash); const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash);
OrderValidationUtils.validateCancelOrderThrowIfInvalid( OrderValidationUtils.validateCancelOrderThrowIfInvalid(
order, cancelTakerTokenAmount, unavailableTakerTokenAmount); order,
cancelTakerTokenAmount,
unavailableTakerTokenAmount,
);
} }
/** /**
* Checks if calling fillOrKill on a given order will succeed and throws an informative error if it won't. * Checks if calling fillOrKill on a given order will succeed and throws an informative error if it won't.
@ -704,16 +792,23 @@ export class ExchangeWrapper extends ContractWrapper {
* @param takerAddress The user Ethereum address who would like to fill this order. * @param takerAddress The user Ethereum address who would like to fill this order.
* Must be available via the supplied Web3.Provider passed to 0x.js. * Must be available via the supplied Web3.Provider passed to 0x.js.
*/ */
public async validateFillOrKillOrderThrowIfInvalidAsync(signedOrder: SignedOrder, public async validateFillOrKillOrderThrowIfInvalidAsync(
signedOrder: SignedOrder,
fillTakerTokenAmount: BigNumber, fillTakerTokenAmount: BigNumber,
takerAddress: string): Promise<void> { takerAddress: string,
): Promise<void> {
assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema); assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount); assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
const zrxTokenAddress = this.getZRXTokenAddress(); const zrxTokenAddress = this.getZRXTokenAddress();
const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest); const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest);
await this._orderValidationUtils.validateFillOrKillOrderThrowIfInvalidAsync( await this._orderValidationUtils.validateFillOrKillOrderThrowIfInvalidAsync(
exchangeTradeEmulator, signedOrder, fillTakerTokenAmount, takerAddress, zrxTokenAddress); exchangeTradeEmulator,
signedOrder,
fillTakerTokenAmount,
takerAddress,
zrxTokenAddress,
);
} }
/** /**
* Checks if rounding error will be > 0.1% when computing makerTokenAmount by doing: * Checks if rounding error will be > 0.1% when computing makerTokenAmount by doing:
@ -724,15 +819,19 @@ export class ExchangeWrapper extends ContractWrapper {
* @param takerTokenAmount The order size on the taker side * @param takerTokenAmount The order size on the taker side
* @param makerTokenAmount The order size on the maker side * @param makerTokenAmount The order size on the maker side
*/ */
public async isRoundingErrorAsync(fillTakerTokenAmount: BigNumber, public async isRoundingErrorAsync(
fillTakerTokenAmount: BigNumber,
takerTokenAmount: BigNumber, takerTokenAmount: BigNumber,
makerTokenAmount: BigNumber): Promise<boolean> { makerTokenAmount: BigNumber,
): Promise<boolean> {
assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount); assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
assert.isValidBaseUnitAmount('takerTokenAmount', takerTokenAmount); assert.isValidBaseUnitAmount('takerTokenAmount', takerTokenAmount);
assert.isValidBaseUnitAmount('makerTokenAmount', makerTokenAmount); assert.isValidBaseUnitAmount('makerTokenAmount', makerTokenAmount);
const exchangeInstance = await this._getExchangeContractAsync(); const exchangeInstance = await this._getExchangeContractAsync();
const isRoundingError = await exchangeInstance.isRoundingError.callAsync( const isRoundingError = await exchangeInstance.isRoundingError.callAsync(
fillTakerTokenAmount, takerTokenAmount, makerTokenAmount, fillTakerTokenAmount,
takerTokenAmount,
makerTokenAmount,
); );
return isRoundingError; return isRoundingError;
} }
@ -756,17 +855,18 @@ export class ExchangeWrapper extends ContractWrapper {
* @return Address of ZRX token * @return Address of ZRX token
*/ */
public getZRXTokenAddress(): string { public getZRXTokenAddress(): string {
const contractAddress = this._getContractAddress( const contractAddress = this._getContractAddress(artifacts.ZRXArtifact, this._zrxContractAddressIfExists);
artifacts.ZRXArtifact, this._zrxContractAddressIfExists,
);
return contractAddress; return contractAddress;
} }
private _invalidateContractInstances(): void { private _invalidateContractInstances(): void {
this.unsubscribeAll(); this.unsubscribeAll();
delete this._exchangeContractIfExists; delete this._exchangeContractIfExists;
} }
private async _isValidSignatureUsingContractCallAsync(dataHex: string, ecSignature: ECSignature, private async _isValidSignatureUsingContractCallAsync(
signerAddressHex: string): Promise<boolean> { dataHex: string,
ecSignature: ECSignature,
signerAddressHex: string,
): Promise<boolean> {
assert.isHexString('dataHex', dataHex); assert.isHexString('dataHex', dataHex);
assert.doesConformToSchema('ecSignature', ecSignature, schemas.ecSignatureSchema); assert.doesConformToSchema('ecSignature', ecSignature, schemas.ecSignatureSchema);
assert.isETHAddressHex('signerAddressHex', signerAddressHex); assert.isETHAddressHex('signerAddressHex', signerAddressHex);
@ -793,7 +893,8 @@ export class ExchangeWrapper extends ContractWrapper {
return this._exchangeContractIfExists; return this._exchangeContractIfExists;
} }
const web3ContractInstance = await this._instantiateContractIfExistsAsync( const web3ContractInstance = await this._instantiateContractIfExistsAsync(
artifacts.ExchangeArtifact, this._contractAddressIfExists, artifacts.ExchangeArtifact,
this._contractAddressIfExists,
); );
const contractInstance = new ExchangeContract(web3ContractInstance, this._web3Wrapper.getContractDefaults()); const contractInstance = new ExchangeContract(web3ContractInstance, this._web3Wrapper.getContractDefaults());
this._exchangeContractIfExists = contractInstance; this._exchangeContractIfExists = contractInstance;

View File

@ -37,9 +37,8 @@ export class TokenRegistryWrapper extends ContractWrapper {
*/ */
public async getTokensAsync(): Promise<Token[]> { public async getTokensAsync(): Promise<Token[]> {
const addresses = await this.getTokenAddressesAsync(); const addresses = await this.getTokenAddressesAsync();
const tokenPromises: Array<Promise<Token|undefined>> = _.map( const tokenPromises: Array<Promise<Token | undefined>> = _.map(addresses, async (address: string) =>
addresses, this.getTokenIfExistsAsync(address),
async (address: string) => this.getTokenIfExistsAsync(address),
); );
const tokens = await Promise.all(tokenPromises); const tokens = await Promise.all(tokenPromises);
return tokens as Token[]; return tokens as Token[];
@ -104,7 +103,8 @@ export class TokenRegistryWrapper extends ContractWrapper {
*/ */
public getContractAddress(): string { public getContractAddress(): string {
const contractAddress = this._getContractAddress( const contractAddress = this._getContractAddress(
artifacts.TokenRegistryArtifact, this._contractAddressIfExists, artifacts.TokenRegistryArtifact,
this._contractAddressIfExists,
); );
return contractAddress; return contractAddress;
} }
@ -116,10 +116,12 @@ export class TokenRegistryWrapper extends ContractWrapper {
return this._tokenRegistryContractIfExists; return this._tokenRegistryContractIfExists;
} }
const web3ContractInstance = await this._instantiateContractIfExistsAsync( const web3ContractInstance = await this._instantiateContractIfExistsAsync(
artifacts.TokenRegistryArtifact, this._contractAddressIfExists, artifacts.TokenRegistryArtifact,
this._contractAddressIfExists,
); );
const contractInstance = new TokenRegistryContract( const contractInstance = new TokenRegistryContract(
web3ContractInstance, this._web3Wrapper.getContractDefaults(), web3ContractInstance,
this._web3Wrapper.getContractDefaults(),
); );
this._tokenRegistryContractIfExists = contractInstance; this._tokenRegistryContractIfExists = contractInstance;
return this._tokenRegistryContractIfExists; return this._tokenRegistryContractIfExists;

View File

@ -42,7 +42,8 @@ export class TokenTransferProxyWrapper extends ContractWrapper {
*/ */
public getContractAddress(): string { public getContractAddress(): string {
const contractAddress = this._getContractAddress( const contractAddress = this._getContractAddress(
artifacts.TokenTransferProxyArtifact, this._contractAddressIfExists, artifacts.TokenTransferProxyArtifact,
this._contractAddressIfExists,
); );
return contractAddress; return contractAddress;
} }
@ -54,10 +55,12 @@ export class TokenTransferProxyWrapper extends ContractWrapper {
return this._tokenTransferProxyContractIfExists; return this._tokenTransferProxyContractIfExists;
} }
const web3ContractInstance = await this._instantiateContractIfExistsAsync( const web3ContractInstance = await this._instantiateContractIfExistsAsync(
artifacts.TokenTransferProxyArtifact, this._contractAddressIfExists, artifacts.TokenTransferProxyArtifact,
this._contractAddressIfExists,
); );
const contractInstance = new TokenTransferProxyContract( const contractInstance = new TokenTransferProxyContract(
web3ContractInstance, this._web3Wrapper.getContractDefaults(), web3ContractInstance,
this._web3Wrapper.getContractDefaults(),
); );
this._tokenTransferProxyContractIfExists = contractInstance; this._tokenTransferProxyContractIfExists = contractInstance;
return this._tokenTransferProxyContractIfExists; return this._tokenTransferProxyContractIfExists;

View File

@ -32,8 +32,12 @@ export class TokenWrapper extends ContractWrapper {
public UNLIMITED_ALLOWANCE_IN_BASE_UNITS = constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS; public UNLIMITED_ALLOWANCE_IN_BASE_UNITS = constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS;
private _tokenContractsByAddress: { [address: string]: TokenContract }; private _tokenContractsByAddress: { [address: string]: TokenContract };
private _tokenTransferProxyWrapper: TokenTransferProxyWrapper; private _tokenTransferProxyWrapper: TokenTransferProxyWrapper;
constructor(web3Wrapper: Web3Wrapper, networkId: number, abiDecoder: AbiDecoder, constructor(
tokenTransferProxyWrapper: TokenTransferProxyWrapper) { web3Wrapper: Web3Wrapper,
networkId: number,
abiDecoder: AbiDecoder,
tokenTransferProxyWrapper: TokenTransferProxyWrapper,
) {
super(web3Wrapper, networkId, abiDecoder); super(web3Wrapper, networkId, abiDecoder);
this._tokenContractsByAddress = {}; this._tokenContractsByAddress = {};
this._tokenTransferProxyWrapper = tokenTransferProxyWrapper; this._tokenTransferProxyWrapper = tokenTransferProxyWrapper;
@ -45,8 +49,11 @@ export class TokenWrapper extends ContractWrapper {
* @param methodOpts Optional arguments this method accepts. * @param methodOpts Optional arguments this method accepts.
* @return The owner's ERC20 token balance in base units. * @return The owner's ERC20 token balance in base units.
*/ */
public async getBalanceAsync(tokenAddress: string, ownerAddress: string, public async getBalanceAsync(
methodOpts?: MethodOpts): Promise<BigNumber> { tokenAddress: string,
ownerAddress: string,
methodOpts?: MethodOpts,
): Promise<BigNumber> {
assert.isETHAddressHex('ownerAddress', ownerAddress); assert.isETHAddressHex('ownerAddress', ownerAddress);
assert.isETHAddressHex('tokenAddress', tokenAddress); assert.isETHAddressHex('tokenAddress', tokenAddress);
@ -68,8 +75,13 @@ export class TokenWrapper extends ContractWrapper {
* @param txOpts Transaction parameters. * @param txOpts Transaction parameters.
* @return Transaction hash. * @return Transaction hash.
*/ */
public async setAllowanceAsync(tokenAddress: string, ownerAddress: string, spenderAddress: string, public async setAllowanceAsync(
amountInBaseUnits: BigNumber, txOpts: TransactionOpts = {}): Promise<string> { tokenAddress: string,
ownerAddress: string,
spenderAddress: string,
amountInBaseUnits: BigNumber,
txOpts: TransactionOpts = {},
): Promise<string> {
await assert.isSenderAddressAsync('ownerAddress', ownerAddress, this._web3Wrapper); await assert.isSenderAddressAsync('ownerAddress', ownerAddress, this._web3Wrapper);
assert.isETHAddressHex('spenderAddress', spenderAddress); assert.isETHAddressHex('spenderAddress', spenderAddress);
assert.isETHAddressHex('tokenAddress', tokenAddress); assert.isETHAddressHex('tokenAddress', tokenAddress);
@ -95,10 +107,18 @@ export class TokenWrapper extends ContractWrapper {
* @param txOpts Transaction parameters. * @param txOpts Transaction parameters.
* @return Transaction hash. * @return Transaction hash.
*/ */
public async setUnlimitedAllowanceAsync(tokenAddress: string, ownerAddress: string, public async setUnlimitedAllowanceAsync(
spenderAddress: string, txOpts: TransactionOpts = {}): Promise<string> { tokenAddress: string,
ownerAddress: string,
spenderAddress: string,
txOpts: TransactionOpts = {},
): Promise<string> {
const txHash = await this.setAllowanceAsync( const txHash = await this.setAllowanceAsync(
tokenAddress, ownerAddress, spenderAddress, this.UNLIMITED_ALLOWANCE_IN_BASE_UNITS, txOpts, tokenAddress,
ownerAddress,
spenderAddress,
this.UNLIMITED_ALLOWANCE_IN_BASE_UNITS,
txOpts,
); );
return txHash; return txHash;
} }
@ -110,8 +130,12 @@ export class TokenWrapper extends ContractWrapper {
* @param spenderAddress The hex encoded user Ethereum address who can spend the allowance you are fetching. * @param spenderAddress The hex encoded user Ethereum address who can spend the allowance you are fetching.
* @param methodOpts Optional arguments this method accepts. * @param methodOpts Optional arguments this method accepts.
*/ */
public async getAllowanceAsync(tokenAddress: string, ownerAddress: string, public async getAllowanceAsync(
spenderAddress: string, methodOpts?: MethodOpts): Promise<BigNumber> { tokenAddress: string,
ownerAddress: string,
spenderAddress: string,
methodOpts?: MethodOpts,
): Promise<BigNumber> {
assert.isETHAddressHex('ownerAddress', ownerAddress); assert.isETHAddressHex('ownerAddress', ownerAddress);
assert.isETHAddressHex('tokenAddress', tokenAddress); assert.isETHAddressHex('tokenAddress', tokenAddress);
@ -128,8 +152,11 @@ export class TokenWrapper extends ContractWrapper {
* @param ownerAddress The hex encoded user Ethereum address whose proxy contract allowance we are retrieving. * @param ownerAddress The hex encoded user Ethereum address whose proxy contract allowance we are retrieving.
* @param methodOpts Optional arguments this method accepts. * @param methodOpts Optional arguments this method accepts.
*/ */
public async getProxyAllowanceAsync(tokenAddress: string, ownerAddress: string, public async getProxyAllowanceAsync(
methodOpts?: MethodOpts): Promise<BigNumber> { tokenAddress: string,
ownerAddress: string,
methodOpts?: MethodOpts,
): Promise<BigNumber> {
assert.isETHAddressHex('ownerAddress', ownerAddress); assert.isETHAddressHex('ownerAddress', ownerAddress);
assert.isETHAddressHex('tokenAddress', tokenAddress); assert.isETHAddressHex('tokenAddress', tokenAddress);
@ -147,15 +174,23 @@ export class TokenWrapper extends ContractWrapper {
* @param txOpts Transaction parameters. * @param txOpts Transaction parameters.
* @return Transaction hash. * @return Transaction hash.
*/ */
public async setProxyAllowanceAsync(tokenAddress: string, ownerAddress: string, public async setProxyAllowanceAsync(
amountInBaseUnits: BigNumber, txOpts: TransactionOpts = {}): Promise<string> { tokenAddress: string,
ownerAddress: string,
amountInBaseUnits: BigNumber,
txOpts: TransactionOpts = {},
): Promise<string> {
assert.isETHAddressHex('ownerAddress', ownerAddress); assert.isETHAddressHex('ownerAddress', ownerAddress);
assert.isETHAddressHex('tokenAddress', tokenAddress); assert.isETHAddressHex('tokenAddress', tokenAddress);
assert.isValidBaseUnitAmount('amountInBaseUnits', amountInBaseUnits); assert.isValidBaseUnitAmount('amountInBaseUnits', amountInBaseUnits);
const proxyAddress = this._tokenTransferProxyWrapper.getContractAddress(); const proxyAddress = this._tokenTransferProxyWrapper.getContractAddress();
const txHash = await this.setAllowanceAsync( const txHash = await this.setAllowanceAsync(
tokenAddress, ownerAddress, proxyAddress, amountInBaseUnits, txOpts, tokenAddress,
ownerAddress,
proxyAddress,
amountInBaseUnits,
txOpts,
); );
return txHash; return txHash;
} }
@ -171,10 +206,15 @@ export class TokenWrapper extends ContractWrapper {
* @return Transaction hash. * @return Transaction hash.
*/ */
public async setUnlimitedProxyAllowanceAsync( public async setUnlimitedProxyAllowanceAsync(
tokenAddress: string, ownerAddress: string, txOpts: TransactionOpts = {}, tokenAddress: string,
ownerAddress: string,
txOpts: TransactionOpts = {},
): Promise<string> { ): Promise<string> {
const txHash = await this.setProxyAllowanceAsync( const txHash = await this.setProxyAllowanceAsync(
tokenAddress, ownerAddress, this.UNLIMITED_ALLOWANCE_IN_BASE_UNITS, txOpts, tokenAddress,
ownerAddress,
this.UNLIMITED_ALLOWANCE_IN_BASE_UNITS,
txOpts,
); );
return txHash; return txHash;
} }
@ -187,8 +227,13 @@ export class TokenWrapper extends ContractWrapper {
* @param txOpts Transaction parameters. * @param txOpts Transaction parameters.
* @return Transaction hash. * @return Transaction hash.
*/ */
public async transferAsync(tokenAddress: string, fromAddress: string, toAddress: string, public async transferAsync(
amountInBaseUnits: BigNumber, txOpts: TransactionOpts = {}): Promise<string> { tokenAddress: string,
fromAddress: string,
toAddress: string,
amountInBaseUnits: BigNumber,
txOpts: TransactionOpts = {},
): Promise<string> {
assert.isETHAddressHex('tokenAddress', tokenAddress); assert.isETHAddressHex('tokenAddress', tokenAddress);
await assert.isSenderAddressAsync('fromAddress', fromAddress, this._web3Wrapper); await assert.isSenderAddressAsync('fromAddress', fromAddress, this._web3Wrapper);
assert.isETHAddressHex('toAddress', toAddress); assert.isETHAddressHex('toAddress', toAddress);
@ -222,9 +267,14 @@ export class TokenWrapper extends ContractWrapper {
* @param txOpts Transaction parameters. * @param txOpts Transaction parameters.
* @return Transaction hash. * @return Transaction hash.
*/ */
public async transferFromAsync(tokenAddress: string, fromAddress: string, toAddress: string, public async transferFromAsync(
senderAddress: string, amountInBaseUnits: BigNumber, txOpts: TransactionOpts = {}): tokenAddress: string,
Promise<string> { fromAddress: string,
toAddress: string,
senderAddress: string,
amountInBaseUnits: BigNumber,
txOpts: TransactionOpts = {},
): Promise<string> {
assert.isETHAddressHex('tokenAddress', tokenAddress); assert.isETHAddressHex('tokenAddress', tokenAddress);
assert.isETHAddressHex('fromAddress', fromAddress); assert.isETHAddressHex('fromAddress', fromAddress);
assert.isETHAddressHex('toAddress', toAddress); assert.isETHAddressHex('toAddress', toAddress);
@ -244,7 +294,9 @@ export class TokenWrapper extends ContractWrapper {
} }
const txHash = await tokenContract.transferFrom.sendTransactionAsync( const txHash = await tokenContract.transferFrom.sendTransactionAsync(
fromAddress, toAddress, amountInBaseUnits, fromAddress,
toAddress,
amountInBaseUnits,
{ {
from: senderAddress, from: senderAddress,
gas: txOpts.gasLimit, gas: txOpts.gasLimit,
@ -263,14 +315,21 @@ export class TokenWrapper extends ContractWrapper {
* @return Subscription token used later to unsubscribe * @return Subscription token used later to unsubscribe
*/ */
public subscribe<ArgsType extends TokenContractEventArgs>( public subscribe<ArgsType extends TokenContractEventArgs>(
tokenAddress: string, eventName: TokenEvents, indexFilterValues: IndexedFilterValues, tokenAddress: string,
callback: EventCallback<ArgsType>): string { eventName: TokenEvents,
indexFilterValues: IndexedFilterValues,
callback: EventCallback<ArgsType>,
): string {
assert.isETHAddressHex('tokenAddress', tokenAddress); assert.isETHAddressHex('tokenAddress', tokenAddress);
assert.doesBelongToStringEnum('eventName', eventName, TokenEvents); assert.doesBelongToStringEnum('eventName', eventName, TokenEvents);
assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema); assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
assert.isFunction('callback', callback); assert.isFunction('callback', callback);
const subscriptionToken = this._subscribe<ArgsType>( const subscriptionToken = this._subscribe<ArgsType>(
tokenAddress, eventName, indexFilterValues, artifacts.TokenArtifact.abi, callback, tokenAddress,
eventName,
indexFilterValues,
artifacts.TokenArtifact.abi,
callback,
); );
return subscriptionToken; return subscriptionToken;
} }
@ -297,14 +356,21 @@ export class TokenWrapper extends ContractWrapper {
* @return Array of logs that match the parameters * @return Array of logs that match the parameters
*/ */
public async getLogsAsync<ArgsType extends TokenContractEventArgs>( public async getLogsAsync<ArgsType extends TokenContractEventArgs>(
tokenAddress: string, eventName: TokenEvents, blockRange: BlockRange, tokenAddress: string,
indexFilterValues: IndexedFilterValues): Promise<Array<LogWithDecodedArgs<ArgsType>>> { eventName: TokenEvents,
blockRange: BlockRange,
indexFilterValues: IndexedFilterValues,
): Promise<Array<LogWithDecodedArgs<ArgsType>>> {
assert.isETHAddressHex('tokenAddress', tokenAddress); assert.isETHAddressHex('tokenAddress', tokenAddress);
assert.doesBelongToStringEnum('eventName', eventName, TokenEvents); assert.doesBelongToStringEnum('eventName', eventName, TokenEvents);
assert.doesConformToSchema('blockRange', blockRange, schemas.blockRangeSchema); assert.doesConformToSchema('blockRange', blockRange, schemas.blockRangeSchema);
assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema); assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
const logs = await this._getLogsAsync<ArgsType>( const logs = await this._getLogsAsync<ArgsType>(
tokenAddress, eventName, blockRange, indexFilterValues, artifacts.TokenArtifact.abi, tokenAddress,
eventName,
blockRange,
indexFilterValues,
artifacts.TokenArtifact.abi,
); );
return logs; return logs;
} }
@ -318,11 +384,10 @@ export class TokenWrapper extends ContractWrapper {
return tokenContract; return tokenContract;
} }
const web3ContractInstance = await this._instantiateContractIfExistsAsync( const web3ContractInstance = await this._instantiateContractIfExistsAsync(
artifacts.TokenArtifact, tokenAddress, artifacts.TokenArtifact,
); tokenAddress,
const contractInstance = new TokenContract(
web3ContractInstance, this._web3Wrapper.getContractDefaults(),
); );
const contractInstance = new TokenContract(web3ContractInstance, this._web3Wrapper.getContractDefaults());
tokenContract = contractInstance; tokenContract = contractInstance;
this._tokenContractsByAddress[tokenAddress] = tokenContract; this._tokenContractsByAddress[tokenAddress] = tokenContract;
return tokenContract; return tokenContract;

View File

@ -57,8 +57,7 @@ declare module 'truffle-hdwallet-provider' {
} }
// abi-decoder declarations // abi-decoder declarations
interface DecodedLogArg { interface DecodedLogArg {}
}
interface DecodedLog { interface DecodedLog {
name: string; name: string;
events: DecodedLogArg[]; events: DecodedLogArg[];

View File

@ -47,6 +47,4 @@ export {
OrderState, OrderState,
} from './types'; } from './types';
export { export { TransactionReceipt } from '@0xproject/types';
TransactionReceipt,
} from '@0xproject/types';

View File

@ -3,11 +3,7 @@ import {Web3Wrapper} from '@0xproject/web3-wrapper';
import * as _ from 'lodash'; import * as _ from 'lodash';
import * as Web3 from 'web3'; import * as Web3 from 'web3';
import { import { BlockParamLiteral, EventWatcherCallback, ZeroExError } from '../types';
BlockParamLiteral,
EventWatcherCallback,
ZeroExError,
} from '../types';
import { assert } from '../utils/assert'; import { assert } from '../utils/assert';
const DEFAULT_EVENT_POLLING_INTERVAL_MS = 200; const DEFAULT_EVENT_POLLING_INTERVAL_MS = 200;
@ -28,9 +24,9 @@ export class EventWatcher {
private _lastEvents: Web3.LogEntry[] = []; private _lastEvents: Web3.LogEntry[] = [];
constructor(web3Wrapper: Web3Wrapper, pollingIntervalIfExistsMs: undefined | number) { constructor(web3Wrapper: Web3Wrapper, pollingIntervalIfExistsMs: undefined | number) {
this._web3Wrapper = web3Wrapper; this._web3Wrapper = web3Wrapper;
this._pollingIntervalMs = _.isUndefined(pollingIntervalIfExistsMs) ? this._pollingIntervalMs = _.isUndefined(pollingIntervalIfExistsMs)
DEFAULT_EVENT_POLLING_INTERVAL_MS : ? DEFAULT_EVENT_POLLING_INTERVAL_MS
pollingIntervalIfExistsMs; : pollingIntervalIfExistsMs;
} }
public subscribe(callback: EventWatcherCallback): void { public subscribe(callback: EventWatcherCallback): void {
assert.isFunction('callback', callback); assert.isFunction('callback', callback);
@ -38,7 +34,8 @@ export class EventWatcher {
throw new Error(ZeroExError.SubscriptionAlreadyPresent); throw new Error(ZeroExError.SubscriptionAlreadyPresent);
} }
this._intervalIdIfExists = intervalUtils.setAsyncExcludingInterval( this._intervalIdIfExists = intervalUtils.setAsyncExcludingInterval(
this._pollForBlockchainEventsAsync.bind(this, callback), this._pollingIntervalMs, this._pollForBlockchainEventsAsync.bind(this, callback),
this._pollingIntervalMs,
); );
} }
public unsubscribe(): void { public unsubscribe(): void {
@ -71,7 +68,9 @@ export class EventWatcher {
return events; return events;
} }
private async _emitDifferencesAsync( private async _emitDifferencesAsync(
logs: Web3.LogEntry[], logEventState: LogEventState, callback: EventWatcherCallback, logs: Web3.LogEntry[],
logEventState: LogEventState,
callback: EventWatcherCallback,
): Promise<void> { ): Promise<void> {
for (const log of logs) { for (const log of logs) {
const logEvent = { const logEvent = {

View File

@ -19,12 +19,10 @@ export class ExpirationWatcher {
private _orderExpirationCheckingIntervalMs: number; private _orderExpirationCheckingIntervalMs: number;
private _expirationMarginMs: number; private _expirationMarginMs: number;
private _orderExpirationCheckingIntervalIdIfExists?: NodeJS.Timer; private _orderExpirationCheckingIntervalIdIfExists?: NodeJS.Timer;
constructor(expirationMarginIfExistsMs?: number, constructor(expirationMarginIfExistsMs?: number, orderExpirationCheckingIntervalIfExistsMs?: number) {
orderExpirationCheckingIntervalIfExistsMs?: number) { this._expirationMarginMs = expirationMarginIfExistsMs || DEFAULT_EXPIRATION_MARGIN_MS;
this._expirationMarginMs = expirationMarginIfExistsMs || this._orderExpirationCheckingIntervalMs =
DEFAULT_EXPIRATION_MARGIN_MS; expirationMarginIfExistsMs || DEFAULT_ORDER_EXPIRATION_CHECKING_INTERVAL_MS;
this._orderExpirationCheckingIntervalMs = expirationMarginIfExistsMs ||
DEFAULT_ORDER_EXPIRATION_CHECKING_INTERVAL_MS;
const scoreFunction = (orderHash: string) => this._expiration[orderHash].toNumber(); const scoreFunction = (orderHash: string) => this._expiration[orderHash].toNumber();
const comparator = (lhs: string, rhs: string) => scoreFunction(lhs) - scoreFunction(rhs); const comparator = (lhs: string, rhs: string) => scoreFunction(lhs) - scoreFunction(rhs);
this._orderHashByExpirationRBTree = new RBTree(comparator); this._orderHashByExpirationRBTree = new RBTree(comparator);
@ -34,7 +32,8 @@ export class ExpirationWatcher {
throw new Error(ZeroExError.SubscriptionAlreadyPresent); throw new Error(ZeroExError.SubscriptionAlreadyPresent);
} }
this._orderExpirationCheckingIntervalIdIfExists = intervalUtils.setAsyncExcludingInterval( this._orderExpirationCheckingIntervalIdIfExists = intervalUtils.setAsyncExcludingInterval(
this._pruneExpiredOrders.bind(this, callback), this._orderExpirationCheckingIntervalMs, this._pruneExpiredOrders.bind(this, callback),
this._orderExpirationCheckingIntervalMs,
); );
} }
public unsubscribe(): void { public unsubscribe(): void {

View File

@ -74,7 +74,10 @@ export class OrderStateWatcher {
private _cleanupJobInterval: number; private _cleanupJobInterval: number;
private _cleanupJobIntervalIdIfExists?: NodeJS.Timer; private _cleanupJobIntervalIdIfExists?: NodeJS.Timer;
constructor( constructor(
web3Wrapper: Web3Wrapper, abiDecoder: AbiDecoder, token: TokenWrapper, exchange: ExchangeWrapper, web3Wrapper: Web3Wrapper,
abiDecoder: AbiDecoder,
token: TokenWrapper,
exchange: ExchangeWrapper,
config?: OrderStateWatcherConfig, config?: OrderStateWatcherConfig,
) { ) {
this._abiDecoder = abiDecoder; this._abiDecoder = abiDecoder;
@ -82,24 +85,26 @@ export class OrderStateWatcher {
const pollingIntervalIfExistsMs = _.isUndefined(config) ? undefined : config.eventPollingIntervalMs; const pollingIntervalIfExistsMs = _.isUndefined(config) ? undefined : config.eventPollingIntervalMs;
this._eventWatcher = new EventWatcher(web3Wrapper, pollingIntervalIfExistsMs); this._eventWatcher = new EventWatcher(web3Wrapper, pollingIntervalIfExistsMs);
this._balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore( this._balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore(
token, BlockParamLiteral.Pending, token,
BlockParamLiteral.Pending,
); );
this._orderFilledCancelledLazyStore = new OrderFilledCancelledLazyStore(exchange); this._orderFilledCancelledLazyStore = new OrderFilledCancelledLazyStore(exchange);
this._orderStateUtils = new OrderStateUtils( this._orderStateUtils = new OrderStateUtils(
this._balanceAndProxyAllowanceLazyStore, this._orderFilledCancelledLazyStore, this._balanceAndProxyAllowanceLazyStore,
this._orderFilledCancelledLazyStore,
); );
const orderExpirationCheckingIntervalMsIfExists = _.isUndefined(config) ? const orderExpirationCheckingIntervalMsIfExists = _.isUndefined(config)
undefined : ? undefined
config.orderExpirationCheckingIntervalMs; : config.orderExpirationCheckingIntervalMs;
const expirationMarginIfExistsMs = _.isUndefined(config) ? const expirationMarginIfExistsMs = _.isUndefined(config) ? undefined : config.expirationMarginMs;
undefined :
config.expirationMarginMs;
this._expirationWatcher = new ExpirationWatcher( this._expirationWatcher = new ExpirationWatcher(
expirationMarginIfExistsMs, orderExpirationCheckingIntervalMsIfExists, expirationMarginIfExistsMs,
orderExpirationCheckingIntervalMsIfExists,
); );
this._cleanupJobInterval = _.isUndefined(config) || _.isUndefined(config.cleanupJobIntervalMs) ? this._cleanupJobInterval =
DEFAULT_CLEANUP_JOB_INTERVAL_MS : _.isUndefined(config) || _.isUndefined(config.cleanupJobIntervalMs)
config.cleanupJobIntervalMs; ? DEFAULT_CLEANUP_JOB_INTERVAL_MS
: config.cleanupJobIntervalMs;
} }
/** /**
* Add an order to the orderStateWatcher. Before the order is added, it's * Add an order to the orderStateWatcher. Before the order is added, it's
@ -148,7 +153,8 @@ export class OrderStateWatcher {
this._eventWatcher.subscribe(this._onEventWatcherCallbackAsync.bind(this)); this._eventWatcher.subscribe(this._onEventWatcherCallbackAsync.bind(this));
this._expirationWatcher.subscribe(this._onOrderExpired.bind(this)); this._expirationWatcher.subscribe(this._onOrderExpired.bind(this));
this._cleanupJobIntervalIdIfExists = intervalUtils.setAsyncExcludingInterval( this._cleanupJobIntervalIdIfExists = intervalUtils.setAsyncExcludingInterval(
this._cleanupAsync.bind(this), this._cleanupJobInterval, this._cleanupAsync.bind(this),
this._cleanupJobInterval,
); );
} }
/** /**
@ -215,23 +221,23 @@ export class OrderStateWatcher {
let makerToken: string; let makerToken: string;
let makerAddress: string; let makerAddress: string;
switch (decodedLog.event) { switch (decodedLog.event) {
case TokenEvents.Approval: case TokenEvents.Approval: {
{
// Invalidate cache // Invalidate cache
const args = decodedLog.args as ApprovalContractEventArgs; const args = decodedLog.args as ApprovalContractEventArgs;
this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(decodedLog.address, args._owner); this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(decodedLog.address, args._owner);
// Revalidate orders // Revalidate orders
makerToken = decodedLog.address; makerToken = decodedLog.address;
makerAddress = args._owner; makerAddress = args._owner;
if (!_.isUndefined(this._dependentOrderHashes[makerAddress]) && if (
!_.isUndefined(this._dependentOrderHashes[makerAddress][makerToken])) { !_.isUndefined(this._dependentOrderHashes[makerAddress]) &&
!_.isUndefined(this._dependentOrderHashes[makerAddress][makerToken])
) {
const orderHashes = Array.from(this._dependentOrderHashes[makerAddress][makerToken]); const orderHashes = Array.from(this._dependentOrderHashes[makerAddress][makerToken]);
await this._emitRevalidateOrdersAsync(orderHashes); await this._emitRevalidateOrdersAsync(orderHashes);
} }
break; break;
} }
case TokenEvents.Transfer: case TokenEvents.Transfer: {
{
// Invalidate cache // Invalidate cache
const args = decodedLog.args as TransferContractEventArgs; const args = decodedLog.args as TransferContractEventArgs;
this._balanceAndProxyAllowanceLazyStore.deleteBalance(decodedLog.address, args._from); this._balanceAndProxyAllowanceLazyStore.deleteBalance(decodedLog.address, args._from);
@ -239,45 +245,48 @@ export class OrderStateWatcher {
// Revalidate orders // Revalidate orders
makerToken = decodedLog.address; makerToken = decodedLog.address;
makerAddress = args._from; makerAddress = args._from;
if (!_.isUndefined(this._dependentOrderHashes[makerAddress]) && if (
!_.isUndefined(this._dependentOrderHashes[makerAddress][makerToken])) { !_.isUndefined(this._dependentOrderHashes[makerAddress]) &&
!_.isUndefined(this._dependentOrderHashes[makerAddress][makerToken])
) {
const orderHashes = Array.from(this._dependentOrderHashes[makerAddress][makerToken]); const orderHashes = Array.from(this._dependentOrderHashes[makerAddress][makerToken]);
await this._emitRevalidateOrdersAsync(orderHashes); await this._emitRevalidateOrdersAsync(orderHashes);
} }
break; break;
} }
case EtherTokenEvents.Deposit: case EtherTokenEvents.Deposit: {
{
// Invalidate cache // Invalidate cache
const args = decodedLog.args as DepositContractEventArgs; const args = decodedLog.args as DepositContractEventArgs;
this._balanceAndProxyAllowanceLazyStore.deleteBalance(decodedLog.address, args._owner); this._balanceAndProxyAllowanceLazyStore.deleteBalance(decodedLog.address, args._owner);
// Revalidate orders // Revalidate orders
makerToken = decodedLog.address; makerToken = decodedLog.address;
makerAddress = args._owner; makerAddress = args._owner;
if (!_.isUndefined(this._dependentOrderHashes[makerAddress]) && if (
!_.isUndefined(this._dependentOrderHashes[makerAddress][makerToken])) { !_.isUndefined(this._dependentOrderHashes[makerAddress]) &&
!_.isUndefined(this._dependentOrderHashes[makerAddress][makerToken])
) {
const orderHashes = Array.from(this._dependentOrderHashes[makerAddress][makerToken]); const orderHashes = Array.from(this._dependentOrderHashes[makerAddress][makerToken]);
await this._emitRevalidateOrdersAsync(orderHashes); await this._emitRevalidateOrdersAsync(orderHashes);
} }
break; break;
} }
case EtherTokenEvents.Withdrawal: case EtherTokenEvents.Withdrawal: {
{
// Invalidate cache // Invalidate cache
const args = decodedLog.args as WithdrawalContractEventArgs; const args = decodedLog.args as WithdrawalContractEventArgs;
this._balanceAndProxyAllowanceLazyStore.deleteBalance(decodedLog.address, args._owner); this._balanceAndProxyAllowanceLazyStore.deleteBalance(decodedLog.address, args._owner);
// Revalidate orders // Revalidate orders
makerToken = decodedLog.address; makerToken = decodedLog.address;
makerAddress = args._owner; makerAddress = args._owner;
if (!_.isUndefined(this._dependentOrderHashes[makerAddress]) && if (
!_.isUndefined(this._dependentOrderHashes[makerAddress][makerToken])) { !_.isUndefined(this._dependentOrderHashes[makerAddress]) &&
!_.isUndefined(this._dependentOrderHashes[makerAddress][makerToken])
) {
const orderHashes = Array.from(this._dependentOrderHashes[makerAddress][makerToken]); const orderHashes = Array.from(this._dependentOrderHashes[makerAddress][makerToken]);
await this._emitRevalidateOrdersAsync(orderHashes); await this._emitRevalidateOrdersAsync(orderHashes);
} }
break; break;
} }
case ExchangeEvents.LogFill: case ExchangeEvents.LogFill: {
{
// Invalidate cache // Invalidate cache
const args = decodedLog.args as LogFillContractEventArgs; const args = decodedLog.args as LogFillContractEventArgs;
this._orderFilledCancelledLazyStore.deleteFilledTakerAmount(args.orderHash); this._orderFilledCancelledLazyStore.deleteFilledTakerAmount(args.orderHash);
@ -289,8 +298,7 @@ export class OrderStateWatcher {
} }
break; break;
} }
case ExchangeEvents.LogCancel: case ExchangeEvents.LogCancel: {
{
// Invalidate cache // Invalidate cache
const args = decodedLog.args as LogCancelContractEventArgs; const args = decodedLog.args as LogCancelContractEventArgs;
this._orderFilledCancelledLazyStore.deleteCancelledTakerAmount(args.orderHash); this._orderFilledCancelledLazyStore.deleteCancelledTakerAmount(args.orderHash);

View File

@ -10,17 +10,20 @@ export class RemainingFillableCalculator {
private _transferrableMakerFeeTokenAmount: BigNumber; private _transferrableMakerFeeTokenAmount: BigNumber;
private _remainingMakerTokenAmount: BigNumber; private _remainingMakerTokenAmount: BigNumber;
private _remainingMakerFeeAmount: BigNumber; private _remainingMakerFeeAmount: BigNumber;
constructor(signedOrder: SignedOrder, constructor(
signedOrder: SignedOrder,
isMakerTokenZRX: boolean, isMakerTokenZRX: boolean,
transferrableMakerTokenAmount: BigNumber, transferrableMakerTokenAmount: BigNumber,
transferrableMakerFeeTokenAmount: BigNumber, transferrableMakerFeeTokenAmount: BigNumber,
remainingMakerTokenAmount: BigNumber) { remainingMakerTokenAmount: BigNumber,
) {
this._signedOrder = signedOrder; this._signedOrder = signedOrder;
this._isMakerTokenZRX = isMakerTokenZRX; this._isMakerTokenZRX = isMakerTokenZRX;
this._transferrableMakerTokenAmount = transferrableMakerTokenAmount; this._transferrableMakerTokenAmount = transferrableMakerTokenAmount;
this._transferrableMakerFeeTokenAmount = transferrableMakerFeeTokenAmount; this._transferrableMakerFeeTokenAmount = transferrableMakerFeeTokenAmount;
this._remainingMakerTokenAmount = remainingMakerTokenAmount; this._remainingMakerTokenAmount = remainingMakerTokenAmount;
this._remainingMakerFeeAmount = remainingMakerTokenAmount.times(signedOrder.makerFee) this._remainingMakerFeeAmount = remainingMakerTokenAmount
.times(signedOrder.makerFee)
.dividedToIntegerBy(signedOrder.makerTokenAmount); .dividedToIntegerBy(signedOrder.makerTokenAmount);
} }
public computeRemainingMakerFillable(): BigNumber { public computeRemainingMakerFillable(): BigNumber {
@ -33,20 +36,24 @@ export class RemainingFillableCalculator {
return this._calculatePartiallyFillableMakerTokenAmount(); return this._calculatePartiallyFillableMakerTokenAmount();
} }
public computeRemainingTakerFillable(): BigNumber { public computeRemainingTakerFillable(): BigNumber {
return this.computeRemainingMakerFillable().times(this._signedOrder.takerTokenAmount) return this.computeRemainingMakerFillable()
.times(this._signedOrder.takerTokenAmount)
.dividedToIntegerBy(this._signedOrder.makerTokenAmount); .dividedToIntegerBy(this._signedOrder.makerTokenAmount);
} }
private _hasSufficientFundsForFeeAndTransferAmount(): boolean { private _hasSufficientFundsForFeeAndTransferAmount(): boolean {
if (this._isMakerTokenZRX) { if (this._isMakerTokenZRX) {
const totalZRXTransferAmountRequired = this._remainingMakerTokenAmount.plus(this._remainingMakerFeeAmount); const totalZRXTransferAmountRequired = this._remainingMakerTokenAmount.plus(this._remainingMakerFeeAmount);
const hasSufficientFunds = this._transferrableMakerTokenAmount.greaterThanOrEqualTo( const hasSufficientFunds = this._transferrableMakerTokenAmount.greaterThanOrEqualTo(
totalZRXTransferAmountRequired); totalZRXTransferAmountRequired,
);
return hasSufficientFunds; return hasSufficientFunds;
} else { } else {
const hasSufficientFundsForTransferAmount = this._transferrableMakerTokenAmount.greaterThanOrEqualTo( const hasSufficientFundsForTransferAmount = this._transferrableMakerTokenAmount.greaterThanOrEqualTo(
this._remainingMakerTokenAmount); this._remainingMakerTokenAmount,
);
const hasSufficientFundsForFeeAmount = this._transferrableMakerFeeTokenAmount.greaterThanOrEqualTo( const hasSufficientFundsForFeeAmount = this._transferrableMakerFeeTokenAmount.greaterThanOrEqualTo(
this._remainingMakerFeeAmount); this._remainingMakerFeeAmount,
);
const hasSufficientFunds = hasSufficientFundsForTransferAmount && hasSufficientFundsForFeeAmount; const hasSufficientFunds = hasSufficientFundsForTransferAmount && hasSufficientFundsForFeeAmount;
return hasSufficientFunds; return hasSufficientFunds;
} }
@ -57,8 +64,10 @@ export class RemainingFillableCalculator {
// The number of times the maker can fill the order, if each fill only required the transfer of a single // The number of times the maker can fill the order, if each fill only required the transfer of a single
// baseUnit of fee tokens. // baseUnit of fee tokens.
// Given 2 ZRXwei, the maximum amount of times Maker can fill this order, in terms of fees, is 2 // Given 2 ZRXwei, the maximum amount of times Maker can fill this order, in terms of fees, is 2
const fillableTimesInFeeTokenBaseUnits = BigNumber.min(this._transferrableMakerFeeTokenAmount, const fillableTimesInFeeTokenBaseUnits = BigNumber.min(
this._remainingMakerFeeAmount); this._transferrableMakerFeeTokenAmount,
this._remainingMakerFeeAmount,
);
// The number of times the Maker can fill the order, given the Maker Token Balance // The number of times the Maker can fill the order, given the Maker Token Balance
// Assuming a balance of 150 wei, and an orderToFeeRatio of 100:1, maker can fill this order 1 time. // Assuming a balance of 150 wei, and an orderToFeeRatio of 100:1, maker can fill this order 1 time.
let fillableTimesInMakerTokenUnits = this._transferrableMakerTokenAmount.dividedBy(orderToFeeRatio); let fillableTimesInMakerTokenUnits = this._transferrableMakerTokenAmount.dividedBy(orderToFeeRatio);
@ -68,9 +77,7 @@ export class RemainingFillableCalculator {
const totalZRXTokenPooled = this._transferrableMakerTokenAmount; const totalZRXTokenPooled = this._transferrableMakerTokenAmount;
// The purchasing power here is less as the tokens are taken from the same Pool // The purchasing power here is less as the tokens are taken from the same Pool
// For every one number of fills, we have to take an extra ZRX out of the pool // For every one number of fills, we have to take an extra ZRX out of the pool
fillableTimesInMakerTokenUnits = totalZRXTokenPooled.dividedBy( fillableTimesInMakerTokenUnits = totalZRXTokenPooled.dividedBy(orderToFeeRatio.plus(new BigNumber(1)));
orderToFeeRatio.plus(new BigNumber(1)));
} }
// When Ratio is not fully divisible there can be remainders which cannot be represented, so they are floored. // When Ratio is not fully divisible there can be remainders which cannot be represented, so they are floored.
// This can result in a RoundingError being thrown by the Exchange Contract. // This can result in a RoundingError being thrown by the Exchange Contract.
@ -80,8 +87,10 @@ export class RemainingFillableCalculator {
const partiallyFillableFeeTokenAmount = fillableTimesInFeeTokenBaseUnits const partiallyFillableFeeTokenAmount = fillableTimesInFeeTokenBaseUnits
.times(this._signedOrder.makerTokenAmount) .times(this._signedOrder.makerTokenAmount)
.dividedToIntegerBy(this._signedOrder.makerFee); .dividedToIntegerBy(this._signedOrder.makerFee);
const partiallyFillableAmount = BigNumber.min(partiallyFillableMakerTokenAmount, const partiallyFillableAmount = BigNumber.min(
partiallyFillableFeeTokenAmount); partiallyFillableMakerTokenAmount,
partiallyFillableFeeTokenAmount,
);
return partiallyFillableAmount; return partiallyFillableAmount;
} }
} }

View File

@ -52,8 +52,10 @@ export class BalanceAndProxyAllowanceLazyStore {
} }
} }
public async getProxyAllowanceAsync(tokenAddress: string, userAddress: string): Promise<BigNumber> { public async getProxyAllowanceAsync(tokenAddress: string, userAddress: string): Promise<BigNumber> {
if (_.isUndefined(this._proxyAllowance[tokenAddress]) || if (
_.isUndefined(this._proxyAllowance[tokenAddress][userAddress])) { _.isUndefined(this._proxyAllowance[tokenAddress]) ||
_.isUndefined(this._proxyAllowance[tokenAddress][userAddress])
) {
const methodOpts = { const methodOpts = {
defaultBlock: this._defaultBlock, defaultBlock: this._defaultBlock,
}; };

View File

@ -42,8 +42,7 @@ export interface ECSignature {
export type OrderAddresses = [string, string, string, string, string]; export type OrderAddresses = [string, string, string, string, string];
export type OrderValues = [BigNumber, BigNumber, BigNumber, export type OrderValues = [BigNumber, BigNumber, BigNumber, BigNumber, BigNumber, BigNumber];
BigNumber, BigNumber, BigNumber];
export type LogEvent = Web3.LogEntryEvent; export type LogEvent = Web3.LogEntryEvent;
export interface DecodedLogEvent<ArgsType> { export interface DecodedLogEvent<ArgsType> {
@ -136,7 +135,10 @@ export interface LogErrorContractEventArgs {
errorId: BigNumber; errorId: BigNumber;
orderHash: string; orderHash: string;
} }
export type ExchangeContractEventArgs = LogFillContractEventArgs|LogCancelContractEventArgs|LogErrorContractEventArgs; export type ExchangeContractEventArgs =
| LogFillContractEventArgs
| LogCancelContractEventArgs
| LogErrorContractEventArgs;
export interface TransferContractEventArgs { export interface TransferContractEventArgs {
_from: string; _from: string;
_to: string; _to: string;
@ -156,7 +158,10 @@ export interface WithdrawalContractEventArgs {
_value: BigNumber; _value: BigNumber;
} }
export type TokenContractEventArgs = TransferContractEventArgs | ApprovalContractEventArgs; export type TokenContractEventArgs = TransferContractEventArgs | ApprovalContractEventArgs;
export type EtherTokenContractEventArgs = TokenContractEventArgs|DepositContractEventArgs|WithdrawalContractEventArgs; export type EtherTokenContractEventArgs =
| TokenContractEventArgs
| DepositContractEventArgs
| WithdrawalContractEventArgs;
export type ContractEventArgs = ExchangeContractEventArgs | TokenContractEventArgs | EtherTokenContractEventArgs; export type ContractEventArgs = ExchangeContractEventArgs | TokenContractEventArgs | EtherTokenContractEventArgs;
export type ContractEventArg = string | BigNumber; export type ContractEventArg = string | BigNumber;

View File

@ -22,7 +22,8 @@ export class AbiDecoder {
} }
// This method can only decode logs from the 0x & ERC20 smart contracts // This method can only decode logs from the 0x & ERC20 smart contracts
public tryToDecodeLogOrNoop<ArgsType extends ContractEventArgs>( public tryToDecodeLogOrNoop<ArgsType extends ContractEventArgs>(
log: Web3.LogEntry): LogWithDecodedArgs<ArgsType>|RawLog { log: Web3.LogEntry,
): LogWithDecodedArgs<ArgsType> | RawLog {
const methodId = log.topics[0]; const methodId = log.topics[0];
const event = this._methodIds[methodId]; const event = this._methodIds[methodId];
if (_.isUndefined(event)) { if (_.isUndefined(event)) {
@ -42,9 +43,11 @@ export class AbiDecoder {
let value = param.indexed ? log.topics[topicsIndex++] : decodedData[dataIndex++]; let value = param.indexed ? log.topics[topicsIndex++] : decodedData[dataIndex++];
if (param.type === SolidityTypes.Address) { if (param.type === SolidityTypes.Address) {
value = AbiDecoder._padZeros(new BigNumber(value).toString(16)); value = AbiDecoder._padZeros(new BigNumber(value).toString(16));
} else if (param.type === SolidityTypes.Uint256 || } else if (
param.type === SolidityTypes.Uint256 ||
param.type === SolidityTypes.Uint8 || param.type === SolidityTypes.Uint8 ||
param.type === SolidityTypes.Uint) { param.type === SolidityTypes.Uint
) {
value = new BigNumber(value); value = new BigNumber(value);
} }
decodedParams[param.name] = value; decodedParams[param.name] = value;

View File

@ -16,11 +16,15 @@ export const assert = {
const isValidSignature = signatureUtils.isValidSignature(orderHash, ecSignature, signerAddress); const isValidSignature = signatureUtils.isValidSignature(orderHash, ecSignature, signerAddress);
this.assert(isValidSignature, `Expected order with hash '${orderHash}' to have a valid signature`); this.assert(isValidSignature, `Expected order with hash '${orderHash}' to have a valid signature`);
}, },
async isSenderAddressAsync(variableName: string, senderAddressHex: string, async isSenderAddressAsync(
web3Wrapper: Web3Wrapper): Promise<void> { variableName: string,
senderAddressHex: string,
web3Wrapper: Web3Wrapper,
): Promise<void> {
sharedAssert.isETHAddressHex(variableName, senderAddressHex); sharedAssert.isETHAddressHex(variableName, senderAddressHex);
const isSenderAddressAvailable = await web3Wrapper.isSenderAddressAvailableAsync(senderAddressHex); const isSenderAddressAvailable = await web3Wrapper.isSenderAddressAvailableAsync(senderAddressHex);
sharedAssert.assert(isSenderAddressAvailable, sharedAssert.assert(
isSenderAddressAvailable,
`Specified ${variableName} ${senderAddressHex} isn't available through the supplied web3 provider`, `Specified ${variableName} ${senderAddressHex} isn't available through the supplied web3 provider`,
); );
}, },

View File

@ -18,8 +18,8 @@ const contractCallErrorTransformer = (error: Error) => {
const schemaErrorTransformer = (error: Error) => { const schemaErrorTransformer = (error: Error) => {
if (_.includes(error.message, constants.INVALID_TAKER_FORMAT)) { if (_.includes(error.message, constants.INVALID_TAKER_FORMAT)) {
// tslint:disable-next-line:max-line-length const errMsg =
const errMsg = 'Order taker must be of type string. If you want anyone to be able to fill an order - pass ZeroEx.NULL_ADDRESS'; 'Order taker must be of type string. If you want anyone to be able to fill an order - pass ZeroEx.NULL_ADDRESS';
return new Error(errMsg); return new Error(errMsg);
} }
return error; return error;
@ -30,9 +30,11 @@ const schemaErrorTransformer = (error: Error) => {
*/ */
const asyncErrorHandlerFactory = (errorTransformer: ErrorTransformer) => { const asyncErrorHandlerFactory = (errorTransformer: ErrorTransformer) => {
const asyncErrorHandlingDecorator = ( const asyncErrorHandlingDecorator = (
target: object, key: string|symbol, descriptor: TypedPropertyDescriptor<AsyncMethod>, target: object,
key: string | symbol,
descriptor: TypedPropertyDescriptor<AsyncMethod>,
) => { ) => {
const originalMethod = (descriptor.value as AsyncMethod); const originalMethod = descriptor.value as AsyncMethod;
// Do not use arrow syntax here. Use a function expression in // Do not use arrow syntax here. Use a function expression in
// order to use the correct value of `this` in this method // order to use the correct value of `this` in this method
@ -55,9 +57,11 @@ const asyncErrorHandlerFactory = (errorTransformer: ErrorTransformer) => {
const syncErrorHandlerFactory = (errorTransformer: ErrorTransformer) => { const syncErrorHandlerFactory = (errorTransformer: ErrorTransformer) => {
const syncErrorHandlingDecorator = ( const syncErrorHandlingDecorator = (
target: object, key: string|symbol, descriptor: TypedPropertyDescriptor<SyncMethod>, target: object,
key: string | symbol,
descriptor: TypedPropertyDescriptor<SyncMethod>,
) => { ) => {
const originalMethod = (descriptor.value as SyncMethod); const originalMethod = descriptor.value as SyncMethod;
// Do not use arrow syntax here. Use a function expression in // Do not use arrow syntax here. Use a function expression in
// order to use the correct value of `this` in this method // order to use the correct value of `this` in this method

View File

@ -36,8 +36,11 @@ const ERR_MSG_MAPPING = {
export class ExchangeTransferSimulator { export class ExchangeTransferSimulator {
private _store: BalanceAndProxyAllowanceLazyStore; private _store: BalanceAndProxyAllowanceLazyStore;
private _UNLIMITED_ALLOWANCE_IN_BASE_UNITS: BigNumber; private _UNLIMITED_ALLOWANCE_IN_BASE_UNITS: BigNumber;
private static _throwValidationError(failureReason: FailureReason, tradeSide: TradeSide, private static _throwValidationError(
transferType: TransferType): never { failureReason: FailureReason,
tradeSide: TradeSide,
transferType: TransferType,
): never {
const errMsg = ERR_MSG_MAPPING[failureReason][tradeSide][transferType]; const errMsg = ERR_MSG_MAPPING[failureReason][tradeSide][transferType];
throw new Error(errMsg); throw new Error(errMsg);
} }
@ -54,9 +57,14 @@ export class ExchangeTransferSimulator {
* @param tradeSide Is Maker/Taker transferring * @param tradeSide Is Maker/Taker transferring
* @param transferType Is it a fee payment or a value transfer * @param transferType Is it a fee payment or a value transfer
*/ */
public async transferFromAsync(tokenAddress: string, from: string, to: string, public async transferFromAsync(
amountInBaseUnits: BigNumber, tradeSide: TradeSide, tokenAddress: string,
transferType: TransferType): Promise<void> { from: string,
to: string,
amountInBaseUnits: BigNumber,
tradeSide: TradeSide,
transferType: TransferType,
): Promise<void> {
const balance = await this._store.getBalanceAsync(tokenAddress, from); const balance = await this._store.getBalanceAsync(tokenAddress, from);
const proxyAllowance = await this._store.getProxyAllowanceAsync(tokenAddress, from); const proxyAllowance = await this._store.getProxyAllowanceAsync(tokenAddress, from);
if (proxyAllowance.lessThan(amountInBaseUnits)) { if (proxyAllowance.lessThan(amountInBaseUnits)) {
@ -69,20 +77,29 @@ export class ExchangeTransferSimulator {
await this._decreaseBalanceAsync(tokenAddress, from, amountInBaseUnits); await this._decreaseBalanceAsync(tokenAddress, from, amountInBaseUnits);
await this._increaseBalanceAsync(tokenAddress, to, amountInBaseUnits); await this._increaseBalanceAsync(tokenAddress, to, amountInBaseUnits);
} }
private async _decreaseProxyAllowanceAsync(tokenAddress: string, userAddress: string, private async _decreaseProxyAllowanceAsync(
amountInBaseUnits: BigNumber): Promise<void> { tokenAddress: string,
userAddress: string,
amountInBaseUnits: BigNumber,
): Promise<void> {
const proxyAllowance = await this._store.getProxyAllowanceAsync(tokenAddress, userAddress); const proxyAllowance = await this._store.getProxyAllowanceAsync(tokenAddress, userAddress);
if (!proxyAllowance.eq(this._UNLIMITED_ALLOWANCE_IN_BASE_UNITS)) { if (!proxyAllowance.eq(this._UNLIMITED_ALLOWANCE_IN_BASE_UNITS)) {
this._store.setProxyAllowance(tokenAddress, userAddress, proxyAllowance.minus(amountInBaseUnits)); this._store.setProxyAllowance(tokenAddress, userAddress, proxyAllowance.minus(amountInBaseUnits));
} }
} }
private async _increaseBalanceAsync(tokenAddress: string, userAddress: string, private async _increaseBalanceAsync(
amountInBaseUnits: BigNumber): Promise<void> { tokenAddress: string,
userAddress: string,
amountInBaseUnits: BigNumber,
): Promise<void> {
const balance = await this._store.getBalanceAsync(tokenAddress, userAddress); const balance = await this._store.getBalanceAsync(tokenAddress, userAddress);
this._store.setBalance(tokenAddress, userAddress, balance.plus(amountInBaseUnits)); this._store.setBalance(tokenAddress, userAddress, balance.plus(amountInBaseUnits));
} }
private async _decreaseBalanceAsync(tokenAddress: string, userAddress: string, private async _decreaseBalanceAsync(
amountInBaseUnits: BigNumber): Promise<void> { tokenAddress: string,
userAddress: string,
amountInBaseUnits: BigNumber,
): Promise<void> {
const balance = await this._store.getBalanceAsync(tokenAddress, userAddress); const balance = await this._store.getBalanceAsync(tokenAddress, userAddress);
this._store.setBalance(tokenAddress, userAddress, balance.minus(amountInBaseUnits)); this._store.setBalance(tokenAddress, userAddress, balance.minus(amountInBaseUnits));
} }

View File

@ -12,9 +12,13 @@ export const filterUtils = {
generateUUID(): string { generateUUID(): string {
return uuid(); return uuid();
}, },
getFilter(address: string, eventName: ContractEvents, getFilter(
indexFilterValues: IndexedFilterValues, abi: Web3.ContractAbi, address: string,
blockRange?: BlockRange): Web3.FilterObject { eventName: ContractEvents,
indexFilterValues: IndexedFilterValues,
abi: Web3.ContractAbi,
blockRange?: BlockRange,
): Web3.FilterObject {
const eventAbi = _.find(abi, { name: eventName }) as Web3.EventAbi; const eventAbi = _.find(abi, { name: eventName }) as Web3.EventAbi;
const eventSignature = filterUtils.getEventSignatureFromAbiByName(eventAbi, eventName); const eventSignature = filterUtils.getEventSignatureFromAbiByName(eventAbi, eventName);
const topicForEventSignature = ethUtil.addHexPrefix(jsSHA3.keccak256(eventSignature)); const topicForEventSignature = ethUtil.addHexPrefix(jsSHA3.keccak256(eventSignature));

View File

@ -46,13 +46,18 @@ export class OrderStateUtils {
const minFillableTakerTokenAmountWithinNoRoundingErrorRange = signedOrder.takerTokenAmount const minFillableTakerTokenAmountWithinNoRoundingErrorRange = signedOrder.takerTokenAmount
.dividedBy(ACCEPTABLE_RELATIVE_ROUNDING_ERROR) .dividedBy(ACCEPTABLE_RELATIVE_ROUNDING_ERROR)
.dividedBy(signedOrder.makerTokenAmount); .dividedBy(signedOrder.makerTokenAmount);
if (orderRelevantState.remainingFillableTakerTokenAmount if (
.lessThan(minFillableTakerTokenAmountWithinNoRoundingErrorRange)) { orderRelevantState.remainingFillableTakerTokenAmount.lessThan(
minFillableTakerTokenAmountWithinNoRoundingErrorRange,
)
) {
throw new Error(ExchangeContractErrs.OrderFillRoundingError); throw new Error(ExchangeContractErrs.OrderFillRoundingError);
} }
} }
constructor(balanceAndProxyAllowanceLazyStore: BalanceAndProxyAllowanceLazyStore, constructor(
orderFilledCancelledLazyStore: OrderFilledCancelledLazyStore) { balanceAndProxyAllowanceLazyStore: BalanceAndProxyAllowanceLazyStore,
orderFilledCancelledLazyStore: OrderFilledCancelledLazyStore,
) {
this._balanceAndProxyAllowanceLazyStore = balanceAndProxyAllowanceLazyStore; this._balanceAndProxyAllowanceLazyStore = balanceAndProxyAllowanceLazyStore;
this._orderFilledCancelledLazyStore = orderFilledCancelledLazyStore; this._orderFilledCancelledLazyStore = orderFilledCancelledLazyStore;
} }
@ -85,16 +90,20 @@ export class OrderStateUtils {
const zrxTokenAddress = exchange.getZRXTokenAddress(); const zrxTokenAddress = exchange.getZRXTokenAddress();
const orderHash = ZeroEx.getOrderHashHex(signedOrder); const orderHash = ZeroEx.getOrderHashHex(signedOrder);
const makerBalance = await this._balanceAndProxyAllowanceLazyStore.getBalanceAsync( const makerBalance = await this._balanceAndProxyAllowanceLazyStore.getBalanceAsync(
signedOrder.makerTokenAddress, signedOrder.maker, signedOrder.makerTokenAddress,
signedOrder.maker,
); );
const makerProxyAllowance = await this._balanceAndProxyAllowanceLazyStore.getProxyAllowanceAsync( const makerProxyAllowance = await this._balanceAndProxyAllowanceLazyStore.getProxyAllowanceAsync(
signedOrder.makerTokenAddress, signedOrder.maker, signedOrder.makerTokenAddress,
signedOrder.maker,
); );
const makerFeeBalance = await this._balanceAndProxyAllowanceLazyStore.getBalanceAsync( const makerFeeBalance = await this._balanceAndProxyAllowanceLazyStore.getBalanceAsync(
zrxTokenAddress, signedOrder.maker, zrxTokenAddress,
signedOrder.maker,
); );
const makerFeeProxyAllowance = await this._balanceAndProxyAllowanceLazyStore.getProxyAllowanceAsync( const makerFeeProxyAllowance = await this._balanceAndProxyAllowanceLazyStore.getProxyAllowanceAsync(
zrxTokenAddress, signedOrder.maker, zrxTokenAddress,
signedOrder.maker,
); );
const filledTakerTokenAmount = await this._orderFilledCancelledLazyStore.getFilledTakerAmountAsync(orderHash); const filledTakerTokenAmount = await this._orderFilledCancelledLazyStore.getFilledTakerAmountAsync(orderHash);
const cancelledTakerTokenAmount = await this._orderFilledCancelledLazyStore.getCancelledTakerAmountAsync( const cancelledTakerTokenAmount = await this._orderFilledCancelledLazyStore.getCancelledTakerAmountAsync(
@ -104,17 +113,20 @@ export class OrderStateUtils {
const totalMakerTokenAmount = signedOrder.makerTokenAmount; const totalMakerTokenAmount = signedOrder.makerTokenAmount;
const totalTakerTokenAmount = signedOrder.takerTokenAmount; const totalTakerTokenAmount = signedOrder.takerTokenAmount;
const remainingTakerTokenAmount = totalTakerTokenAmount.minus(unavailableTakerTokenAmount); const remainingTakerTokenAmount = totalTakerTokenAmount.minus(unavailableTakerTokenAmount);
const remainingMakerTokenAmount = remainingTakerTokenAmount.times(totalMakerTokenAmount) const remainingMakerTokenAmount = remainingTakerTokenAmount
.times(totalMakerTokenAmount)
.dividedToIntegerBy(totalTakerTokenAmount); .dividedToIntegerBy(totalTakerTokenAmount);
const transferrableMakerTokenAmount = BigNumber.min([makerProxyAllowance, makerBalance]); const transferrableMakerTokenAmount = BigNumber.min([makerProxyAllowance, makerBalance]);
const transferrableFeeTokenAmount = BigNumber.min([makerFeeProxyAllowance, makerFeeBalance]); const transferrableFeeTokenAmount = BigNumber.min([makerFeeProxyAllowance, makerFeeBalance]);
const isMakerTokenZRX = signedOrder.makerTokenAddress === zrxTokenAddress; const isMakerTokenZRX = signedOrder.makerTokenAddress === zrxTokenAddress;
const remainingFillableCalculator = new RemainingFillableCalculator(signedOrder, const remainingFillableCalculator = new RemainingFillableCalculator(
signedOrder,
isMakerTokenZRX, isMakerTokenZRX,
transferrableMakerTokenAmount, transferrableMakerTokenAmount,
transferrableFeeTokenAmount, transferrableFeeTokenAmount,
remainingMakerTokenAmount); remainingMakerTokenAmount,
);
const remainingFillableMakerTokenAmount = remainingFillableCalculator.computeRemainingMakerFillable(); const remainingFillableMakerTokenAmount = remainingFillableCalculator.computeRemainingMakerFillable();
const remainingFillableTakerTokenAmount = remainingFillableCalculator.computeRemainingTakerFillable(); const remainingFillableTakerTokenAmount = remainingFillableCalculator.computeRemainingTakerFillable();
const orderRelevantState = { const orderRelevantState = {

View File

@ -12,7 +12,9 @@ import {ExchangeTransferSimulator} from './exchange_transfer_simulator';
export class OrderValidationUtils { export class OrderValidationUtils {
private _exchangeWrapper: ExchangeWrapper; private _exchangeWrapper: ExchangeWrapper;
public static validateCancelOrderThrowIfInvalid( public static validateCancelOrderThrowIfInvalid(
order: Order, cancelTakerTokenAmount: BigNumber, unavailableTakerTokenAmount: BigNumber, order: Order,
cancelTakerTokenAmount: BigNumber,
unavailableTakerTokenAmount: BigNumber,
): void { ): void {
if (cancelTakerTokenAmount.eq(0)) { if (cancelTakerTokenAmount.eq(0)) {
throw new Error(ExchangeContractErrs.OrderCancelAmountZero); throw new Error(ExchangeContractErrs.OrderCancelAmountZero);
@ -26,8 +28,11 @@ export class OrderValidationUtils {
} }
} }
public static async validateFillOrderBalancesAllowancesThrowIfInvalidAsync( public static async validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
exchangeTradeEmulator: ExchangeTransferSimulator, signedOrder: SignedOrder, exchangeTradeEmulator: ExchangeTransferSimulator,
fillTakerTokenAmount: BigNumber, senderAddress: string, zrxTokenAddress: string, signedOrder: SignedOrder,
fillTakerTokenAmount: BigNumber,
senderAddress: string,
zrxTokenAddress: string,
): Promise<void> { ): Promise<void> {
const fillMakerTokenAmount = OrderValidationUtils._getPartialAmount( const fillMakerTokenAmount = OrderValidationUtils._getPartialAmount(
fillTakerTokenAmount, fillTakerTokenAmount,
@ -35,12 +40,20 @@ export class OrderValidationUtils {
signedOrder.makerTokenAmount, signedOrder.makerTokenAmount,
); );
await exchangeTradeEmulator.transferFromAsync( await exchangeTradeEmulator.transferFromAsync(
signedOrder.makerTokenAddress, signedOrder.maker, senderAddress, fillMakerTokenAmount, signedOrder.makerTokenAddress,
TradeSide.Maker, TransferType.Trade, signedOrder.maker,
senderAddress,
fillMakerTokenAmount,
TradeSide.Maker,
TransferType.Trade,
); );
await exchangeTradeEmulator.transferFromAsync( await exchangeTradeEmulator.transferFromAsync(
signedOrder.takerTokenAddress, senderAddress, signedOrder.maker, fillTakerTokenAmount, signedOrder.takerTokenAddress,
TradeSide.Taker, TransferType.Trade, senderAddress,
signedOrder.maker,
fillTakerTokenAmount,
TradeSide.Taker,
TransferType.Trade,
); );
const makerFeeAmount = OrderValidationUtils._getPartialAmount( const makerFeeAmount = OrderValidationUtils._getPartialAmount(
fillTakerTokenAmount, fillTakerTokenAmount,
@ -48,7 +61,11 @@ export class OrderValidationUtils {
signedOrder.makerFee, signedOrder.makerFee,
); );
await exchangeTradeEmulator.transferFromAsync( await exchangeTradeEmulator.transferFromAsync(
zrxTokenAddress, signedOrder.maker, signedOrder.feeRecipient, makerFeeAmount, TradeSide.Maker, zrxTokenAddress,
signedOrder.maker,
signedOrder.feeRecipient,
makerFeeAmount,
TradeSide.Maker,
TransferType.Fee, TransferType.Fee,
); );
const takerFeeAmount = OrderValidationUtils._getPartialAmount( const takerFeeAmount = OrderValidationUtils._getPartialAmount(
@ -57,12 +74,17 @@ export class OrderValidationUtils {
signedOrder.takerFee, signedOrder.takerFee,
); );
await exchangeTradeEmulator.transferFromAsync( await exchangeTradeEmulator.transferFromAsync(
zrxTokenAddress, senderAddress, signedOrder.feeRecipient, takerFeeAmount, TradeSide.Taker, zrxTokenAddress,
senderAddress,
signedOrder.feeRecipient,
takerFeeAmount,
TradeSide.Taker,
TransferType.Fee, TransferType.Fee,
); );
} }
private static _validateRemainingFillAmountNotZeroOrThrow( private static _validateRemainingFillAmountNotZeroOrThrow(
takerTokenAmount: BigNumber, unavailableTakerTokenAmount: BigNumber, takerTokenAmount: BigNumber,
unavailableTakerTokenAmount: BigNumber,
) { ) {
if (takerTokenAmount.eq(unavailableTakerTokenAmount)) { if (takerTokenAmount.eq(unavailableTakerTokenAmount)) {
throw new Error(ExchangeContractErrs.OrderRemainingFillAmountZero); throw new Error(ExchangeContractErrs.OrderRemainingFillAmountZero);
@ -74,8 +96,7 @@ export class OrderValidationUtils {
throw new Error(ExchangeContractErrs.OrderFillExpired); throw new Error(ExchangeContractErrs.OrderFillExpired);
} }
} }
private static _getPartialAmount(numerator: BigNumber, denominator: BigNumber, private static _getPartialAmount(numerator: BigNumber, denominator: BigNumber, target: BigNumber): BigNumber {
target: BigNumber): BigNumber {
const fillMakerTokenAmount = numerator const fillMakerTokenAmount = numerator
.mul(target) .mul(target)
.div(denominator) .div(denominator)
@ -86,12 +107,16 @@ export class OrderValidationUtils {
this._exchangeWrapper = exchangeWrapper; this._exchangeWrapper = exchangeWrapper;
} }
public async validateOrderFillableOrThrowAsync( public async validateOrderFillableOrThrowAsync(
exchangeTradeEmulator: ExchangeTransferSimulator, signedOrder: SignedOrder, zrxTokenAddress: string, exchangeTradeEmulator: ExchangeTransferSimulator,
expectedFillTakerTokenAmount?: BigNumber): Promise<void> { signedOrder: SignedOrder,
zrxTokenAddress: string,
expectedFillTakerTokenAmount?: BigNumber,
): Promise<void> {
const orderHash = utils.getOrderHashHex(signedOrder); const orderHash = utils.getOrderHashHex(signedOrder);
const unavailableTakerTokenAmount = await this._exchangeWrapper.getUnavailableTakerAmountAsync(orderHash); const unavailableTakerTokenAmount = await this._exchangeWrapper.getUnavailableTakerAmountAsync(orderHash);
OrderValidationUtils._validateRemainingFillAmountNotZeroOrThrow( OrderValidationUtils._validateRemainingFillAmountNotZeroOrThrow(
signedOrder.takerTokenAmount, unavailableTakerTokenAmount, signedOrder.takerTokenAmount,
unavailableTakerTokenAmount,
); );
OrderValidationUtils._validateOrderNotExpiredOrThrow(signedOrder.expirationUnixTimestampSec); OrderValidationUtils._validateOrderNotExpiredOrThrow(signedOrder.expirationUnixTimestampSec);
let fillTakerTokenAmount = signedOrder.takerTokenAmount.minus(unavailableTakerTokenAmount); let fillTakerTokenAmount = signedOrder.takerTokenAmount.minus(unavailableTakerTokenAmount);
@ -104,8 +129,12 @@ export class OrderValidationUtils {
signedOrder.makerTokenAmount, signedOrder.makerTokenAmount,
); );
await exchangeTradeEmulator.transferFromAsync( await exchangeTradeEmulator.transferFromAsync(
signedOrder.makerTokenAddress, signedOrder.maker, signedOrder.taker, fillMakerTokenAmount, signedOrder.makerTokenAddress,
TradeSide.Maker, TransferType.Trade, signedOrder.maker,
signedOrder.taker,
fillMakerTokenAmount,
TradeSide.Maker,
TransferType.Trade,
); );
const makerFeeAmount = OrderValidationUtils._getPartialAmount( const makerFeeAmount = OrderValidationUtils._getPartialAmount(
fillTakerTokenAmount, fillTakerTokenAmount,
@ -113,14 +142,21 @@ export class OrderValidationUtils {
signedOrder.makerFee, signedOrder.makerFee,
); );
await exchangeTradeEmulator.transferFromAsync( await exchangeTradeEmulator.transferFromAsync(
zrxTokenAddress, signedOrder.maker, signedOrder.feeRecipient, makerFeeAmount, zrxTokenAddress,
TradeSide.Maker, TransferType.Fee, signedOrder.maker,
signedOrder.feeRecipient,
makerFeeAmount,
TradeSide.Maker,
TransferType.Fee,
); );
} }
public async validateFillOrderThrowIfInvalidAsync( public async validateFillOrderThrowIfInvalidAsync(
exchangeTradeEmulator: ExchangeTransferSimulator, signedOrder: SignedOrder, exchangeTradeEmulator: ExchangeTransferSimulator,
fillTakerTokenAmount: BigNumber, takerAddress: string, signedOrder: SignedOrder,
zrxTokenAddress: string): Promise<BigNumber> { fillTakerTokenAmount: BigNumber,
takerAddress: string,
zrxTokenAddress: string,
): Promise<BigNumber> {
if (fillTakerTokenAmount.eq(0)) { if (fillTakerTokenAmount.eq(0)) {
throw new Error(ExchangeContractErrs.OrderFillAmountZero); throw new Error(ExchangeContractErrs.OrderFillAmountZero);
} }
@ -130,22 +166,29 @@ export class OrderValidationUtils {
} }
const unavailableTakerTokenAmount = await this._exchangeWrapper.getUnavailableTakerAmountAsync(orderHash); const unavailableTakerTokenAmount = await this._exchangeWrapper.getUnavailableTakerAmountAsync(orderHash);
OrderValidationUtils._validateRemainingFillAmountNotZeroOrThrow( OrderValidationUtils._validateRemainingFillAmountNotZeroOrThrow(
signedOrder.takerTokenAmount, unavailableTakerTokenAmount, signedOrder.takerTokenAmount,
unavailableTakerTokenAmount,
); );
if (signedOrder.taker !== constants.NULL_ADDRESS && signedOrder.taker !== takerAddress) { if (signedOrder.taker !== constants.NULL_ADDRESS && signedOrder.taker !== takerAddress) {
throw new Error(ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker); throw new Error(ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker);
} }
OrderValidationUtils._validateOrderNotExpiredOrThrow(signedOrder.expirationUnixTimestampSec); OrderValidationUtils._validateOrderNotExpiredOrThrow(signedOrder.expirationUnixTimestampSec);
const remainingTakerTokenAmount = signedOrder.takerTokenAmount.minus(unavailableTakerTokenAmount); const remainingTakerTokenAmount = signedOrder.takerTokenAmount.minus(unavailableTakerTokenAmount);
const filledTakerTokenAmount = remainingTakerTokenAmount.lessThan(fillTakerTokenAmount) ? const filledTakerTokenAmount = remainingTakerTokenAmount.lessThan(fillTakerTokenAmount)
remainingTakerTokenAmount : ? remainingTakerTokenAmount
fillTakerTokenAmount; : fillTakerTokenAmount;
await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync( await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
exchangeTradeEmulator, signedOrder, filledTakerTokenAmount, takerAddress, zrxTokenAddress, exchangeTradeEmulator,
signedOrder,
filledTakerTokenAmount,
takerAddress,
zrxTokenAddress,
); );
const wouldRoundingErrorOccur = await this._exchangeWrapper.isRoundingErrorAsync( const wouldRoundingErrorOccur = await this._exchangeWrapper.isRoundingErrorAsync(
filledTakerTokenAmount, signedOrder.takerTokenAmount, signedOrder.makerTokenAmount, filledTakerTokenAmount,
signedOrder.takerTokenAmount,
signedOrder.makerTokenAmount,
); );
if (wouldRoundingErrorOccur) { if (wouldRoundingErrorOccur) {
throw new Error(ExchangeContractErrs.OrderFillRoundingError); throw new Error(ExchangeContractErrs.OrderFillRoundingError);
@ -153,10 +196,18 @@ export class OrderValidationUtils {
return filledTakerTokenAmount; return filledTakerTokenAmount;
} }
public async validateFillOrKillOrderThrowIfInvalidAsync( public async validateFillOrKillOrderThrowIfInvalidAsync(
exchangeTradeEmulator: ExchangeTransferSimulator, signedOrder: SignedOrder, exchangeTradeEmulator: ExchangeTransferSimulator,
fillTakerTokenAmount: BigNumber, takerAddress: string, zrxTokenAddress: string): Promise<void> { signedOrder: SignedOrder,
fillTakerTokenAmount: BigNumber,
takerAddress: string,
zrxTokenAddress: string,
): Promise<void> {
const filledTakerTokenAmount = await this.validateFillOrderThrowIfInvalidAsync( const filledTakerTokenAmount = await this.validateFillOrderThrowIfInvalidAsync(
exchangeTradeEmulator, signedOrder, fillTakerTokenAmount, takerAddress, zrxTokenAddress, exchangeTradeEmulator,
signedOrder,
fillTakerTokenAmount,
takerAddress,
zrxTokenAddress,
); );
if (filledTakerTokenAmount !== fillTakerTokenAmount) { if (filledTakerTokenAmount !== fillTakerTokenAmount) {
throw new Error(ExchangeContractErrs.InsufficientRemainingFillAmount); throw new Error(ExchangeContractErrs.InsufficientRemainingFillAmount);

View File

@ -11,7 +11,8 @@ export const signatureUtils = {
msgHashBuff, msgHashBuff,
signature.v, signature.v,
ethUtil.toBuffer(signature.r), ethUtil.toBuffer(signature.r),
ethUtil.toBuffer(signature.s)); ethUtil.toBuffer(signature.s),
);
const retrievedAddress = ethUtil.bufferToHex(ethUtil.pubToAddress(pubKey)); const retrievedAddress = ethUtil.bufferToHex(ethUtil.pubToAddress(pubKey));
return retrievedAddress === signerAddress; return retrievedAddress === signerAddress;
} catch (err) { } catch (err) {

View File

@ -37,11 +37,26 @@ export const utils = {
{ value: order.makerTokenAddress, type: SolidityTypes.Address }, { value: order.makerTokenAddress, type: SolidityTypes.Address },
{ value: order.takerTokenAddress, type: SolidityTypes.Address }, { value: order.takerTokenAddress, type: SolidityTypes.Address },
{ value: order.feeRecipient, type: SolidityTypes.Address }, { value: order.feeRecipient, type: SolidityTypes.Address },
{value: utils.bigNumberToBN(order.makerTokenAmount), type: SolidityTypes.Uint256}, {
{value: utils.bigNumberToBN(order.takerTokenAmount), type: SolidityTypes.Uint256}, value: utils.bigNumberToBN(order.makerTokenAmount),
{value: utils.bigNumberToBN(order.makerFee), type: SolidityTypes.Uint256}, type: SolidityTypes.Uint256,
{value: utils.bigNumberToBN(order.takerFee), type: SolidityTypes.Uint256}, },
{value: utils.bigNumberToBN(order.expirationUnixTimestampSec), type: SolidityTypes.Uint256}, {
value: utils.bigNumberToBN(order.takerTokenAmount),
type: SolidityTypes.Uint256,
},
{
value: utils.bigNumberToBN(order.makerFee),
type: SolidityTypes.Uint256,
},
{
value: utils.bigNumberToBN(order.takerFee),
type: SolidityTypes.Uint256,
},
{
value: utils.bigNumberToBN(order.expirationUnixTimestampSec),
type: SolidityTypes.Uint256,
},
{ value: utils.bigNumberToBN(order.salt), type: SolidityTypes.Uint256 }, { value: utils.bigNumberToBN(order.salt), type: SolidityTypes.Uint256 },
]; ];
const types = _.map(orderParts, o => o.type); const types = _.map(orderParts, o => o.type);

View File

@ -41,11 +41,11 @@ describe('ZeroEx library', () => {
// Check that all nested web3 wrapper instances return the updated provider // Check that all nested web3 wrapper instances return the updated provider
const nestedWeb3WrapperProvider = (zeroEx as any)._web3Wrapper.getCurrentProvider(); const nestedWeb3WrapperProvider = (zeroEx as any)._web3Wrapper.getCurrentProvider();
expect((nestedWeb3WrapperProvider).zeroExTestId).to.be.a('number'); expect(nestedWeb3WrapperProvider.zeroExTestId).to.be.a('number');
const exchangeWeb3WrapperProvider = (zeroEx.exchange as any)._web3Wrapper.getCurrentProvider(); const exchangeWeb3WrapperProvider = (zeroEx.exchange as any)._web3Wrapper.getCurrentProvider();
expect((exchangeWeb3WrapperProvider).zeroExTestId).to.be.a('number'); expect(exchangeWeb3WrapperProvider.zeroExTestId).to.be.a('number');
const tokenRegistryWeb3WrapperProvider = (zeroEx.tokenRegistry as any)._web3Wrapper.getCurrentProvider(); const tokenRegistryWeb3WrapperProvider = (zeroEx.tokenRegistry as any)._web3Wrapper.getCurrentProvider();
expect((tokenRegistryWeb3WrapperProvider).zeroExTestId).to.be.a('number'); expect(tokenRegistryWeb3WrapperProvider.zeroExTestId).to.be.a('number');
}); });
}); });
describe('#isValidSignature', () => { describe('#isValidSignature', () => {
@ -58,21 +58,24 @@ describe('ZeroEx library', () => {
s: '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254', s: '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
}; };
const address = '0x5409ed021d9299bf6814279a6a1411a7e866a631'; const address = '0x5409ed021d9299bf6814279a6a1411a7e866a631';
it('should return false if the data doesn\'t pertain to the signature & address', async () => { it("should return false if the data doesn't pertain to the signature & address", async () => {
expect(ZeroEx.isValidSignature('0x0', signature, address)).to.be.false(); expect(ZeroEx.isValidSignature('0x0', signature, address)).to.be.false();
return expect( return expect(
(zeroEx.exchange as any)._isValidSignatureUsingContractCallAsync('0x0', signature, address), (zeroEx.exchange as any)._isValidSignatureUsingContractCallAsync('0x0', signature, address),
).to.become(false); ).to.become(false);
}); });
it('should return false if the address doesn\'t pertain to the signature & data', async () => { it("should return false if the address doesn't pertain to the signature & data", async () => {
const validUnrelatedAddress = '0x8b0292b11a196601ed2ce54b665cafeca0347d42'; const validUnrelatedAddress = '0x8b0292b11a196601ed2ce54b665cafeca0347d42';
expect(ZeroEx.isValidSignature(dataHex, signature, validUnrelatedAddress)).to.be.false(); expect(ZeroEx.isValidSignature(dataHex, signature, validUnrelatedAddress)).to.be.false();
return expect( return expect(
(zeroEx.exchange as any)._isValidSignatureUsingContractCallAsync(dataHex, signature, (zeroEx.exchange as any)._isValidSignatureUsingContractCallAsync(
validUnrelatedAddress), dataHex,
signature,
validUnrelatedAddress,
),
).to.become(false); ).to.become(false);
}); });
it('should return false if the signature doesn\'t pertain to the dataHex & address', async () => { it("should return false if the signature doesn't pertain to the dataHex & address", async () => {
const wrongSignature = _.assign({}, signature, { v: 28 }); const wrongSignature = _.assign({}, signature, { v: 28 });
expect(ZeroEx.isValidSignature(dataHex, wrongSignature, address)).to.be.false(); expect(ZeroEx.isValidSignature(dataHex, wrongSignature, address)).to.be.false();
return expect( return expect(
@ -117,8 +120,9 @@ describe('ZeroEx library', () => {
it('should throw if invalid baseUnit amount supplied as argument', () => { it('should throw if invalid baseUnit amount supplied as argument', () => {
const invalidBaseUnitAmount = new BigNumber(1000000000.4); const invalidBaseUnitAmount = new BigNumber(1000000000.4);
const decimals = 6; const decimals = 6;
expect(() => ZeroEx.toUnitAmount(invalidBaseUnitAmount, decimals)) expect(() => ZeroEx.toUnitAmount(invalidBaseUnitAmount, decimals)).to.throw(
.to.throw('amount should be in baseUnits (no decimals), found value: 1000000000.4'); 'amount should be in baseUnits (no decimals), found value: 1000000000.4',
);
}); });
it('Should return the expected unit amount for the decimals passed in', () => { it('Should return the expected unit amount for the decimals passed in', () => {
const baseUnitAmount = new BigNumber(1000000000); const baseUnitAmount = new BigNumber(1000000000);
@ -139,8 +143,9 @@ describe('ZeroEx library', () => {
it('should throw if unitAmount has more decimals then specified as the max decimal precision', () => { it('should throw if unitAmount has more decimals then specified as the max decimal precision', () => {
const unitAmount = new BigNumber(0.823091); const unitAmount = new BigNumber(0.823091);
const decimals = 5; const decimals = 5;
expect(() => ZeroEx.toBaseUnitAmount(unitAmount, decimals)) expect(() => ZeroEx.toBaseUnitAmount(unitAmount, decimals)).to.throw(
.to.throw('Invalid unit amount: 0.823091 - Too many decimal places'); 'Invalid unit amount: 0.823091 - Too many decimal places',
);
}); });
}); });
describe('#getOrderHashHex', () => { describe('#getOrderHashHex', () => {
@ -167,10 +172,10 @@ describe('ZeroEx library', () => {
it('throws a readable error message if taker format is invalid', async () => { it('throws a readable error message if taker format is invalid', async () => {
const orderWithInvalidtakerFormat = { const orderWithInvalidtakerFormat = {
...order, ...order,
taker: null as any as string, taker: (null as any) as string,
}; };
// tslint:disable-next-line:max-line-length const expectedErrorMessage =
const expectedErrorMessage = 'Order taker must be of type string. If you want anyone to be able to fill an order - pass ZeroEx.NULL_ADDRESS'; 'Order taker must be of type string. If you want anyone to be able to fill an order - pass ZeroEx.NULL_ADDRESS';
expect(() => ZeroEx.getOrderHashHex(orderWithInvalidtakerFormat)).to.throw(expectedErrorMessage); expect(() => ZeroEx.getOrderHashHex(orderWithInvalidtakerFormat)).to.throw(expectedErrorMessage);
}); });
}); });
@ -198,16 +203,15 @@ describe('ZeroEx library', () => {
}); });
it('should return the correct ECSignature for signatureHex concatenated as R + S + V', async () => { it('should return the correct ECSignature for signatureHex concatenated as R + S + V', async () => {
const orderHash = '0x34decbedc118904df65f379a175bb39ca18209d6ce41d5ed549d54e6e0a95004'; const orderHash = '0x34decbedc118904df65f379a175bb39ca18209d6ce41d5ed549d54e6e0a95004';
// tslint:disable-next-line: max-line-length const signature =
const signature = '0x22109d11d79cb8bf96ed88625e1cd9558800c4073332a9a02857499883ee5ce3050aa3cc1f2c435e67e114cdce54b9527b4f50548342401bc5d2b77adbdacb021b'; '0x22109d11d79cb8bf96ed88625e1cd9558800c4073332a9a02857499883ee5ce3050aa3cc1f2c435e67e114cdce54b9527b4f50548342401bc5d2b77adbdacb021b';
const expectedECSignature = { const expectedECSignature = {
v: 27, v: 27,
r: '0x22109d11d79cb8bf96ed88625e1cd9558800c4073332a9a02857499883ee5ce3', r: '0x22109d11d79cb8bf96ed88625e1cd9558800c4073332a9a02857499883ee5ce3',
s: '0x050aa3cc1f2c435e67e114cdce54b9527b4f50548342401bc5d2b77adbdacb02', s: '0x050aa3cc1f2c435e67e114cdce54b9527b4f50548342401bc5d2b77adbdacb02',
}; };
stubs = [ stubs = [
Sinon.stub((zeroEx as any)._web3Wrapper, 'signTransactionAsync') Sinon.stub((zeroEx as any)._web3Wrapper, 'signTransactionAsync').returns(Promise.resolve(signature)),
.returns(Promise.resolve(signature)),
Sinon.stub(ZeroEx, 'isValidSignature').returns(true), Sinon.stub(ZeroEx, 'isValidSignature').returns(true),
]; ];
@ -216,16 +220,15 @@ describe('ZeroEx library', () => {
}); });
it('should return the correct ECSignature for signatureHex concatenated as V + R + S', async () => { it('should return the correct ECSignature for signatureHex concatenated as V + R + S', async () => {
const orderHash = '0xc793e33ffded933b76f2f48d9aa3339fc090399d5e7f5dec8d3660f5480793f7'; const orderHash = '0xc793e33ffded933b76f2f48d9aa3339fc090399d5e7f5dec8d3660f5480793f7';
// tslint:disable-next-line: max-line-length const signature =
const signature = '0x1bc80bedc6756722672753413efdd749b5adbd4fd552595f59c13427407ee9aee02dea66f25a608bbae457e020fb6decb763deb8b7192abab624997242da248960'; '0x1bc80bedc6756722672753413efdd749b5adbd4fd552595f59c13427407ee9aee02dea66f25a608bbae457e020fb6decb763deb8b7192abab624997242da248960';
const expectedECSignature = { const expectedECSignature = {
v: 27, v: 27,
r: '0xc80bedc6756722672753413efdd749b5adbd4fd552595f59c13427407ee9aee0', r: '0xc80bedc6756722672753413efdd749b5adbd4fd552595f59c13427407ee9aee0',
s: '0x2dea66f25a608bbae457e020fb6decb763deb8b7192abab624997242da248960', s: '0x2dea66f25a608bbae457e020fb6decb763deb8b7192abab624997242da248960',
}; };
stubs = [ stubs = [
Sinon.stub((zeroEx as any)._web3Wrapper, 'signTransactionAsync') Sinon.stub((zeroEx as any)._web3Wrapper, 'signTransactionAsync').returns(Promise.resolve(signature)),
.returns(Promise.resolve(signature)),
Sinon.stub(ZeroEx, 'isValidSignature').returns(true), Sinon.stub(ZeroEx, 'isValidSignature').returns(true),
]; ];
@ -271,8 +274,9 @@ describe('ZeroEx library', () => {
networkId: constants.TESTRPC_NETWORK_ID, networkId: constants.TESTRPC_NETWORK_ID,
}; };
const zeroExWithWrongTokenRegistryAddress = new ZeroEx(web3.currentProvider, zeroExConfig); const zeroExWithWrongTokenRegistryAddress = new ZeroEx(web3.currentProvider, zeroExConfig);
expect(zeroExWithWrongTokenRegistryAddress.tokenRegistry.getContractAddress()) expect(zeroExWithWrongTokenRegistryAddress.tokenRegistry.getContractAddress()).to.be.equal(
.to.be.equal(ZeroEx.NULL_ADDRESS); ZeroEx.NULL_ADDRESS,
);
}); });
}); });
}); });

View File

@ -19,22 +19,25 @@ describe('Assertion library', () => {
it('throws when address is invalid', async () => { it('throws when address is invalid', async () => {
const address = '0xdeadbeef'; const address = '0xdeadbeef';
const varName = 'address'; const varName = 'address';
return expect(assert.isSenderAddressAsync(varName, address, (zeroEx as any)._web3Wrapper)) return expect(
.to.be.rejectedWith(`Expected ${varName} to be of type ETHAddressHex, encountered: ${address}`); assert.isSenderAddressAsync(varName, address, (zeroEx as any)._web3Wrapper),
).to.be.rejectedWith(`Expected ${varName} to be of type ETHAddressHex, encountered: ${address}`);
}); });
it('throws when address is unavailable', async () => { it('throws when address is unavailable', async () => {
const validUnrelatedAddress = '0x8b0292b11a196601eddce54b665cafeca0347d42'; const validUnrelatedAddress = '0x8b0292b11a196601eddce54b665cafeca0347d42';
const varName = 'address'; const varName = 'address';
return expect(assert.isSenderAddressAsync(varName, validUnrelatedAddress, (zeroEx as any)._web3Wrapper)) return expect(
.to.be.rejectedWith( assert.isSenderAddressAsync(varName, validUnrelatedAddress, (zeroEx as any)._web3Wrapper),
).to.be.rejectedWith(
`Specified ${varName} ${validUnrelatedAddress} isn't available through the supplied web3 provider`, `Specified ${varName} ${validUnrelatedAddress} isn't available through the supplied web3 provider`,
); );
}); });
it('doesn\'t throw if address is available', async () => { it("doesn't throw if address is available", async () => {
const availableAddress = (await zeroEx.getAvailableAddressesAsync())[0]; const availableAddress = (await zeroEx.getAvailableAddressesAsync())[0];
const varName = 'address'; const varName = 'address';
return expect(assert.isSenderAddressAsync(varName, availableAddress, (zeroEx as any)._web3Wrapper)) return expect(
.to.become(undefined); assert.isSenderAddressAsync(varName, availableAddress, (zeroEx as any)._web3Wrapper),
).to.become(undefined);
}); });
}); });
}); });

View File

@ -167,10 +167,12 @@ describe('EtherTokenWrapper', () => {
done(); done();
}; };
await zeroEx.etherToken.depositAsync(etherTokenAddress, transferAmount, addressWithETH); await zeroEx.etherToken.depositAsync(etherTokenAddress, transferAmount, addressWithETH);
zeroEx.etherToken.subscribe( zeroEx.etherToken.subscribe(etherTokenAddress, EtherTokenEvents.Transfer, indexFilterValues, callback);
etherTokenAddress, EtherTokenEvents.Transfer, indexFilterValues, callback);
await zeroEx.token.transferAsync( await zeroEx.token.transferAsync(
etherTokenAddress, addressWithETH, addressWithoutFunds, transferAmount, etherTokenAddress,
addressWithETH,
addressWithoutFunds,
transferAmount,
); );
})().catch(done); })().catch(done);
}); });
@ -186,10 +188,12 @@ describe('EtherTokenWrapper', () => {
expect(args._value).to.be.bignumber.equal(allowanceAmount); expect(args._value).to.be.bignumber.equal(allowanceAmount);
done(); done();
}; };
zeroEx.etherToken.subscribe( zeroEx.etherToken.subscribe(etherTokenAddress, EtherTokenEvents.Approval, indexFilterValues, callback);
etherTokenAddress, EtherTokenEvents.Approval, indexFilterValues, callback);
await zeroEx.token.setAllowanceAsync( await zeroEx.token.setAllowanceAsync(
etherTokenAddress, addressWithETH, addressWithoutFunds, allowanceAmount, etherTokenAddress,
addressWithETH,
addressWithoutFunds,
allowanceAmount,
); );
})().catch(done); })().catch(done);
}); });
@ -204,11 +208,8 @@ describe('EtherTokenWrapper', () => {
expect(args._value).to.be.bignumber.equal(depositAmount); expect(args._value).to.be.bignumber.equal(depositAmount);
done(); done();
}; };
zeroEx.etherToken.subscribe( zeroEx.etherToken.subscribe(etherTokenAddress, EtherTokenEvents.Deposit, indexFilterValues, callback);
etherTokenAddress, EtherTokenEvents.Deposit, indexFilterValues, callback); await zeroEx.etherToken.depositAsync(etherTokenAddress, depositAmount, addressWithETH);
await zeroEx.etherToken.depositAsync(
etherTokenAddress, depositAmount, addressWithETH,
);
})().catch(done); })().catch(done);
}); });
it('Should receive the Withdrawal event when ether is being withdrawn', (done: DoneCallback) => { it('Should receive the Withdrawal event when ether is being withdrawn', (done: DoneCallback) => {
@ -222,14 +223,14 @@ describe('EtherTokenWrapper', () => {
expect(args._value).to.be.bignumber.equal(depositAmount); expect(args._value).to.be.bignumber.equal(depositAmount);
done(); done();
}; };
await zeroEx.etherToken.depositAsync( await zeroEx.etherToken.depositAsync(etherTokenAddress, depositAmount, addressWithETH);
etherTokenAddress, depositAmount, addressWithETH,
);
zeroEx.etherToken.subscribe( zeroEx.etherToken.subscribe(
etherTokenAddress, EtherTokenEvents.Withdrawal, indexFilterValues, callback); etherTokenAddress,
await zeroEx.etherToken.withdrawAsync( EtherTokenEvents.Withdrawal,
etherTokenAddress, withdrawalAmount, addressWithETH, indexFilterValues,
callback,
); );
await zeroEx.etherToken.withdrawAsync(etherTokenAddress, withdrawalAmount, addressWithETH);
})().catch(done); })().catch(done);
}); });
it('should cancel outstanding subscriptions when ZeroEx.setProvider is called', (done: DoneCallback) => { it('should cancel outstanding subscriptions when ZeroEx.setProvider is called', (done: DoneCallback) => {
@ -238,7 +239,10 @@ describe('EtherTokenWrapper', () => {
done(new Error('Expected this subscription to have been cancelled')); done(new Error('Expected this subscription to have been cancelled'));
}; };
zeroEx.etherToken.subscribe( zeroEx.etherToken.subscribe(
etherTokenAddress, EtherTokenEvents.Transfer, indexFilterValues, callbackNeverToBeCalled, etherTokenAddress,
EtherTokenEvents.Transfer,
indexFilterValues,
callbackNeverToBeCalled,
); );
const callbackToBeCalled = (err: Error, logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => { const callbackToBeCalled = (err: Error, logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => {
done(); done();
@ -247,10 +251,16 @@ describe('EtherTokenWrapper', () => {
zeroEx.setProvider(newProvider, constants.TESTRPC_NETWORK_ID); zeroEx.setProvider(newProvider, constants.TESTRPC_NETWORK_ID);
await zeroEx.etherToken.depositAsync(etherTokenAddress, transferAmount, addressWithETH); await zeroEx.etherToken.depositAsync(etherTokenAddress, transferAmount, addressWithETH);
zeroEx.etherToken.subscribe( zeroEx.etherToken.subscribe(
etherTokenAddress, EtherTokenEvents.Transfer, indexFilterValues, callbackToBeCalled, etherTokenAddress,
EtherTokenEvents.Transfer,
indexFilterValues,
callbackToBeCalled,
); );
await zeroEx.token.transferAsync( await zeroEx.token.transferAsync(
etherTokenAddress, addressWithETH, addressWithoutFunds, transferAmount, etherTokenAddress,
addressWithETH,
addressWithoutFunds,
transferAmount,
); );
})().catch(done); })().catch(done);
}); });
@ -261,10 +271,17 @@ describe('EtherTokenWrapper', () => {
}; };
await zeroEx.etherToken.depositAsync(etherTokenAddress, transferAmount, addressWithETH); await zeroEx.etherToken.depositAsync(etherTokenAddress, transferAmount, addressWithETH);
const subscriptionToken = zeroEx.etherToken.subscribe( const subscriptionToken = zeroEx.etherToken.subscribe(
etherTokenAddress, EtherTokenEvents.Transfer, indexFilterValues, callbackNeverToBeCalled); etherTokenAddress,
EtherTokenEvents.Transfer,
indexFilterValues,
callbackNeverToBeCalled,
);
zeroEx.etherToken.unsubscribe(subscriptionToken); zeroEx.etherToken.unsubscribe(subscriptionToken);
await zeroEx.token.transferAsync( await zeroEx.token.transferAsync(
etherTokenAddress, addressWithETH, addressWithoutFunds, transferAmount, etherTokenAddress,
addressWithETH,
addressWithoutFunds,
transferAmount,
); );
done(); done();
})().catch(done); })().catch(done);
@ -291,7 +308,10 @@ describe('EtherTokenWrapper', () => {
const eventName = EtherTokenEvents.Approval; const eventName = EtherTokenEvents.Approval;
const indexFilterValues = {}; const indexFilterValues = {};
const logs = await zeroEx.etherToken.getLogsAsync<ApprovalContractEventArgs>( const logs = await zeroEx.etherToken.getLogsAsync<ApprovalContractEventArgs>(
etherTokenAddress, eventName, blockRange, indexFilterValues, etherTokenAddress,
eventName,
blockRange,
indexFilterValues,
); );
expect(logs).to.have.length(1); expect(logs).to.have.length(1);
const args = logs[0].args; const args = logs[0].args;
@ -305,7 +325,10 @@ describe('EtherTokenWrapper', () => {
const eventName = EtherTokenEvents.Deposit; const eventName = EtherTokenEvents.Deposit;
const indexFilterValues = {}; const indexFilterValues = {};
const logs = await zeroEx.etherToken.getLogsAsync<DepositContractEventArgs>( const logs = await zeroEx.etherToken.getLogsAsync<DepositContractEventArgs>(
etherTokenAddress, eventName, blockRange, indexFilterValues, etherTokenAddress,
eventName,
blockRange,
indexFilterValues,
); );
expect(logs).to.have.length(1); expect(logs).to.have.length(1);
const args = logs[0].args; const args = logs[0].args;
@ -319,7 +342,10 @@ describe('EtherTokenWrapper', () => {
const differentEventName = EtherTokenEvents.Transfer; const differentEventName = EtherTokenEvents.Transfer;
const indexFilterValues = {}; const indexFilterValues = {};
const logs = await zeroEx.etherToken.getLogsAsync( const logs = await zeroEx.etherToken.getLogsAsync(
etherTokenAddress, differentEventName, blockRange, indexFilterValues, etherTokenAddress,
differentEventName,
blockRange,
indexFilterValues,
); );
expect(logs).to.have.length(0); expect(logs).to.have.length(0);
}); });
@ -333,7 +359,10 @@ describe('EtherTokenWrapper', () => {
_owner: addressWithETH, _owner: addressWithETH,
}; };
const logs = await zeroEx.etherToken.getLogsAsync<ApprovalContractEventArgs>( const logs = await zeroEx.etherToken.getLogsAsync<ApprovalContractEventArgs>(
etherTokenAddress, eventName, blockRange, indexFilterValues, etherTokenAddress,
eventName,
blockRange,
indexFilterValues,
); );
expect(logs).to.have.length(1); expect(logs).to.have.length(1);
const args = logs[0].args; const args = logs[0].args;

View File

@ -5,9 +5,7 @@ import 'mocha';
import * as Sinon from 'sinon'; import * as Sinon from 'sinon';
import * as Web3 from 'web3'; import * as Web3 from 'web3';
import { import { LogEvent } from '../src';
LogEvent,
} from '../src';
import { EventWatcher } from '../src/order_watcher/event_watcher'; import { EventWatcher } from '../src/order_watcher/event_watcher';
import { DoneCallback } from '../src/types'; import { DoneCallback } from '../src/types';

View File

@ -45,17 +45,31 @@ describe('ExchangeTransferSimulator', () => {
beforeEach(() => { beforeEach(() => {
exchangeTransferSimulator = new ExchangeTransferSimulator(zeroEx.token, BlockParamLiteral.Latest); exchangeTransferSimulator = new ExchangeTransferSimulator(zeroEx.token, BlockParamLiteral.Latest);
}); });
it('throws if the user doesn\'t have enough allowance', async () => { it("throws if the user doesn't have enough allowance", async () => {
return expect(exchangeTransferSimulator.transferFromAsync( return expect(
exampleTokenAddress, sender, recipient, transferAmount, TradeSide.Taker, TransferType.Trade, exchangeTransferSimulator.transferFromAsync(
)).to.be.rejectedWith(ExchangeContractErrs.InsufficientTakerAllowance); exampleTokenAddress,
sender,
recipient,
transferAmount,
TradeSide.Taker,
TransferType.Trade,
),
).to.be.rejectedWith(ExchangeContractErrs.InsufficientTakerAllowance);
}); });
it('throws if the user doesn\'t have enough balance', async () => { it("throws if the user doesn't have enough balance", async () => {
txHash = await zeroEx.token.setProxyAllowanceAsync(exampleTokenAddress, sender, transferAmount); txHash = await zeroEx.token.setProxyAllowanceAsync(exampleTokenAddress, sender, transferAmount);
await zeroEx.awaitTransactionMinedAsync(txHash); await zeroEx.awaitTransactionMinedAsync(txHash);
return expect(exchangeTransferSimulator.transferFromAsync( return expect(
exampleTokenAddress, sender, recipient, transferAmount, TradeSide.Maker, TransferType.Trade, exchangeTransferSimulator.transferFromAsync(
)).to.be.rejectedWith(ExchangeContractErrs.InsufficientMakerBalance); exampleTokenAddress,
sender,
recipient,
transferAmount,
TradeSide.Maker,
TransferType.Trade,
),
).to.be.rejectedWith(ExchangeContractErrs.InsufficientMakerBalance);
}); });
it('updates balances and proxyAllowance after transfer', async () => { it('updates balances and proxyAllowance after transfer', async () => {
txHash = await zeroEx.token.transferAsync(exampleTokenAddress, coinbase, sender, transferAmount); txHash = await zeroEx.token.transferAsync(exampleTokenAddress, coinbase, sender, transferAmount);
@ -63,7 +77,12 @@ describe('ExchangeTransferSimulator', () => {
txHash = await zeroEx.token.setProxyAllowanceAsync(exampleTokenAddress, sender, transferAmount); txHash = await zeroEx.token.setProxyAllowanceAsync(exampleTokenAddress, sender, transferAmount);
await zeroEx.awaitTransactionMinedAsync(txHash); await zeroEx.awaitTransactionMinedAsync(txHash);
await exchangeTransferSimulator.transferFromAsync( await exchangeTransferSimulator.transferFromAsync(
exampleTokenAddress, sender, recipient, transferAmount, TradeSide.Taker, TransferType.Trade, exampleTokenAddress,
sender,
recipient,
transferAmount,
TradeSide.Taker,
TransferType.Trade,
); );
const store = (exchangeTransferSimulator as any)._store; const store = (exchangeTransferSimulator as any)._store;
const senderBalance = await store.getBalanceAsync(exampleTokenAddress, sender); const senderBalance = await store.getBalanceAsync(exampleTokenAddress, sender);
@ -73,13 +92,18 @@ describe('ExchangeTransferSimulator', () => {
expect(recipientBalance).to.be.bignumber.equal(transferAmount); expect(recipientBalance).to.be.bignumber.equal(transferAmount);
expect(senderProxyAllowance).to.be.bignumber.equal(0); expect(senderProxyAllowance).to.be.bignumber.equal(0);
}); });
it('doesn\'t update proxyAllowance after transfer if unlimited', async () => { it("doesn't update proxyAllowance after transfer if unlimited", async () => {
txHash = await zeroEx.token.transferAsync(exampleTokenAddress, coinbase, sender, transferAmount); txHash = await zeroEx.token.transferAsync(exampleTokenAddress, coinbase, sender, transferAmount);
await zeroEx.awaitTransactionMinedAsync(txHash); await zeroEx.awaitTransactionMinedAsync(txHash);
txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(exampleTokenAddress, sender); txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(exampleTokenAddress, sender);
await zeroEx.awaitTransactionMinedAsync(txHash); await zeroEx.awaitTransactionMinedAsync(txHash);
await exchangeTransferSimulator.transferFromAsync( await exchangeTransferSimulator.transferFromAsync(
exampleTokenAddress, sender, recipient, transferAmount, TradeSide.Taker, TransferType.Trade, exampleTokenAddress,
sender,
recipient,
transferAmount,
TradeSide.Taker,
TransferType.Trade,
); );
const store = (exchangeTransferSimulator as any)._store; const store = (exchangeTransferSimulator as any)._store;
const senderBalance = await store.getBalanceAsync(exampleTokenAddress, sender); const senderBalance = await store.getBalanceAsync(exampleTokenAddress, sender);

View File

@ -80,10 +80,18 @@ describe('ExchangeWrapper', () => {
const fillableAmount = new BigNumber(5); const fillableAmount = new BigNumber(5);
const partialFillTakerAmount = new BigNumber(2); const partialFillTakerAmount = new BigNumber(2);
const signedOrder = await fillScenarios.createFillableSignedOrderAsync( const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, makerTokenAddress,
takerTokenAddress,
makerAddress,
takerAddress,
fillableAmount,
); );
const anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync( const anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, makerTokenAddress,
takerTokenAddress,
makerAddress,
takerAddress,
fillableAmount,
); );
const orderFillRequests = [ const orderFillRequests = [
{ {
@ -103,7 +111,11 @@ describe('ExchangeWrapper', () => {
const fillableAmount = new BigNumber(5); const fillableAmount = new BigNumber(5);
beforeEach(async () => { beforeEach(async () => {
signedOrder = await fillScenarios.createFillableSignedOrderAsync( signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, makerTokenAddress,
takerTokenAddress,
makerAddress,
takerAddress,
fillableAmount,
); );
orderFillRequests = [ orderFillRequests = [
{ {
@ -113,18 +125,23 @@ describe('ExchangeWrapper', () => {
]; ];
}); });
it('should validate when orderTransactionOptions are not present', async () => { it('should validate when orderTransactionOptions are not present', async () => {
return expect(zeroEx.exchange.batchFillOrKillAsync(orderFillRequests, takerAddress)) return expect(
.to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); zeroEx.exchange.batchFillOrKillAsync(orderFillRequests, takerAddress),
).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
}); });
it('should validate when orderTransactionOptions specify to validate', async () => { it('should validate when orderTransactionOptions specify to validate', async () => {
return expect(zeroEx.exchange.batchFillOrKillAsync(orderFillRequests, takerAddress, { return expect(
zeroEx.exchange.batchFillOrKillAsync(orderFillRequests, takerAddress, {
shouldValidate: true, shouldValidate: true,
})).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); }),
).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
}); });
it('should not validate when orderTransactionOptions specify not to validate', async () => { it('should not validate when orderTransactionOptions specify not to validate', async () => {
return expect(zeroEx.exchange.batchFillOrKillAsync(orderFillRequests, takerAddress, { return expect(
zeroEx.exchange.batchFillOrKillAsync(orderFillRequests, takerAddress, {
shouldValidate: false, shouldValidate: false,
})).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); }),
).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
}); });
}); });
}); });
@ -133,57 +150,78 @@ describe('ExchangeWrapper', () => {
const fillableAmount = new BigNumber(5); const fillableAmount = new BigNumber(5);
beforeEach(async () => { beforeEach(async () => {
signedOrder = await fillScenarios.createFillableSignedOrderAsync( signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, makerTokenAddress,
takerTokenAddress,
makerAddress,
takerAddress,
fillableAmount,
); );
}); });
describe('successful fills', () => { describe('successful fills', () => {
it('should fill a valid order', async () => { it('should fill a valid order', async () => {
expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress)) expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress)).to.be.bignumber.equal(
.to.be.bignumber.equal(fillableAmount); fillableAmount,
expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress)) );
.to.be.bignumber.equal(0); expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress)).to.be.bignumber.equal(
expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, takerAddress)) 0,
.to.be.bignumber.equal(0); );
expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress)) expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, takerAddress)).to.be.bignumber.equal(
.to.be.bignumber.equal(fillableAmount); 0,
);
expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress)).to.be.bignumber.equal(
fillableAmount,
);
await zeroEx.exchange.fillOrKillOrderAsync(signedOrder, takerTokenFillAmount, takerAddress); await zeroEx.exchange.fillOrKillOrderAsync(signedOrder, takerTokenFillAmount, takerAddress);
expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress)) expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress)).to.be.bignumber.equal(
.to.be.bignumber.equal(fillableAmount.minus(takerTokenFillAmount)); fillableAmount.minus(takerTokenFillAmount),
expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress)) );
.to.be.bignumber.equal(takerTokenFillAmount); expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress)).to.be.bignumber.equal(
expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, takerAddress)) takerTokenFillAmount,
.to.be.bignumber.equal(takerTokenFillAmount); );
expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress)) expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, takerAddress)).to.be.bignumber.equal(
.to.be.bignumber.equal(fillableAmount.minus(takerTokenFillAmount)); takerTokenFillAmount,
);
expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress)).to.be.bignumber.equal(
fillableAmount.minus(takerTokenFillAmount),
);
}); });
it('should partially fill a valid order', async () => { it('should partially fill a valid order', async () => {
const partialFillAmount = new BigNumber(3); const partialFillAmount = new BigNumber(3);
await zeroEx.exchange.fillOrKillOrderAsync(signedOrder, partialFillAmount, takerAddress); await zeroEx.exchange.fillOrKillOrderAsync(signedOrder, partialFillAmount, takerAddress);
expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress)) expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress)).to.be.bignumber.equal(
.to.be.bignumber.equal(fillableAmount.minus(partialFillAmount)); fillableAmount.minus(partialFillAmount),
expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress)) );
.to.be.bignumber.equal(partialFillAmount); expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress)).to.be.bignumber.equal(
expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, takerAddress)) partialFillAmount,
.to.be.bignumber.equal(partialFillAmount); );
expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress)) expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, takerAddress)).to.be.bignumber.equal(
.to.be.bignumber.equal(fillableAmount.minus(partialFillAmount)); partialFillAmount,
);
expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress)).to.be.bignumber.equal(
fillableAmount.minus(partialFillAmount),
);
}); });
}); });
describe('order transaction options', () => { describe('order transaction options', () => {
const emptyFillableAmount = new BigNumber(0); const emptyFillableAmount = new BigNumber(0);
it('should validate when orderTransactionOptions are not present', async () => { it('should validate when orderTransactionOptions are not present', async () => {
return expect(zeroEx.exchange.fillOrKillOrderAsync(signedOrder, emptyFillableAmount, takerAddress)) return expect(
.to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); zeroEx.exchange.fillOrKillOrderAsync(signedOrder, emptyFillableAmount, takerAddress),
).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
}); });
it('should validate when orderTransactionOptions specify to validate', async () => { it('should validate when orderTransactionOptions specify to validate', async () => {
return expect(zeroEx.exchange.fillOrKillOrderAsync(signedOrder, emptyFillableAmount, takerAddress, { return expect(
zeroEx.exchange.fillOrKillOrderAsync(signedOrder, emptyFillableAmount, takerAddress, {
shouldValidate: true, shouldValidate: true,
})).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); }),
).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
}); });
it('should not validate when orderTransactionOptions specify not to validate', async () => { it('should not validate when orderTransactionOptions specify not to validate', async () => {
return expect(zeroEx.exchange.fillOrKillOrderAsync(signedOrder, emptyFillableAmount, takerAddress, { return expect(
zeroEx.exchange.fillOrKillOrderAsync(signedOrder, emptyFillableAmount, takerAddress, {
shouldValidate: false, shouldValidate: false,
})).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); }),
).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
}); });
}); });
}); });
@ -209,57 +247,96 @@ describe('ExchangeWrapper', () => {
describe('successful fills', () => { describe('successful fills', () => {
it('should fill a valid order', async () => { it('should fill a valid order', async () => {
const signedOrder = await fillScenarios.createFillableSignedOrderAsync( const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, makerTokenAddress,
takerTokenAddress,
makerAddress,
takerAddress,
fillableAmount,
);
expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress)).to.be.bignumber.equal(
fillableAmount,
);
expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress)).to.be.bignumber.equal(
0,
);
expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, takerAddress)).to.be.bignumber.equal(
0,
);
expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress)).to.be.bignumber.equal(
fillableAmount,
); );
expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress))
.to.be.bignumber.equal(fillableAmount);
expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress))
.to.be.bignumber.equal(0);
expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, takerAddress))
.to.be.bignumber.equal(0);
expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress))
.to.be.bignumber.equal(fillableAmount);
const txHash = await zeroEx.exchange.fillOrderAsync( const txHash = await zeroEx.exchange.fillOrderAsync(
signedOrder, takerTokenFillAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress); signedOrder,
takerTokenFillAmount,
shouldThrowOnInsufficientBalanceOrAllowance,
takerAddress,
);
await zeroEx.awaitTransactionMinedAsync(txHash); await zeroEx.awaitTransactionMinedAsync(txHash);
expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress)) expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress)).to.be.bignumber.equal(
.to.be.bignumber.equal(fillableAmount.minus(takerTokenFillAmount)); fillableAmount.minus(takerTokenFillAmount),
expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress)) );
.to.be.bignumber.equal(takerTokenFillAmount); expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress)).to.be.bignumber.equal(
expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, takerAddress)) takerTokenFillAmount,
.to.be.bignumber.equal(takerTokenFillAmount); );
expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress)) expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, takerAddress)).to.be.bignumber.equal(
.to.be.bignumber.equal(fillableAmount.minus(takerTokenFillAmount)); takerTokenFillAmount,
);
expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress)).to.be.bignumber.equal(
fillableAmount.minus(takerTokenFillAmount),
);
}); });
it('should partially fill the valid order', async () => { it('should partially fill the valid order', async () => {
const signedOrder = await fillScenarios.createFillableSignedOrderAsync( const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, makerTokenAddress,
takerTokenAddress,
makerAddress,
takerAddress,
fillableAmount,
); );
const partialFillAmount = new BigNumber(3); const partialFillAmount = new BigNumber(3);
const txHash = await zeroEx.exchange.fillOrderAsync( const txHash = await zeroEx.exchange.fillOrderAsync(
signedOrder, partialFillAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress); signedOrder,
partialFillAmount,
shouldThrowOnInsufficientBalanceOrAllowance,
takerAddress,
);
await zeroEx.awaitTransactionMinedAsync(txHash); await zeroEx.awaitTransactionMinedAsync(txHash);
expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress)) expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress)).to.be.bignumber.equal(
.to.be.bignumber.equal(fillableAmount.minus(partialFillAmount)); fillableAmount.minus(partialFillAmount),
expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress)) );
.to.be.bignumber.equal(partialFillAmount); expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress)).to.be.bignumber.equal(
expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, takerAddress)) partialFillAmount,
.to.be.bignumber.equal(partialFillAmount); );
expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress)) expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, takerAddress)).to.be.bignumber.equal(
.to.be.bignumber.equal(fillableAmount.minus(partialFillAmount)); partialFillAmount,
);
expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress)).to.be.bignumber.equal(
fillableAmount.minus(partialFillAmount),
);
}); });
it('should fill the valid orders with fees', async () => { it('should fill the valid orders with fees', async () => {
const makerFee = new BigNumber(1); const makerFee = new BigNumber(1);
const takerFee = new BigNumber(2); const takerFee = new BigNumber(2);
const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync( const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
makerTokenAddress, takerTokenAddress, makerFee, takerFee, makerTokenAddress,
makerAddress, takerAddress, fillableAmount, feeRecipient, takerTokenAddress,
makerFee,
takerFee,
makerAddress,
takerAddress,
fillableAmount,
feeRecipient,
); );
const txHash = await zeroEx.exchange.fillOrderAsync( const txHash = await zeroEx.exchange.fillOrderAsync(
signedOrder, takerTokenFillAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress); signedOrder,
takerTokenFillAmount,
shouldThrowOnInsufficientBalanceOrAllowance,
takerAddress,
);
await zeroEx.awaitTransactionMinedAsync(txHash); await zeroEx.awaitTransactionMinedAsync(txHash);
expect(await zeroEx.token.getBalanceAsync(zrxTokenAddress, feeRecipient)) expect(await zeroEx.token.getBalanceAsync(zrxTokenAddress, feeRecipient)).to.be.bignumber.equal(
.to.be.bignumber.equal(makerFee.plus(takerFee)); makerFee.plus(takerFee),
);
}); });
}); });
describe('order transaction options', () => { describe('order transaction options', () => {
@ -267,25 +344,48 @@ describe('ExchangeWrapper', () => {
const emptyFillTakerAmount = new BigNumber(0); const emptyFillTakerAmount = new BigNumber(0);
beforeEach(async () => { beforeEach(async () => {
signedOrder = await fillScenarios.createFillableSignedOrderAsync( signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, makerTokenAddress,
takerTokenAddress,
makerAddress,
takerAddress,
fillableAmount,
); );
}); });
it('should validate when orderTransactionOptions are not present', async () => { it('should validate when orderTransactionOptions are not present', async () => {
return expect(zeroEx.exchange.fillOrderAsync( return expect(
signedOrder, emptyFillTakerAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, zeroEx.exchange.fillOrderAsync(
)).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); signedOrder,
emptyFillTakerAmount,
shouldThrowOnInsufficientBalanceOrAllowance,
takerAddress,
),
).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
}); });
it('should validate when orderTransactionOptions specify to validate', async () => { it('should validate when orderTransactionOptions specify to validate', async () => {
return expect(zeroEx.exchange.fillOrderAsync( return expect(
signedOrder, emptyFillTakerAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, { zeroEx.exchange.fillOrderAsync(
signedOrder,
emptyFillTakerAmount,
shouldThrowOnInsufficientBalanceOrAllowance,
takerAddress,
{
shouldValidate: true, shouldValidate: true,
})).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); },
),
).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
}); });
it('should not validate when orderTransactionOptions specify not to validate', async () => { it('should not validate when orderTransactionOptions specify not to validate', async () => {
return expect(zeroEx.exchange.fillOrderAsync( return expect(
signedOrder, emptyFillTakerAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, { zeroEx.exchange.fillOrderAsync(
signedOrder,
emptyFillTakerAmount,
shouldThrowOnInsufficientBalanceOrAllowance,
takerAddress,
{
shouldValidate: false, shouldValidate: false,
})).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); },
),
).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
}); });
}); });
}); });
@ -297,11 +397,19 @@ describe('ExchangeWrapper', () => {
let orderFillBatch: OrderFillRequest[]; let orderFillBatch: OrderFillRequest[];
beforeEach(async () => { beforeEach(async () => {
signedOrder = await fillScenarios.createFillableSignedOrderAsync( signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, makerTokenAddress,
takerTokenAddress,
makerAddress,
takerAddress,
fillableAmount,
); );
signedOrderHashHex = ZeroEx.getOrderHashHex(signedOrder); signedOrderHashHex = ZeroEx.getOrderHashHex(signedOrder);
anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync( anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, makerTokenAddress,
takerTokenAddress,
makerAddress,
takerAddress,
fillableAmount,
); );
anotherOrderHashHex = ZeroEx.getOrderHashHex(anotherSignedOrder); anotherOrderHashHex = ZeroEx.getOrderHashHex(anotherSignedOrder);
}); });
@ -319,13 +427,20 @@ describe('ExchangeWrapper', () => {
]; ];
}); });
it('should throw if a batch is empty', async () => { it('should throw if a batch is empty', async () => {
return expect(zeroEx.exchange.batchFillOrdersAsync( return expect(
[], shouldThrowOnInsufficientBalanceOrAllowance, takerAddress), zeroEx.exchange.batchFillOrdersAsync(
[],
shouldThrowOnInsufficientBalanceOrAllowance,
takerAddress,
),
).to.be.rejectedWith(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem); ).to.be.rejectedWith(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
}); });
it('should successfully fill multiple orders', async () => { it('should successfully fill multiple orders', async () => {
const txHash = await zeroEx.exchange.batchFillOrdersAsync( const txHash = await zeroEx.exchange.batchFillOrdersAsync(
orderFillBatch, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress); orderFillBatch,
shouldThrowOnInsufficientBalanceOrAllowance,
takerAddress,
);
await zeroEx.awaitTransactionMinedAsync(txHash); await zeroEx.awaitTransactionMinedAsync(txHash);
const filledAmount = await zeroEx.exchange.getFilledTakerAmountAsync(signedOrderHashHex); const filledAmount = await zeroEx.exchange.getFilledTakerAmountAsync(signedOrderHashHex);
const anotherFilledAmount = await zeroEx.exchange.getFilledTakerAmountAsync(anotherOrderHashHex); const anotherFilledAmount = await zeroEx.exchange.getFilledTakerAmountAsync(anotherOrderHashHex);
@ -348,21 +463,37 @@ describe('ExchangeWrapper', () => {
]; ];
}); });
it('should validate when orderTransactionOptions are not present', async () => { it('should validate when orderTransactionOptions are not present', async () => {
return expect(zeroEx.exchange.batchFillOrdersAsync( return expect(
orderFillBatch, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress), zeroEx.exchange.batchFillOrdersAsync(
orderFillBatch,
shouldThrowOnInsufficientBalanceOrAllowance,
takerAddress,
),
).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
}); });
it('should validate when orderTransactionOptions specify to validate', async () => { it('should validate when orderTransactionOptions specify to validate', async () => {
return expect(zeroEx.exchange.batchFillOrdersAsync( return expect(
orderFillBatch, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, { zeroEx.exchange.batchFillOrdersAsync(
orderFillBatch,
shouldThrowOnInsufficientBalanceOrAllowance,
takerAddress,
{
shouldValidate: true, shouldValidate: true,
})).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); },
),
).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
}); });
it('should not validate when orderTransactionOptions specify not to validate', async () => { it('should not validate when orderTransactionOptions specify not to validate', async () => {
return expect(zeroEx.exchange.batchFillOrdersAsync( return expect(
orderFillBatch, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, { zeroEx.exchange.batchFillOrdersAsync(
orderFillBatch,
shouldThrowOnInsufficientBalanceOrAllowance,
takerAddress,
{
shouldValidate: false, shouldValidate: false,
})).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); },
),
).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
}); });
}); });
}); });
@ -375,24 +506,40 @@ describe('ExchangeWrapper', () => {
const fillUpToAmount = fillableAmount.plus(fillableAmount).minus(1); const fillUpToAmount = fillableAmount.plus(fillableAmount).minus(1);
beforeEach(async () => { beforeEach(async () => {
signedOrder = await fillScenarios.createFillableSignedOrderAsync( signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, makerTokenAddress,
takerTokenAddress,
makerAddress,
takerAddress,
fillableAmount,
); );
signedOrderHashHex = ZeroEx.getOrderHashHex(signedOrder); signedOrderHashHex = ZeroEx.getOrderHashHex(signedOrder);
anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync( anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, makerTokenAddress,
takerTokenAddress,
makerAddress,
takerAddress,
fillableAmount,
); );
anotherOrderHashHex = ZeroEx.getOrderHashHex(anotherSignedOrder); anotherOrderHashHex = ZeroEx.getOrderHashHex(anotherSignedOrder);
signedOrders = [signedOrder, anotherSignedOrder]; signedOrders = [signedOrder, anotherSignedOrder];
}); });
describe('successful batch fills', () => { describe('successful batch fills', () => {
it('should throw if a batch is empty', async () => { it('should throw if a batch is empty', async () => {
return expect(zeroEx.exchange.fillOrdersUpToAsync( return expect(
[], fillUpToAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress), zeroEx.exchange.fillOrdersUpToAsync(
[],
fillUpToAmount,
shouldThrowOnInsufficientBalanceOrAllowance,
takerAddress,
),
).to.be.rejectedWith(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem); ).to.be.rejectedWith(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
}); });
it('should successfully fill up to specified amount', async () => { it('should successfully fill up to specified amount', async () => {
const txHash = await zeroEx.exchange.fillOrdersUpToAsync( const txHash = await zeroEx.exchange.fillOrdersUpToAsync(
signedOrders, fillUpToAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, signedOrders,
fillUpToAmount,
shouldThrowOnInsufficientBalanceOrAllowance,
takerAddress,
); );
await zeroEx.awaitTransactionMinedAsync(txHash); await zeroEx.awaitTransactionMinedAsync(txHash);
const filledAmount = await zeroEx.exchange.getFilledTakerAmountAsync(signedOrderHashHex); const filledAmount = await zeroEx.exchange.getFilledTakerAmountAsync(signedOrderHashHex);
@ -405,21 +552,40 @@ describe('ExchangeWrapper', () => {
describe('order transaction options', () => { describe('order transaction options', () => {
const emptyFillUpToAmount = new BigNumber(0); const emptyFillUpToAmount = new BigNumber(0);
it('should validate when orderTransactionOptions are not present', async () => { it('should validate when orderTransactionOptions are not present', async () => {
return expect(zeroEx.exchange.fillOrdersUpToAsync( return expect(
signedOrders, emptyFillUpToAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, zeroEx.exchange.fillOrdersUpToAsync(
)).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); signedOrders,
emptyFillUpToAmount,
shouldThrowOnInsufficientBalanceOrAllowance,
takerAddress,
),
).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
}); });
it('should validate when orderTransactionOptions specify to validate', async () => { it('should validate when orderTransactionOptions specify to validate', async () => {
return expect(zeroEx.exchange.fillOrdersUpToAsync( return expect(
signedOrders, emptyFillUpToAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, { zeroEx.exchange.fillOrdersUpToAsync(
signedOrders,
emptyFillUpToAmount,
shouldThrowOnInsufficientBalanceOrAllowance,
takerAddress,
{
shouldValidate: true, shouldValidate: true,
})).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); },
),
).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
}); });
it('should not validate when orderTransactionOptions specify not to validate', async () => { it('should not validate when orderTransactionOptions specify not to validate', async () => {
return expect(zeroEx.exchange.fillOrdersUpToAsync( return expect(
signedOrders, emptyFillUpToAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, { zeroEx.exchange.fillOrdersUpToAsync(
signedOrders,
emptyFillUpToAmount,
shouldThrowOnInsufficientBalanceOrAllowance,
takerAddress,
{
shouldValidate: false, shouldValidate: false,
})).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); },
),
).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
}); });
}); });
}); });
@ -440,7 +606,11 @@ describe('ExchangeWrapper', () => {
makerTokenAddress = makerToken.address; makerTokenAddress = makerToken.address;
takerTokenAddress = takerToken.address; takerTokenAddress = takerToken.address;
signedOrder = await fillScenarios.createFillableSignedOrderAsync( signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, makerTokenAddress,
takerTokenAddress,
makerAddress,
takerAddress,
fillableAmount,
); );
orderHashHex = ZeroEx.getOrderHashHex(signedOrder); orderHashHex = ZeroEx.getOrderHashHex(signedOrder);
}); });
@ -456,18 +626,23 @@ describe('ExchangeWrapper', () => {
describe('order transaction options', () => { describe('order transaction options', () => {
const emptyCancelTakerTokenAmount = new BigNumber(0); const emptyCancelTakerTokenAmount = new BigNumber(0);
it('should validate when orderTransactionOptions are not present', async () => { it('should validate when orderTransactionOptions are not present', async () => {
return expect(zeroEx.exchange.cancelOrderAsync(signedOrder, emptyCancelTakerTokenAmount)) return expect(
.to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero); zeroEx.exchange.cancelOrderAsync(signedOrder, emptyCancelTakerTokenAmount),
).to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero);
}); });
it('should validate when orderTransactionOptions specify to validate', async () => { it('should validate when orderTransactionOptions specify to validate', async () => {
return expect(zeroEx.exchange.cancelOrderAsync(signedOrder, emptyCancelTakerTokenAmount, { return expect(
zeroEx.exchange.cancelOrderAsync(signedOrder, emptyCancelTakerTokenAmount, {
shouldValidate: true, shouldValidate: true,
})).to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero); }),
).to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero);
}); });
it('should not validate when orderTransactionOptions specify not to validate', async () => { it('should not validate when orderTransactionOptions specify not to validate', async () => {
return expect(zeroEx.exchange.cancelOrderAsync(signedOrder, emptyCancelTakerTokenAmount, { return expect(
zeroEx.exchange.cancelOrderAsync(signedOrder, emptyCancelTakerTokenAmount, {
shouldValidate: false, shouldValidate: false,
})).to.not.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero); }),
).to.not.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero);
}); });
}); });
}); });
@ -477,7 +652,11 @@ describe('ExchangeWrapper', () => {
let cancelBatch: OrderCancellationRequest[]; let cancelBatch: OrderCancellationRequest[];
beforeEach(async () => { beforeEach(async () => {
anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync( anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, makerTokenAddress,
takerTokenAddress,
makerAddress,
takerAddress,
fillableAmount,
); );
anotherOrderHashHex = ZeroEx.getOrderHashHex(anotherSignedOrder); anotherOrderHashHex = ZeroEx.getOrderHashHex(anotherSignedOrder);
cancelBatch = [ cancelBatch = [
@ -494,15 +673,21 @@ describe('ExchangeWrapper', () => {
describe('failed batch cancels', () => { describe('failed batch cancels', () => {
it('should throw when orders have different makers', async () => { it('should throw when orders have different makers', async () => {
const signedOrderWithDifferentMaker = await fillScenarios.createFillableSignedOrderAsync( const signedOrderWithDifferentMaker = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, takerAddress, takerAddress, fillableAmount, makerTokenAddress,
takerTokenAddress,
takerAddress,
takerAddress,
fillableAmount,
); );
return expect(zeroEx.exchange.batchCancelOrdersAsync([ return expect(
zeroEx.exchange.batchCancelOrdersAsync([
cancelBatch[0], cancelBatch[0],
{ {
order: signedOrderWithDifferentMaker, order: signedOrderWithDifferentMaker,
takerTokenCancelAmount: cancelAmount, takerTokenCancelAmount: cancelAmount,
}, },
])).to.be.rejectedWith(ExchangeContractErrs.MultipleMakersInSingleCancelBatchDisallowed); ]),
).to.be.rejectedWith(ExchangeContractErrs.MultipleMakersInSingleCancelBatchDisallowed);
}); });
}); });
describe('successful batch cancels', () => { describe('successful batch cancels', () => {
@ -531,18 +716,23 @@ describe('ExchangeWrapper', () => {
]; ];
}); });
it('should validate when orderTransactionOptions are not present', async () => { it('should validate when orderTransactionOptions are not present', async () => {
return expect(zeroEx.exchange.batchCancelOrdersAsync(cancelBatch)) return expect(zeroEx.exchange.batchCancelOrdersAsync(cancelBatch)).to.be.rejectedWith(
.to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero); ExchangeContractErrs.OrderCancelAmountZero,
);
}); });
it('should validate when orderTransactionOptions specify to validate', async () => { it('should validate when orderTransactionOptions specify to validate', async () => {
return expect(zeroEx.exchange.batchCancelOrdersAsync(cancelBatch, { return expect(
zeroEx.exchange.batchCancelOrdersAsync(cancelBatch, {
shouldValidate: true, shouldValidate: true,
})).to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero); }),
).to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero);
}); });
it('should not validate when orderTransactionOptions specify not to validate', async () => { it('should not validate when orderTransactionOptions specify not to validate', async () => {
return expect(zeroEx.exchange.batchCancelOrdersAsync(cancelBatch, { return expect(
zeroEx.exchange.batchCancelOrdersAsync(cancelBatch, {
shouldValidate: false, shouldValidate: false,
})).to.not.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero); }),
).to.not.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero);
}); });
}); });
}); });
@ -566,7 +756,11 @@ describe('ExchangeWrapper', () => {
fillableAmount = new BigNumber(5); fillableAmount = new BigNumber(5);
partialFillAmount = new BigNumber(2); partialFillAmount = new BigNumber(2);
signedOrder = await fillScenarios.createPartiallyFilledSignedOrderAsync( signedOrder = await fillScenarios.createPartiallyFilledSignedOrderAsync(
makerTokenAddress, takerTokenAddress, takerAddress, fillableAmount, partialFillAmount, makerTokenAddress,
takerTokenAddress,
takerAddress,
fillableAmount,
partialFillAmount,
); );
orderHash = ZeroEx.getOrderHashHex(signedOrder); orderHash = ZeroEx.getOrderHashHex(signedOrder);
}); });
@ -590,8 +784,7 @@ describe('ExchangeWrapper', () => {
return expect(zeroEx.exchange.getFilledTakerAmountAsync(invalidOrderHashHex)).to.be.rejected(); return expect(zeroEx.exchange.getFilledTakerAmountAsync(invalidOrderHashHex)).to.be.rejected();
}); });
it('should return zero if passed a valid but non-existent orderHash', async () => { it('should return zero if passed a valid but non-existent orderHash', async () => {
const filledValueT = await zeroEx.exchange.getFilledTakerAmountAsync(NON_EXISTENT_ORDER_HASH, const filledValueT = await zeroEx.exchange.getFilledTakerAmountAsync(NON_EXISTENT_ORDER_HASH);
);
expect(filledValueT).to.be.bignumber.equal(0); expect(filledValueT).to.be.bignumber.equal(0);
}); });
it('should return the filledValueT for a valid and partially filled orderHash', async () => { it('should return the filledValueT for a valid and partially filled orderHash', async () => {
@ -641,7 +834,11 @@ describe('ExchangeWrapper', () => {
beforeEach(async () => { beforeEach(async () => {
fillableAmount = new BigNumber(5); fillableAmount = new BigNumber(5);
signedOrder = await fillScenarios.createFillableSignedOrderAsync( signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, makerTokenAddress,
takerTokenAddress,
makerAddress,
takerAddress,
fillableAmount,
); );
}); });
afterEach(async () => { afterEach(async () => {
@ -654,42 +851,35 @@ describe('ExchangeWrapper', () => {
// Source: https://github.com/mochajs/mocha/issues/2407 // Source: https://github.com/mochajs/mocha/issues/2407
it('Should receive the LogFill event when an order is filled', (done: DoneCallback) => { it('Should receive the LogFill event when an order is filled', (done: DoneCallback) => {
(async () => { (async () => {
const callback = (err: Error, logEvent: DecodedLogEvent<LogFillContractEventArgs>) => { const callback = (err: Error, logEvent: DecodedLogEvent<LogFillContractEventArgs>) => {
expect(logEvent.log.event).to.be.equal(ExchangeEvents.LogFill); expect(logEvent.log.event).to.be.equal(ExchangeEvents.LogFill);
done(); done();
}; };
zeroEx.exchange.subscribe( zeroEx.exchange.subscribe(ExchangeEvents.LogFill, indexFilterValues, callback);
ExchangeEvents.LogFill, indexFilterValues, callback,
);
await zeroEx.exchange.fillOrderAsync( await zeroEx.exchange.fillOrderAsync(
signedOrder, takerTokenFillAmountInBaseUnits, shouldThrowOnInsufficientBalanceOrAllowance, signedOrder,
takerTokenFillAmountInBaseUnits,
shouldThrowOnInsufficientBalanceOrAllowance,
takerAddress, takerAddress,
); );
})().catch(done); })().catch(done);
}); });
it('Should receive the LogCancel event when an order is cancelled', (done: DoneCallback) => { it('Should receive the LogCancel event when an order is cancelled', (done: DoneCallback) => {
(async () => { (async () => {
const callback = (err: Error, logEvent: DecodedLogEvent<LogCancelContractEventArgs>) => { const callback = (err: Error, logEvent: DecodedLogEvent<LogCancelContractEventArgs>) => {
expect(logEvent.log.event).to.be.equal(ExchangeEvents.LogCancel); expect(logEvent.log.event).to.be.equal(ExchangeEvents.LogCancel);
done(); done();
}; };
zeroEx.exchange.subscribe( zeroEx.exchange.subscribe(ExchangeEvents.LogCancel, indexFilterValues, callback);
ExchangeEvents.LogCancel, indexFilterValues, callback,
);
await zeroEx.exchange.cancelOrderAsync(signedOrder, cancelTakerAmountInBaseUnits); await zeroEx.exchange.cancelOrderAsync(signedOrder, cancelTakerAmountInBaseUnits);
})().catch(done); })().catch(done);
}); });
it('Outstanding subscriptions are cancelled when zeroEx.setProvider called', (done: DoneCallback) => { it('Outstanding subscriptions are cancelled when zeroEx.setProvider called', (done: DoneCallback) => {
(async () => { (async () => {
const callbackNeverToBeCalled = (err: Error, logEvent: DecodedLogEvent<LogFillContractEventArgs>) => { const callbackNeverToBeCalled = (err: Error, logEvent: DecodedLogEvent<LogFillContractEventArgs>) => {
done(new Error('Expected this subscription to have been cancelled')); done(new Error('Expected this subscription to have been cancelled'));
}; };
zeroEx.exchange.subscribe( zeroEx.exchange.subscribe(ExchangeEvents.LogFill, indexFilterValues, callbackNeverToBeCalled);
ExchangeEvents.LogFill, indexFilterValues, callbackNeverToBeCalled,
);
const newProvider = web3Factory.getRpcProvider(); const newProvider = web3Factory.getRpcProvider();
zeroEx.setProvider(newProvider, constants.TESTRPC_NETWORK_ID); zeroEx.setProvider(newProvider, constants.TESTRPC_NETWORK_ID);
@ -698,11 +888,11 @@ describe('ExchangeWrapper', () => {
expect(logEvent.log.event).to.be.equal(ExchangeEvents.LogFill); expect(logEvent.log.event).to.be.equal(ExchangeEvents.LogFill);
done(); done();
}; };
zeroEx.exchange.subscribe( zeroEx.exchange.subscribe(ExchangeEvents.LogFill, indexFilterValues, callback);
ExchangeEvents.LogFill, indexFilterValues, callback,
);
await zeroEx.exchange.fillOrderAsync( await zeroEx.exchange.fillOrderAsync(
signedOrder, takerTokenFillAmountInBaseUnits, shouldThrowOnInsufficientBalanceOrAllowance, signedOrder,
takerTokenFillAmountInBaseUnits,
shouldThrowOnInsufficientBalanceOrAllowance,
takerAddress, takerAddress,
); );
})().catch(done); })().catch(done);
@ -713,11 +903,15 @@ describe('ExchangeWrapper', () => {
done(new Error('Expected this subscription to have been cancelled')); done(new Error('Expected this subscription to have been cancelled'));
}; };
const subscriptionToken = zeroEx.exchange.subscribe( const subscriptionToken = zeroEx.exchange.subscribe(
ExchangeEvents.LogFill, indexFilterValues, callbackNeverToBeCalled, ExchangeEvents.LogFill,
indexFilterValues,
callbackNeverToBeCalled,
); );
zeroEx.exchange.unsubscribe(subscriptionToken); zeroEx.exchange.unsubscribe(subscriptionToken);
await zeroEx.exchange.fillOrderAsync( await zeroEx.exchange.fillOrderAsync(
signedOrder, takerTokenFillAmountInBaseUnits, shouldThrowOnInsufficientBalanceOrAllowance, signedOrder,
takerTokenFillAmountInBaseUnits,
shouldThrowOnInsufficientBalanceOrAllowance,
takerAddress, takerAddress,
); );
done(); done();
@ -736,13 +930,18 @@ describe('ExchangeWrapper', () => {
makerTokenAddress = makerToken.address; makerTokenAddress = makerToken.address;
takerTokenAddress = takerToken.address; takerTokenAddress = takerToken.address;
}); });
it('get\'s the same hash as the local function', async () => { it("get's the same hash as the local function", async () => {
const signedOrder = await fillScenarios.createFillableSignedOrderAsync( const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, makerTokenAddress,
takerTokenAddress,
makerAddress,
takerAddress,
fillableAmount,
); );
const orderHash = ZeroEx.getOrderHashHex(signedOrder); const orderHash = ZeroEx.getOrderHashHex(signedOrder);
const orderHashFromContract = await (zeroEx.exchange as any) const orderHashFromContract = await (zeroEx.exchange as any)._getOrderHashHexUsingContractCallAsync(
._getOrderHashHexUsingContractCallAsync(signedOrder); signedOrder,
);
expect(orderHash).to.equal(orderHashFromContract); expect(orderHash).to.equal(orderHashFromContract);
}); });
}); });
@ -773,10 +972,17 @@ describe('ExchangeWrapper', () => {
}); });
it('should get logs with decoded args emitted by LogFill', async () => { it('should get logs with decoded args emitted by LogFill', async () => {
const signedOrder = await fillScenarios.createFillableSignedOrderAsync( const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, makerTokenAddress,
takerTokenAddress,
makerAddress,
takerAddress,
fillableAmount,
); );
txHash = await zeroEx.exchange.fillOrderAsync( txHash = await zeroEx.exchange.fillOrderAsync(
signedOrder, fillableAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, signedOrder,
fillableAmount,
shouldThrowOnInsufficientBalanceOrAllowance,
takerAddress,
); );
await zeroEx.awaitTransactionMinedAsync(txHash); await zeroEx.awaitTransactionMinedAsync(txHash);
const eventName = ExchangeEvents.LogFill; const eventName = ExchangeEvents.LogFill;
@ -787,10 +993,17 @@ describe('ExchangeWrapper', () => {
}); });
it('should only get the logs with the correct event name', async () => { it('should only get the logs with the correct event name', async () => {
const signedOrder = await fillScenarios.createFillableSignedOrderAsync( const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, makerTokenAddress,
takerTokenAddress,
makerAddress,
takerAddress,
fillableAmount,
); );
txHash = await zeroEx.exchange.fillOrderAsync( txHash = await zeroEx.exchange.fillOrderAsync(
signedOrder, fillableAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, signedOrder,
fillableAmount,
shouldThrowOnInsufficientBalanceOrAllowance,
takerAddress,
); );
await zeroEx.awaitTransactionMinedAsync(txHash); await zeroEx.awaitTransactionMinedAsync(txHash);
const differentEventName = ExchangeEvents.LogCancel; const differentEventName = ExchangeEvents.LogCancel;
@ -800,19 +1013,33 @@ describe('ExchangeWrapper', () => {
}); });
it('should only get the logs with the correct indexed fields', async () => { it('should only get the logs with the correct indexed fields', async () => {
const signedOrder = await fillScenarios.createFillableSignedOrderAsync( const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, makerTokenAddress,
takerTokenAddress,
makerAddress,
takerAddress,
fillableAmount,
); );
txHash = await zeroEx.exchange.fillOrderAsync( txHash = await zeroEx.exchange.fillOrderAsync(
signedOrder, fillableAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, signedOrder,
fillableAmount,
shouldThrowOnInsufficientBalanceOrAllowance,
takerAddress,
); );
await zeroEx.awaitTransactionMinedAsync(txHash); await zeroEx.awaitTransactionMinedAsync(txHash);
const differentMakerAddress = userAddresses[2]; const differentMakerAddress = userAddresses[2];
const anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync( const anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, differentMakerAddress, takerAddress, fillableAmount, makerTokenAddress,
takerTokenAddress,
differentMakerAddress,
takerAddress,
fillableAmount,
); );
txHash = await zeroEx.exchange.fillOrderAsync( txHash = await zeroEx.exchange.fillOrderAsync(
anotherSignedOrder, fillableAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, anotherSignedOrder,
fillableAmount,
shouldThrowOnInsufficientBalanceOrAllowance,
takerAddress,
); );
await zeroEx.awaitTransactionMinedAsync(txHash); await zeroEx.awaitTransactionMinedAsync(txHash);
@ -821,7 +1048,9 @@ describe('ExchangeWrapper', () => {
maker: differentMakerAddress, maker: differentMakerAddress,
}; };
const logs = await zeroEx.exchange.getLogsAsync<LogFillContractEventArgs>( const logs = await zeroEx.exchange.getLogsAsync<LogFillContractEventArgs>(
eventName, blockRange, indexFilterValues, eventName,
blockRange,
indexFilterValues,
); );
expect(logs).to.have.length(1); expect(logs).to.have.length(1);
const args = logs[0].args; const args = logs[0].args;

View File

@ -78,7 +78,11 @@ describe('ExpirationWatcher', () => {
const orderLifetimeSec = 60; const orderLifetimeSec = 60;
const expirationUnixTimestampSec = currentUnixTimestampSec.plus(orderLifetimeSec); const expirationUnixTimestampSec = currentUnixTimestampSec.plus(orderLifetimeSec);
const signedOrder = await fillScenarios.createFillableSignedOrderAsync( const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, makerTokenAddress,
takerTokenAddress,
makerAddress,
takerAddress,
fillableAmount,
expirationUnixTimestampSec, expirationUnixTimestampSec,
); );
const orderHash = ZeroEx.getOrderHashHex(signedOrder); const orderHash = ZeroEx.getOrderHashHex(signedOrder);
@ -92,12 +96,16 @@ describe('ExpirationWatcher', () => {
timer.tick(orderLifetimeSec * 1000); timer.tick(orderLifetimeSec * 1000);
})().catch(done); })().catch(done);
}); });
it('doesn\'t emit events before order expires', (done: DoneCallback) => { it("doesn't emit events before order expires", (done: DoneCallback) => {
(async () => { (async () => {
const orderLifetimeSec = 60; const orderLifetimeSec = 60;
const expirationUnixTimestampSec = currentUnixTimestampSec.plus(orderLifetimeSec); const expirationUnixTimestampSec = currentUnixTimestampSec.plus(orderLifetimeSec);
const signedOrder = await fillScenarios.createFillableSignedOrderAsync( const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, makerTokenAddress,
takerTokenAddress,
makerAddress,
takerAddress,
fillableAmount,
expirationUnixTimestampSec, expirationUnixTimestampSec,
); );
const orderHash = ZeroEx.getOrderHashHex(signedOrder); const orderHash = ZeroEx.getOrderHashHex(signedOrder);
@ -118,11 +126,19 @@ describe('ExpirationWatcher', () => {
const order1ExpirationUnixTimestampSec = currentUnixTimestampSec.plus(order1Lifetime); const order1ExpirationUnixTimestampSec = currentUnixTimestampSec.plus(order1Lifetime);
const order2ExpirationUnixTimestampSec = currentUnixTimestampSec.plus(order2Lifetime); const order2ExpirationUnixTimestampSec = currentUnixTimestampSec.plus(order2Lifetime);
const signedOrder1 = await fillScenarios.createFillableSignedOrderAsync( const signedOrder1 = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, makerTokenAddress,
takerTokenAddress,
makerAddress,
takerAddress,
fillableAmount,
order1ExpirationUnixTimestampSec, order1ExpirationUnixTimestampSec,
); );
const signedOrder2 = await fillScenarios.createFillableSignedOrderAsync( const signedOrder2 = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, makerTokenAddress,
takerTokenAddress,
makerAddress,
takerAddress,
fillableAmount,
order2ExpirationUnixTimestampSec, order2ExpirationUnixTimestampSec,
); );
const orderHash1 = ZeroEx.getOrderHashHex(signedOrder1); const orderHash1 = ZeroEx.getOrderHashHex(signedOrder1);

View File

@ -71,7 +71,11 @@ describe('OrderStateWatcher', () => {
describe('#removeOrder', async () => { describe('#removeOrder', async () => {
it('should successfully remove existing order', async () => { it('should successfully remove existing order', async () => {
signedOrder = await fillScenarios.createFillableSignedOrderAsync( signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerToken.address, takerToken.address, maker, taker, fillableAmount, makerToken.address,
takerToken.address,
maker,
taker,
fillableAmount,
); );
const orderHash = ZeroEx.getOrderHashHex(signedOrder); const orderHash = ZeroEx.getOrderHashHex(signedOrder);
zeroEx.orderStateWatcher.addOrder(signedOrder); zeroEx.orderStateWatcher.addOrder(signedOrder);
@ -89,10 +93,18 @@ describe('OrderStateWatcher', () => {
}); });
it('should no-op when removing a non-existing order', async () => { it('should no-op when removing a non-existing order', async () => {
signedOrder = await fillScenarios.createFillableSignedOrderAsync( signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerToken.address, takerToken.address, maker, taker, fillableAmount, makerToken.address,
takerToken.address,
maker,
taker,
fillableAmount,
); );
const orderHash = ZeroEx.getOrderHashHex(signedOrder); const orderHash = ZeroEx.getOrderHashHex(signedOrder);
const nonExistentOrderHash = `0x${orderHash.substr(2).split('').reverse().join('')}`; const nonExistentOrderHash = `0x${orderHash
.substr(2)
.split('')
.reverse()
.join('')}`;
zeroEx.orderStateWatcher.removeOrder(nonExistentOrderHash); zeroEx.orderStateWatcher.removeOrder(nonExistentOrderHash);
}); });
}); });
@ -102,8 +114,7 @@ describe('OrderStateWatcher', () => {
}); });
it('should fail when trying to subscribe twice', async () => { it('should fail when trying to subscribe twice', async () => {
zeroEx.orderStateWatcher.subscribe(_.noop); zeroEx.orderStateWatcher.subscribe(_.noop);
expect(() => zeroEx.orderStateWatcher.subscribe(_.noop)) expect(() => zeroEx.orderStateWatcher.subscribe(_.noop)).to.throw(ZeroExError.SubscriptionAlreadyPresent);
.to.throw(ZeroExError.SubscriptionAlreadyPresent);
}); });
}); });
describe('tests with cleanup', async () => { describe('tests with cleanup', async () => {
@ -115,7 +126,11 @@ describe('OrderStateWatcher', () => {
it('should emit orderStateInvalid when maker allowance set to 0 for watched order', (done: DoneCallback) => { it('should emit orderStateInvalid when maker allowance set to 0 for watched order', (done: DoneCallback) => {
(async () => { (async () => {
signedOrder = await fillScenarios.createFillableSignedOrderAsync( signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerToken.address, takerToken.address, maker, taker, fillableAmount, makerToken.address,
takerToken.address,
maker,
taker,
fillableAmount,
); );
const orderHash = ZeroEx.getOrderHashHex(signedOrder); const orderHash = ZeroEx.getOrderHashHex(signedOrder);
zeroEx.orderStateWatcher.addOrder(signedOrder); zeroEx.orderStateWatcher.addOrder(signedOrder);
@ -133,7 +148,11 @@ describe('OrderStateWatcher', () => {
it('should not emit an orderState event when irrelevant Transfer event received', (done: DoneCallback) => { it('should not emit an orderState event when irrelevant Transfer event received', (done: DoneCallback) => {
(async () => { (async () => {
signedOrder = await fillScenarios.createFillableSignedOrderAsync( signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerToken.address, takerToken.address, maker, taker, fillableAmount, makerToken.address,
takerToken.address,
maker,
taker,
fillableAmount,
); );
zeroEx.orderStateWatcher.addOrder(signedOrder); zeroEx.orderStateWatcher.addOrder(signedOrder);
const callback = reportCallbackErrors(done)((orderState: OrderState) => { const callback = reportCallbackErrors(done)((orderState: OrderState) => {
@ -152,7 +171,11 @@ describe('OrderStateWatcher', () => {
it('should emit orderStateInvalid when maker moves balance backing watched order', (done: DoneCallback) => { it('should emit orderStateInvalid when maker moves balance backing watched order', (done: DoneCallback) => {
(async () => { (async () => {
signedOrder = await fillScenarios.createFillableSignedOrderAsync( signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerToken.address, takerToken.address, maker, taker, fillableAmount, makerToken.address,
takerToken.address,
maker,
taker,
fillableAmount,
); );
const orderHash = ZeroEx.getOrderHashHex(signedOrder); const orderHash = ZeroEx.getOrderHashHex(signedOrder);
zeroEx.orderStateWatcher.addOrder(signedOrder); zeroEx.orderStateWatcher.addOrder(signedOrder);
@ -172,7 +195,11 @@ describe('OrderStateWatcher', () => {
it('should emit orderStateInvalid when watched order fully filled', (done: DoneCallback) => { it('should emit orderStateInvalid when watched order fully filled', (done: DoneCallback) => {
(async () => { (async () => {
signedOrder = await fillScenarios.createFillableSignedOrderAsync( signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerToken.address, takerToken.address, maker, taker, fillableAmount, makerToken.address,
takerToken.address,
maker,
taker,
fillableAmount,
); );
const orderHash = ZeroEx.getOrderHashHex(signedOrder); const orderHash = ZeroEx.getOrderHashHex(signedOrder);
zeroEx.orderStateWatcher.addOrder(signedOrder); zeroEx.orderStateWatcher.addOrder(signedOrder);
@ -188,14 +215,21 @@ describe('OrderStateWatcher', () => {
const shouldThrowOnInsufficientBalanceOrAllowance = true; const shouldThrowOnInsufficientBalanceOrAllowance = true;
await zeroEx.exchange.fillOrderAsync( await zeroEx.exchange.fillOrderAsync(
signedOrder, fillableAmount, shouldThrowOnInsufficientBalanceOrAllowance, taker, signedOrder,
fillableAmount,
shouldThrowOnInsufficientBalanceOrAllowance,
taker,
); );
})().catch(done); })().catch(done);
}); });
it('should emit orderStateValid when watched order partially filled', (done: DoneCallback) => { it('should emit orderStateValid when watched order partially filled', (done: DoneCallback) => {
(async () => { (async () => {
signedOrder = await fillScenarios.createFillableSignedOrderAsync( signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerToken.address, takerToken.address, maker, taker, fillableAmount, makerToken.address,
takerToken.address,
maker,
taker,
fillableAmount,
); );
const makerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, maker); const makerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, maker);
@ -211,16 +245,21 @@ describe('OrderStateWatcher', () => {
const remainingMakerBalance = makerBalance.sub(fillAmountInBaseUnits); const remainingMakerBalance = makerBalance.sub(fillAmountInBaseUnits);
const remainingFillable = fillableAmount.minus(fillAmountInBaseUnits); const remainingFillable = fillableAmount.minus(fillAmountInBaseUnits);
expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal( expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
remainingFillable); remainingFillable,
);
expect(orderRelevantState.remainingFillableTakerTokenAmount).to.be.bignumber.equal( expect(orderRelevantState.remainingFillableTakerTokenAmount).to.be.bignumber.equal(
remainingFillable); remainingFillable,
);
expect(orderRelevantState.makerBalance).to.be.bignumber.equal(remainingMakerBalance); expect(orderRelevantState.makerBalance).to.be.bignumber.equal(remainingMakerBalance);
done(); done();
}); });
zeroEx.orderStateWatcher.subscribe(callback); zeroEx.orderStateWatcher.subscribe(callback);
const shouldThrowOnInsufficientBalanceOrAllowance = true; const shouldThrowOnInsufficientBalanceOrAllowance = true;
await zeroEx.exchange.fillOrderAsync( await zeroEx.exchange.fillOrderAsync(
signedOrder, fillAmountInBaseUnits, shouldThrowOnInsufficientBalanceOrAllowance, taker, signedOrder,
fillAmountInBaseUnits,
shouldThrowOnInsufficientBalanceOrAllowance,
taker,
); );
})().catch(done); })().catch(done);
}); });
@ -229,8 +268,15 @@ describe('OrderStateWatcher', () => {
const makerFee = ZeroEx.toBaseUnitAmount(new BigNumber(2), 18); const makerFee = ZeroEx.toBaseUnitAmount(new BigNumber(2), 18);
const takerFee = ZeroEx.toBaseUnitAmount(new BigNumber(0), 18); const takerFee = ZeroEx.toBaseUnitAmount(new BigNumber(0), 18);
signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync( signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
makerToken.address, takerToken.address, makerFee, takerFee, maker, taker, fillableAmount, makerToken.address,
taker); takerToken.address,
makerFee,
takerFee,
maker,
taker,
fillableAmount,
taker,
);
const callback = reportCallbackErrors(done)((orderState: OrderState) => { const callback = reportCallbackErrors(done)((orderState: OrderState) => {
done(); done();
}); });
@ -245,7 +291,11 @@ describe('OrderStateWatcher', () => {
const takerFillableAmount = ZeroEx.toBaseUnitAmount(new BigNumber(10), decimals); const takerFillableAmount = ZeroEx.toBaseUnitAmount(new BigNumber(10), decimals);
const makerFillableAmount = ZeroEx.toBaseUnitAmount(new BigNumber(20), decimals); const makerFillableAmount = ZeroEx.toBaseUnitAmount(new BigNumber(20), decimals);
signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync( signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync(
makerToken.address, takerToken.address, maker, taker, makerFillableAmount, makerToken.address,
takerToken.address,
maker,
taker,
makerFillableAmount,
takerFillableAmount, takerFillableAmount,
); );
const fillAmountInBaseUnits = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals); const fillAmountInBaseUnits = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals);
@ -257,22 +307,31 @@ describe('OrderStateWatcher', () => {
expect(validOrderState.orderHash).to.be.equal(orderHash); expect(validOrderState.orderHash).to.be.equal(orderHash);
const orderRelevantState = validOrderState.orderRelevantState; const orderRelevantState = validOrderState.orderRelevantState;
expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal( expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
ZeroEx.toBaseUnitAmount(new BigNumber(16), decimals)); ZeroEx.toBaseUnitAmount(new BigNumber(16), decimals),
);
expect(orderRelevantState.remainingFillableTakerTokenAmount).to.be.bignumber.equal( expect(orderRelevantState.remainingFillableTakerTokenAmount).to.be.bignumber.equal(
ZeroEx.toBaseUnitAmount(new BigNumber(8), decimals)); ZeroEx.toBaseUnitAmount(new BigNumber(8), decimals),
);
done(); done();
}); });
zeroEx.orderStateWatcher.subscribe(callback); zeroEx.orderStateWatcher.subscribe(callback);
const shouldThrowOnInsufficientBalanceOrAllowance = true; const shouldThrowOnInsufficientBalanceOrAllowance = true;
await zeroEx.exchange.fillOrderAsync( await zeroEx.exchange.fillOrderAsync(
signedOrder, fillAmountInBaseUnits, shouldThrowOnInsufficientBalanceOrAllowance, taker, signedOrder,
fillAmountInBaseUnits,
shouldThrowOnInsufficientBalanceOrAllowance,
taker,
); );
})().catch(done); })().catch(done);
}); });
it('should equal approved amount when approved amount is lowest', (done: DoneCallback) => { it('should equal approved amount when approved amount is lowest', (done: DoneCallback) => {
(async () => { (async () => {
signedOrder = await fillScenarios.createFillableSignedOrderAsync( signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerToken.address, takerToken.address, maker, taker, fillableAmount, makerToken.address,
takerToken.address,
maker,
taker,
fillableAmount,
); );
const changedMakerApprovalAmount = ZeroEx.toBaseUnitAmount(new BigNumber(3), decimals); const changedMakerApprovalAmount = ZeroEx.toBaseUnitAmount(new BigNumber(3), decimals);
@ -282,9 +341,11 @@ describe('OrderStateWatcher', () => {
const validOrderState = orderState as OrderStateValid; const validOrderState = orderState as OrderStateValid;
const orderRelevantState = validOrderState.orderRelevantState; const orderRelevantState = validOrderState.orderRelevantState;
expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal( expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
changedMakerApprovalAmount); changedMakerApprovalAmount,
);
expect(orderRelevantState.remainingFillableTakerTokenAmount).to.be.bignumber.equal( expect(orderRelevantState.remainingFillableTakerTokenAmount).to.be.bignumber.equal(
changedMakerApprovalAmount); changedMakerApprovalAmount,
);
done(); done();
}); });
zeroEx.orderStateWatcher.subscribe(callback); zeroEx.orderStateWatcher.subscribe(callback);
@ -294,7 +355,11 @@ describe('OrderStateWatcher', () => {
it('should equal balance amount when balance amount is lowest', (done: DoneCallback) => { it('should equal balance amount when balance amount is lowest', (done: DoneCallback) => {
(async () => { (async () => {
signedOrder = await fillScenarios.createFillableSignedOrderAsync( signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerToken.address, takerToken.address, maker, taker, fillableAmount, makerToken.address,
takerToken.address,
maker,
taker,
fillableAmount,
); );
const makerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, maker); const makerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, maker);
@ -308,14 +373,15 @@ describe('OrderStateWatcher', () => {
const validOrderState = orderState as OrderStateValid; const validOrderState = orderState as OrderStateValid;
const orderRelevantState = validOrderState.orderRelevantState; const orderRelevantState = validOrderState.orderRelevantState;
expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal( expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
remainingAmount); remainingAmount,
);
expect(orderRelevantState.remainingFillableTakerTokenAmount).to.be.bignumber.equal( expect(orderRelevantState.remainingFillableTakerTokenAmount).to.be.bignumber.equal(
remainingAmount); remainingAmount,
);
done(); done();
}); });
zeroEx.orderStateWatcher.subscribe(callback); zeroEx.orderStateWatcher.subscribe(callback);
await zeroEx.token.transferAsync( await zeroEx.token.transferAsync(makerToken.address, maker, ZeroEx.NULL_ADDRESS, transferAmount);
makerToken.address, maker, ZeroEx.NULL_ADDRESS, transferAmount);
})().catch(done); })().catch(done);
}); });
it('should equal remaining amount when partially cancelled and order has fees', (done: DoneCallback) => { it('should equal remaining amount when partially cancelled and order has fees', (done: DoneCallback) => {
@ -324,8 +390,15 @@ describe('OrderStateWatcher', () => {
const makerFee = ZeroEx.toBaseUnitAmount(new BigNumber(5), decimals); const makerFee = ZeroEx.toBaseUnitAmount(new BigNumber(5), decimals);
const feeRecipient = taker; const feeRecipient = taker;
signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync( signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
makerToken.address, takerToken.address, makerFee, takerFee, maker, makerToken.address,
taker, fillableAmount, feeRecipient); takerToken.address,
makerFee,
takerFee,
maker,
taker,
fillableAmount,
feeRecipient,
);
const remainingTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(4), decimals); const remainingTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(4), decimals);
const transferTokenAmount = makerFee.sub(remainingTokenAmount); const transferTokenAmount = makerFee.sub(remainingTokenAmount);
@ -336,7 +409,8 @@ describe('OrderStateWatcher', () => {
const validOrderState = orderState as OrderStateValid; const validOrderState = orderState as OrderStateValid;
const orderRelevantState = validOrderState.orderRelevantState; const orderRelevantState = validOrderState.orderRelevantState;
expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal( expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
remainingTokenAmount); remainingTokenAmount,
);
done(); done();
}); });
zeroEx.orderStateWatcher.subscribe(callback); zeroEx.orderStateWatcher.subscribe(callback);
@ -349,8 +423,15 @@ describe('OrderStateWatcher', () => {
const makerFee = ZeroEx.toBaseUnitAmount(new BigNumber(5), decimals); const makerFee = ZeroEx.toBaseUnitAmount(new BigNumber(5), decimals);
const feeRecipient = taker; const feeRecipient = taker;
signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync( signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
makerToken.address, takerToken.address, makerFee, takerFee, maker, makerToken.address,
taker, fillableAmount, feeRecipient); takerToken.address,
makerFee,
takerFee,
maker,
taker,
fillableAmount,
feeRecipient,
);
const remainingFeeAmount = ZeroEx.toBaseUnitAmount(new BigNumber(3), decimals); const remainingFeeAmount = ZeroEx.toBaseUnitAmount(new BigNumber(3), decimals);
@ -362,13 +443,18 @@ describe('OrderStateWatcher', () => {
const validOrderState = orderState as OrderStateValid; const validOrderState = orderState as OrderStateValid;
const orderRelevantState = validOrderState.orderRelevantState; const orderRelevantState = validOrderState.orderRelevantState;
expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal( expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
remainingFeeAmount); remainingFeeAmount,
);
done(); done();
}); });
zeroEx.orderStateWatcher.subscribe(callback); zeroEx.orderStateWatcher.subscribe(callback);
await zeroEx.token.setProxyAllowanceAsync(zrxTokenAddress, maker, remainingFeeAmount); await zeroEx.token.setProxyAllowanceAsync(zrxTokenAddress, maker, remainingFeeAmount);
await zeroEx.token.transferAsync( await zeroEx.token.transferAsync(
makerToken.address, maker, ZeroEx.NULL_ADDRESS, transferTokenAmount); makerToken.address,
maker,
ZeroEx.NULL_ADDRESS,
transferTokenAmount,
);
})().catch(done); })().catch(done);
}); });
it('should calculate full amount when all available and non-divisible', (done: DoneCallback) => { it('should calculate full amount when all available and non-divisible', (done: DoneCallback) => {
@ -377,8 +463,15 @@ describe('OrderStateWatcher', () => {
const makerFee = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals); const makerFee = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals);
const feeRecipient = taker; const feeRecipient = taker;
signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync( signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
makerToken.address, takerToken.address, makerFee, takerFee, maker, makerToken.address,
taker, fillableAmount, feeRecipient); takerToken.address,
makerFee,
takerFee,
maker,
taker,
fillableAmount,
feeRecipient,
);
zeroEx.orderStateWatcher.addOrder(signedOrder); zeroEx.orderStateWatcher.addOrder(signedOrder);
@ -386,19 +479,27 @@ describe('OrderStateWatcher', () => {
const validOrderState = orderState as OrderStateValid; const validOrderState = orderState as OrderStateValid;
const orderRelevantState = validOrderState.orderRelevantState; const orderRelevantState = validOrderState.orderRelevantState;
expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal( expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal(
fillableAmount); fillableAmount,
);
done(); done();
}); });
zeroEx.orderStateWatcher.subscribe(callback); zeroEx.orderStateWatcher.subscribe(callback);
await zeroEx.token.setProxyAllowanceAsync( await zeroEx.token.setProxyAllowanceAsync(
makerToken.address, maker, ZeroEx.toBaseUnitAmount(new BigNumber(100), decimals)); makerToken.address,
maker,
ZeroEx.toBaseUnitAmount(new BigNumber(100), decimals),
);
})().catch(done); })().catch(done);
}); });
}); });
it('should emit orderStateInvalid when watched order cancelled', (done: DoneCallback) => { it('should emit orderStateInvalid when watched order cancelled', (done: DoneCallback) => {
(async () => { (async () => {
signedOrder = await fillScenarios.createFillableSignedOrderAsync( signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerToken.address, takerToken.address, maker, taker, fillableAmount, makerToken.address,
takerToken.address,
maker,
taker,
fillableAmount,
); );
const orderHash = ZeroEx.getOrderHashHex(signedOrder); const orderHash = ZeroEx.getOrderHashHex(signedOrder);
zeroEx.orderStateWatcher.addOrder(signedOrder); zeroEx.orderStateWatcher.addOrder(signedOrder);
@ -419,7 +520,11 @@ describe('OrderStateWatcher', () => {
(async () => { (async () => {
const remainingFillableAmountInBaseUnits = new BigNumber(100); const remainingFillableAmountInBaseUnits = new BigNumber(100);
signedOrder = await fillScenarios.createFillableSignedOrderAsync( signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerToken.address, takerToken.address, maker, taker, fillableAmount, makerToken.address,
takerToken.address,
maker,
taker,
fillableAmount,
); );
const orderHash = ZeroEx.getOrderHashHex(signedOrder); const orderHash = ZeroEx.getOrderHashHex(signedOrder);
zeroEx.orderStateWatcher.addOrder(signedOrder); zeroEx.orderStateWatcher.addOrder(signedOrder);
@ -433,14 +538,19 @@ describe('OrderStateWatcher', () => {
}); });
zeroEx.orderStateWatcher.subscribe(callback); zeroEx.orderStateWatcher.subscribe(callback);
await zeroEx.exchange.cancelOrderAsync( await zeroEx.exchange.cancelOrderAsync(
signedOrder, fillableAmount.minus(remainingFillableAmountInBaseUnits), signedOrder,
fillableAmount.minus(remainingFillableAmountInBaseUnits),
); );
})().catch(done); })().catch(done);
}); });
it('should emit orderStateValid when watched order partially cancelled', (done: DoneCallback) => { it('should emit orderStateValid when watched order partially cancelled', (done: DoneCallback) => {
(async () => { (async () => {
signedOrder = await fillScenarios.createFillableSignedOrderAsync( signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerToken.address, takerToken.address, maker, taker, fillableAmount, makerToken.address,
takerToken.address,
maker,
taker,
fillableAmount,
); );
const cancelAmountInBaseUnits = new BigNumber(2); const cancelAmountInBaseUnits = new BigNumber(2);

View File

@ -62,108 +62,152 @@ describe('OrderValidation', () => {
describe('validateOrderFillableOrThrowAsync', () => { describe('validateOrderFillableOrThrowAsync', () => {
it('should succeed if the order is fillable', async () => { it('should succeed if the order is fillable', async () => {
const signedOrder = await fillScenarios.createFillableSignedOrderAsync( const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, makerTokenAddress,
); takerTokenAddress,
await zeroEx.exchange.validateOrderFillableOrThrowAsync( makerAddress,
signedOrder, takerAddress,
fillableAmount,
); );
await zeroEx.exchange.validateOrderFillableOrThrowAsync(signedOrder);
}); });
it('should succeed if the order is asymmetric and fillable', async () => { it('should succeed if the order is asymmetric and fillable', async () => {
const makerFillableAmount = fillableAmount; const makerFillableAmount = fillableAmount;
const takerFillableAmount = fillableAmount.minus(4); const takerFillableAmount = fillableAmount.minus(4);
const signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync( const signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, makerTokenAddress,
makerFillableAmount, takerFillableAmount, takerTokenAddress,
); makerAddress,
await zeroEx.exchange.validateOrderFillableOrThrowAsync( takerAddress,
signedOrder, makerFillableAmount,
takerFillableAmount,
); );
await zeroEx.exchange.validateOrderFillableOrThrowAsync(signedOrder);
}); });
it('should throw when the order is fully filled or cancelled', async () => { it('should throw when the order is fully filled or cancelled', async () => {
const signedOrder = await fillScenarios.createFillableSignedOrderAsync( const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, makerTokenAddress,
takerTokenAddress,
makerAddress,
takerAddress,
fillableAmount,
); );
await zeroEx.exchange.cancelOrderAsync(signedOrder, fillableAmount); await zeroEx.exchange.cancelOrderAsync(signedOrder, fillableAmount);
return expect(zeroEx.exchange.validateOrderFillableOrThrowAsync( return expect(zeroEx.exchange.validateOrderFillableOrThrowAsync(signedOrder)).to.be.rejectedWith(
signedOrder, ExchangeContractErrs.OrderRemainingFillAmountZero,
)).to.be.rejectedWith(ExchangeContractErrs.OrderRemainingFillAmountZero); );
}); });
it('should throw when order is expired', async () => { it('should throw when order is expired', async () => {
const expirationInPast = new BigNumber(1496826058); // 7th Jun 2017 const expirationInPast = new BigNumber(1496826058); // 7th Jun 2017
const signedOrder = await fillScenarios.createFillableSignedOrderAsync( const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, makerTokenAddress,
fillableAmount, expirationInPast, takerTokenAddress,
makerAddress,
takerAddress,
fillableAmount,
expirationInPast,
);
return expect(zeroEx.exchange.validateOrderFillableOrThrowAsync(signedOrder)).to.be.rejectedWith(
ExchangeContractErrs.OrderFillExpired,
); );
return expect(zeroEx.exchange.validateOrderFillableOrThrowAsync(
signedOrder,
)).to.be.rejectedWith(ExchangeContractErrs.OrderFillExpired);
}); });
}); });
describe('validateFillOrderAndThrowIfInvalidAsync', () => { describe('validateFillOrderAndThrowIfInvalidAsync', () => {
it('should throw when the fill amount is zero', async () => { it('should throw when the fill amount is zero', async () => {
const signedOrder = await fillScenarios.createFillableSignedOrderAsync( const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, makerTokenAddress,
takerTokenAddress,
makerAddress,
takerAddress,
fillableAmount,
); );
const zeroFillAmount = new BigNumber(0); const zeroFillAmount = new BigNumber(0);
return expect(zeroEx.exchange.validateFillOrderThrowIfInvalidAsync( return expect(
signedOrder, zeroFillAmount, takerAddress, zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(signedOrder, zeroFillAmount, takerAddress),
)).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
}); });
it('should throw when the signature is invalid', async () => { it('should throw when the signature is invalid', async () => {
const signedOrder = await fillScenarios.createFillableSignedOrderAsync( const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, makerTokenAddress,
takerTokenAddress,
makerAddress,
takerAddress,
fillableAmount,
); );
// 27 <--> 28 // 27 <--> 28
signedOrder.ecSignature.v = (28 - signedOrder.ecSignature.v) + 27; signedOrder.ecSignature.v = 28 - signedOrder.ecSignature.v + 27;
return expect(zeroEx.exchange.validateFillOrderThrowIfInvalidAsync( return expect(
signedOrder, fillableAmount, takerAddress, zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(signedOrder, fillableAmount, takerAddress),
)).to.be.rejectedWith(ZeroExError.InvalidSignature); ).to.be.rejectedWith(ZeroExError.InvalidSignature);
}); });
it('should throw when the order is fully filled or cancelled', async () => { it('should throw when the order is fully filled or cancelled', async () => {
const signedOrder = await fillScenarios.createFillableSignedOrderAsync( const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, makerTokenAddress,
takerTokenAddress,
makerAddress,
takerAddress,
fillableAmount,
); );
await zeroEx.exchange.cancelOrderAsync(signedOrder, fillableAmount); await zeroEx.exchange.cancelOrderAsync(signedOrder, fillableAmount);
return expect(zeroEx.exchange.validateFillOrderThrowIfInvalidAsync( return expect(
signedOrder, fillableAmount, takerAddress, zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(signedOrder, fillableAmount, takerAddress),
)).to.be.rejectedWith(ExchangeContractErrs.OrderRemainingFillAmountZero); ).to.be.rejectedWith(ExchangeContractErrs.OrderRemainingFillAmountZero);
}); });
it('should throw when sender is not a taker', async () => { it('should throw when sender is not a taker', async () => {
const signedOrder = await fillScenarios.createFillableSignedOrderAsync( const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, makerTokenAddress,
takerTokenAddress,
makerAddress,
takerAddress,
fillableAmount,
); );
const nonTakerAddress = userAddresses[6]; const nonTakerAddress = userAddresses[6];
return expect(zeroEx.exchange.validateFillOrderThrowIfInvalidAsync( return expect(
signedOrder, fillTakerAmount, nonTakerAddress, zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(signedOrder, fillTakerAmount, nonTakerAddress),
)).to.be.rejectedWith(ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker); ).to.be.rejectedWith(ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker);
}); });
it('should throw when order is expired', async () => { it('should throw when order is expired', async () => {
const expirationInPast = new BigNumber(1496826058); // 7th Jun 2017 const expirationInPast = new BigNumber(1496826058); // 7th Jun 2017
const signedOrder = await fillScenarios.createFillableSignedOrderAsync( const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, makerTokenAddress,
fillableAmount, expirationInPast, takerTokenAddress,
makerAddress,
takerAddress,
fillableAmount,
expirationInPast,
); );
return expect(zeroEx.exchange.validateFillOrderThrowIfInvalidAsync( return expect(
signedOrder, fillTakerAmount, takerAddress, zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(signedOrder, fillTakerAmount, takerAddress),
)).to.be.rejectedWith(ExchangeContractErrs.OrderFillExpired); ).to.be.rejectedWith(ExchangeContractErrs.OrderFillExpired);
}); });
it('should throw when there a rounding error would have occurred', async () => { it('should throw when there a rounding error would have occurred', async () => {
const makerAmount = new BigNumber(3); const makerAmount = new BigNumber(3);
const takerAmount = new BigNumber(5); const takerAmount = new BigNumber(5);
const signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync( const signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, makerTokenAddress,
makerAmount, takerAmount, takerTokenAddress,
makerAddress,
takerAddress,
makerAmount,
takerAmount,
); );
const fillTakerAmountThatCausesRoundingError = new BigNumber(3); const fillTakerAmountThatCausesRoundingError = new BigNumber(3);
return expect(zeroEx.exchange.validateFillOrderThrowIfInvalidAsync( return expect(
signedOrder, fillTakerAmountThatCausesRoundingError, takerAddress, zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(
)).to.be.rejectedWith(ExchangeContractErrs.OrderFillRoundingError); signedOrder,
fillTakerAmountThatCausesRoundingError,
takerAddress,
),
).to.be.rejectedWith(ExchangeContractErrs.OrderFillRoundingError);
}); });
}); });
describe('#validateFillOrKillOrderAndThrowIfInvalidAsync', () => { describe('#validateFillOrKillOrderAndThrowIfInvalidAsync', () => {
it('should throw if remaining fillAmount is less then the desired fillAmount', async () => { it('should throw if remaining fillAmount is less then the desired fillAmount', async () => {
const signedOrder = await fillScenarios.createFillableSignedOrderAsync( const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, makerTokenAddress,
takerTokenAddress,
makerAddress,
takerAddress,
fillableAmount,
); );
const tooLargeFillAmount = new BigNumber(7); const tooLargeFillAmount = new BigNumber(7);
const fillAmountDifference = tooLargeFillAmount.minus(fillableAmount); const fillAmountDifference = tooLargeFillAmount.minus(fillableAmount);
@ -172,9 +216,13 @@ describe('OrderValidation', () => {
await zeroEx.token.transferAsync(makerTokenAddress, coinbase, makerAddress, fillAmountDifference); await zeroEx.token.transferAsync(makerTokenAddress, coinbase, makerAddress, fillAmountDifference);
await zeroEx.token.setProxyAllowanceAsync(makerTokenAddress, makerAddress, tooLargeFillAmount); await zeroEx.token.setProxyAllowanceAsync(makerTokenAddress, makerAddress, tooLargeFillAmount);
return expect(zeroEx.exchange.validateFillOrKillOrderThrowIfInvalidAsync( return expect(
signedOrder, tooLargeFillAmount, takerAddress, zeroEx.exchange.validateFillOrKillOrderThrowIfInvalidAsync(
)).to.be.rejectedWith(ExchangeContractErrs.InsufficientRemainingFillAmount); signedOrder,
tooLargeFillAmount,
takerAddress,
),
).to.be.rejectedWith(ExchangeContractErrs.InsufficientRemainingFillAmount);
}); });
}); });
describe('validateCancelOrderAndThrowIfInvalidAsync', () => { describe('validateCancelOrderAndThrowIfInvalidAsync', () => {
@ -186,27 +234,38 @@ describe('OrderValidation', () => {
makerTokenAddress = makerToken.address; makerTokenAddress = makerToken.address;
takerTokenAddress = takerToken.address; takerTokenAddress = takerToken.address;
signedOrder = await fillScenarios.createFillableSignedOrderAsync( signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, makerTokenAddress,
takerTokenAddress,
makerAddress,
takerAddress,
fillableAmount,
); );
}); });
it('should throw when cancel amount is zero', async () => { it('should throw when cancel amount is zero', async () => {
const zeroCancelAmount = new BigNumber(0); const zeroCancelAmount = new BigNumber(0);
return expect(zeroEx.exchange.validateCancelOrderThrowIfInvalidAsync(signedOrder, zeroCancelAmount)) return expect(
.to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero); zeroEx.exchange.validateCancelOrderThrowIfInvalidAsync(signedOrder, zeroCancelAmount),
).to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero);
}); });
it('should throw when order is expired', async () => { it('should throw when order is expired', async () => {
const expirationInPast = new BigNumber(1496826058); // 7th Jun 2017 const expirationInPast = new BigNumber(1496826058); // 7th Jun 2017
const expiredSignedOrder = await fillScenarios.createFillableSignedOrderAsync( const expiredSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, makerTokenAddress,
fillableAmount, expirationInPast, takerTokenAddress,
makerAddress,
takerAddress,
fillableAmount,
expirationInPast,
); );
return expect(zeroEx.exchange.validateCancelOrderThrowIfInvalidAsync(expiredSignedOrder, cancelAmount)) return expect(
.to.be.rejectedWith(ExchangeContractErrs.OrderCancelExpired); zeroEx.exchange.validateCancelOrderThrowIfInvalidAsync(expiredSignedOrder, cancelAmount),
).to.be.rejectedWith(ExchangeContractErrs.OrderCancelExpired);
}); });
it('should throw when order is already cancelled or filled', async () => { it('should throw when order is already cancelled or filled', async () => {
await zeroEx.exchange.cancelOrderAsync(signedOrder, fillableAmount); await zeroEx.exchange.cancelOrderAsync(signedOrder, fillableAmount);
return expect(zeroEx.exchange.validateCancelOrderThrowIfInvalidAsync(signedOrder, fillableAmount)) return expect(
.to.be.rejectedWith(ExchangeContractErrs.OrderAlreadyCancelledOrFilled); zeroEx.exchange.validateCancelOrderThrowIfInvalidAsync(signedOrder, fillableAmount),
).to.be.rejectedWith(ExchangeContractErrs.OrderAlreadyCancelledOrFilled);
}); });
}); });
describe('#validateFillOrderBalancesAllowancesThrowIfInvalidAsync', () => { describe('#validateFillOrderBalancesAllowancesThrowIfInvalidAsync', () => {
@ -224,35 +283,69 @@ describe('OrderValidation', () => {
const makerFee = new BigNumber(2); const makerFee = new BigNumber(2);
const takerFee = new BigNumber(2); const takerFee = new BigNumber(2);
const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync( const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
makerTokenAddress, takerTokenAddress, makerFee, takerFee, makerTokenAddress,
makerAddress, takerAddress, fillableAmount, feeRecipient, takerTokenAddress,
makerFee,
takerFee,
makerAddress,
takerAddress,
fillableAmount,
feeRecipient,
); );
await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync( await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
exchangeTransferSimulator, signedOrder, fillableAmount, takerAddress, zrxTokenAddress, exchangeTransferSimulator,
signedOrder,
fillableAmount,
takerAddress,
zrxTokenAddress,
); );
expect(transferFromAsync.callCount).to.be.equal(4); expect(transferFromAsync.callCount).to.be.equal(4);
expect( expect(
transferFromAsync.getCall(0).calledWith( transferFromAsync
makerTokenAddress, makerAddress, takerAddress, bigNumberMatch(fillableAmount), .getCall(0)
TradeSide.Maker, TransferType.Trade, .calledWith(
makerTokenAddress,
makerAddress,
takerAddress,
bigNumberMatch(fillableAmount),
TradeSide.Maker,
TransferType.Trade,
), ),
).to.be.true(); ).to.be.true();
expect( expect(
transferFromAsync.getCall(1).calledWith( transferFromAsync
takerTokenAddress, takerAddress, makerAddress, bigNumberMatch(fillableAmount), .getCall(1)
TradeSide.Taker, TransferType.Trade, .calledWith(
takerTokenAddress,
takerAddress,
makerAddress,
bigNumberMatch(fillableAmount),
TradeSide.Taker,
TransferType.Trade,
), ),
).to.be.true(); ).to.be.true();
expect( expect(
transferFromAsync.getCall(2).calledWith( transferFromAsync
zrxTokenAddress, makerAddress, feeRecipient, bigNumberMatch(makerFee), .getCall(2)
TradeSide.Maker, TransferType.Fee, .calledWith(
zrxTokenAddress,
makerAddress,
feeRecipient,
bigNumberMatch(makerFee),
TradeSide.Maker,
TransferType.Fee,
), ),
).to.be.true(); ).to.be.true();
expect( expect(
transferFromAsync.getCall(3).calledWith( transferFromAsync
zrxTokenAddress, takerAddress, feeRecipient, bigNumberMatch(takerFee), .getCall(3)
TradeSide.Taker, TransferType.Fee, .calledWith(
zrxTokenAddress,
takerAddress,
feeRecipient,
bigNumberMatch(takerFee),
TradeSide.Taker,
TransferType.Fee,
), ),
).to.be.true(); ).to.be.true();
}); });
@ -260,35 +353,69 @@ describe('OrderValidation', () => {
const makerFee = new BigNumber(2); const makerFee = new BigNumber(2);
const takerFee = new BigNumber(2); const takerFee = new BigNumber(2);
const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync( const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
makerTokenAddress, takerTokenAddress, makerFee, takerFee, makerTokenAddress,
makerAddress, ZeroEx.NULL_ADDRESS, fillableAmount, feeRecipient, takerTokenAddress,
makerFee,
takerFee,
makerAddress,
ZeroEx.NULL_ADDRESS,
fillableAmount,
feeRecipient,
); );
await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync( await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
exchangeTransferSimulator, signedOrder, fillableAmount, takerAddress, zrxTokenAddress, exchangeTransferSimulator,
signedOrder,
fillableAmount,
takerAddress,
zrxTokenAddress,
); );
expect(transferFromAsync.callCount).to.be.equal(4); expect(transferFromAsync.callCount).to.be.equal(4);
expect( expect(
transferFromAsync.getCall(0).calledWith( transferFromAsync
makerTokenAddress, makerAddress, takerAddress, bigNumberMatch(fillableAmount), .getCall(0)
TradeSide.Maker, TransferType.Trade, .calledWith(
makerTokenAddress,
makerAddress,
takerAddress,
bigNumberMatch(fillableAmount),
TradeSide.Maker,
TransferType.Trade,
), ),
).to.be.true(); ).to.be.true();
expect( expect(
transferFromAsync.getCall(1).calledWith( transferFromAsync
takerTokenAddress, takerAddress, makerAddress, bigNumberMatch(fillableAmount), .getCall(1)
TradeSide.Taker, TransferType.Trade, .calledWith(
takerTokenAddress,
takerAddress,
makerAddress,
bigNumberMatch(fillableAmount),
TradeSide.Taker,
TransferType.Trade,
), ),
).to.be.true(); ).to.be.true();
expect( expect(
transferFromAsync.getCall(2).calledWith( transferFromAsync
zrxTokenAddress, makerAddress, feeRecipient, bigNumberMatch(makerFee), .getCall(2)
TradeSide.Maker, TransferType.Fee, .calledWith(
zrxTokenAddress,
makerAddress,
feeRecipient,
bigNumberMatch(makerFee),
TradeSide.Maker,
TransferType.Fee,
), ),
).to.be.true(); ).to.be.true();
expect( expect(
transferFromAsync.getCall(3).calledWith( transferFromAsync
zrxTokenAddress, takerAddress, feeRecipient, bigNumberMatch(takerFee), .getCall(3)
TradeSide.Taker, TransferType.Fee, .calledWith(
zrxTokenAddress,
takerAddress,
feeRecipient,
bigNumberMatch(takerFee),
TradeSide.Taker,
TransferType.Fee,
), ),
).to.be.true(); ).to.be.true();
}); });
@ -296,10 +423,19 @@ describe('OrderValidation', () => {
const makerTokenAmount = new BigNumber(3); const makerTokenAmount = new BigNumber(3);
const takerTokenAmount = new BigNumber(1); const takerTokenAmount = new BigNumber(1);
const signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync( const signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, makerTokenAmount, takerTokenAmount, makerTokenAddress,
takerTokenAddress,
makerAddress,
takerAddress,
makerTokenAmount,
takerTokenAmount,
); );
await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync( await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
exchangeTransferSimulator, signedOrder, takerTokenAmount, takerAddress, zrxTokenAddress, exchangeTransferSimulator,
signedOrder,
takerTokenAmount,
takerAddress,
zrxTokenAddress,
); );
expect(transferFromAsync.callCount).to.be.equal(4); expect(transferFromAsync.callCount).to.be.equal(4);
const makerFillAmount = transferFromAsync.getCall(0).args[3]; const makerFillAmount = transferFromAsync.getCall(0).args[3];
@ -309,12 +445,22 @@ describe('OrderValidation', () => {
const makerFee = new BigNumber(2); const makerFee = new BigNumber(2);
const takerFee = new BigNumber(4); const takerFee = new BigNumber(4);
const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync( const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
makerTokenAddress, takerTokenAddress, makerFee, takerFee, makerAddress, takerAddress, makerTokenAddress,
fillableAmount, ZeroEx.NULL_ADDRESS, takerTokenAddress,
makerFee,
takerFee,
makerAddress,
takerAddress,
fillableAmount,
ZeroEx.NULL_ADDRESS,
); );
const fillTakerTokenAmount = fillableAmount.div(2).round(0); const fillTakerTokenAmount = fillableAmount.div(2).round(0);
await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync( await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
exchangeTransferSimulator, signedOrder, fillTakerTokenAmount, takerAddress, zrxTokenAddress, exchangeTransferSimulator,
signedOrder,
fillTakerTokenAmount,
takerAddress,
zrxTokenAddress,
); );
const makerPartialFee = makerFee.div(2); const makerPartialFee = makerFee.div(2);
const takerPartialFee = takerFee.div(2); const takerPartialFee = takerFee.div(2);

View File

@ -28,15 +28,19 @@ describe('RemainingFillableCalculator', () => {
const zeroAddress = '0x0'; const zeroAddress = '0x0';
const signature: ECSignature = { v: 27, r: '', s: '' }; const signature: ECSignature = { v: 27, r: '', s: '' };
beforeEach(async () => { beforeEach(async () => {
[makerAmount, takerAmount, makerFeeAmount] = [ZeroEx.toBaseUnitAmount(new BigNumber(50), decimals), [makerAmount, takerAmount, makerFeeAmount] = [
ZeroEx.toBaseUnitAmount(new BigNumber(50), decimals),
ZeroEx.toBaseUnitAmount(new BigNumber(5), decimals), ZeroEx.toBaseUnitAmount(new BigNumber(5), decimals),
ZeroEx.toBaseUnitAmount(new BigNumber(1), decimals)]; ZeroEx.toBaseUnitAmount(new BigNumber(1), decimals),
];
[transferrableMakerTokenAmount, transferrableMakerFeeTokenAmount] = [ [transferrableMakerTokenAmount, transferrableMakerFeeTokenAmount] = [
ZeroEx.toBaseUnitAmount(new BigNumber(50), decimals), ZeroEx.toBaseUnitAmount(new BigNumber(50), decimals),
ZeroEx.toBaseUnitAmount(new BigNumber(5), decimals)]; ZeroEx.toBaseUnitAmount(new BigNumber(5), decimals),
];
}); });
function buildSignedOrder(): SignedOrder { function buildSignedOrder(): SignedOrder {
return { ecSignature: signature, return {
ecSignature: signature,
exchangeContractAddress: zeroAddress, exchangeContractAddress: zeroAddress,
feeRecipient: zeroAddress, feeRecipient: zeroAddress,
maker: zeroAddress, maker: zeroAddress,
@ -48,7 +52,8 @@ describe('RemainingFillableCalculator', () => {
makerTokenAddress: makerToken, makerTokenAddress: makerToken,
takerTokenAddress: takerToken, takerTokenAddress: takerToken,
salt: zero, salt: zero,
expirationUnixTimestampSec: zero }; expirationUnixTimestampSec: zero,
};
} }
describe('Maker token is NOT ZRX', () => { describe('Maker token is NOT ZRX', () => {
before(async () => { before(async () => {
@ -57,23 +62,38 @@ describe('RemainingFillableCalculator', () => {
it('calculates the correct amount when unfilled and funds available', () => { it('calculates the correct amount when unfilled and funds available', () => {
signedOrder = buildSignedOrder(); signedOrder = buildSignedOrder();
remainingMakerTokenAmount = signedOrder.makerTokenAmount; remainingMakerTokenAmount = signedOrder.makerTokenAmount;
calculator = new RemainingFillableCalculator(signedOrder, isMakerTokenZRX, calculator = new RemainingFillableCalculator(
transferrableMakerTokenAmount, transferrableMakerFeeTokenAmount, remainingMakerTokenAmount); signedOrder,
isMakerTokenZRX,
transferrableMakerTokenAmount,
transferrableMakerFeeTokenAmount,
remainingMakerTokenAmount,
);
expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(remainingMakerTokenAmount); expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(remainingMakerTokenAmount);
}); });
it('calculates the correct amount when partially filled and funds available', () => { it('calculates the correct amount when partially filled and funds available', () => {
signedOrder = buildSignedOrder(); signedOrder = buildSignedOrder();
remainingMakerTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(1), decimals); remainingMakerTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(1), decimals);
calculator = new RemainingFillableCalculator(signedOrder, isMakerTokenZRX, calculator = new RemainingFillableCalculator(
transferrableMakerTokenAmount, transferrableMakerFeeTokenAmount, remainingMakerTokenAmount); signedOrder,
isMakerTokenZRX,
transferrableMakerTokenAmount,
transferrableMakerFeeTokenAmount,
remainingMakerTokenAmount,
);
expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(remainingMakerTokenAmount); expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(remainingMakerTokenAmount);
}); });
it('calculates the amount to be 0 when all fee funds are transferred', () => { it('calculates the amount to be 0 when all fee funds are transferred', () => {
signedOrder = buildSignedOrder(); signedOrder = buildSignedOrder();
transferrableMakerFeeTokenAmount = zero; transferrableMakerFeeTokenAmount = zero;
remainingMakerTokenAmount = signedOrder.makerTokenAmount; remainingMakerTokenAmount = signedOrder.makerTokenAmount;
calculator = new RemainingFillableCalculator(signedOrder, isMakerTokenZRX, calculator = new RemainingFillableCalculator(
transferrableMakerTokenAmount, transferrableMakerFeeTokenAmount, remainingMakerTokenAmount); signedOrder,
isMakerTokenZRX,
transferrableMakerTokenAmount,
transferrableMakerFeeTokenAmount,
remainingMakerTokenAmount,
);
expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(zero); expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(zero);
}); });
it('calculates the correct amount when balance is less than remaining fillable', () => { it('calculates the correct amount when balance is less than remaining fillable', () => {
@ -81,41 +101,58 @@ describe('RemainingFillableCalculator', () => {
const partiallyFilledAmount = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals); const partiallyFilledAmount = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals);
remainingMakerTokenAmount = signedOrder.makerTokenAmount.minus(partiallyFilledAmount); remainingMakerTokenAmount = signedOrder.makerTokenAmount.minus(partiallyFilledAmount);
transferrableMakerTokenAmount = remainingMakerTokenAmount.minus(partiallyFilledAmount); transferrableMakerTokenAmount = remainingMakerTokenAmount.minus(partiallyFilledAmount);
calculator = new RemainingFillableCalculator(signedOrder, isMakerTokenZRX, calculator = new RemainingFillableCalculator(
transferrableMakerTokenAmount, transferrableMakerFeeTokenAmount, remainingMakerTokenAmount); signedOrder,
isMakerTokenZRX,
transferrableMakerTokenAmount,
transferrableMakerFeeTokenAmount,
remainingMakerTokenAmount,
);
expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(transferrableMakerTokenAmount); expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(transferrableMakerTokenAmount);
}); });
describe('Order to Fee Ratio is < 1', () => { describe('Order to Fee Ratio is < 1', () => {
beforeEach(async () => { beforeEach(async () => {
[makerAmount, takerAmount, makerFeeAmount] = [ZeroEx.toBaseUnitAmount(new BigNumber(3), decimals), [makerAmount, takerAmount, makerFeeAmount] = [
ZeroEx.toBaseUnitAmount(new BigNumber(3), decimals),
ZeroEx.toBaseUnitAmount(new BigNumber(6), decimals), ZeroEx.toBaseUnitAmount(new BigNumber(6), decimals),
ZeroEx.toBaseUnitAmount(new BigNumber(6), decimals)]; ZeroEx.toBaseUnitAmount(new BigNumber(6), decimals),
];
}); });
it('calculates the correct amount when funds unavailable', () => { it('calculates the correct amount when funds unavailable', () => {
signedOrder = buildSignedOrder(); signedOrder = buildSignedOrder();
remainingMakerTokenAmount = signedOrder.makerTokenAmount; remainingMakerTokenAmount = signedOrder.makerTokenAmount;
const transferredAmount = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals); const transferredAmount = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals);
transferrableMakerTokenAmount = remainingMakerTokenAmount.minus(transferredAmount); transferrableMakerTokenAmount = remainingMakerTokenAmount.minus(transferredAmount);
calculator = new RemainingFillableCalculator(signedOrder, isMakerTokenZRX, calculator = new RemainingFillableCalculator(
transferrableMakerTokenAmount, transferrableMakerFeeTokenAmount, signedOrder,
remainingMakerTokenAmount); isMakerTokenZRX,
transferrableMakerTokenAmount,
transferrableMakerFeeTokenAmount,
remainingMakerTokenAmount,
);
expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(transferrableMakerTokenAmount); expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(transferrableMakerTokenAmount);
}); });
}); });
describe('Ratio is not evenly divisble', () => { describe('Ratio is not evenly divisble', () => {
beforeEach(async () => { beforeEach(async () => {
[makerAmount, takerAmount, makerFeeAmount] = [ZeroEx.toBaseUnitAmount(new BigNumber(3), decimals), [makerAmount, takerAmount, makerFeeAmount] = [
ZeroEx.toBaseUnitAmount(new BigNumber(3), decimals),
ZeroEx.toBaseUnitAmount(new BigNumber(7), decimals), ZeroEx.toBaseUnitAmount(new BigNumber(7), decimals),
ZeroEx.toBaseUnitAmount(new BigNumber(7), decimals)]; ZeroEx.toBaseUnitAmount(new BigNumber(7), decimals),
];
}); });
it('calculates the correct amount when funds unavailable', () => { it('calculates the correct amount when funds unavailable', () => {
signedOrder = buildSignedOrder(); signedOrder = buildSignedOrder();
remainingMakerTokenAmount = signedOrder.makerTokenAmount; remainingMakerTokenAmount = signedOrder.makerTokenAmount;
const transferredAmount = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals); const transferredAmount = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals);
transferrableMakerTokenAmount = remainingMakerTokenAmount.minus(transferredAmount); transferrableMakerTokenAmount = remainingMakerTokenAmount.minus(transferredAmount);
calculator = new RemainingFillableCalculator(signedOrder, isMakerTokenZRX, calculator = new RemainingFillableCalculator(
transferrableMakerTokenAmount, transferrableMakerFeeTokenAmount, signedOrder,
remainingMakerTokenAmount); isMakerTokenZRX,
transferrableMakerTokenAmount,
transferrableMakerFeeTokenAmount,
remainingMakerTokenAmount,
);
const calculatedFillableAmount = calculator.computeRemainingMakerFillable(); const calculatedFillableAmount = calculator.computeRemainingMakerFillable();
expect(calculatedFillableAmount.lessThanOrEqualTo(transferrableMakerTokenAmount)).to.be.true(); expect(calculatedFillableAmount.lessThanOrEqualTo(transferrableMakerTokenAmount)).to.be.true();
expect(calculatedFillableAmount).to.be.bignumber.greaterThan(new BigNumber(0)); expect(calculatedFillableAmount).to.be.bignumber.greaterThan(new BigNumber(0));
@ -134,15 +171,25 @@ describe('RemainingFillableCalculator', () => {
transferrableMakerTokenAmount = makerAmount.plus(makerFeeAmount); transferrableMakerTokenAmount = makerAmount.plus(makerFeeAmount);
transferrableMakerFeeTokenAmount = transferrableMakerTokenAmount; transferrableMakerFeeTokenAmount = transferrableMakerTokenAmount;
remainingMakerTokenAmount = signedOrder.makerTokenAmount; remainingMakerTokenAmount = signedOrder.makerTokenAmount;
calculator = new RemainingFillableCalculator(signedOrder, isMakerTokenZRX, calculator = new RemainingFillableCalculator(
transferrableMakerTokenAmount, transferrableMakerFeeTokenAmount, remainingMakerTokenAmount); signedOrder,
isMakerTokenZRX,
transferrableMakerTokenAmount,
transferrableMakerFeeTokenAmount,
remainingMakerTokenAmount,
);
expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(remainingMakerTokenAmount); expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(remainingMakerTokenAmount);
}); });
it('calculates the correct amount when partially filled and funds available', () => { it('calculates the correct amount when partially filled and funds available', () => {
signedOrder = buildSignedOrder(); signedOrder = buildSignedOrder();
remainingMakerTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(1), decimals); remainingMakerTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(1), decimals);
calculator = new RemainingFillableCalculator(signedOrder, isMakerTokenZRX, calculator = new RemainingFillableCalculator(
transferrableMakerTokenAmount, transferrableMakerFeeTokenAmount, remainingMakerTokenAmount); signedOrder,
isMakerTokenZRX,
transferrableMakerTokenAmount,
transferrableMakerFeeTokenAmount,
remainingMakerTokenAmount,
);
expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(remainingMakerTokenAmount); expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(remainingMakerTokenAmount);
}); });
it('calculates the amount to be 0 when all fee funds are transferred', () => { it('calculates the amount to be 0 when all fee funds are transferred', () => {
@ -150,8 +197,13 @@ describe('RemainingFillableCalculator', () => {
transferrableMakerTokenAmount = zero; transferrableMakerTokenAmount = zero;
transferrableMakerFeeTokenAmount = zero; transferrableMakerFeeTokenAmount = zero;
remainingMakerTokenAmount = signedOrder.makerTokenAmount; remainingMakerTokenAmount = signedOrder.makerTokenAmount;
calculator = new RemainingFillableCalculator(signedOrder, isMakerTokenZRX, calculator = new RemainingFillableCalculator(
transferrableMakerTokenAmount, transferrableMakerFeeTokenAmount, remainingMakerTokenAmount); signedOrder,
isMakerTokenZRX,
transferrableMakerTokenAmount,
transferrableMakerFeeTokenAmount,
remainingMakerTokenAmount,
);
expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(zero); expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(zero);
}); });
it('calculates the correct amount when balance is less than remaining fillable', () => { it('calculates the correct amount when balance is less than remaining fillable', () => {
@ -163,8 +215,13 @@ describe('RemainingFillableCalculator', () => {
const orderToFeeRatio = signedOrder.makerTokenAmount.dividedToIntegerBy(signedOrder.makerFee); const orderToFeeRatio = signedOrder.makerTokenAmount.dividedToIntegerBy(signedOrder.makerFee);
const expectedFillableAmount = new BigNumber(450980); const expectedFillableAmount = new BigNumber(450980);
calculator = new RemainingFillableCalculator(signedOrder, isMakerTokenZRX, calculator = new RemainingFillableCalculator(
transferrableMakerTokenAmount, transferrableMakerFeeTokenAmount, remainingMakerTokenAmount); signedOrder,
isMakerTokenZRX,
transferrableMakerTokenAmount,
transferrableMakerFeeTokenAmount,
remainingMakerTokenAmount,
);
const calculatedFillableAmount = calculator.computeRemainingMakerFillable(); const calculatedFillableAmount = calculator.computeRemainingMakerFillable();
const numberOfFillsInRatio = calculatedFillableAmount.dividedToIntegerBy(orderToFeeRatio); const numberOfFillsInRatio = calculatedFillableAmount.dividedToIntegerBy(orderToFeeRatio);
const calculatedFillableAmountPlusFees = calculatedFillableAmount.plus(numberOfFillsInRatio); const calculatedFillableAmountPlusFees = calculatedFillableAmount.plus(numberOfFillsInRatio);

View File

@ -6,13 +6,7 @@ import 'mocha';
import * as Sinon from 'sinon'; import * as Sinon from 'sinon';
import * as Web3 from 'web3'; import * as Web3 from 'web3';
import { import { ApprovalContractEventArgs, DecodedLogEvent, Token, TokenEvents, ZeroEx } from '../src';
ApprovalContractEventArgs,
DecodedLogEvent,
Token,
TokenEvents,
ZeroEx,
} from '../src';
import { DoneCallback } from '../src/types'; import { DoneCallback } from '../src/types';
import { chaiSetup } from './utils/chai_setup'; import { chaiSetup } from './utils/chai_setup';
@ -72,12 +66,8 @@ describe('SubscriptionTest', () => {
done(); done();
}, },
); );
stubs = [ stubs = [Sinon.stub((zeroEx as any)._web3Wrapper, 'getBlockAsync').throws(new Error(errMsg))];
Sinon.stub((zeroEx as any)._web3Wrapper, 'getBlockAsync') zeroEx.token.subscribe(tokenAddress, TokenEvents.Approval, indexFilterValues, callback);
.throws(new Error(errMsg)),
];
zeroEx.token.subscribe(
tokenAddress, TokenEvents.Approval, indexFilterValues, callback);
await zeroEx.token.setAllowanceAsync(tokenAddress, coinbase, addressWithoutFunds, allowanceAmount); await zeroEx.token.setAllowanceAsync(tokenAddress, coinbase, addressWithoutFunds, allowanceAmount);
})().catch(done); })().catch(done);
}); });
@ -91,24 +81,16 @@ describe('SubscriptionTest', () => {
done(); done();
}, },
); );
stubs = [ stubs = [Sinon.stub((zeroEx as any)._web3Wrapper, 'getLogsAsync').throws(new Error(errMsg))];
Sinon.stub((zeroEx as any)._web3Wrapper, 'getLogsAsync') zeroEx.token.subscribe(tokenAddress, TokenEvents.Approval, indexFilterValues, callback);
.throws(new Error(errMsg)),
];
zeroEx.token.subscribe(
tokenAddress, TokenEvents.Approval, indexFilterValues, callback);
await zeroEx.token.setAllowanceAsync(tokenAddress, coinbase, addressWithoutFunds, allowanceAmount); await zeroEx.token.setAllowanceAsync(tokenAddress, coinbase, addressWithoutFunds, allowanceAmount);
})().catch(done); })().catch(done);
}); });
it('Should allow unsubscribeAll to be called successfully after an error', (done: DoneCallback) => { it('Should allow unsubscribeAll to be called successfully after an error', (done: DoneCallback) => {
(async () => { (async () => {
const callback = (err: Error, logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => _.noop; const callback = (err: Error, logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => _.noop;
zeroEx.token.subscribe( zeroEx.token.subscribe(tokenAddress, TokenEvents.Approval, indexFilterValues, callback);
tokenAddress, TokenEvents.Approval, indexFilterValues, callback); stubs = [Sinon.stub((zeroEx as any)._web3Wrapper, 'getBlockAsync').throws(new Error('JSON RPC error'))];
stubs = [
Sinon.stub((zeroEx as any)._web3Wrapper, 'getBlockAsync')
.throws(new Error('JSON RPC error')),
];
zeroEx.token.unsubscribeAll(); zeroEx.token.unsubscribeAll();
done(); done();
})().catch(done); })().catch(done);

View File

@ -74,17 +74,17 @@ describe('TokenWrapper', () => {
it('should fail to transfer tokens if fromAddress has an insufficient balance', async () => { it('should fail to transfer tokens if fromAddress has an insufficient balance', async () => {
const fromAddress = addressWithoutFunds; const fromAddress = addressWithoutFunds;
const toAddress = coinbase; const toAddress = coinbase;
return expect(zeroEx.token.transferAsync( return expect(
token.address, fromAddress, toAddress, transferAmount, zeroEx.token.transferAsync(token.address, fromAddress, toAddress, transferAmount),
)).to.be.rejectedWith(ZeroExError.InsufficientBalanceForTransfer); ).to.be.rejectedWith(ZeroExError.InsufficientBalanceForTransfer);
}); });
it('should throw a CONTRACT_DOES_NOT_EXIST error for a non-existent token contract', async () => { it('should throw a CONTRACT_DOES_NOT_EXIST error for a non-existent token contract', async () => {
const nonExistentTokenAddress = '0x9dd402f14d67e001d8efbe6583e51bf9706aa065'; const nonExistentTokenAddress = '0x9dd402f14d67e001d8efbe6583e51bf9706aa065';
const fromAddress = coinbase; const fromAddress = coinbase;
const toAddress = coinbase; const toAddress = coinbase;
return expect(zeroEx.token.transferAsync( return expect(
nonExistentTokenAddress, fromAddress, toAddress, transferAmount, zeroEx.token.transferAsync(nonExistentTokenAddress, fromAddress, toAddress, transferAmount),
)).to.be.rejectedWith(ZeroExError.TokenContractDoesNotExist); ).to.be.rejectedWith(ZeroExError.TokenContractDoesNotExist);
}); });
}); });
describe('#transferFromAsync', () => { describe('#transferFromAsync', () => {
@ -103,24 +103,22 @@ describe('TokenWrapper', () => {
const fromAddressBalance = await zeroEx.token.getBalanceAsync(token.address, fromAddress); const fromAddressBalance = await zeroEx.token.getBalanceAsync(token.address, fromAddress);
expect(fromAddressBalance).to.be.bignumber.greaterThan(transferAmount); expect(fromAddressBalance).to.be.bignumber.greaterThan(transferAmount);
const fromAddressAllowance = await zeroEx.token.getAllowanceAsync(token.address, fromAddress, const fromAddressAllowance = await zeroEx.token.getAllowanceAsync(token.address, fromAddress, toAddress);
toAddress);
expect(fromAddressAllowance).to.be.bignumber.equal(0); expect(fromAddressAllowance).to.be.bignumber.equal(0);
return expect(zeroEx.token.transferFromAsync( return expect(
token.address, fromAddress, toAddress, senderAddress, transferAmount, zeroEx.token.transferFromAsync(token.address, fromAddress, toAddress, senderAddress, transferAmount),
)).to.be.rejectedWith(ZeroExError.InsufficientAllowanceForTransfer); ).to.be.rejectedWith(ZeroExError.InsufficientAllowanceForTransfer);
}); });
it('[regression] should fail to transfer tokens if set allowance for toAddress instead of senderAddress', it('[regression] should fail to transfer tokens if set allowance for toAddress instead of senderAddress', async () => {
async () => {
const fromAddress = coinbase; const fromAddress = coinbase;
const transferAmount = new BigNumber(42); const transferAmount = new BigNumber(42);
await zeroEx.token.setAllowanceAsync(token.address, fromAddress, toAddress, transferAmount); await zeroEx.token.setAllowanceAsync(token.address, fromAddress, toAddress, transferAmount);
return expect(zeroEx.token.transferFromAsync( return expect(
token.address, fromAddress, toAddress, senderAddress, transferAmount, zeroEx.token.transferFromAsync(token.address, fromAddress, toAddress, senderAddress, transferAmount),
)).to.be.rejectedWith(ZeroExError.InsufficientAllowanceForTransfer); ).to.be.rejectedWith(ZeroExError.InsufficientAllowanceForTransfer);
}); });
it('should fail to transfer tokens if fromAddress has insufficient balance', async () => { it('should fail to transfer tokens if fromAddress has insufficient balance', async () => {
const fromAddress = addressWithoutFunds; const fromAddress = addressWithoutFunds;
@ -130,13 +128,16 @@ describe('TokenWrapper', () => {
expect(fromAddressBalance).to.be.bignumber.equal(0); expect(fromAddressBalance).to.be.bignumber.equal(0);
await zeroEx.token.setAllowanceAsync(token.address, fromAddress, senderAddress, transferAmount); await zeroEx.token.setAllowanceAsync(token.address, fromAddress, senderAddress, transferAmount);
const fromAddressAllowance = await zeroEx.token.getAllowanceAsync(token.address, fromAddress, const fromAddressAllowance = await zeroEx.token.getAllowanceAsync(
senderAddress); token.address,
fromAddress,
senderAddress,
);
expect(fromAddressAllowance).to.be.bignumber.equal(transferAmount); expect(fromAddressAllowance).to.be.bignumber.equal(transferAmount);
return expect(zeroEx.token.transferFromAsync( return expect(
token.address, fromAddress, toAddress, senderAddress, transferAmount, zeroEx.token.transferFromAsync(token.address, fromAddress, toAddress, senderAddress, transferAmount),
)).to.be.rejectedWith(ZeroExError.InsufficientBalanceForTransfer); ).to.be.rejectedWith(ZeroExError.InsufficientBalanceForTransfer);
}); });
it('should successfully transfer tokens', async () => { it('should successfully transfer tokens', async () => {
const fromAddress = coinbase; const fromAddress = coinbase;
@ -147,17 +148,22 @@ describe('TokenWrapper', () => {
const transferAmount = new BigNumber(42); const transferAmount = new BigNumber(42);
await zeroEx.token.setAllowanceAsync(token.address, fromAddress, senderAddress, transferAmount); await zeroEx.token.setAllowanceAsync(token.address, fromAddress, senderAddress, transferAmount);
await zeroEx.token.transferFromAsync(token.address, fromAddress, toAddress, senderAddress, await zeroEx.token.transferFromAsync(token.address, fromAddress, toAddress, senderAddress, transferAmount);
transferAmount);
const postBalance = await zeroEx.token.getBalanceAsync(token.address, toAddress); const postBalance = await zeroEx.token.getBalanceAsync(token.address, toAddress);
return expect(postBalance).to.be.bignumber.equal(transferAmount); return expect(postBalance).to.be.bignumber.equal(transferAmount);
}); });
it('should throw a CONTRACT_DOES_NOT_EXIST error for a non-existent token contract', async () => { it('should throw a CONTRACT_DOES_NOT_EXIST error for a non-existent token contract', async () => {
const fromAddress = coinbase; const fromAddress = coinbase;
const nonExistentTokenAddress = '0x9dd402f14d67e001d8efbe6583e51bf9706aa065'; const nonExistentTokenAddress = '0x9dd402f14d67e001d8efbe6583e51bf9706aa065';
return expect(zeroEx.token.transferFromAsync( return expect(
nonExistentTokenAddress, fromAddress, toAddress, senderAddress, new BigNumber(42), zeroEx.token.transferFromAsync(
)).to.be.rejectedWith(ZeroExError.TokenContractDoesNotExist); nonExistentTokenAddress,
fromAddress,
toAddress,
senderAddress,
new BigNumber(42),
),
).to.be.rejectedWith(ZeroExError.TokenContractDoesNotExist);
}); });
}); });
describe('#getBalanceAsync', () => { describe('#getBalanceAsync', () => {
@ -172,8 +178,9 @@ describe('TokenWrapper', () => {
it('should throw a CONTRACT_DOES_NOT_EXIST error for a non-existent token contract', async () => { it('should throw a CONTRACT_DOES_NOT_EXIST error for a non-existent token contract', async () => {
const nonExistentTokenAddress = '0x9dd402f14d67e001d8efbe6583e51bf9706aa065'; const nonExistentTokenAddress = '0x9dd402f14d67e001d8efbe6583e51bf9706aa065';
const ownerAddress = coinbase; const ownerAddress = coinbase;
return expect(zeroEx.token.getBalanceAsync(nonExistentTokenAddress, ownerAddress)) return expect(zeroEx.token.getBalanceAsync(nonExistentTokenAddress, ownerAddress)).to.be.rejectedWith(
.to.be.rejectedWith(ZeroExError.TokenContractDoesNotExist); ZeroExError.TokenContractDoesNotExist,
);
}); });
it('should return a balance of 0 for a non-existent owner address', async () => { it('should return a balance of 0 for a non-existent owner address', async () => {
const token = tokens[0]; const token = tokens[0];
@ -200,13 +207,16 @@ describe('TokenWrapper', () => {
}); });
}); });
describe('#setAllowanceAsync', () => { describe('#setAllowanceAsync', () => {
it('should set the spender\'s allowance', async () => { it("should set the spender's allowance", async () => {
const token = tokens[0]; const token = tokens[0];
const ownerAddress = coinbase; const ownerAddress = coinbase;
const spenderAddress = addressWithoutFunds; const spenderAddress = addressWithoutFunds;
const allowanceBeforeSet = await zeroEx.token.getAllowanceAsync(token.address, ownerAddress, const allowanceBeforeSet = await zeroEx.token.getAllowanceAsync(
spenderAddress); token.address,
ownerAddress,
spenderAddress,
);
const expectedAllowanceBeforeAllowanceSet = new BigNumber(0); const expectedAllowanceBeforeAllowanceSet = new BigNumber(0);
expect(allowanceBeforeSet).to.be.bignumber.equal(expectedAllowanceBeforeAllowanceSet); expect(allowanceBeforeSet).to.be.bignumber.equal(expectedAllowanceBeforeAllowanceSet);
@ -219,7 +229,7 @@ describe('TokenWrapper', () => {
}); });
}); });
describe('#setUnlimitedAllowanceAsync', () => { describe('#setUnlimitedAllowanceAsync', () => {
it('should set the unlimited spender\'s allowance', async () => { it("should set the unlimited spender's allowance", async () => {
const token = tokens[0]; const token = tokens[0];
const ownerAddress = coinbase; const ownerAddress = coinbase;
const spenderAddress = addressWithoutFunds; const spenderAddress = addressWithoutFunds;
@ -241,10 +251,18 @@ describe('TokenWrapper', () => {
); );
await zeroEx.token.transferFromAsync( await zeroEx.token.transferFromAsync(
zrx.address, coinbase, userWithNormalAllowance, userWithNormalAllowance, transferAmount, zrx.address,
coinbase,
userWithNormalAllowance,
userWithNormalAllowance,
transferAmount,
); );
await zeroEx.token.transferFromAsync( await zeroEx.token.transferFromAsync(
zrx.address, coinbase, userWithUnlimitedAllowance, userWithUnlimitedAllowance, transferAmount, zrx.address,
coinbase,
userWithUnlimitedAllowance,
userWithUnlimitedAllowance,
transferAmount,
); );
const finalBalanceWithNormalAllowance = await web3Wrapper.getBalanceInWeiAsync(userWithNormalAllowance); const finalBalanceWithNormalAllowance = await web3Wrapper.getBalanceInWeiAsync(userWithNormalAllowance);
@ -300,7 +318,9 @@ describe('TokenWrapper', () => {
await zeroEx.token.setAllowanceAsync(token.address, ownerAddress, spenderAddress, amountInBaseUnits); await zeroEx.token.setAllowanceAsync(token.address, ownerAddress, spenderAddress, amountInBaseUnits);
const allowance = await zeroExWithoutAccounts.token.getAllowanceAsync( const allowance = await zeroExWithoutAccounts.token.getAllowanceAsync(
token.address, ownerAddress, spenderAddress, token.address,
ownerAddress,
spenderAddress,
); );
const expectedAllowance = amountInBaseUnits; const expectedAllowance = amountInBaseUnits;
return expect(allowance).to.be.bignumber.equal(expectedAllowance); return expect(allowance).to.be.bignumber.equal(expectedAllowance);
@ -378,8 +398,7 @@ describe('TokenWrapper', () => {
expect(args._value).to.be.bignumber.equal(transferAmount); expect(args._value).to.be.bignumber.equal(transferAmount);
done(); done();
}; };
zeroEx.token.subscribe( zeroEx.token.subscribe(tokenAddress, TokenEvents.Transfer, indexFilterValues, callback);
tokenAddress, TokenEvents.Transfer, indexFilterValues, callback);
await zeroEx.token.transferAsync(tokenAddress, coinbase, addressWithoutFunds, transferAmount); await zeroEx.token.transferAsync(tokenAddress, coinbase, addressWithoutFunds, transferAmount);
})().catch(done); })().catch(done);
}); });
@ -394,8 +413,7 @@ describe('TokenWrapper', () => {
expect(args._value).to.be.bignumber.equal(allowanceAmount); expect(args._value).to.be.bignumber.equal(allowanceAmount);
done(); done();
}; };
zeroEx.token.subscribe( zeroEx.token.subscribe(tokenAddress, TokenEvents.Approval, indexFilterValues, callback);
tokenAddress, TokenEvents.Approval, indexFilterValues, callback);
await zeroEx.token.setAllowanceAsync(tokenAddress, coinbase, addressWithoutFunds, allowanceAmount); await zeroEx.token.setAllowanceAsync(tokenAddress, coinbase, addressWithoutFunds, allowanceAmount);
})().catch(done); })().catch(done);
}); });
@ -404,17 +422,13 @@ describe('TokenWrapper', () => {
const callbackNeverToBeCalled = (err: Error, logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => { const callbackNeverToBeCalled = (err: Error, logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => {
done(new Error('Expected this subscription to have been cancelled')); done(new Error('Expected this subscription to have been cancelled'));
}; };
zeroEx.token.subscribe( zeroEx.token.subscribe(tokenAddress, TokenEvents.Transfer, indexFilterValues, callbackNeverToBeCalled);
tokenAddress, TokenEvents.Transfer, indexFilterValues, callbackNeverToBeCalled,
);
const callbackToBeCalled = (err: Error, logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => { const callbackToBeCalled = (err: Error, logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => {
done(); done();
}; };
const newProvider = web3Factory.getRpcProvider(); const newProvider = web3Factory.getRpcProvider();
zeroEx.setProvider(newProvider, constants.TESTRPC_NETWORK_ID); zeroEx.setProvider(newProvider, constants.TESTRPC_NETWORK_ID);
zeroEx.token.subscribe( zeroEx.token.subscribe(tokenAddress, TokenEvents.Transfer, indexFilterValues, callbackToBeCalled);
tokenAddress, TokenEvents.Transfer, indexFilterValues, callbackToBeCalled,
);
await zeroEx.token.transferAsync(tokenAddress, coinbase, addressWithoutFunds, transferAmount); await zeroEx.token.transferAsync(tokenAddress, coinbase, addressWithoutFunds, transferAmount);
})().catch(done); })().catch(done);
}); });
@ -424,7 +438,11 @@ describe('TokenWrapper', () => {
done(new Error('Expected this subscription to have been cancelled')); done(new Error('Expected this subscription to have been cancelled'));
}; };
const subscriptionToken = zeroEx.token.subscribe( const subscriptionToken = zeroEx.token.subscribe(
tokenAddress, TokenEvents.Transfer, indexFilterValues, callbackNeverToBeCalled); tokenAddress,
TokenEvents.Transfer,
indexFilterValues,
callbackNeverToBeCalled,
);
zeroEx.token.unsubscribe(subscriptionToken); zeroEx.token.unsubscribe(subscriptionToken);
await zeroEx.token.transferAsync(tokenAddress, coinbase, addressWithoutFunds, transferAmount); await zeroEx.token.transferAsync(tokenAddress, coinbase, addressWithoutFunds, transferAmount);
done(); done();
@ -450,7 +468,10 @@ describe('TokenWrapper', () => {
const eventName = TokenEvents.Approval; const eventName = TokenEvents.Approval;
const indexFilterValues = {}; const indexFilterValues = {};
const logs = await zeroEx.token.getLogsAsync<ApprovalContractEventArgs>( const logs = await zeroEx.token.getLogsAsync<ApprovalContractEventArgs>(
tokenAddress, eventName, blockRange, indexFilterValues, tokenAddress,
eventName,
blockRange,
indexFilterValues,
); );
expect(logs).to.have.length(1); expect(logs).to.have.length(1);
const args = logs[0].args; const args = logs[0].args;
@ -465,7 +486,10 @@ describe('TokenWrapper', () => {
const differentEventName = TokenEvents.Transfer; const differentEventName = TokenEvents.Transfer;
const indexFilterValues = {}; const indexFilterValues = {};
const logs = await zeroEx.token.getLogsAsync( const logs = await zeroEx.token.getLogsAsync(
tokenAddress, differentEventName, blockRange, indexFilterValues, tokenAddress,
differentEventName,
blockRange,
indexFilterValues,
); );
expect(logs).to.have.length(0); expect(logs).to.have.length(0);
}); });
@ -479,7 +503,10 @@ describe('TokenWrapper', () => {
_owner: coinbase, _owner: coinbase,
}; };
const logs = await zeroEx.token.getLogsAsync<ApprovalContractEventArgs>( const logs = await zeroEx.token.getLogsAsync<ApprovalContractEventArgs>(
tokenAddress, eventName, blockRange, indexFilterValues, tokenAddress,
eventName,
blockRange,
indexFilterValues,
); );
expect(logs).to.have.length(1); expect(logs).to.have.length(1);
const args = logs[0].args; const args = logs[0].args;
@ -487,3 +514,4 @@ describe('TokenWrapper', () => {
}); });
}); });
}); });
// tslint:disable:max-file-line-count

View File

@ -17,8 +17,13 @@ export class FillScenarios {
private _coinbase: string; private _coinbase: string;
private _zrxTokenAddress: string; private _zrxTokenAddress: string;
private _exchangeContractAddress: string; private _exchangeContractAddress: string;
constructor(zeroEx: ZeroEx, userAddresses: string[], constructor(
tokens: Token[], zrxTokenAddress: string, exchangeContractAddress: string) { zeroEx: ZeroEx,
userAddresses: string[],
tokens: Token[],
zrxTokenAddress: string,
exchangeContractAddress: string,
) {
this._zeroEx = zeroEx; this._zeroEx = zeroEx;
this._userAddresses = userAddresses; this._userAddresses = userAddresses;
this._tokens = tokens; this._tokens = tokens;
@ -31,7 +36,8 @@ export class FillScenarios {
for (const token of this._tokens) { for (const token of this._tokens) {
if (token.symbol !== 'ZRX' && token.symbol !== 'WETH') { if (token.symbol !== 'ZRX' && token.symbol !== 'WETH') {
const contractInstance = web3Wrapper.getContractInstance( const contractInstance = web3Wrapper.getContractInstance(
artifacts.DummyTokenArtifact.abi, token.address, artifacts.DummyTokenArtifact.abi,
token.address,
); );
const defaults = {}; const defaults = {};
const dummyToken = new DummyTokenContract(contractInstance, defaults); const dummyToken = new DummyTokenContract(contractInstance, defaults);
@ -43,61 +49,110 @@ export class FillScenarios {
} }
} }
} }
public async createFillableSignedOrderAsync(makerTokenAddress: string, takerTokenAddress: string, public async createFillableSignedOrderAsync(
makerAddress: string, takerAddress: string, makerTokenAddress: string,
takerTokenAddress: string,
makerAddress: string,
takerAddress: string,
fillableAmount: BigNumber, fillableAmount: BigNumber,
expirationUnixTimestampSec?: BigNumber): expirationUnixTimestampSec?: BigNumber,
Promise<SignedOrder> { ): Promise<SignedOrder> {
return this.createAsymmetricFillableSignedOrderAsync( return this.createAsymmetricFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, makerTokenAddress,
fillableAmount, fillableAmount, expirationUnixTimestampSec, takerTokenAddress,
makerAddress,
takerAddress,
fillableAmount,
fillableAmount,
expirationUnixTimestampSec,
); );
} }
public async createFillableSignedOrderWithFeesAsync( public async createFillableSignedOrderWithFeesAsync(
makerTokenAddress: string, takerTokenAddress: string, makerTokenAddress: string,
makerFee: BigNumber, takerFee: BigNumber, takerTokenAddress: string,
makerAddress: string, takerAddress: string, makerFee: BigNumber,
takerFee: BigNumber,
makerAddress: string,
takerAddress: string,
fillableAmount: BigNumber, fillableAmount: BigNumber,
feeRecepient: string, expirationUnixTimestampSec?: BigNumber, feeRecepient: string,
expirationUnixTimestampSec?: BigNumber,
): Promise<SignedOrder> { ): Promise<SignedOrder> {
return this._createAsymmetricFillableSignedOrderWithFeesAsync( return this._createAsymmetricFillableSignedOrderWithFeesAsync(
makerTokenAddress, takerTokenAddress, makerFee, takerFee, makerAddress, takerAddress, makerTokenAddress,
fillableAmount, fillableAmount, feeRecepient, expirationUnixTimestampSec, takerTokenAddress,
makerFee,
takerFee,
makerAddress,
takerAddress,
fillableAmount,
fillableAmount,
feeRecepient,
expirationUnixTimestampSec,
); );
} }
public async createAsymmetricFillableSignedOrderAsync( public async createAsymmetricFillableSignedOrderAsync(
makerTokenAddress: string, takerTokenAddress: string, makerAddress: string, takerAddress: string, makerTokenAddress: string,
makerFillableAmount: BigNumber, takerFillableAmount: BigNumber, takerTokenAddress: string,
expirationUnixTimestampSec?: BigNumber): Promise<SignedOrder> { makerAddress: string,
takerAddress: string,
makerFillableAmount: BigNumber,
takerFillableAmount: BigNumber,
expirationUnixTimestampSec?: BigNumber,
): Promise<SignedOrder> {
const makerFee = new BigNumber(0); const makerFee = new BigNumber(0);
const takerFee = new BigNumber(0); const takerFee = new BigNumber(0);
const feeRecepient = constants.NULL_ADDRESS; const feeRecepient = constants.NULL_ADDRESS;
return this._createAsymmetricFillableSignedOrderWithFeesAsync( return this._createAsymmetricFillableSignedOrderWithFeesAsync(
makerTokenAddress, takerTokenAddress, makerFee, takerFee, makerAddress, takerAddress, makerTokenAddress,
makerFillableAmount, takerFillableAmount, feeRecepient, expirationUnixTimestampSec, takerTokenAddress,
makerFee,
takerFee,
makerAddress,
takerAddress,
makerFillableAmount,
takerFillableAmount,
feeRecepient,
expirationUnixTimestampSec,
); );
} }
public async createPartiallyFilledSignedOrderAsync(makerTokenAddress: string, takerTokenAddress: string, public async createPartiallyFilledSignedOrderAsync(
takerAddress: string, fillableAmount: BigNumber, makerTokenAddress: string,
partialFillAmount: BigNumber) { takerTokenAddress: string,
takerAddress: string,
fillableAmount: BigNumber,
partialFillAmount: BigNumber,
) {
const [makerAddress] = this._userAddresses; const [makerAddress] = this._userAddresses;
const signedOrder = await this.createAsymmetricFillableSignedOrderAsync( const signedOrder = await this.createAsymmetricFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, makerTokenAddress,
fillableAmount, fillableAmount, takerTokenAddress,
makerAddress,
takerAddress,
fillableAmount,
fillableAmount,
); );
const shouldThrowOnInsufficientBalanceOrAllowance = false; const shouldThrowOnInsufficientBalanceOrAllowance = false;
await this._zeroEx.exchange.fillOrderAsync( await this._zeroEx.exchange.fillOrderAsync(
signedOrder, partialFillAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, signedOrder,
partialFillAmount,
shouldThrowOnInsufficientBalanceOrAllowance,
takerAddress,
); );
return signedOrder; return signedOrder;
} }
private async _createAsymmetricFillableSignedOrderWithFeesAsync( private async _createAsymmetricFillableSignedOrderWithFeesAsync(
makerTokenAddress: string, takerTokenAddress: string, makerTokenAddress: string,
makerFee: BigNumber, takerFee: BigNumber, takerTokenAddress: string,
makerAddress: string, takerAddress: string, makerFee: BigNumber,
makerFillableAmount: BigNumber, takerFillableAmount: BigNumber, takerFee: BigNumber,
feeRecepient: string, expirationUnixTimestampSec?: BigNumber): Promise<SignedOrder> { makerAddress: string,
takerAddress: string,
makerFillableAmount: BigNumber,
takerFillableAmount: BigNumber,
feeRecepient: string,
expirationUnixTimestampSec?: BigNumber,
): Promise<SignedOrder> {
await Promise.all([ await Promise.all([
this._increaseBalanceAndAllowanceAsync(makerTokenAddress, makerAddress, makerFillableAmount), this._increaseBalanceAndAllowanceAsync(makerTokenAddress, makerAddress, makerFillableAmount),
this._increaseBalanceAndAllowanceAsync(takerTokenAddress, takerAddress, takerFillableAmount), this._increaseBalanceAndAllowanceAsync(takerTokenAddress, takerAddress, takerFillableAmount),
@ -107,14 +162,27 @@ export class FillScenarios {
this._increaseBalanceAndAllowanceAsync(this._zrxTokenAddress, takerAddress, takerFee), this._increaseBalanceAndAllowanceAsync(this._zrxTokenAddress, takerAddress, takerFee),
]); ]);
const signedOrder = await orderFactory.createSignedOrderAsync(this._zeroEx, const signedOrder = await orderFactory.createSignedOrderAsync(
makerAddress, takerAddress, makerFee, takerFee, this._zeroEx,
makerFillableAmount, makerTokenAddress, takerFillableAmount, takerTokenAddress, makerAddress,
this._exchangeContractAddress, feeRecepient, expirationUnixTimestampSec); takerAddress,
makerFee,
takerFee,
makerFillableAmount,
makerTokenAddress,
takerFillableAmount,
takerTokenAddress,
this._exchangeContractAddress,
feeRecepient,
expirationUnixTimestampSec,
);
return signedOrder; return signedOrder;
} }
private async _increaseBalanceAndAllowanceAsync( private async _increaseBalanceAndAllowanceAsync(
tokenAddress: string, address: string, amount: BigNumber): Promise<void> { tokenAddress: string,
address: string,
amount: BigNumber,
): Promise<void> {
if (amount.isZero() || address === ZeroEx.NULL_ADDRESS) { if (amount.isZero() || address === ZeroEx.NULL_ADDRESS) {
return; // noop return; // noop
} }
@ -123,16 +191,12 @@ export class FillScenarios {
this._increaseAllowanceAsync(tokenAddress, address, amount), this._increaseAllowanceAsync(tokenAddress, address, amount),
]); ]);
} }
private async _increaseBalanceAsync( private async _increaseBalanceAsync(tokenAddress: string, address: string, amount: BigNumber): Promise<void> {
tokenAddress: string, address: string, amount: BigNumber): Promise<void> {
await this._zeroEx.token.transferAsync(tokenAddress, this._coinbase, address, amount); await this._zeroEx.token.transferAsync(tokenAddress, this._coinbase, address, amount);
} }
private async _increaseAllowanceAsync( private async _increaseAllowanceAsync(tokenAddress: string, address: string, amount: BigNumber): Promise<void> {
tokenAddress: string, address: string, amount: BigNumber): Promise<void> {
const oldMakerAllowance = await this._zeroEx.token.getProxyAllowanceAsync(tokenAddress, address); const oldMakerAllowance = await this._zeroEx.token.getProxyAllowanceAsync(tokenAddress, address);
const newMakerAllowance = oldMakerAllowance.plus(amount); const newMakerAllowance = oldMakerAllowance.plus(amount);
await this._zeroEx.token.setProxyAllowanceAsync( await this._zeroEx.token.setProxyAllowanceAsync(tokenAddress, address, newMakerAllowance);
tokenAddress, address, newMakerAllowance,
);
} }
} }

View File

@ -16,11 +16,12 @@ export const orderFactory = {
takerTokenAddress: string, takerTokenAddress: string,
exchangeContractAddress: string, exchangeContractAddress: string,
feeRecipient: string, feeRecipient: string,
expirationUnixTimestampSecIfExists?: BigNumber): Promise<SignedOrder> { expirationUnixTimestampSecIfExists?: BigNumber,
): Promise<SignedOrder> {
const defaultExpirationUnixTimestampSec = new BigNumber(2524604400); // Close to infinite const defaultExpirationUnixTimestampSec = new BigNumber(2524604400); // Close to infinite
const expirationUnixTimestampSec = _.isUndefined(expirationUnixTimestampSecIfExists) ? const expirationUnixTimestampSec = _.isUndefined(expirationUnixTimestampSecIfExists)
defaultExpirationUnixTimestampSec : ? defaultExpirationUnixTimestampSec
expirationUnixTimestampSecIfExists; : expirationUnixTimestampSecIfExists;
const order = { const order = {
maker, maker,
taker, taker,

View File

@ -31,9 +31,11 @@ export const web3Factory = {
provider.addProvider(new EmptyWalletSubprovider()); provider.addProvider(new EmptyWalletSubprovider());
} }
provider.addProvider(new FakeGasEstimateSubprovider(constants.GAS_ESTIMATE)); provider.addProvider(new FakeGasEstimateSubprovider(constants.GAS_ESTIMATE));
provider.addProvider(new RpcSubprovider({ provider.addProvider(
new RpcSubprovider({
rpcUrl: constants.RPC_URL, rpcUrl: constants.RPC_URL,
})); }),
);
provider.start(); provider.start();
return provider; return provider;
}, },

View File

@ -1,4 +1,7 @@
# CHANGELOG # CHANGELOG
vx.x.x v0.x.x - _TBD, 2018_
------------------------ ------------------------
* Fixed array typings with union types (#295)
* Add event ABIs to context data passed to templates (#302)
* Add constructor ABIs to context data passed to templates (#304)

View File

@ -5,9 +5,9 @@ It's heavily inspired by [Geth abigen](https://github.com/ethereum/go-ethereum/w
You can write your custom handlebars templates which will allow you to seamlessly integrate the generated code into your existing codebase with existing conventions. You can write your custom handlebars templates which will allow you to seamlessly integrate the generated code into your existing codebase with existing conventions.
For an example of the generated [wrapper files](https://github.com/0xProject/0x.js/tree/development/packages/0x.js/src/contract_wrappers/generated) check out 0x.js. For an example of the generated [wrapper files](https://github.com/0xProject/0x.js/tree/development/packages/0x.js/src/contract_wrappers/generated) check out 0x.js.
[Here](https://github.com/0xProject/0x.js/tree/development/packages/0x.js/src/contract_templates) are the templates used to generate those files. [Here](https://github.com/0xProject/0x.js/tree/development/packages/0x.js/contract_templates) are the templates used to generate those files.
## Instalation ## Installation
`yarn add -g @0xproject/abi-gen` `yarn add -g @0xproject/abi-gen`
## Usage ## Usage
``` ```
@ -29,8 +29,8 @@ We could've just used `--abiGlob 'src/artifacts/*.json` but we wanted to exclude
The abi file should be either a [Truffle](http://truffleframework.com/) contract artifact (a JSON object with an abi key) or a JSON abi array. The abi file should be either a [Truffle](http://truffleframework.com/) contract artifact (a JSON object with an abi key) or a JSON abi array.
## How to write custom templates? ## How to write custom templates?
The best way to get started is to copy [0x.js templates](https://github.com/0xProject/0x.js/tree/development/packages/0x.js/src/contract_templates) and start adjusting them for your needs. The best way to get started is to copy [0x.js templates](https://github.com/0xProject/0x.js/tree/development/packages/0x.js/contract_templates) and start adjusting them for your needs.
We use [handlebars](handlebarsjs.com) template engine under the hood. We use [handlebars](http://handlebarsjs.com/) template engine under the hood.
You need to have a master template called `contract.mustache`. it will be used to generate each contract wrapper. Although - you don't need and probably shouldn't write all your logic in a single template file. You can write [partial templates](http://handlebarsjs.com/partials.html) and as long as they are within a partials folder - they will be registered and available. You need to have a master template called `contract.mustache`. it will be used to generate each contract wrapper. Although - you don't need and probably shouldn't write all your logic in a single template file. You can write [partial templates](http://handlebarsjs.com/partials.html) and as long as they are within a partials folder - they will be registered and available.
## Which data/context do I get in my templates? ## Which data/context do I get in my templates?
For now you don't get much on top of methods abi, some useful helpers and a contract name because it was enough for our use-case, but if you need something else - create a PR. For now you don't get much on top of methods abi, some useful helpers and a contract name because it was enough for our use-case, but if you need something else - create a PR.

View File

@ -1,6 +1,6 @@
{ {
"name": "@0xproject/abi-gen", "name": "@0xproject/abi-gen",
"version": "0.0.3", "version": "0.0.4",
"description": "Generate contract wrappers from ABI and handlebars templates", "description": "Generate contract wrappers from ABI and handlebars templates",
"main": "lib/index.js", "main": "lib/index.js",
"types": "lib/index.d.ts", "types": "lib/index.d.ts",
@ -33,7 +33,7 @@
"yargs": "^10.0.3" "yargs": "^10.0.3"
}, },
"devDependencies": { "devDependencies": {
"@0xproject/tslint-config": "^0.3.0", "@0xproject/tslint-config": "^0.4.0",
"@types/glob": "^5.0.33", "@types/glob": "^5.0.33",
"@types/handlebars": "^4.0.36", "@types/handlebars": "^4.0.36",
"@types/mkdirp": "^0.5.1", "@types/mkdirp": "^0.5.1",
@ -43,6 +43,6 @@
"shx": "^0.2.2", "shx": "^0.2.2",
"tslint": "5.8.0", "tslint": "5.8.0",
"typescript": "~2.6.1", "typescript": "~2.6.1",
"web3-typescript-typings": "^0.7.2" "web3-typescript-typings": "^0.9.0"
} }
} }

View File

@ -14,7 +14,9 @@ import * as Web3 from 'web3';
import { ContextData, ParamKind } from './types'; import { ContextData, ParamKind } from './types';
import { utils } from './utils'; import { utils } from './utils';
const ABI_TYPE_CONSTRUCTOR = 'constructor';
const ABI_TYPE_METHOD = 'function'; const ABI_TYPE_METHOD = 'function';
const ABI_TYPE_EVENT = 'event';
const MAIN_TEMPLATE_NAME = 'contract.mustache'; const MAIN_TEMPLATE_NAME = 'contract.mustache';
const args = yargs const args = yargs
@ -32,8 +34,7 @@ const args = yargs
describe: 'Folder where to put the output files', describe: 'Folder where to put the output files',
type: 'string', type: 'string',
demand: true, demand: true,
}) }).argv;
.argv;
function writeOutputFile(name: string, renderedTsCode: string): void { function writeOutputFile(name: string, renderedTsCode: string): void {
const fileName = toSnakeCase(name); const fileName = toSnakeCase(name);
@ -66,14 +67,20 @@ for (const abiFileName of abiFileNames) {
const namedContent = utils.getNamedContent(abiFileName); const namedContent = utils.getNamedContent(abiFileName);
utils.log(`Processing: ${chalk.bold(namedContent.name)}...`); utils.log(`Processing: ${chalk.bold(namedContent.name)}...`);
const parsedContent = JSON.parse(namedContent.content); const parsedContent = JSON.parse(namedContent.content);
const ABI = _.isArray(parsedContent) ? const ABI = _.isArray(parsedContent)
parsedContent : // ABI file ? parsedContent // ABI file
parsedContent.abi; // Truffle contracts file : parsedContent.abi; // Truffle contracts file
if (_.isUndefined(ABI)) { if (_.isUndefined(ABI)) {
utils.log(`${chalk.red(`ABI not found in ${abiFileName}.`)}`); utils.log(`${chalk.red(`ABI not found in ${abiFileName}.`)}`);
utils.log(`Please make sure your ABI file is either an array with ABI entries or an object with the abi key`); utils.log(`Please make sure your ABI file is either an array with ABI entries or an object with the abi key`);
process.exit(1); process.exit(1);
} }
let ctor = ABI.find((abi: Web3.AbiDefinition) => abi.type === ABI_TYPE_CONSTRUCTOR) as Web3.ConstructorAbi;
if (_.isUndefined(ctor)) {
ctor = utils.getEmptyConstructor(); // The constructor exists, but it's implicit in JSON's ABI definition
}
const methodAbis = ABI.filter((abi: Web3.AbiDefinition) => abi.type === ABI_TYPE_METHOD) as Web3.MethodAbi[]; const methodAbis = ABI.filter((abi: Web3.AbiDefinition) => abi.type === ABI_TYPE_METHOD) as Web3.MethodAbi[];
const methodsData = _.map(methodAbis, methodAbi => { const methodsData = _.map(methodAbis, methodAbi => {
_.map(methodAbi.inputs, input => { _.map(methodAbi.inputs, input => {
@ -89,9 +96,14 @@ for (const abiFileName of abiFileNames) {
}; };
return methodData; return methodData;
}); });
const eventAbis = ABI.filter((abi: Web3.AbiDefinition) => abi.type === ABI_TYPE_EVENT) as Web3.EventAbi[];
const contextData = { const contextData = {
contractName: namedContent.name, contractName: namedContent.name,
ctor,
methods: methodsData, methods: methodsData,
events: eventAbis,
}; };
const renderedTsCode = template(contextData); const renderedTsCode = template(contextData);
writeOutputFile(namedContent.name, renderedTsCode); writeOutputFile(namedContent.name, renderedTsCode);

View File

@ -12,4 +12,5 @@ export interface Method extends Web3.MethodAbi {
export interface ContextData { export interface ContextData {
contractName: string; contractName: string;
methods: Method[]; methods: Method[];
events: Web3.EventAbi[];
} }

View File

@ -1,6 +1,7 @@
import * as fs from 'fs'; import * as fs from 'fs';
import * as _ from 'lodash'; import * as _ from 'lodash';
import * as path from 'path'; import * as path from 'path';
import * as Web3 from 'web3';
import { ParamKind } from './types'; import { ParamKind } from './types';
@ -10,7 +11,7 @@ export const utils = {
if (solType.match(trailingArrayRegex)) { if (solType.match(trailingArrayRegex)) {
const arrayItemSolType = solType.replace(trailingArrayRegex, ''); const arrayItemSolType = solType.replace(trailingArrayRegex, '');
const arrayItemTsType = utils.solTypeToTsType(paramKind, arrayItemSolType); const arrayItemTsType = utils.solTypeToTsType(paramKind, arrayItemSolType);
const arrayTsType = `${arrayItemTsType}[]`; const arrayTsType = `(${arrayItemTsType})[]`;
return arrayTsType; return arrayTsType;
} else { } else {
const solTypeRegexToTsType = [ const solTypeRegexToTsType = [
@ -23,7 +24,10 @@ export const utils = {
if (paramKind === ParamKind.Input) { if (paramKind === ParamKind.Input) {
// web3 allows to pass those an non-bignumbers and that's nice // web3 allows to pass those an non-bignumbers and that's nice
// but it always returns stuff as BigNumbers // but it always returns stuff as BigNumbers
solTypeRegexToTsType.unshift({regex: '^u?int(8|16|32)?$', tsType: 'number|BigNumber'}); solTypeRegexToTsType.unshift({
regex: '^u?int(8|16|32)?$',
tsType: 'number|BigNumber',
});
} }
for (const regexAndTxType of solTypeRegexToTsType) { for (const regexAndTxType of solTypeRegexToTsType) {
const { regex, tsType } = regexAndTxType; const { regex, tsType } = regexAndTxType;
@ -53,4 +57,12 @@ export const utils = {
throw new Error(`Failed to read ${filename}: ${err}`); throw new Error(`Failed to read ${filename}: ${err}`);
} }
}, },
getEmptyConstructor(): Web3.ConstructorAbi {
return {
type: 'constructor',
stateMutability: 'nonpayable',
payable: false,
inputs: [],
};
},
}; };

View File

@ -1,6 +1,6 @@
{ {
"name": "@0xproject/assert", "name": "@0xproject/assert",
"version": "0.0.8", "version": "0.0.9",
"description": "Provides a standard way of performing type and schema validation across 0x projects", "description": "Provides a standard way of performing type and schema validation across 0x projects",
"main": "lib/src/index.js", "main": "lib/src/index.js",
"types": "lib/src/index.d.ts", "types": "lib/src/index.d.ts",
@ -23,7 +23,7 @@
}, },
"homepage": "https://github.com/0xProject/0x.js/packages/assert/README.md", "homepage": "https://github.com/0xProject/0x.js/packages/assert/README.md",
"devDependencies": { "devDependencies": {
"@0xproject/tslint-config": "^0.3.0", "@0xproject/tslint-config": "^0.4.0",
"@types/lodash": "^4.14.86", "@types/lodash": "^4.14.86",
"@types/mocha": "^2.2.42", "@types/mocha": "^2.2.42",
"@types/valid-url": "^1.0.2", "@types/valid-url": "^1.0.2",
@ -37,8 +37,8 @@
"typescript": "~2.6.1" "typescript": "~2.6.1"
}, },
"dependencies": { "dependencies": {
"@0xproject/json-schemas": "^0.7.0", "@0xproject/json-schemas": "^0.7.1",
"@0xproject/utils": "^0.1.1", "@0xproject/utils": "^0.1.2",
"bignumber.js": "~4.1.0", "bignumber.js": "~4.1.0",
"lodash": "^4.17.4", "lodash": "^4.17.4",
"valid-url": "^1.0.9" "valid-url": "^1.0.9"

View File

@ -1,7 +1,4 @@
import { import { Schema, SchemaValidator } from '@0xproject/json-schemas';
Schema,
SchemaValidator,
} from '@0xproject/json-schemas';
import { addressUtils } from '@0xproject/utils'; import { addressUtils } from '@0xproject/utils';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import * as _ from 'lodash'; import * as _ from 'lodash';
@ -18,7 +15,8 @@ export const assert = {
assert.isBigNumber(variableName, value); assert.isBigNumber(variableName, value);
const hasDecimals = value.decimalPlaces() !== 0; const hasDecimals = value.decimalPlaces() !== 0;
this.assert( this.assert(
!hasDecimals, `${variableName} should be in baseUnits (no decimals), found value: ${value.toNumber()}`, !hasDecimals,
`${variableName} should be in baseUnits (no decimals), found value: ${value.toNumber()}`,
); );
}, },
isUndefined(value: any, variableName?: string): void { isUndefined(value: any, variableName?: string): void {
@ -31,8 +29,10 @@ export const assert = {
this.assert(_.isFunction(value), this.typeAssertionMessage(variableName, 'function', value)); this.assert(_.isFunction(value), this.typeAssertionMessage(variableName, 'function', value));
}, },
isHexString(variableName: string, value: string): void { isHexString(variableName: string, value: string): void {
this.assert(_.isString(value) && HEX_REGEX.test(value), this.assert(
this.typeAssertionMessage(variableName, 'HexString', value)); _.isString(value) && HEX_REGEX.test(value),
this.typeAssertionMessage(variableName, 'HexString', value),
);
}, },
isETHAddressHex(variableName: string, value: string): void { isETHAddressHex(variableName: string, value: string): void {
this.assert(addressUtils.isAddress(value), this.typeAssertionMessage(variableName, 'ETHAddressHex', value)); this.assert(addressUtils.isAddress(value), this.typeAssertionMessage(variableName, 'ETHAddressHex', value));
@ -41,8 +41,11 @@ export const assert = {
`Checksummed addresses are not supported. Convert ${variableName} to lower case before passing`, `Checksummed addresses are not supported. Convert ${variableName} to lower case before passing`,
); );
}, },
doesBelongToStringEnum(variableName: string, value: string, doesBelongToStringEnum(
stringEnum: any /* There is no base type for every string enum */): void { variableName: string,
value: string,
stringEnum: any /* There is no base type for every string enum */,
): void {
const doesBelongToStringEnum = !_.isUndefined(stringEnum[value]); const doesBelongToStringEnum = !_.isUndefined(stringEnum[value]);
const enumValues = _.keys(stringEnum); const enumValues = _.keys(stringEnum);
const enumValuesAsStrings = _.map(enumValues, enumValue => `'${enumValue}'`); const enumValuesAsStrings = _.map(enumValues, enumValue => `'${enumValue}'`);
@ -62,7 +65,7 @@ export const assert = {
this.assert(_.isBoolean(value), this.typeAssertionMessage(variableName, 'boolean', value)); this.assert(_.isBoolean(value), this.typeAssertionMessage(variableName, 'boolean', value));
}, },
isWeb3Provider(variableName: string, value: any): void { isWeb3Provider(variableName: string, value: any): void {
const isWeb3Provider = _.isFunction((value).send) || _.isFunction((value).sendAsync); const isWeb3Provider = _.isFunction(value.send) || _.isFunction(value.sendAsync);
this.assert(isWeb3Provider, this.typeAssertionMessage(variableName, 'Web3.Provider', value)); this.assert(isWeb3Provider, this.typeAssertionMessage(variableName, 'Web3.Provider', value));
}, },
doesConformToSchema(variableName: string, value: any, schema: Schema): void { doesConformToSchema(variableName: string, value: any, schema: Schema): void {

View File

@ -14,75 +14,41 @@ describe('Assertions', () => {
const variableName = 'variable'; const variableName = 'variable';
describe('#isBigNumber', () => { describe('#isBigNumber', () => {
it('should not throw for valid input', () => { it('should not throw for valid input', () => {
const validInputs = [ const validInputs = [new BigNumber(23), new BigNumber('45')];
new BigNumber(23),
new BigNumber('45'),
];
validInputs.forEach(input => expect(assert.isBigNumber.bind(assert, variableName, input)).to.not.throw()); validInputs.forEach(input => expect(assert.isBigNumber.bind(assert, variableName, input)).to.not.throw());
}); });
it('should throw for invalid input', () => { it('should throw for invalid input', () => {
const invalidInputs = [ const invalidInputs = ['test', 42, false, { random: 'test' }, undefined];
'test',
42,
false,
{random: 'test'},
undefined,
];
invalidInputs.forEach(input => expect(assert.isBigNumber.bind(assert, variableName, input)).to.throw()); invalidInputs.forEach(input => expect(assert.isBigNumber.bind(assert, variableName, input)).to.throw());
}); });
}); });
describe('#isUndefined', () => { describe('#isUndefined', () => {
it('should not throw for valid input', () => { it('should not throw for valid input', () => {
const validInputs = [ const validInputs = [undefined];
undefined,
];
validInputs.forEach(input => expect(assert.isUndefined.bind(assert, input, variableName)).to.not.throw()); validInputs.forEach(input => expect(assert.isUndefined.bind(assert, input, variableName)).to.not.throw());
}); });
it('should throw for invalid input', () => { it('should throw for invalid input', () => {
const invalidInputs = [ const invalidInputs = ['test', 42, false, { random: 'test' }];
'test',
42,
false,
{random: 'test'},
];
invalidInputs.forEach(input => expect(assert.isUndefined.bind(assert, input, variableName)).to.throw()); invalidInputs.forEach(input => expect(assert.isUndefined.bind(assert, input, variableName)).to.throw());
}); });
}); });
describe('#isString', () => { describe('#isString', () => {
it('should not throw for valid input', () => { it('should not throw for valid input', () => {
const validInputs = [ const validInputs = ['hello', 'goodbye'];
'hello',
'goodbye',
];
validInputs.forEach(input => expect(assert.isString.bind(assert, variableName, input)).to.not.throw()); validInputs.forEach(input => expect(assert.isString.bind(assert, variableName, input)).to.not.throw());
}); });
it('should throw for invalid input', () => { it('should throw for invalid input', () => {
const invalidInputs = [ const invalidInputs = [42, false, { random: 'test' }, undefined, new BigNumber(45)];
42,
false,
{random: 'test'},
undefined,
new BigNumber(45),
];
invalidInputs.forEach(input => expect(assert.isString.bind(assert, variableName, input)).to.throw()); invalidInputs.forEach(input => expect(assert.isString.bind(assert, variableName, input)).to.throw());
}); });
}); });
describe('#isFunction', () => { describe('#isFunction', () => {
it('should not throw for valid input', () => { it('should not throw for valid input', () => {
const validInputs = [ const validInputs = [BigNumber, assert.isString.bind(this)];
BigNumber,
assert.isString.bind(this),
];
validInputs.forEach(input => expect(assert.isFunction.bind(assert, variableName, input)).to.not.throw()); validInputs.forEach(input => expect(assert.isFunction.bind(assert, variableName, input)).to.not.throw());
}); });
it('should throw for invalid input', () => { it('should throw for invalid input', () => {
const invalidInputs = [ const invalidInputs = [42, false, { random: 'test' }, undefined, new BigNumber(45)];
42,
false,
{random: 'test'},
undefined,
new BigNumber(45),
];
invalidInputs.forEach(input => expect(assert.isFunction.bind(assert, variableName, input)).to.throw()); invalidInputs.forEach(input => expect(assert.isFunction.bind(assert, variableName, input)).to.throw());
}); });
}); });
@ -127,9 +93,7 @@ describe('Assertions', () => {
'0x6FFFd0ae3f7d88c9b4925323f54c6e4b2918c5fd', '0x6FFFd0ae3f7d88c9b4925323f54c6e4b2918c5fd',
'0x6FFFd0ae3f7d88c9b4925323f54c6e4', '0x6FFFd0ae3f7d88c9b4925323f54c6e4',
]; ];
invalidInputs.forEach(input => invalidInputs.forEach(input => expect(assert.isETHAddressHex.bind(assert, variableName, input)).to.throw());
expect(assert.isETHAddressHex.bind(assert, variableName, input)).to.throw(),
);
}); });
}); });
describe('#doesBelongToStringEnum', () => { describe('#doesBelongToStringEnum', () => {
@ -138,22 +102,13 @@ describe('Assertions', () => {
Test2 = 'Test2', Test2 = 'Test2',
} }
it('should not throw for valid input', () => { it('should not throw for valid input', () => {
const validInputs = [ const validInputs = [TestEnums.Test1, TestEnums.Test2];
TestEnums.Test1,
TestEnums.Test2,
];
validInputs.forEach(input => validInputs.forEach(input =>
expect(assert.doesBelongToStringEnum.bind(assert, variableName, input, TestEnums)).to.not.throw(), expect(assert.doesBelongToStringEnum.bind(assert, variableName, input, TestEnums)).to.not.throw(),
); );
}); });
it('should throw for invalid input', () => { it('should throw for invalid input', () => {
const invalidInputs = [ const invalidInputs = [42, false, { random: 'test' }, undefined, new BigNumber(45)];
42,
false,
{random: 'test'},
undefined,
new BigNumber(45),
];
invalidInputs.forEach(input => invalidInputs.forEach(input =>
expect(assert.doesBelongToStringEnum.bind(assert, variableName, input, TestEnums)).to.throw(), expect(assert.doesBelongToStringEnum.bind(assert, variableName, input, TestEnums)).to.throw(),
); );
@ -162,19 +117,13 @@ describe('Assertions', () => {
describe('#hasAtMostOneUniqueValue', () => { describe('#hasAtMostOneUniqueValue', () => {
const errorMsg = 'more than one unique value'; const errorMsg = 'more than one unique value';
it('should not throw for valid input', () => { it('should not throw for valid input', () => {
const validInputs = [ const validInputs = [['hello'], ['goodbye', 'goodbye', 'goodbye']];
['hello'],
['goodbye', 'goodbye', 'goodbye'],
];
validInputs.forEach(input => validInputs.forEach(input =>
expect(assert.hasAtMostOneUniqueValue.bind(assert, input, errorMsg)).to.not.throw(), expect(assert.hasAtMostOneUniqueValue.bind(assert, input, errorMsg)).to.not.throw(),
); );
}); });
it('should throw for invalid input', () => { it('should throw for invalid input', () => {
const invalidInputs = [ const invalidInputs = [['hello', 'goodbye'], ['goodbye', 42, false, false]];
['hello', 'goodbye'],
['goodbye', 42, false, false],
];
invalidInputs.forEach(input => invalidInputs.forEach(input =>
expect(assert.hasAtMostOneUniqueValue.bind(assert, input, errorMsg)).to.throw(), expect(assert.hasAtMostOneUniqueValue.bind(assert, input, errorMsg)).to.throw(),
); );
@ -182,61 +131,34 @@ describe('Assertions', () => {
}); });
describe('#isNumber', () => { describe('#isNumber', () => {
it('should not throw for valid input', () => { it('should not throw for valid input', () => {
const validInputs = [ const validInputs = [42, 0, 21e42];
42,
0,
21e+42,
];
validInputs.forEach(input => expect(assert.isNumber.bind(assert, variableName, input)).to.not.throw()); validInputs.forEach(input => expect(assert.isNumber.bind(assert, variableName, input)).to.not.throw());
}); });
it('should throw for invalid input', () => { it('should throw for invalid input', () => {
const invalidInputs = [ const invalidInputs = [false, { random: 'test' }, undefined, new BigNumber(45)];
false,
{random: 'test'},
undefined,
new BigNumber(45),
];
invalidInputs.forEach(input => expect(assert.isNumber.bind(assert, variableName, input)).to.throw()); invalidInputs.forEach(input => expect(assert.isNumber.bind(assert, variableName, input)).to.throw());
}); });
}); });
describe('#isBoolean', () => { describe('#isBoolean', () => {
it('should not throw for valid input', () => { it('should not throw for valid input', () => {
const validInputs = [ const validInputs = [true, false];
true,
false,
];
validInputs.forEach(input => expect(assert.isBoolean.bind(assert, variableName, input)).to.not.throw()); validInputs.forEach(input => expect(assert.isBoolean.bind(assert, variableName, input)).to.not.throw());
}); });
it('should throw for invalid input', () => { it('should throw for invalid input', () => {
const invalidInputs = [ const invalidInputs = [42, { random: 'test' }, undefined, new BigNumber(45)];
42,
{random: 'test'},
undefined,
new BigNumber(45),
];
invalidInputs.forEach(input => expect(assert.isBoolean.bind(assert, variableName, input)).to.throw()); invalidInputs.forEach(input => expect(assert.isBoolean.bind(assert, variableName, input)).to.throw());
}); });
}); });
describe('#isWeb3Provider', () => { describe('#isWeb3Provider', () => {
it('should not throw for valid input', () => { it('should not throw for valid input', () => {
const validInputs = [ const validInputs = [{ send: () => 45 }, { sendAsync: () => 45 }];
{send: () => 45},
{sendAsync: () => 45},
];
validInputs.forEach(input => validInputs.forEach(input =>
expect(assert.isWeb3Provider.bind(assert, variableName, input)).to.not.throw(), expect(assert.isWeb3Provider.bind(assert, variableName, input)).to.not.throw(),
); );
}); });
it('should throw for invalid input', () => { it('should throw for invalid input', () => {
const invalidInputs = [ const invalidInputs = [42, { random: 'test' }, undefined, new BigNumber(45)];
42, invalidInputs.forEach(input => expect(assert.isWeb3Provider.bind(assert, variableName, input)).to.throw());
{random: 'test'},
undefined,
new BigNumber(45),
];
invalidInputs.forEach(input =>
expect(assert.isWeb3Provider.bind(assert, variableName, input)).to.throw(),
);
}); });
}); });
describe('#doesConformToSchema', () => { describe('#doesConformToSchema', () => {
@ -251,12 +173,7 @@ describe('Assertions', () => {
); );
}); });
it('should throw for invalid input', () => { it('should throw for invalid input', () => {
const invalidInputs = [ const invalidInputs = [42, { random: 'test' }, undefined, new BigNumber(45)];
42,
{random: 'test'},
undefined,
new BigNumber(45),
];
invalidInputs.forEach(input => invalidInputs.forEach(input =>
expect(assert.doesConformToSchema.bind(assert, variableName, input, schema)).to.throw(), expect(assert.doesConformToSchema.bind(assert, variableName, input, schema)).to.throw(),
); );
@ -270,9 +187,7 @@ describe('Assertions', () => {
'https://api.radarrelay.com/0x/v0/', 'https://api.radarrelay.com/0x/v0/',
'https://zeroex.beta.radarrelay.com:8000/0x/v0/', 'https://zeroex.beta.radarrelay.com:8000/0x/v0/',
]; ];
validInputs.forEach(input => validInputs.forEach(input => expect(assert.isHttpUrl.bind(assert, variableName, input)).to.not.throw());
expect(assert.isHttpUrl.bind(assert, variableName, input)).to.not.throw(),
);
}); });
it('should throw for invalid input', () => { it('should throw for invalid input', () => {
const invalidInputs = [ const invalidInputs = [
@ -286,9 +201,7 @@ describe('Assertions', () => {
'user:password@api.example-relayer.net', 'user:password@api.example-relayer.net',
'//api.example-relayer.net', '//api.example-relayer.net',
]; ];
invalidInputs.forEach(input => invalidInputs.forEach(input => expect(assert.isHttpUrl.bind(assert, variableName, input)).to.throw());
expect(assert.isHttpUrl.bind(assert, variableName, input)).to.throw(),
);
}); });
}); });
describe('#isUri', () => { describe('#isUri', () => {
@ -302,9 +215,7 @@ describe('Assertions', () => {
'wss://www.api.example-relayer.net', 'wss://www.api.example-relayer.net',
'user:password@api.example-relayer.net', 'user:password@api.example-relayer.net',
]; ];
validInputs.forEach(input => validInputs.forEach(input => expect(assert.isUri.bind(assert, variableName, input)).to.not.throw());
expect(assert.isUri.bind(assert, variableName, input)).to.not.throw(),
);
}); });
it('should throw for invalid input', () => { it('should throw for invalid input', () => {
const invalidInputs = [ const invalidInputs = [
@ -316,9 +227,7 @@ describe('Assertions', () => {
'api.example-relayer.net', 'api.example-relayer.net',
'//api.example-relayer.net', '//api.example-relayer.net',
]; ];
invalidInputs.forEach(input => invalidInputs.forEach(input => expect(assert.isUri.bind(assert, variableName, input)).to.throw());
expect(assert.isUri.bind(assert, variableName, input)).to.throw(),
);
}); });
}); });
describe('#assert', () => { describe('#assert', () => {
@ -332,8 +241,9 @@ describe('Assertions', () => {
}); });
describe('#typeAssertionMessage', () => { describe('#typeAssertionMessage', () => {
it('should render correct message', () => { it('should render correct message', () => {
expect(assert.typeAssertionMessage('variable', 'string', 'number')) expect(assert.typeAssertionMessage('variable', 'string', 'number')).to.equal(
.to.equal(`Expected variable to be of type string, encountered: number`); `Expected variable to be of type string, encountered: number`,
);
}); });
}); });
}); });

View File

@ -1,6 +1,6 @@
{ {
"name": "@0xproject/connect", "name": "@0xproject/connect",
"version": "0.3.1", "version": "0.3.2",
"description": "A javascript library for interacting with the standard relayer api", "description": "A javascript library for interacting with the standard relayer api",
"keywords": [ "keywords": [
"connect", "connect",
@ -36,9 +36,9 @@
}, },
"homepage": "https://github.com/0xProject/0x.js/packages/connect/README.md", "homepage": "https://github.com/0xProject/0x.js/packages/connect/README.md",
"dependencies": { "dependencies": {
"@0xproject/assert": "^0.0.8", "@0xproject/assert": "^0.0.9",
"@0xproject/json-schemas": "^0.7.0", "@0xproject/json-schemas": "^0.7.1",
"@0xproject/utils": "^0.1.1", "@0xproject/utils": "^0.1.2",
"bignumber.js": "~4.1.0", "bignumber.js": "~4.1.0",
"isomorphic-fetch": "^2.2.1", "isomorphic-fetch": "^2.2.1",
"lodash": "^4.17.4", "lodash": "^4.17.4",
@ -46,7 +46,7 @@
"websocket": "^1.0.25" "websocket": "^1.0.25"
}, },
"devDependencies": { "devDependencies": {
"@0xproject/tslint-config": "^0.3.0", "@0xproject/tslint-config": "^0.4.0",
"@types/fetch-mock": "^5.12.1", "@types/fetch-mock": "^5.12.1",
"@types/lodash": "^4.14.86", "@types/lodash": "^4.14.86",
"@types/mocha": "^2.2.42", "@types/mocha": "^2.2.42",

View File

@ -49,8 +49,7 @@ export class HttpClient implements Client {
params: request, params: request,
}; };
const tokenPairs = await this._requestAsync('/token_pairs', HttpRequestType.Get, requestOpts); const tokenPairs = await this._requestAsync('/token_pairs', HttpRequestType.Get, requestOpts);
assert.doesConformToSchema( assert.doesConformToSchema('tokenPairs', tokenPairs, schemas.relayerApiTokenPairsResponseSchema);
'tokenPairs', tokenPairs, schemas.relayerApiTokenPairsResponseSchema);
_.each(tokenPairs, (tokenPair: object) => { _.each(tokenPairs, (tokenPair: object) => {
typeConverters.convertStringsFieldsToBigNumbers(tokenPair, [ typeConverters.convertStringsFieldsToBigNumbers(tokenPair, [
'tokenA.minAmount', 'tokenA.minAmount',
@ -131,8 +130,11 @@ export class HttpClient implements Client {
}; };
await this._requestAsync('/order', HttpRequestType.Post, requestOpts); await this._requestAsync('/order', HttpRequestType.Post, requestOpts);
} }
private async _requestAsync(path: string, requestType: HttpRequestType, private async _requestAsync(
requestOptions?: HttpRequestOptions): Promise<any> { path: string,
requestType: HttpRequestType,
requestOptions?: HttpRequestOptions,
): Promise<any> {
const params = _.get(requestOptions, 'params'); const params = _.get(requestOptions, 'params');
const payload = _.get(requestOptions, 'payload'); const payload = _.get(requestOptions, 'payload');
let query = ''; let query = '';

View File

@ -1,12 +1,6 @@
import { import { relayerOrderBookRequestSchema } from './relayer_orderbook_request_schema';
relayerOrderBookRequestSchema, import { relayerOrdersRequestSchema } from './relayer_orders_request_schema';
} from './relayer_orderbook_request_schema'; import { relayerTokenPairsRequestSchema } from './relayer_token_pairs_request_schema';
import {
relayerOrdersRequestSchema,
} from './relayer_orders_request_schema';
import {
relayerTokenPairsRequestSchema,
} from './relayer_token_pairs_request_schema';
export const schemas = { export const schemas = {
relayerOrderBookRequestSchema, relayerOrderBookRequestSchema,

View File

@ -57,19 +57,24 @@ export interface OrderbookChannelSubscriptionOpts {
} }
export interface OrderbookChannelHandler { export interface OrderbookChannelHandler {
onSnapshot: (channel: OrderbookChannel, subscriptionOpts: OrderbookChannelSubscriptionOpts, onSnapshot: (
snapshot: OrderbookResponse) => void; channel: OrderbookChannel,
onUpdate: (channel: OrderbookChannel, subscriptionOpts: OrderbookChannelSubscriptionOpts, subscriptionOpts: OrderbookChannelSubscriptionOpts,
order: SignedOrder) => void; snapshot: OrderbookResponse,
onError: (channel: OrderbookChannel, subscriptionOpts: OrderbookChannelSubscriptionOpts, ) => void;
err: Error) => void; onUpdate: (
channel: OrderbookChannel,
subscriptionOpts: OrderbookChannelSubscriptionOpts,
order: SignedOrder,
) => void;
onError: (channel: OrderbookChannel, subscriptionOpts: OrderbookChannelSubscriptionOpts, err: Error) => void;
onClose: (channel: OrderbookChannel, subscriptionOpts: OrderbookChannelSubscriptionOpts) => void; onClose: (channel: OrderbookChannel, subscriptionOpts: OrderbookChannelSubscriptionOpts) => void;
} }
export type OrderbookChannelMessage = export type OrderbookChannelMessage =
SnapshotOrderbookChannelMessage | | SnapshotOrderbookChannelMessage
UpdateOrderbookChannelMessage | | UpdateOrderbookChannelMessage
UnknownOrderbookChannelMessage; | UnknownOrderbookChannelMessage;
export enum OrderbookChannelMessageTypes { export enum OrderbookChannelMessageTypes {
Snapshot = 'snapshot', Snapshot = 'snapshot',

View File

@ -2,10 +2,7 @@ import {assert} from '@0xproject/assert';
import { schemas } from '@0xproject/json-schemas'; import { schemas } from '@0xproject/json-schemas';
import * as _ from 'lodash'; import * as _ from 'lodash';
import { import { OrderbookChannelMessage, OrderbookChannelMessageTypes } from '../types';
OrderbookChannelMessage,
OrderbookChannelMessageTypes,
} from '../types';
import { typeConverters } from './type_converters'; import { typeConverters } from './type_converters';
@ -16,13 +13,13 @@ export const orderbookChannelMessageParsers = {
assert.assert(!_.isUndefined(type), `Message is missing a type parameter: ${utf8Data}`); assert.assert(!_.isUndefined(type), `Message is missing a type parameter: ${utf8Data}`);
assert.isString('type', type); assert.isString('type', type);
switch (type) { switch (type) {
case (OrderbookChannelMessageTypes.Snapshot): { case OrderbookChannelMessageTypes.Snapshot: {
assert.doesConformToSchema('message', messageObj, schemas.relayerApiOrderbookChannelSnapshotSchema); assert.doesConformToSchema('message', messageObj, schemas.relayerApiOrderbookChannelSnapshotSchema);
const orderbook = messageObj.payload; const orderbook = messageObj.payload;
typeConverters.convertOrderbookStringFieldsToBigNumber(orderbook); typeConverters.convertOrderbookStringFieldsToBigNumber(orderbook);
return messageObj; return messageObj;
} }
case (OrderbookChannelMessageTypes.Update): { case OrderbookChannelMessageTypes.Update: {
assert.doesConformToSchema('message', messageObj, schemas.relayerApiOrderbookChannelUpdateSchema); assert.doesConformToSchema('message', messageObj, schemas.relayerApiOrderbookChannelUpdateSchema);
const order = messageObj.payload; const order = messageObj.payload;
typeConverters.convertOrderStringFieldsToBigNumber(order); typeConverters.convertOrderStringFieldsToBigNumber(order);

View File

@ -41,7 +41,10 @@ export class WebSocketOrderbookChannel implements OrderbookChannel {
*/ */
public subscribe(subscriptionOpts: OrderbookChannelSubscriptionOpts, handler: OrderbookChannelHandler): void { public subscribe(subscriptionOpts: OrderbookChannelSubscriptionOpts, handler: OrderbookChannelHandler): void {
assert.doesConformToSchema( assert.doesConformToSchema(
'subscriptionOpts', subscriptionOpts, schemas.relayerApiOrderbookChannelSubscribePayload); 'subscriptionOpts',
subscriptionOpts,
schemas.relayerApiOrderbookChannelSubscribePayload,
);
assert.isFunction('handler.onSnapshot', _.get(handler, 'onSnapshot')); assert.isFunction('handler.onSnapshot', _.get(handler, 'onSnapshot'));
assert.isFunction('handler.onUpdate', _.get(handler, 'onUpdate')); assert.isFunction('handler.onUpdate', _.get(handler, 'onUpdate'));
assert.isFunction('handler.onError', _.get(handler, 'onError')); assert.isFunction('handler.onError', _.get(handler, 'onError'));
@ -92,25 +95,32 @@ export class WebSocketOrderbookChannel implements OrderbookChannel {
this._client.connect(this._apiEndpointUrl); this._client.connect(this._apiEndpointUrl);
} }
} }
private _handleWebSocketMessage(requestId: number, subscriptionOpts: OrderbookChannelSubscriptionOpts, private _handleWebSocketMessage(
message: WebSocket.IMessage, handler: OrderbookChannelHandler): void { requestId: number,
subscriptionOpts: OrderbookChannelSubscriptionOpts,
message: WebSocket.IMessage,
handler: OrderbookChannelHandler,
): void {
if (!_.isUndefined(message.utf8Data)) { if (!_.isUndefined(message.utf8Data)) {
try { try {
const utf8Data = message.utf8Data; const utf8Data = message.utf8Data;
const parserResult = orderbookChannelMessageParsers.parser(utf8Data); const parserResult = orderbookChannelMessageParsers.parser(utf8Data);
if (parserResult.requestId === requestId) { if (parserResult.requestId === requestId) {
switch (parserResult.type) { switch (parserResult.type) {
case (OrderbookChannelMessageTypes.Snapshot): { case OrderbookChannelMessageTypes.Snapshot: {
handler.onSnapshot(this, subscriptionOpts, parserResult.payload); handler.onSnapshot(this, subscriptionOpts, parserResult.payload);
break; break;
} }
case (OrderbookChannelMessageTypes.Update): { case OrderbookChannelMessageTypes.Update: {
handler.onUpdate(this, subscriptionOpts, parserResult.payload); handler.onUpdate(this, subscriptionOpts, parserResult.payload);
break; break;
} }
default: { default: {
handler.onError( handler.onError(
this, subscriptionOpts, new Error(`Message has missing a type parameter: ${utf8Data}`)); this,
subscriptionOpts,
new Error(`Message has missing a type parameter: ${utf8Data}`),
);
} }
} }
} }

View File

@ -9,10 +9,7 @@ import {HttpClient} from '../src/index';
import { feesResponse } from './fixtures/standard_relayer_api/fees'; import { feesResponse } from './fixtures/standard_relayer_api/fees';
import * as feesResponseJSON from './fixtures/standard_relayer_api/fees.json'; import * as feesResponseJSON from './fixtures/standard_relayer_api/fees.json';
import { import { orderResponse } from './fixtures/standard_relayer_api/order/0xabc67323774bdbd24d94f977fa9ac94a50f016026fd13f42990861238897721f';
orderResponse,
} from './fixtures/standard_relayer_api/order/0xabc67323774bdbd24d94f977fa9ac94a50f016026fd13f42990861238897721f';
// tslint:disable-next-line:max-line-length
import * as orderResponseJSON from './fixtures/standard_relayer_api/order/0xabc67323774bdbd24d94f977fa9ac94a50f016026fd13f42990861238897721f.json'; import * as orderResponseJSON from './fixtures/standard_relayer_api/order/0xabc67323774bdbd24d94f977fa9ac94a50f016026fd13f42990861238897721f.json';
import { orderbookResponse } from './fixtures/standard_relayer_api/orderbook'; import { orderbookResponse } from './fixtures/standard_relayer_api/orderbook';
import * as orderbookJSON from './fixtures/standard_relayer_api/orderbook.json'; import * as orderbookJSON from './fixtures/standard_relayer_api/orderbook.json';
@ -94,8 +91,9 @@ describe('HttpClient', () => {
baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d', baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
quoteTokenAddress: '0xa2b31dacf30a9c50ca473337c01d8a201ae33e32', quoteTokenAddress: '0xa2b31dacf30a9c50ca473337c01d8a201ae33e32',
}; };
// tslint:disable-next-line:max-line-length const url = `${relayUrl}/v0/orderbook?baseTokenAddress=${request.baseTokenAddress}&quoteTokenAddress=${
const url = `${relayUrl}/v0/orderbook?baseTokenAddress=${request.baseTokenAddress}&quoteTokenAddress=${request.quoteTokenAddress}`; request.quoteTokenAddress
}`;
it('gets order book', async () => { it('gets order book', async () => {
fetchMock.get(url, orderbookJSON); fetchMock.get(url, orderbookJSON);
const orderbook = await relayerClient.getOrderbookAsync(request); const orderbook = await relayerClient.getOrderbookAsync(request);

View File

@ -4,7 +4,6 @@ import 'mocha';
import { orderbookChannelMessageParsers } from '../src/utils/orderbook_channel_message_parsers'; import { orderbookChannelMessageParsers } from '../src/utils/orderbook_channel_message_parsers';
// tslint:disable-next-line:max-line-length
import { orderResponse } from './fixtures/standard_relayer_api/order/0xabc67323774bdbd24d94f977fa9ac94a50f016026fd13f42990861238897721f'; import { orderResponse } from './fixtures/standard_relayer_api/order/0xabc67323774bdbd24d94f977fa9ac94a50f016026fd13f42990861238897721f';
import { orderbookResponse } from './fixtures/standard_relayer_api/orderbook'; import { orderbookResponse } from './fixtures/standard_relayer_api/orderbook';
import { import {
@ -58,15 +57,13 @@ describe('orderbookChannelMessageParsers', () => {
expect(badCall).throws('Expected type to be of type string, encountered: 1'); expect(badCall).throws('Expected type to be of type string, encountered: 1');
}); });
it('throws when snapshot message has malformed payload', () => { it('throws when snapshot message has malformed payload', () => {
const badCall = () => const badCall = () => orderbookChannelMessageParsers.parser(malformedSnapshotOrderbookChannelMessage);
orderbookChannelMessageParsers.parser(malformedSnapshotOrderbookChannelMessage); const errMsg =
// tslint:disable-next-line:max-line-length 'Validation errors: instance.payload requires property "bids", instance.payload requires property "asks"';
const errMsg = 'Validation errors: instance.payload requires property "bids", instance.payload requires property "asks"';
expect(badCall).throws(errMsg); expect(badCall).throws(errMsg);
}); });
it('throws when update message has malformed payload', () => { it('throws when update message has malformed payload', () => {
const badCall = () => const badCall = () => orderbookChannelMessageParsers.parser(malformedUpdateOrderbookChannelMessage);
orderbookChannelMessageParsers.parser(malformedUpdateOrderbookChannelMessage);
expect(badCall).throws(/^Expected message to conform to schema/); expect(badCall).throws(/^Expected message to conform to schema/);
}); });
it('throws when input message is not valid JSON', () => { it('throws when input message is not valid JSON', () => {

View File

@ -3,9 +3,7 @@ import * as dirtyChai from 'dirty-chai';
import * as _ from 'lodash'; import * as _ from 'lodash';
import 'mocha'; import 'mocha';
import { import { WebSocketOrderbookChannel } from '../src/ws_orderbook_channel';
WebSocketOrderbookChannel,
} from '../src/ws_orderbook_channel';
chai.config.includeStack = true; chai.config.includeStack = true;
chai.use(dirtyChai); chai.use(dirtyChai);
@ -21,26 +19,42 @@ describe('WebSocketOrderbookChannel', () => {
limit: 100, limit: 100,
}; };
const emptyOrderbookChannelHandler = { const emptyOrderbookChannelHandler = {
onSnapshot: () => { _.noop(); }, onSnapshot: () => {
onUpdate: () => { _.noop(); }, _.noop();
onError: () => { _.noop(); }, },
onClose: () => { _.noop(); }, onUpdate: () => {
_.noop();
},
onError: () => {
_.noop();
},
onClose: () => {
_.noop();
},
}; };
describe('#subscribe', () => { describe('#subscribe', () => {
it('throws when subscriptionOpts does not conform to schema', () => { it('throws when subscriptionOpts does not conform to schema', () => {
const badSubscribeCall = orderbookChannel.subscribe.bind( const badSubscribeCall = orderbookChannel.subscribe.bind(
orderbookChannel, {}, emptyOrderbookChannelHandler); orderbookChannel,
// tslint:disable-next-line:max-line-length {},
expect(badSubscribeCall).throws('Expected subscriptionOpts to conform to schema /RelayerApiOrderbookChannelSubscribePayload\nEncountered: {}\nValidation errors: instance requires property "baseTokenAddress", instance requires property "quoteTokenAddress"'); emptyOrderbookChannelHandler,
);
expect(badSubscribeCall).throws(
'Expected subscriptionOpts to conform to schema /RelayerApiOrderbookChannelSubscribePayload\nEncountered: {}\nValidation errors: instance requires property "baseTokenAddress", instance requires property "quoteTokenAddress"',
);
}); });
it('throws when handler has the incorrect members', () => { it('throws when handler has the incorrect members', () => {
const badSubscribeCall = orderbookChannel.subscribe.bind(orderbookChannel, subscriptionOpts, {}); const badSubscribeCall = orderbookChannel.subscribe.bind(orderbookChannel, subscriptionOpts, {});
expect(badSubscribeCall) expect(badSubscribeCall).throws(
.throws('Expected handler.onSnapshot to be of type function, encountered: undefined'); 'Expected handler.onSnapshot to be of type function, encountered: undefined',
);
}); });
it('does not throw when inputs are of correct types', () => { it('does not throw when inputs are of correct types', () => {
const goodSubscribeCall = orderbookChannel.subscribe.bind( const goodSubscribeCall = orderbookChannel.subscribe.bind(
orderbookChannel, subscriptionOpts, emptyOrderbookChannelHandler); orderbookChannel,
subscriptionOpts,
emptyOrderbookChannelHandler,
);
expect(goodSubscribeCall).to.not.throw(); expect(goodSubscribeCall).to.not.throw();
}); });
}); });

View File

@ -6,18 +6,14 @@ import * as Web3 from 'web3';
import * as yargs from 'yargs'; import * as yargs from 'yargs';
import { commands } from './src/commands'; import { commands } from './src/commands';
import { import { CliOptions, CompilerOptions, DeployerOptions } from './src/utils/types';
CliOptions,
CompilerOptions,
DeployerOptions,
} from './src/utils/types';
const DEFAULT_OPTIMIZER_ENABLED = false; const DEFAULT_OPTIMIZER_ENABLED = false;
const DEFAULT_CONTRACTS_DIR = path.resolve('contracts'); const DEFAULT_CONTRACTS_DIR = path.resolve('contracts');
const DEFAULT_ARTIFACTS_DIR = `${path.resolve('build')}/artifacts/`; const DEFAULT_ARTIFACTS_DIR = `${path.resolve('build')}/artifacts/`;
const DEFAULT_NETWORK_ID = 50; const DEFAULT_NETWORK_ID = 50;
const DEFAULT_JSONRPC_PORT = 8545; const DEFAULT_JSONRPC_PORT = 8545;
const DEFAULT_GAS_PRICE = ((10 ** 9) * 2).toString(); const DEFAULT_GAS_PRICE = (10 ** 9 * 2).toString();
/** /**
* Compiles all contracts with options passed in through CLI. * Compiles all contracts with options passed in through CLI.
@ -108,8 +104,7 @@ function deployCommandBuilder(yargsInstance: any) {
description: 'comma separated list of constructor args to deploy contract with', description: 'comma separated list of constructor args to deploy contract with',
}) })
.demandOption(['contract', 'args']) .demandOption(['contract', 'args'])
.help() .help().argv;
.argv;
} }
(() => { (() => {
@ -149,18 +144,13 @@ function deployCommandBuilder(yargsInstance: any) {
type: 'string', type: 'string',
description: 'account to use for deploying contracts', description: 'account to use for deploying contracts',
}) })
.command('compile', .command('compile', 'compile contracts', identityCommandBuilder, onCompileCommand)
'compile contracts', .command(
identityCommandBuilder, 'migrate',
onCompileCommand)
.command('migrate',
'compile and deploy contracts using migration scripts', 'compile and deploy contracts using migration scripts',
identityCommandBuilder, identityCommandBuilder,
onMigrateCommand) onMigrateCommand,
.command('deploy', )
'deploy a single contract with provided arguments', .command('deploy', 'deploy a single contract with provided arguments', deployCommandBuilder, onDeployCommand)
deployCommandBuilder, .help().argv;
onDeployCommand)
.help()
.argv;
})(); })();

View File

@ -29,7 +29,8 @@ export const migrator = {
const multiSigArgs = [owners, confirmationsRequired, secondsRequired, tokenTransferProxy.address]; const multiSigArgs = [owners, confirmationsRequired, secondsRequired, tokenTransferProxy.address];
const exchange = await deployer.deployAndSaveAsync('Exchange', exchangeArgs); const exchange = await deployer.deployAndSaveAsync('Exchange', exchangeArgs);
const multiSig = await deployer.deployAndSaveAsync( const multiSig = await deployer.deployAndSaveAsync(
'MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress', multiSigArgs, 'MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress',
multiSigArgs,
); );
const owner = accounts[0]; const owner = accounts[0];
@ -70,12 +71,7 @@ export const migrator = {
); );
for (const token of tokenInfo) { for (const token of tokenInfo) {
const totalSupply = new BigNumber(0); const totalSupply = new BigNumber(0);
const args = [ const args = [token.name, token.symbol, token.decimals, totalSupply];
token.name,
token.symbol,
token.decimals,
totalSupply,
];
const dummyToken = await deployer.deployAsync('DummyToken', args); const dummyToken = await deployer.deployAsync('DummyToken', args);
await tokenReg.addToken.sendTransactionAsync( await tokenReg.addToken.sendTransactionAsync(
dummyToken.address, dummyToken.address,

View File

@ -150,7 +150,8 @@ export class Compiler {
currentArtifact = JSON.parse(currentArtifactString); currentArtifact = JSON.parse(currentArtifactString);
oldNetworks = currentArtifact.networks; oldNetworks = currentArtifact.networks;
const oldNetwork: ContractData = oldNetworks[this._networkId]; const oldNetwork: ContractData = oldNetworks[this._networkId];
shouldCompile = _.isUndefined(oldNetwork) || shouldCompile =
_.isUndefined(oldNetwork) ||
oldNetwork.keccak256 !== sourceHash || oldNetwork.keccak256 !== sourceHash ||
oldNetwork.optimizer_enabled !== this._optimizerEnabled; oldNetwork.optimizer_enabled !== this._optimizerEnabled;
} catch (err) { } catch (err) {
@ -174,9 +175,11 @@ export class Compiler {
const sourcesToCompile = { const sourcesToCompile = {
sources: input, sources: input,
}; };
const compiled = solcInstance.compile(sourcesToCompile, const compiled = solcInstance.compile(
sourcesToCompile,
this._optimizerEnabled, this._optimizerEnabled,
this._findImportsIfSourcesExist.bind(this)); this._findImportsIfSourcesExist.bind(this),
);
if (!_.isUndefined(compiled.errors)) { if (!_.isUndefined(compiled.errors)) {
_.each(compiled.errors, errMsg => { _.each(compiled.errors, errMsg => {

View File

@ -6,11 +6,7 @@ import * as Web3 from 'web3';
import { Contract } from './utils/contract'; import { Contract } from './utils/contract';
import { encoder } from './utils/encoder'; import { encoder } from './utils/encoder';
import { fsWrapper } from './utils/fs_wrapper'; import { fsWrapper } from './utils/fs_wrapper';
import { import { ContractArtifact, ContractData, DeployerOptions } from './utils/types';
ContractArtifact,
ContractData,
DeployerOptions,
} from './utils/types';
import { utils } from './utils/utils'; import { utils } from './utils/utils';
// Gas added to gas estimate to make sure there is sufficient gas for deployment. // Gas added to gas estimate to make sure there is sufficient gas for deployment.
@ -99,8 +95,11 @@ export class Deployer {
* @param contractAddress Contract address to save to artifact. * @param contractAddress Contract address to save to artifact.
* @param args Contract constructor arguments that will be encoded and saved to artifact. * @param args Contract constructor arguments that will be encoded and saved to artifact.
*/ */
private async _saveContractDataToArtifactAsync(contractName: string, private async _saveContractDataToArtifactAsync(
contractAddress: string, args: any[]): Promise<void> { contractName: string,
contractAddress: string,
args: any[],
): Promise<void> {
const contractArtifact: ContractArtifact = this._loadContractArtifactIfExists(contractName); const contractArtifact: ContractArtifact = this._loadContractArtifactIfExists(contractName);
const contractData: ContractData = this._getContractDataFromArtifactIfExists(contractArtifact); const contractData: ContractData = this._getContractDataFromArtifactIfExists(contractArtifact);
const abi = contractData.abi; const abi = contractData.abi;

File diff suppressed because one or more lines are too long

View File

@ -1,11 +1,6 @@
import { Artifacts } from '../util/artifacts'; import { Artifacts } from '../util/artifacts';
import { MultiSigConfigByNetwork } from '../util/types'; import { MultiSigConfigByNetwork } from '../util/types';
const { const { MultiSigWalletWithTimeLock, TokenTransferProxy, EtherToken, TokenRegistry } = new Artifacts(artifacts);
MultiSigWalletWithTimeLock,
TokenTransferProxy,
EtherToken,
TokenRegistry,
} = new Artifacts(artifacts);
let multiSigConfigByNetwork: MultiSigConfigByNetwork; let multiSigConfigByNetwork: MultiSigConfigByNetwork;
try { try {
@ -25,13 +20,15 @@ module.exports = (deployer: any, network: string, accounts: string[]) => {
}; };
const config = multiSigConfigByNetwork[network] || defaultConfig; const config = multiSigConfigByNetwork[network] || defaultConfig;
if (network !== 'live') { if (network !== 'live') {
deployer.deploy(MultiSigWalletWithTimeLock, config.owners, deployer
config.confirmationsRequired, config.secondsRequired) .deploy(MultiSigWalletWithTimeLock, config.owners, config.confirmationsRequired, config.secondsRequired)
.then(() => { .then(() => {
return deployer.deploy(TokenTransferProxy); return deployer.deploy(TokenTransferProxy);
}).then(() => { })
.then(() => {
return deployer.deploy(TokenRegistry); return deployer.deploy(TokenRegistry);
}).then(() => { })
.then(() => {
return deployer.deploy(EtherToken); return deployer.deploy(EtherToken);
}); });
} else { } else {

View File

@ -6,26 +6,21 @@ import {constants} from '../util/constants';
import { ContractInstance, Token } from '../util/types'; import { ContractInstance, Token } from '../util/types';
import { tokenInfo } from './config/token_info'; import { tokenInfo } from './config/token_info';
const { const { DummyToken, EtherToken, ZRXToken, TokenRegistry } = new Artifacts(artifacts);
DummyToken,
EtherToken,
ZRXToken,
TokenRegistry,
} = new Artifacts(artifacts);
module.exports = (deployer: any, network: string) => { module.exports = (deployer: any, network: string) => {
const tokens = network === 'live' ? tokenInfo.live : tokenInfo.development; const tokens = network === 'live' ? tokenInfo.live : tokenInfo.development;
deployer.then(() => { deployer
.then(() => {
return TokenRegistry.deployed(); return TokenRegistry.deployed();
}).then((tokenRegistry: ContractInstance) => { })
.then((tokenRegistry: ContractInstance) => {
if (network !== 'live') { if (network !== 'live') {
const totalSupply = Math.pow(10, 18) * 1000000000; const totalSupply = Math.pow(10, 18) * 1000000000;
return Bluebird.each(tokens.map((token: Token) => DummyToken.new( return Bluebird.each(
token.name, tokens.map((token: Token) => DummyToken.new(token.name, token.symbol, token.decimals, totalSupply)),
token.symbol, _.noop,
token.decimals, ).then((dummyTokens: ContractInstance[]) => {
totalSupply,
)), _.noop).then((dummyTokens: ContractInstance[]) => {
const weth = { const weth = {
address: EtherToken.address, address: EtherToken.address,
name: 'Ether Token', name: 'Ether Token',
@ -35,7 +30,9 @@ module.exports = (deployer: any, network: string) => {
ipfsHash: constants.NULL_BYTES, ipfsHash: constants.NULL_BYTES,
swarmHash: constants.NULL_BYTES, swarmHash: constants.NULL_BYTES,
}; };
return Bluebird.each(dummyTokens.map((tokenContract: ContractInstance, i: number) => { return Bluebird.each(
dummyTokens
.map((tokenContract: ContractInstance, i: number) => {
const token = tokens[i]; const token = tokens[i];
return tokenRegistry.addToken( return tokenRegistry.addToken(
tokenContract.address, tokenContract.address,
@ -45,14 +42,19 @@ module.exports = (deployer: any, network: string) => {
token.ipfsHash, token.ipfsHash,
token.swarmHash, token.swarmHash,
); );
}).concat(tokenRegistry.addToken( })
.concat(
tokenRegistry.addToken(
weth.address, weth.address,
weth.name, weth.name,
weth.symbol, weth.symbol,
weth.decimals, weth.decimals,
weth.ipfsHash, weth.ipfsHash,
weth.swarmHash, weth.swarmHash,
)), _.noop); ),
),
_.noop,
);
}); });
} else { } else {
const zrx = { const zrx = {
@ -64,7 +66,9 @@ module.exports = (deployer: any, network: string) => {
ipfsHash: constants.NULL_BYTES, ipfsHash: constants.NULL_BYTES,
swarmHash: constants.NULL_BYTES, swarmHash: constants.NULL_BYTES,
}; };
return Bluebird.each(tokens.map((token: Token) => { return Bluebird.each(
tokens
.map((token: Token) => {
return tokenRegistry.addToken( return tokenRegistry.addToken(
token.address, token.address,
token.name, token.name,
@ -73,14 +77,19 @@ module.exports = (deployer: any, network: string) => {
token.ipfsHash, token.ipfsHash,
token.swarmHash, token.swarmHash,
); );
}).concat(tokenRegistry.addToken( })
.concat(
tokenRegistry.addToken(
zrx.address, zrx.address,
zrx.name, zrx.name,
zrx.symbol, zrx.symbol,
zrx.decimals, zrx.decimals,
zrx.ipfsHash, zrx.ipfsHash,
zrx.swarmHash, zrx.swarmHash,
)), _.noop); ),
),
_.noop,
);
} }
}); });
}; };

View File

@ -1,18 +1,12 @@
import { Artifacts } from '../util/artifacts'; import { Artifacts } from '../util/artifacts';
import { ContractInstance } from '../util/types'; import { ContractInstance } from '../util/types';
const { const { TokenTransferProxy, Exchange, TokenRegistry } = new Artifacts(artifacts);
TokenTransferProxy,
Exchange,
TokenRegistry,
} = new Artifacts(artifacts);
let tokenTransferProxy: ContractInstance; let tokenTransferProxy: ContractInstance;
module.exports = (deployer: any) => { module.exports = (deployer: any) => {
deployer.then(async () => { deployer
return Promise.all([ .then(async () => {
TokenTransferProxy.deployed(), return Promise.all([TokenTransferProxy.deployed(), TokenRegistry.deployed()]);
TokenRegistry.deployed(),
]);
}) })
.then((instances: ContractInstance[]) => { .then((instances: ContractInstance[]) => {
let tokenRegistry: ContractInstance; let tokenRegistry: ContractInstance;
@ -21,7 +15,8 @@ module.exports = (deployer: any) => {
}) })
.then((ptAddress: string) => { .then((ptAddress: string) => {
return deployer.deploy(Exchange, ptAddress, tokenTransferProxy.address); return deployer.deploy(Exchange, ptAddress, tokenTransferProxy.address);
}).then(() => { })
.then(() => {
return tokenTransferProxy.addAuthorizedAddress(Exchange.address); return tokenTransferProxy.addAuthorizedAddress(Exchange.address);
}); });
}; };

View File

@ -1,23 +1,18 @@
import { Artifacts } from '../util/artifacts'; import { Artifacts } from '../util/artifacts';
import { ContractInstance } from '../util/types'; import { ContractInstance } from '../util/types';
const { const { TokenTransferProxy, MultiSigWalletWithTimeLock, TokenRegistry } = new Artifacts(artifacts);
TokenTransferProxy,
MultiSigWalletWithTimeLock,
TokenRegistry,
} = new Artifacts(artifacts);
let tokenRegistry: ContractInstance; let tokenRegistry: ContractInstance;
module.exports = (deployer: any, network: string) => { module.exports = (deployer: any, network: string) => {
if (network !== 'development') { if (network !== 'development') {
deployer.then(async () => { deployer.then(async () => {
return Promise.all([ return Promise.all([TokenTransferProxy.deployed(), TokenRegistry.deployed()])
TokenTransferProxy.deployed(), .then((instances: ContractInstance[]) => {
TokenRegistry.deployed(),
]).then((instances: ContractInstance[]) => {
let tokenTransferProxy: ContractInstance; let tokenTransferProxy: ContractInstance;
[tokenTransferProxy, tokenRegistry] = instances; [tokenTransferProxy, tokenRegistry] = instances;
return tokenTransferProxy.transferOwnership(MultiSigWalletWithTimeLock.address); return tokenTransferProxy.transferOwnership(MultiSigWalletWithTimeLock.address);
}).then(() => { })
.then(() => {
return tokenRegistry.transferOwnership(MultiSigWalletWithTimeLock.address); return tokenRegistry.transferOwnership(MultiSigWalletWithTimeLock.address);
}); });
}); });

View File

@ -1,7 +1,7 @@
{ {
"private": true, "private": true,
"name": "contracts", "name": "contracts",
"version": "2.1.0", "version": "2.1.1",
"description": "Smart contract components of 0x protocol", "description": "Smart contract components of 0x protocol",
"main": "index.js", "main": "index.js",
"directories": { "directories": {
@ -29,9 +29,9 @@
}, },
"homepage": "https://github.com/0xProject/0x.js/packages/contracts/README.md", "homepage": "https://github.com/0xProject/0x.js/packages/contracts/README.md",
"devDependencies": { "devDependencies": {
"@0xproject/dev-utils": "^0.0.2", "@0xproject/dev-utils": "^0.0.3",
"@0xproject/tslint-config": "^0.3.0", "@0xproject/tslint-config": "^0.4.0",
"@0xproject/types": "^0.1.1", "@0xproject/types": "^0.1.2",
"@types/bluebird": "^3.5.3", "@types/bluebird": "^3.5.3",
"@types/lodash": "^4.14.86", "@types/lodash": "^4.14.86",
"@types/node": "^8.0.53", "@types/node": "^8.0.53",
@ -55,10 +55,10 @@
"yargs": "^10.0.3" "yargs": "^10.0.3"
}, },
"dependencies": { "dependencies": {
"0x.js": "^0.28.0", "0x.js": "^0.29.0",
"@0xproject/json-schemas": "^0.7.0", "@0xproject/json-schemas": "^0.7.1",
"@0xproject/utils": "^0.1.1", "@0xproject/utils": "^0.1.2",
"@0xproject/web3-wrapper": "^0.1.1", "@0xproject/web3-wrapper": "^0.1.2",
"bignumber.js": "~4.1.0", "bignumber.js": "~4.1.0",
"bluebird": "^3.5.0", "bluebird": "^3.5.0",
"bn.js": "^4.11.8", "bn.js": "^4.11.8",

View File

@ -44,8 +44,9 @@ contract('EtherToken', (accounts: string[]) => {
const initEthBalance = await getEthBalanceAsync(account); const initEthBalance = await getEthBalanceAsync(account);
const ethToDeposit = initEthBalance.plus(1); const ethToDeposit = initEthBalance.plus(1);
return expect(zeroEx.etherToken.depositAsync(etherTokenAddress, ethToDeposit, account)) return expect(zeroEx.etherToken.depositAsync(etherTokenAddress, ethToDeposit, account)).to.be.rejectedWith(
.to.be.rejectedWith(ZeroExError.InsufficientEthBalanceForDeposit); ZeroExError.InsufficientEthBalanceForDeposit,
);
}); });
it('should convert deposited Ether to wrapped Ether tokens', async () => { it('should convert deposited Ether to wrapped Ether tokens', async () => {
@ -71,8 +72,9 @@ contract('EtherToken', (accounts: string[]) => {
const initEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account); const initEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account);
const ethTokensToWithdraw = initEthTokenBalance.plus(1); const ethTokensToWithdraw = initEthTokenBalance.plus(1);
return expect(zeroEx.etherToken.withdrawAsync(etherTokenAddress, ethTokensToWithdraw, account)) return expect(
.to.be.rejectedWith(ZeroExError.InsufficientWEthBalanceForWithdrawal); zeroEx.etherToken.withdrawAsync(etherTokenAddress, ethTokensToWithdraw, account),
).to.be.rejectedWith(ZeroExError.InsufficientWEthBalanceForWithdrawal);
}); });
it('should convert ether tokens to ether with sufficient balance', async () => { it('should convert ether tokens to ether with sufficient balance', async () => {
@ -89,8 +91,9 @@ contract('EtherToken', (accounts: string[]) => {
const finalEthBalance = await getEthBalanceAsync(account); const finalEthBalance = await getEthBalanceAsync(account);
const finalEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account); const finalEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account);
expect(finalEthBalance).to.be.bignumber expect(finalEthBalance).to.be.bignumber.equal(
.equal(initEthBalance.plus(ethTokensToWithdraw.minus(ethSpentOnGas))); initEthBalance.plus(ethTokensToWithdraw.minus(ethSpentOnGas)),
);
expect(finalEthTokenBalance).to.be.bignumber.equal(initEthTokenBalance.minus(ethTokensToWithdraw)); expect(finalEthTokenBalance).to.be.bignumber.equal(initEthTokenBalance.minus(ethTokensToWithdraw));
}); });
}); });

View File

@ -16,13 +16,7 @@ import {chaiSetup} from '../utils/chai_setup';
chaiSetup.configure(); chaiSetup.configure();
const expect = chai.expect; const expect = chai.expect;
const { const { Exchange, TokenTransferProxy, DummyToken, TokenRegistry, MaliciousToken } = new Artifacts(artifacts);
Exchange,
TokenTransferProxy,
DummyToken,
TokenRegistry,
MaliciousToken,
} = new Artifacts(artifacts);
// In order to benefit from type-safety, we re-assign the global web3 instance injected by Truffle // In order to benefit from type-safety, we re-assign the global web3 instance injected by Truffle
// with type `any` to a variable of type `Web3`. // with type `any` to a variable of type `Web3`.
@ -52,10 +46,7 @@ contract('Exchange', (accounts: string[]) => {
let zeroEx: ZeroEx; let zeroEx: ZeroEx;
before(async () => { before(async () => {
[tokenRegistry, exchange] = await Promise.all([ [tokenRegistry, exchange] = await Promise.all([TokenRegistry.deployed(), Exchange.deployed()]);
TokenRegistry.deployed(),
Exchange.deployed(),
]);
exWrapper = new ExchangeWrapper(exchange); exWrapper = new ExchangeWrapper(exchange);
zeroEx = new ZeroEx(web3.currentProvider, { zeroEx = new ZeroEx(web3.currentProvider, {
exchangeContractAddress: exchange.address, exchangeContractAddress: exchange.address,
@ -88,16 +79,28 @@ contract('Exchange', (accounts: string[]) => {
]); ]);
dmyBalances = new Balances([rep, dgd, zrx], [maker, taker, feeRecipient]); dmyBalances = new Balances([rep, dgd, zrx], [maker, taker, feeRecipient]);
await Promise.all([ await Promise.all([
rep.approve(TokenTransferProxy.address, INITIAL_ALLOWANCE, {from: maker}), rep.approve(TokenTransferProxy.address, INITIAL_ALLOWANCE, {
rep.approve(TokenTransferProxy.address, INITIAL_ALLOWANCE, {from: taker}), from: maker,
}),
rep.approve(TokenTransferProxy.address, INITIAL_ALLOWANCE, {
from: taker,
}),
rep.setBalance(maker, INITIAL_BALANCE, { from: tokenOwner }), rep.setBalance(maker, INITIAL_BALANCE, { from: tokenOwner }),
rep.setBalance(taker, INITIAL_BALANCE, { from: tokenOwner }), rep.setBalance(taker, INITIAL_BALANCE, { from: tokenOwner }),
dgd.approve(TokenTransferProxy.address, INITIAL_ALLOWANCE, {from: maker}), dgd.approve(TokenTransferProxy.address, INITIAL_ALLOWANCE, {
dgd.approve(TokenTransferProxy.address, INITIAL_ALLOWANCE, {from: taker}), from: maker,
}),
dgd.approve(TokenTransferProxy.address, INITIAL_ALLOWANCE, {
from: taker,
}),
dgd.setBalance(maker, INITIAL_BALANCE, { from: tokenOwner }), dgd.setBalance(maker, INITIAL_BALANCE, { from: tokenOwner }),
dgd.setBalance(taker, INITIAL_BALANCE, { from: tokenOwner }), dgd.setBalance(taker, INITIAL_BALANCE, { from: tokenOwner }),
zrx.approve(TokenTransferProxy.address, INITIAL_ALLOWANCE, {from: maker}), zrx.approve(TokenTransferProxy.address, INITIAL_ALLOWANCE, {
zrx.approve(TokenTransferProxy.address, INITIAL_ALLOWANCE, {from: taker}), from: maker,
}),
zrx.approve(TokenTransferProxy.address, INITIAL_ALLOWANCE, {
from: taker,
}),
zrx.setBalance(maker, INITIAL_BALANCE, { from: tokenOwner }), zrx.setBalance(maker, INITIAL_BALANCE, { from: tokenOwner }),
zrx.setBalance(taker, INITIAL_BALANCE, { from: tokenOwner }), zrx.setBalance(taker, INITIAL_BALANCE, { from: tokenOwner }),
]); ]);
@ -133,22 +136,29 @@ contract('Exchange', (accounts: string[]) => {
takerTokenAmount: new BigNumber(3), takerTokenAmount: new BigNumber(3),
}); });
const filledTakerTokenAmountBefore = const filledTakerTokenAmountBefore = await zeroEx.exchange.getFilledTakerAmountAsync(
await zeroEx.exchange.getFilledTakerAmountAsync(order.params.orderHashHex); order.params.orderHashHex,
);
expect(filledTakerTokenAmountBefore).to.be.bignumber.equal(0); expect(filledTakerTokenAmountBefore).to.be.bignumber.equal(0);
const fillTakerTokenAmount1 = new BigNumber(2); const fillTakerTokenAmount1 = new BigNumber(2);
await exWrapper.fillOrderAsync(order, taker, {fillTakerTokenAmount: fillTakerTokenAmount1}); await exWrapper.fillOrderAsync(order, taker, {
fillTakerTokenAmount: fillTakerTokenAmount1,
});
const filledTakerTokenAmountAfter1 = const filledTakerTokenAmountAfter1 = await zeroEx.exchange.getFilledTakerAmountAsync(
await zeroEx.exchange.getFilledTakerAmountAsync(order.params.orderHashHex); order.params.orderHashHex,
);
expect(filledTakerTokenAmountAfter1).to.be.bignumber.equal(fillTakerTokenAmount1); expect(filledTakerTokenAmountAfter1).to.be.bignumber.equal(fillTakerTokenAmount1);
const fillTakerTokenAmount2 = new BigNumber(1); const fillTakerTokenAmount2 = new BigNumber(1);
await exWrapper.fillOrderAsync(order, taker, {fillTakerTokenAmount: fillTakerTokenAmount2}); await exWrapper.fillOrderAsync(order, taker, {
fillTakerTokenAmount: fillTakerTokenAmount2,
});
const filledTakerTokenAmountAfter2 = const filledTakerTokenAmountAfter2 = await zeroEx.exchange.getFilledTakerAmountAsync(
await zeroEx.exchange.getFilledTakerAmountAsync(order.params.orderHashHex); order.params.orderHashHex,
);
expect(filledTakerTokenAmountAfter2).to.be.bignumber.equal(filledTakerTokenAmountAfter1); expect(filledTakerTokenAmountAfter2).to.be.bignumber.equal(filledTakerTokenAmountAfter1);
}); });
@ -158,15 +168,17 @@ contract('Exchange', (accounts: string[]) => {
takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18), takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
}); });
const filledTakerTokenAmountBefore = const filledTakerTokenAmountBefore = await zeroEx.exchange.getFilledTakerAmountAsync(
await zeroEx.exchange.getFilledTakerAmountAsync(order.params.orderHashHex); order.params.orderHashHex,
);
expect(filledTakerTokenAmountBefore).to.be.bignumber.equal(0); expect(filledTakerTokenAmountBefore).to.be.bignumber.equal(0);
const fillTakerTokenAmount = order.params.takerTokenAmount.div(2); const fillTakerTokenAmount = order.params.takerTokenAmount.div(2);
await exWrapper.fillOrderAsync(order, taker, { fillTakerTokenAmount }); await exWrapper.fillOrderAsync(order, taker, { fillTakerTokenAmount });
const filledTakerTokenAmountAfter = const filledTakerTokenAmountAfter = await zeroEx.exchange.getFilledTakerAmountAsync(
await zeroEx.exchange.getFilledTakerAmountAsync(order.params.orderHashHex); order.params.orderHashHex,
);
expect(filledTakerTokenAmountAfter).to.be.bignumber.equal(fillTakerTokenAmount); expect(filledTakerTokenAmountAfter).to.be.bignumber.equal(fillTakerTokenAmount);
const newBalances = await dmyBalances.getAsync(); const newBalances = await dmyBalances.getAsync();
@ -180,20 +192,27 @@ contract('Exchange', (accounts: string[]) => {
const paidTakerFee = order.params.takerFee const paidTakerFee = order.params.takerFee
.times(fillMakerTokenAmount) .times(fillMakerTokenAmount)
.dividedToIntegerBy(order.params.makerTokenAmount); .dividedToIntegerBy(order.params.makerTokenAmount);
expect(newBalances[maker][order.params.makerToken]) expect(newBalances[maker][order.params.makerToken]).to.be.bignumber.equal(
.to.be.bignumber.equal(balances[maker][order.params.makerToken].minus(fillMakerTokenAmount)); balances[maker][order.params.makerToken].minus(fillMakerTokenAmount),
expect(newBalances[maker][order.params.takerToken]) );
.to.be.bignumber.equal(balances[maker][order.params.takerToken].add(fillTakerTokenAmount)); expect(newBalances[maker][order.params.takerToken]).to.be.bignumber.equal(
expect(newBalances[maker][zrx.address]).to.be.bignumber balances[maker][order.params.takerToken].add(fillTakerTokenAmount),
.equal(balances[maker][zrx.address].minus(paidMakerFee)); );
expect(newBalances[taker][order.params.takerToken]) expect(newBalances[maker][zrx.address]).to.be.bignumber.equal(
.to.be.bignumber.equal(balances[taker][order.params.takerToken].minus(fillTakerTokenAmount)); balances[maker][zrx.address].minus(paidMakerFee),
expect(newBalances[taker][order.params.makerToken]) );
.to.be.bignumber.equal(balances[taker][order.params.makerToken].add(fillMakerTokenAmount)); expect(newBalances[taker][order.params.takerToken]).to.be.bignumber.equal(
expect(newBalances[taker][zrx.address]).to.be.bignumber balances[taker][order.params.takerToken].minus(fillTakerTokenAmount),
.equal(balances[taker][zrx.address].minus(paidTakerFee)); );
expect(newBalances[feeRecipient][zrx.address]) expect(newBalances[taker][order.params.makerToken]).to.be.bignumber.equal(
.to.be.bignumber.equal(balances[feeRecipient][zrx.address].add(paidMakerFee.add(paidTakerFee))); balances[taker][order.params.makerToken].add(fillMakerTokenAmount),
);
expect(newBalances[taker][zrx.address]).to.be.bignumber.equal(
balances[taker][zrx.address].minus(paidTakerFee),
);
expect(newBalances[feeRecipient][zrx.address]).to.be.bignumber.equal(
balances[feeRecipient][zrx.address].add(paidMakerFee.add(paidTakerFee)),
);
}); });
it('should transfer the correct amounts when makerTokenAmount > takerTokenAmount', async () => { it('should transfer the correct amounts when makerTokenAmount > takerTokenAmount', async () => {
@ -202,15 +221,17 @@ contract('Exchange', (accounts: string[]) => {
takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18), takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
}); });
const filledTakerTokenAmountBefore = const filledTakerTokenAmountBefore = await zeroEx.exchange.getFilledTakerAmountAsync(
await zeroEx.exchange.getFilledTakerAmountAsync(order.params.orderHashHex); order.params.orderHashHex,
);
expect(filledTakerTokenAmountBefore).to.be.bignumber.equal(0); expect(filledTakerTokenAmountBefore).to.be.bignumber.equal(0);
const fillTakerTokenAmount = order.params.takerTokenAmount.div(2); const fillTakerTokenAmount = order.params.takerTokenAmount.div(2);
await exWrapper.fillOrderAsync(order, taker, { fillTakerTokenAmount }); await exWrapper.fillOrderAsync(order, taker, { fillTakerTokenAmount });
const filledTakerTokenAmountAfter = const filledTakerTokenAmountAfter = await zeroEx.exchange.getFilledTakerAmountAsync(
await zeroEx.exchange.getFilledTakerAmountAsync(order.params.orderHashHex); order.params.orderHashHex,
);
expect(filledTakerTokenAmountAfter).to.be.bignumber.equal(fillTakerTokenAmount); expect(filledTakerTokenAmountAfter).to.be.bignumber.equal(fillTakerTokenAmount);
const newBalances = await dmyBalances.getAsync(); const newBalances = await dmyBalances.getAsync();
@ -224,20 +245,27 @@ contract('Exchange', (accounts: string[]) => {
const paidTakerFee = order.params.takerFee const paidTakerFee = order.params.takerFee
.times(fillMakerTokenAmount) .times(fillMakerTokenAmount)
.dividedToIntegerBy(order.params.makerTokenAmount); .dividedToIntegerBy(order.params.makerTokenAmount);
expect(newBalances[maker][order.params.makerToken]) expect(newBalances[maker][order.params.makerToken]).to.be.bignumber.equal(
.to.be.bignumber.equal(balances[maker][order.params.makerToken].minus(fillMakerTokenAmount)); balances[maker][order.params.makerToken].minus(fillMakerTokenAmount),
expect(newBalances[maker][order.params.takerToken]) );
.to.be.bignumber.equal(balances[maker][order.params.takerToken].add(fillTakerTokenAmount)); expect(newBalances[maker][order.params.takerToken]).to.be.bignumber.equal(
expect(newBalances[maker][zrx.address]).to.be.bignumber balances[maker][order.params.takerToken].add(fillTakerTokenAmount),
.equal(balances[maker][zrx.address].minus(paidMakerFee)); );
expect(newBalances[taker][order.params.takerToken]) expect(newBalances[maker][zrx.address]).to.be.bignumber.equal(
.to.be.bignumber.equal(balances[taker][order.params.takerToken].minus(fillTakerTokenAmount)); balances[maker][zrx.address].minus(paidMakerFee),
expect(newBalances[taker][order.params.makerToken]) );
.to.be.bignumber.equal(balances[taker][order.params.makerToken].add(fillMakerTokenAmount)); expect(newBalances[taker][order.params.takerToken]).to.be.bignumber.equal(
expect(newBalances[taker][zrx.address]).to.be.bignumber balances[taker][order.params.takerToken].minus(fillTakerTokenAmount),
.equal(balances[taker][zrx.address].minus(paidTakerFee)); );
expect(newBalances[feeRecipient][zrx.address]) expect(newBalances[taker][order.params.makerToken]).to.be.bignumber.equal(
.to.be.bignumber.equal(balances[feeRecipient][zrx.address].add(paidMakerFee.add(paidTakerFee))); balances[taker][order.params.makerToken].add(fillMakerTokenAmount),
);
expect(newBalances[taker][zrx.address]).to.be.bignumber.equal(
balances[taker][zrx.address].minus(paidTakerFee),
);
expect(newBalances[feeRecipient][zrx.address]).to.be.bignumber.equal(
balances[feeRecipient][zrx.address].add(paidMakerFee.add(paidTakerFee)),
);
}); });
it('should transfer the correct amounts when makerTokenAmount < takerTokenAmount', async () => { it('should transfer the correct amounts when makerTokenAmount < takerTokenAmount', async () => {
@ -246,15 +274,17 @@ contract('Exchange', (accounts: string[]) => {
takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18), takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18),
}); });
const filledTakerTokenAmountBefore = const filledTakerTokenAmountBefore = await zeroEx.exchange.getFilledTakerAmountAsync(
await zeroEx.exchange.getFilledTakerAmountAsync(order.params.orderHashHex); order.params.orderHashHex,
);
expect(filledTakerTokenAmountBefore).to.be.bignumber.equal(0); expect(filledTakerTokenAmountBefore).to.be.bignumber.equal(0);
const fillTakerTokenAmount = order.params.takerTokenAmount.div(2); const fillTakerTokenAmount = order.params.takerTokenAmount.div(2);
await exWrapper.fillOrderAsync(order, taker, { fillTakerTokenAmount }); await exWrapper.fillOrderAsync(order, taker, { fillTakerTokenAmount });
const filledTakerTokenAmountAfter = const filledTakerTokenAmountAfter = await zeroEx.exchange.getFilledTakerAmountAsync(
await zeroEx.exchange.getFilledTakerAmountAsync(order.params.orderHashHex); order.params.orderHashHex,
);
expect(filledTakerTokenAmountAfter).to.be.bignumber.equal(fillTakerTokenAmount); expect(filledTakerTokenAmountAfter).to.be.bignumber.equal(fillTakerTokenAmount);
const newBalances = await dmyBalances.getAsync(); const newBalances = await dmyBalances.getAsync();
@ -268,20 +298,27 @@ contract('Exchange', (accounts: string[]) => {
const paidTakerFee = order.params.takerFee const paidTakerFee = order.params.takerFee
.times(fillMakerTokenAmount) .times(fillMakerTokenAmount)
.dividedToIntegerBy(order.params.makerTokenAmount); .dividedToIntegerBy(order.params.makerTokenAmount);
expect(newBalances[maker][order.params.makerToken]) expect(newBalances[maker][order.params.makerToken]).to.be.bignumber.equal(
.to.be.bignumber.equal(balances[maker][order.params.makerToken].minus(fillMakerTokenAmount)); balances[maker][order.params.makerToken].minus(fillMakerTokenAmount),
expect(newBalances[maker][order.params.takerToken]) );
.to.be.bignumber.equal(balances[maker][order.params.takerToken].add(fillTakerTokenAmount)); expect(newBalances[maker][order.params.takerToken]).to.be.bignumber.equal(
expect(newBalances[maker][zrx.address]).to.be.bignumber balances[maker][order.params.takerToken].add(fillTakerTokenAmount),
.equal(balances[maker][zrx.address].minus(paidMakerFee)); );
expect(newBalances[taker][order.params.takerToken]) expect(newBalances[maker][zrx.address]).to.be.bignumber.equal(
.to.be.bignumber.equal(balances[taker][order.params.takerToken].minus(fillTakerTokenAmount)); balances[maker][zrx.address].minus(paidMakerFee),
expect(newBalances[taker][order.params.makerToken]) );
.to.be.bignumber.equal(balances[taker][order.params.makerToken].add(fillMakerTokenAmount)); expect(newBalances[taker][order.params.takerToken]).to.be.bignumber.equal(
expect(newBalances[taker][zrx.address]).to.be.bignumber balances[taker][order.params.takerToken].minus(fillTakerTokenAmount),
.equal(balances[taker][zrx.address].minus(paidTakerFee)); );
expect(newBalances[feeRecipient][zrx.address]) expect(newBalances[taker][order.params.makerToken]).to.be.bignumber.equal(
.to.be.bignumber.equal(balances[feeRecipient][zrx.address].add(paidMakerFee.add(paidTakerFee))); balances[taker][order.params.makerToken].add(fillMakerTokenAmount),
);
expect(newBalances[taker][zrx.address]).to.be.bignumber.equal(
balances[taker][zrx.address].minus(paidTakerFee),
);
expect(newBalances[feeRecipient][zrx.address]).to.be.bignumber.equal(
balances[feeRecipient][zrx.address].add(paidMakerFee.add(paidTakerFee)),
);
}); });
it('should transfer the correct amounts when taker is specified and order is claimed by taker', async () => { it('should transfer the correct amounts when taker is specified and order is claimed by taker', async () => {
@ -291,15 +328,17 @@ contract('Exchange', (accounts: string[]) => {
takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18), takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18),
}); });
const filledTakerTokenAmountBefore = const filledTakerTokenAmountBefore = await zeroEx.exchange.getFilledTakerAmountAsync(
await zeroEx.exchange.getFilledTakerAmountAsync(order.params.orderHashHex); order.params.orderHashHex,
);
expect(filledTakerTokenAmountBefore).to.be.bignumber.equal(0); expect(filledTakerTokenAmountBefore).to.be.bignumber.equal(0);
const fillTakerTokenAmount = order.params.takerTokenAmount.div(2); const fillTakerTokenAmount = order.params.takerTokenAmount.div(2);
await exWrapper.fillOrderAsync(order, taker, { fillTakerTokenAmount }); await exWrapper.fillOrderAsync(order, taker, { fillTakerTokenAmount });
const filledTakerTokenAmountAfter = const filledTakerTokenAmountAfter = await zeroEx.exchange.getFilledTakerAmountAsync(
await zeroEx.exchange.getFilledTakerAmountAsync(order.params.orderHashHex); order.params.orderHashHex,
);
const expectedFillAmountTAfter = fillTakerTokenAmount.add(filledTakerTokenAmountBefore); const expectedFillAmountTAfter = fillTakerTokenAmount.add(filledTakerTokenAmountBefore);
expect(filledTakerTokenAmountAfter).to.be.bignumber.equal(expectedFillAmountTAfter); expect(filledTakerTokenAmountAfter).to.be.bignumber.equal(expectedFillAmountTAfter);
@ -314,55 +353,70 @@ contract('Exchange', (accounts: string[]) => {
const paidTakerFee = order.params.takerFee const paidTakerFee = order.params.takerFee
.times(fillMakerTokenAmount) .times(fillMakerTokenAmount)
.dividedToIntegerBy(order.params.makerTokenAmount); .dividedToIntegerBy(order.params.makerTokenAmount);
expect(newBalances[maker][order.params.makerToken]) expect(newBalances[maker][order.params.makerToken]).to.be.bignumber.equal(
.to.be.bignumber.equal(balances[maker][order.params.makerToken].minus(fillMakerTokenAmount)); balances[maker][order.params.makerToken].minus(fillMakerTokenAmount),
expect(newBalances[maker][order.params.takerToken]) );
.to.be.bignumber.equal(balances[maker][order.params.takerToken].add(fillTakerTokenAmount)); expect(newBalances[maker][order.params.takerToken]).to.be.bignumber.equal(
expect(newBalances[maker][zrx.address]).to.be.bignumber balances[maker][order.params.takerToken].add(fillTakerTokenAmount),
.equal(balances[maker][zrx.address].minus(paidMakerFee)); );
expect(newBalances[taker][order.params.takerToken]) expect(newBalances[maker][zrx.address]).to.be.bignumber.equal(
.to.be.bignumber.equal(balances[taker][order.params.takerToken].minus(fillTakerTokenAmount)); balances[maker][zrx.address].minus(paidMakerFee),
expect(newBalances[taker][order.params.makerToken]) );
.to.be.bignumber.equal(balances[taker][order.params.makerToken].add(fillMakerTokenAmount)); expect(newBalances[taker][order.params.takerToken]).to.be.bignumber.equal(
expect(newBalances[taker][zrx.address]).to.be.bignumber balances[taker][order.params.takerToken].minus(fillTakerTokenAmount),
.equal(balances[taker][zrx.address].minus(paidTakerFee)); );
expect(newBalances[feeRecipient][zrx.address]) expect(newBalances[taker][order.params.makerToken]).to.be.bignumber.equal(
.to.be.bignumber.equal(balances[feeRecipient][zrx.address].add(paidMakerFee.add(paidTakerFee))); balances[taker][order.params.makerToken].add(fillMakerTokenAmount),
);
expect(newBalances[taker][zrx.address]).to.be.bignumber.equal(
balances[taker][zrx.address].minus(paidTakerFee),
);
expect(newBalances[feeRecipient][zrx.address]).to.be.bignumber.equal(
balances[feeRecipient][zrx.address].add(paidMakerFee.add(paidTakerFee)),
);
}); });
it('should fill remaining value if fillTakerTokenAmount > remaining takerTokenAmount', async () => { it('should fill remaining value if fillTakerTokenAmount > remaining takerTokenAmount', async () => {
const fillTakerTokenAmount = order.params.takerTokenAmount.div(2); const fillTakerTokenAmount = order.params.takerTokenAmount.div(2);
await exWrapper.fillOrderAsync(order, taker, { fillTakerTokenAmount }); await exWrapper.fillOrderAsync(order, taker, { fillTakerTokenAmount });
const res = await exWrapper.fillOrderAsync(order, taker, const res = await exWrapper.fillOrderAsync(order, taker, {
{fillTakerTokenAmount: order.params.takerTokenAmount}); fillTakerTokenAmount: order.params.takerTokenAmount,
});
expect(res.logs[0].args.filledTakerTokenAmount) expect(res.logs[0].args.filledTakerTokenAmount).to.be.bignumber.equal(
.to.be.bignumber.equal(order.params.takerTokenAmount.minus(fillTakerTokenAmount)); order.params.takerTokenAmount.minus(fillTakerTokenAmount),
);
const newBalances = await dmyBalances.getAsync(); const newBalances = await dmyBalances.getAsync();
expect(newBalances[maker][order.params.makerToken]) expect(newBalances[maker][order.params.makerToken]).to.be.bignumber.equal(
.to.be.bignumber.equal(balances[maker][order.params.makerToken].minus(order.params.makerTokenAmount)); balances[maker][order.params.makerToken].minus(order.params.makerTokenAmount),
expect(newBalances[maker][order.params.takerToken]) );
.to.be.bignumber.equal(balances[maker][order.params.takerToken].add(order.params.takerTokenAmount)); expect(newBalances[maker][order.params.takerToken]).to.be.bignumber.equal(
expect(newBalances[maker][zrx.address]) balances[maker][order.params.takerToken].add(order.params.takerTokenAmount),
.to.be.bignumber.equal(balances[maker][zrx.address].minus(order.params.makerFee)); );
expect(newBalances[taker][order.params.takerToken]) expect(newBalances[maker][zrx.address]).to.be.bignumber.equal(
.to.be.bignumber.equal(balances[taker][order.params.takerToken].minus(order.params.takerTokenAmount)); balances[maker][zrx.address].minus(order.params.makerFee),
expect(newBalances[taker][order.params.makerToken]) );
.to.be.bignumber.equal(balances[taker][order.params.makerToken].add(order.params.makerTokenAmount)); expect(newBalances[taker][order.params.takerToken]).to.be.bignumber.equal(
expect(newBalances[taker][zrx.address]) balances[taker][order.params.takerToken].minus(order.params.takerTokenAmount),
.to.be.bignumber.equal(balances[taker][zrx.address].minus(order.params.takerFee)); );
expect(newBalances[feeRecipient][zrx.address]) expect(newBalances[taker][order.params.makerToken]).to.be.bignumber.equal(
.to.be.bignumber.equal( balances[taker][order.params.makerToken].add(order.params.makerTokenAmount),
);
expect(newBalances[taker][zrx.address]).to.be.bignumber.equal(
balances[taker][zrx.address].minus(order.params.takerFee),
);
expect(newBalances[feeRecipient][zrx.address]).to.be.bignumber.equal(
balances[feeRecipient][zrx.address].add(order.params.makerFee.add(order.params.takerFee)), balances[feeRecipient][zrx.address].add(order.params.makerFee.add(order.params.takerFee)),
); );
}); });
it('should log 1 event with the correct arguments when order has a feeRecipient', async () => { it('should log 1 event with the correct arguments when order has a feeRecipient', async () => {
const divisor = 2; const divisor = 2;
const res = await exWrapper.fillOrderAsync(order, taker, const res = await exWrapper.fillOrderAsync(order, taker, {
{fillTakerTokenAmount: order.params.takerTokenAmount.div(divisor)}); fillTakerTokenAmount: order.params.takerTokenAmount.div(divisor),
});
expect(res.logs).to.have.length(1); expect(res.logs).to.have.length(1);
const logArgs = res.logs[0].args; const logArgs = res.logs[0].args;
@ -391,8 +445,9 @@ contract('Exchange', (accounts: string[]) => {
feeRecipient: ZeroEx.NULL_ADDRESS, feeRecipient: ZeroEx.NULL_ADDRESS,
}); });
const divisor = 2; const divisor = 2;
const res = await exWrapper.fillOrderAsync(order, taker, const res = await exWrapper.fillOrderAsync(order, taker, {
{fillTakerTokenAmount: order.params.takerTokenAmount.div(divisor)}); fillTakerTokenAmount: order.params.takerTokenAmount.div(divisor),
});
expect(res.logs).to.have.length(1); expect(res.logs).to.have.length(1);
const logArgs = res.logs[0].args; const logArgs = res.logs[0].args;
@ -455,13 +510,15 @@ contract('Exchange', (accounts: string[]) => {
it('should throw if fillTakerTokenAmount is 0', async () => { it('should throw if fillTakerTokenAmount is 0', async () => {
order = await orderFactory.newSignedOrderAsync(); order = await orderFactory.newSignedOrderAsync();
return expect(exWrapper.fillOrderAsync(order, taker, {fillTakerTokenAmount: new BigNumber(0)})) return expect(
.to.be.rejectedWith(constants.REVERT); exWrapper.fillOrderAsync(order, taker, {
fillTakerTokenAmount: new BigNumber(0),
}),
).to.be.rejectedWith(constants.REVERT);
}); });
it('should not change balances if maker balances are too low to fill order and \ it('should not change balances if maker balances are too low to fill order and \
shouldThrowOnInsufficientBalanceOrAllowance = false', shouldThrowOnInsufficientBalanceOrAllowance = false', async () => {
async () => {
order = await orderFactory.newSignedOrderAsync({ order = await orderFactory.newSignedOrderAsync({
makerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100000), 18), makerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100000), 18),
}); });
@ -472,19 +529,20 @@ contract('Exchange', (accounts: string[]) => {
}); });
it('should throw if maker balances are too low to fill order and \ it('should throw if maker balances are too low to fill order and \
shouldThrowOnInsufficientBalanceOrAllowance = true', shouldThrowOnInsufficientBalanceOrAllowance = true', async () => {
async () => {
order = await orderFactory.newSignedOrderAsync({ order = await orderFactory.newSignedOrderAsync({
makerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100000), 18), makerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100000), 18),
}); });
return expect(exWrapper.fillOrderAsync(order, taker, {shouldThrowOnInsufficientBalanceOrAllowance: true})) return expect(
.to.be.rejectedWith(constants.REVERT); exWrapper.fillOrderAsync(order, taker, {
shouldThrowOnInsufficientBalanceOrAllowance: true,
}),
).to.be.rejectedWith(constants.REVERT);
}); });
it('should not change balances if taker balances are too low to fill order and \ it('should not change balances if taker balances are too low to fill order and \
shouldThrowOnInsufficientBalanceOrAllowance = false', shouldThrowOnInsufficientBalanceOrAllowance = false', async () => {
async () => {
order = await orderFactory.newSignedOrderAsync({ order = await orderFactory.newSignedOrderAsync({
takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100000), 18), takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100000), 18),
}); });
@ -495,59 +553,70 @@ contract('Exchange', (accounts: string[]) => {
}); });
it('should throw if taker balances are too low to fill order and \ it('should throw if taker balances are too low to fill order and \
shouldThrowOnInsufficientBalanceOrAllowance = true', shouldThrowOnInsufficientBalanceOrAllowance = true', async () => {
async () => {
order = await orderFactory.newSignedOrderAsync({ order = await orderFactory.newSignedOrderAsync({
takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100000), 18), takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100000), 18),
}); });
return expect(exWrapper.fillOrderAsync(order, taker, {shouldThrowOnInsufficientBalanceOrAllowance: true})) return expect(
.to.be.rejectedWith(constants.REVERT); exWrapper.fillOrderAsync(order, taker, {
shouldThrowOnInsufficientBalanceOrAllowance: true,
}),
).to.be.rejectedWith(constants.REVERT);
}); });
it('should not change balances if maker allowances are too low to fill order and \ it('should not change balances if maker allowances are too low to fill order and \
shouldThrowOnInsufficientBalanceOrAllowance = false', shouldThrowOnInsufficientBalanceOrAllowance = false', async () => {
async () => {
await rep.approve(TokenTransferProxy.address, 0, { from: maker }); await rep.approve(TokenTransferProxy.address, 0, { from: maker });
await exWrapper.fillOrderAsync(order, taker); await exWrapper.fillOrderAsync(order, taker);
await rep.approve(TokenTransferProxy.address, INITIAL_ALLOWANCE, {from: maker}); await rep.approve(TokenTransferProxy.address, INITIAL_ALLOWANCE, {
from: maker,
});
const newBalances = await dmyBalances.getAsync(); const newBalances = await dmyBalances.getAsync();
expect(newBalances).to.be.deep.equal(balances); expect(newBalances).to.be.deep.equal(balances);
}); });
it('should throw if maker allowances are too low to fill order and \ it('should throw if maker allowances are too low to fill order and \
shouldThrowOnInsufficientBalanceOrAllowance = true', shouldThrowOnInsufficientBalanceOrAllowance = true', async () => {
async () => {
await rep.approve(TokenTransferProxy.address, 0, { from: maker }); await rep.approve(TokenTransferProxy.address, 0, { from: maker });
expect(exWrapper.fillOrderAsync(order, taker, {shouldThrowOnInsufficientBalanceOrAllowance: true})) expect(
.to.be.rejectedWith(constants.REVERT); exWrapper.fillOrderAsync(order, taker, {
await rep.approve(TokenTransferProxy.address, INITIAL_ALLOWANCE, {from: maker}); shouldThrowOnInsufficientBalanceOrAllowance: true,
}),
).to.be.rejectedWith(constants.REVERT);
await rep.approve(TokenTransferProxy.address, INITIAL_ALLOWANCE, {
from: maker,
});
}); });
it('should not change balances if taker allowances are too low to fill order and \ it('should not change balances if taker allowances are too low to fill order and \
shouldThrowOnInsufficientBalanceOrAllowance = false', shouldThrowOnInsufficientBalanceOrAllowance = false', async () => {
async () => {
await dgd.approve(TokenTransferProxy.address, 0, { from: taker }); await dgd.approve(TokenTransferProxy.address, 0, { from: taker });
await exWrapper.fillOrderAsync(order, taker); await exWrapper.fillOrderAsync(order, taker);
await dgd.approve(TokenTransferProxy.address, INITIAL_ALLOWANCE, {from: taker}); await dgd.approve(TokenTransferProxy.address, INITIAL_ALLOWANCE, {
from: taker,
});
const newBalances = await dmyBalances.getAsync(); const newBalances = await dmyBalances.getAsync();
expect(newBalances).to.be.deep.equal(balances); expect(newBalances).to.be.deep.equal(balances);
}); });
it('should throw if taker allowances are too low to fill order and \ it('should throw if taker allowances are too low to fill order and \
shouldThrowOnInsufficientBalanceOrAllowance = true', shouldThrowOnInsufficientBalanceOrAllowance = true', async () => {
async () => {
await dgd.approve(TokenTransferProxy.address, 0, { from: taker }); await dgd.approve(TokenTransferProxy.address, 0, { from: taker });
expect(exWrapper.fillOrderAsync(order, taker, {shouldThrowOnInsufficientBalanceOrAllowance: true})) expect(
.to.be.rejectedWith(constants.REVERT); exWrapper.fillOrderAsync(order, taker, {
await dgd.approve(TokenTransferProxy.address, INITIAL_ALLOWANCE, {from: taker}); shouldThrowOnInsufficientBalanceOrAllowance: true,
}),
).to.be.rejectedWith(constants.REVERT);
await dgd.approve(TokenTransferProxy.address, INITIAL_ALLOWANCE, {
from: taker,
});
}); });
it('should not change balances if makerToken is ZRX, makerTokenAmount + makerFee > maker balance, \ it('should not change balances if makerToken is ZRX, makerTokenAmount + makerFee > maker balance, \
and shouldThrowOnInsufficientBalanceOrAllowance = false', and shouldThrowOnInsufficientBalanceOrAllowance = false', async () => {
async () => {
const makerZRXBalance = new BigNumber(balances[maker][zrx.address]); const makerZRXBalance = new BigNumber(balances[maker][zrx.address]);
order = await orderFactory.newSignedOrderAsync({ order = await orderFactory.newSignedOrderAsync({
makerToken: zrx.address, makerToken: zrx.address,
@ -560,8 +629,7 @@ contract('Exchange', (accounts: string[]) => {
}); });
it('should not change balances if makerToken is ZRX, makerTokenAmount + makerFee > maker allowance, \ it('should not change balances if makerToken is ZRX, makerTokenAmount + makerFee > maker allowance, \
and shouldThrowOnInsufficientBalanceOrAllowance = false', and shouldThrowOnInsufficientBalanceOrAllowance = false', async () => {
async () => {
const makerZRXAllowance = await zrx.allowance(maker, TokenTransferProxy.address); const makerZRXAllowance = await zrx.allowance(maker, TokenTransferProxy.address);
order = await orderFactory.newSignedOrderAsync({ order = await orderFactory.newSignedOrderAsync({
makerToken: zrx.address, makerToken: zrx.address,
@ -574,8 +642,7 @@ contract('Exchange', (accounts: string[]) => {
}); });
it('should not change balances if takerToken is ZRX, takerTokenAmount + takerFee > taker balance, \ it('should not change balances if takerToken is ZRX, takerTokenAmount + takerFee > taker balance, \
and shouldThrowOnInsufficientBalanceOrAllowance = false', and shouldThrowOnInsufficientBalanceOrAllowance = false', async () => {
async () => {
const takerZRXBalance = new BigNumber(balances[taker][zrx.address]); const takerZRXBalance = new BigNumber(balances[taker][zrx.address]);
order = await orderFactory.newSignedOrderAsync({ order = await orderFactory.newSignedOrderAsync({
takerToken: zrx.address, takerToken: zrx.address,
@ -588,8 +655,7 @@ contract('Exchange', (accounts: string[]) => {
}); });
it('should not change balances if takerToken is ZRX, takerTokenAmount + takerFee > taker allowance, \ it('should not change balances if takerToken is ZRX, takerTokenAmount + takerFee > taker allowance, \
and shouldThrowOnInsufficientBalanceOrAllowance = false', and shouldThrowOnInsufficientBalanceOrAllowance = false', async () => {
async () => {
const takerZRXAllowance = await zrx.allowance(taker, TokenTransferProxy.address); const takerZRXAllowance = await zrx.allowance(taker, TokenTransferProxy.address);
order = await orderFactory.newSignedOrderAsync({ order = await orderFactory.newSignedOrderAsync({
takerToken: zrx.address, takerToken: zrx.address,
@ -610,8 +676,11 @@ contract('Exchange', (accounts: string[]) => {
takerToken: maliciousToken.address, takerToken: maliciousToken.address,
}); });
return expect(exWrapper.fillOrderAsync(order, taker, {shouldThrowOnInsufficientBalanceOrAllowance: false})) return expect(
.to.be.rejectedWith(constants.REVERT); exWrapper.fillOrderAsync(order, taker, {
shouldThrowOnInsufficientBalanceOrAllowance: false,
}),
).to.be.rejectedWith(constants.REVERT);
}); });
it('should not change balances if an order is expired', async () => { it('should not change balances if an order is expired', async () => {
@ -674,13 +743,18 @@ contract('Exchange', (accounts: string[]) => {
it('should throw if cancelTakerTokenAmount is 0', async () => { it('should throw if cancelTakerTokenAmount is 0', async () => {
order = await orderFactory.newSignedOrderAsync(); order = await orderFactory.newSignedOrderAsync();
return expect(exWrapper.cancelOrderAsync(order, maker, {cancelTakerTokenAmount: new BigNumber(0)})) return expect(
.to.be.rejectedWith(constants.REVERT); exWrapper.cancelOrderAsync(order, maker, {
cancelTakerTokenAmount: new BigNumber(0),
}),
).to.be.rejectedWith(constants.REVERT);
}); });
it('should be able to cancel a full order', async () => { it('should be able to cancel a full order', async () => {
await exWrapper.cancelOrderAsync(order, maker); await exWrapper.cancelOrderAsync(order, maker);
await exWrapper.fillOrderAsync(order, taker, {fillTakerTokenAmount: order.params.takerTokenAmount.div(2)}); await exWrapper.fillOrderAsync(order, taker, {
fillTakerTokenAmount: order.params.takerTokenAmount.div(2),
});
const newBalances = await dmyBalances.getAsync(); const newBalances = await dmyBalances.getAsync();
expect(newBalances).to.be.deep.equal(balances); expect(newBalances).to.be.deep.equal(balances);
@ -688,12 +762,16 @@ contract('Exchange', (accounts: string[]) => {
it('should be able to cancel part of an order', async () => { it('should be able to cancel part of an order', async () => {
const cancelTakerTokenAmount = order.params.takerTokenAmount.div(2); const cancelTakerTokenAmount = order.params.takerTokenAmount.div(2);
await exWrapper.cancelOrderAsync(order, maker, {cancelTakerTokenAmount}); await exWrapper.cancelOrderAsync(order, maker, {
cancelTakerTokenAmount,
});
const res = await exWrapper.fillOrderAsync(order, taker, const res = await exWrapper.fillOrderAsync(order, taker, {
{fillTakerTokenAmount: order.params.takerTokenAmount}); fillTakerTokenAmount: order.params.takerTokenAmount,
expect(res.logs[0].args.filledTakerTokenAmount) });
.to.be.bignumber.equal(order.params.takerTokenAmount.minus(cancelTakerTokenAmount)); expect(res.logs[0].args.filledTakerTokenAmount).to.be.bignumber.equal(
order.params.takerTokenAmount.minus(cancelTakerTokenAmount),
);
const newBalances = await dmyBalances.getAsync(); const newBalances = await dmyBalances.getAsync();
const cancelMakerTokenAmount = cancelTakerTokenAmount const cancelMakerTokenAmount = cancelTakerTokenAmount
@ -705,26 +783,34 @@ contract('Exchange', (accounts: string[]) => {
const paidTakerFee = order.params.takerFee const paidTakerFee = order.params.takerFee
.times(cancelMakerTokenAmount) .times(cancelMakerTokenAmount)
.dividedToIntegerBy(order.params.makerTokenAmount); .dividedToIntegerBy(order.params.makerTokenAmount);
expect(newBalances[maker][order.params.makerToken]) expect(newBalances[maker][order.params.makerToken]).to.be.bignumber.equal(
.to.be.bignumber.equal(balances[maker][order.params.makerToken].minus(cancelMakerTokenAmount)); balances[maker][order.params.makerToken].minus(cancelMakerTokenAmount),
expect(newBalances[maker][order.params.takerToken]) );
.to.be.bignumber.equal(balances[maker][order.params.takerToken].add(cancelTakerTokenAmount)); expect(newBalances[maker][order.params.takerToken]).to.be.bignumber.equal(
expect(newBalances[maker][zrx.address]) balances[maker][order.params.takerToken].add(cancelTakerTokenAmount),
.to.be.bignumber.equal(balances[maker][zrx.address].minus(paidMakerFee)); );
expect(newBalances[taker][order.params.takerToken]) expect(newBalances[maker][zrx.address]).to.be.bignumber.equal(
.to.be.bignumber.equal(balances[taker][order.params.takerToken].minus(cancelTakerTokenAmount)); balances[maker][zrx.address].minus(paidMakerFee),
expect(newBalances[taker][order.params.makerToken]) );
.to.be.bignumber.equal(balances[taker][order.params.makerToken].add(cancelMakerTokenAmount)); expect(newBalances[taker][order.params.takerToken]).to.be.bignumber.equal(
expect(newBalances[taker][zrx.address]).to.be.bignumber balances[taker][order.params.takerToken].minus(cancelTakerTokenAmount),
.equal(balances[taker][zrx.address].minus(paidTakerFee)); );
expect(newBalances[feeRecipient][zrx.address]) expect(newBalances[taker][order.params.makerToken]).to.be.bignumber.equal(
.to.be.bignumber.equal(balances[feeRecipient][zrx.address].add(paidMakerFee.add(paidTakerFee))); balances[taker][order.params.makerToken].add(cancelMakerTokenAmount),
);
expect(newBalances[taker][zrx.address]).to.be.bignumber.equal(
balances[taker][zrx.address].minus(paidTakerFee),
);
expect(newBalances[feeRecipient][zrx.address]).to.be.bignumber.equal(
balances[feeRecipient][zrx.address].add(paidMakerFee.add(paidTakerFee)),
);
}); });
it('should log 1 event with correct arguments', async () => { it('should log 1 event with correct arguments', async () => {
const divisor = 2; const divisor = 2;
const res = await exWrapper.cancelOrderAsync(order, maker, const res = await exWrapper.cancelOrderAsync(order, maker, {
{cancelTakerTokenAmount: order.params.takerTokenAmount.div(divisor)}); cancelTakerTokenAmount: order.params.takerTokenAmount.div(divisor),
});
expect(res.logs).to.have.length(1); expect(res.logs).to.have.length(1);
const logArgs = res.logs[0].args; const logArgs = res.logs[0].args;

View File

@ -12,10 +12,7 @@ import {chaiSetup} from '../utils/chai_setup';
chaiSetup.configure(); chaiSetup.configure();
const expect = chai.expect; const expect = chai.expect;
const { const { Exchange, TokenRegistry } = new Artifacts(artifacts);
Exchange,
TokenRegistry,
} = new Artifacts(artifacts);
contract('Exchange', (accounts: string[]) => { contract('Exchange', (accounts: string[]) => {
const maker = accounts[0]; const maker = accounts[0];
@ -26,10 +23,7 @@ contract('Exchange', (accounts: string[]) => {
let orderFactory: OrderFactory; let orderFactory: OrderFactory;
before(async () => { before(async () => {
const [tokenRegistry, exchange] = await Promise.all([ const [tokenRegistry, exchange] = await Promise.all([TokenRegistry.deployed(), Exchange.deployed()]);
TokenRegistry.deployed(),
Exchange.deployed(),
]);
exchangeWrapper = new ExchangeWrapper(exchange); exchangeWrapper = new ExchangeWrapper(exchange);
const [repAddress, dgdAddress] = await Promise.all([ const [repAddress, dgdAddress] = await Promise.all([
tokenRegistry.getTokenAddressBySymbol('REP'), tokenRegistry.getTokenAddressBySymbol('REP'),

View File

@ -14,12 +14,7 @@ import {chaiSetup} from '../utils/chai_setup';
chaiSetup.configure(); chaiSetup.configure();
const expect = chai.expect; const expect = chai.expect;
const { const { Exchange, TokenTransferProxy, DummyToken, TokenRegistry } = new Artifacts(artifacts);
Exchange,
TokenTransferProxy,
DummyToken,
TokenRegistry,
} = new Artifacts(artifacts);
contract('Exchange', (accounts: string[]) => { contract('Exchange', (accounts: string[]) => {
const maker = accounts[0]; const maker = accounts[0];
@ -43,10 +38,7 @@ contract('Exchange', (accounts: string[]) => {
let orderFactory: OrderFactory; let orderFactory: OrderFactory;
before(async () => { before(async () => {
[tokenRegistry, exchange] = await Promise.all([ [tokenRegistry, exchange] = await Promise.all([TokenRegistry.deployed(), Exchange.deployed()]);
TokenRegistry.deployed(),
Exchange.deployed(),
]);
exWrapper = new ExchangeWrapper(exchange); exWrapper = new ExchangeWrapper(exchange);
const [repAddress, dgdAddress, zrxAddress] = await Promise.all([ const [repAddress, dgdAddress, zrxAddress] = await Promise.all([
tokenRegistry.getTokenAddressBySymbol('REP'), tokenRegistry.getTokenAddressBySymbol('REP'),
@ -100,7 +92,9 @@ contract('Exchange', (accounts: string[]) => {
takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18), takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18),
}); });
const fillTakerTokenAmount = order.params.takerTokenAmount.div(2); const fillTakerTokenAmount = order.params.takerTokenAmount.div(2);
await exWrapper.fillOrKillOrderAsync(order, taker, {fillTakerTokenAmount}); await exWrapper.fillOrKillOrderAsync(order, taker, {
fillTakerTokenAmount,
});
const newBalances = await dmyBalances.getAsync(); const newBalances = await dmyBalances.getAsync();
@ -113,18 +107,23 @@ contract('Exchange', (accounts: string[]) => {
const takerFee = order.params.takerFee const takerFee = order.params.takerFee
.times(fillMakerTokenAmount) .times(fillMakerTokenAmount)
.dividedToIntegerBy(order.params.makerTokenAmount); .dividedToIntegerBy(order.params.makerTokenAmount);
expect(newBalances[maker][order.params.makerToken]) expect(newBalances[maker][order.params.makerToken]).to.be.bignumber.equal(
.to.be.bignumber.equal(balances[maker][order.params.makerToken].minus(fillMakerTokenAmount)); balances[maker][order.params.makerToken].minus(fillMakerTokenAmount),
expect(newBalances[maker][order.params.takerToken]) );
.to.be.bignumber.equal(balances[maker][order.params.takerToken].add(fillTakerTokenAmount)); expect(newBalances[maker][order.params.takerToken]).to.be.bignumber.equal(
balances[maker][order.params.takerToken].add(fillTakerTokenAmount),
);
expect(newBalances[maker][zrx.address]).to.be.bignumber.equal(balances[maker][zrx.address].minus(makerFee)); expect(newBalances[maker][zrx.address]).to.be.bignumber.equal(balances[maker][zrx.address].minus(makerFee));
expect(newBalances[taker][order.params.takerToken]) expect(newBalances[taker][order.params.takerToken]).to.be.bignumber.equal(
.to.be.bignumber.equal(balances[taker][order.params.takerToken].minus(fillTakerTokenAmount)); balances[taker][order.params.takerToken].minus(fillTakerTokenAmount),
expect(newBalances[taker][order.params.makerToken]) );
.to.be.bignumber.equal(balances[taker][order.params.makerToken].add(fillMakerTokenAmount)); expect(newBalances[taker][order.params.makerToken]).to.be.bignumber.equal(
balances[taker][order.params.makerToken].add(fillMakerTokenAmount),
);
expect(newBalances[taker][zrx.address]).to.be.bignumber.equal(balances[taker][zrx.address].minus(takerFee)); expect(newBalances[taker][zrx.address]).to.be.bignumber.equal(balances[taker][zrx.address].minus(takerFee));
expect(newBalances[feeRecipient][zrx.address]) expect(newBalances[feeRecipient][zrx.address]).to.be.bignumber.equal(
.to.be.bignumber.equal(balances[feeRecipient][zrx.address].add(makerFee.add(takerFee))); balances[feeRecipient][zrx.address].add(makerFee.add(takerFee)),
);
}); });
it('should throw if an order is expired', async () => { it('should throw if an order is expired', async () => {
@ -132,18 +131,18 @@ contract('Exchange', (accounts: string[]) => {
expirationTimestampInSec: new BigNumber(Math.floor((Date.now() - 10000) / 1000)), expirationTimestampInSec: new BigNumber(Math.floor((Date.now() - 10000) / 1000)),
}); });
return expect(exWrapper.fillOrKillOrderAsync(order, taker)) return expect(exWrapper.fillOrKillOrderAsync(order, taker)).to.be.rejectedWith(constants.REVERT);
.to.be.rejectedWith(constants.REVERT);
}); });
it('should throw if entire fillTakerTokenAmount not filled', async () => { it('should throw if entire fillTakerTokenAmount not filled', async () => {
const order = await orderFactory.newSignedOrderAsync(); const order = await orderFactory.newSignedOrderAsync();
const from = taker; const from = taker;
await exWrapper.fillOrderAsync(order, from, {fillTakerTokenAmount: order.params.takerTokenAmount.div(2)}); await exWrapper.fillOrderAsync(order, from, {
fillTakerTokenAmount: order.params.takerTokenAmount.div(2),
});
return expect(exWrapper.fillOrKillOrderAsync(order, taker)) return expect(exWrapper.fillOrKillOrderAsync(order, taker)).to.be.rejectedWith(constants.REVERT);
.to.be.rejectedWith(constants.REVERT);
}); });
}); });
@ -181,11 +180,14 @@ contract('Exchange', (accounts: string[]) => {
balances[taker][makerToken] = balances[taker][makerToken].add(fillMakerTokenAmount); balances[taker][makerToken] = balances[taker][makerToken].add(fillMakerTokenAmount);
balances[taker][takerToken] = balances[taker][takerToken].minus(fillTakerTokenAmount); balances[taker][takerToken] = balances[taker][takerToken].minus(fillTakerTokenAmount);
balances[taker][zrx.address] = balances[taker][zrx.address].minus(takerFee); balances[taker][zrx.address] = balances[taker][zrx.address].minus(takerFee);
balances[feeRecipient][zrx.address] = balances[feeRecipient][zrx.address] = balances[feeRecipient][zrx.address].add(
balances[feeRecipient][zrx.address].add(makerFee.add(takerFee)); makerFee.add(takerFee),
);
}); });
await exWrapper.batchFillOrdersAsync(orders, taker, {fillTakerTokenAmounts}); await exWrapper.batchFillOrdersAsync(orders, taker, {
fillTakerTokenAmounts,
});
const newBalances = await dmyBalances.getAsync(); const newBalances = await dmyBalances.getAsync();
expect(newBalances).to.be.deep.equal(balances); expect(newBalances).to.be.deep.equal(balances);
@ -215,11 +217,14 @@ contract('Exchange', (accounts: string[]) => {
balances[taker][makerToken] = balances[taker][makerToken].add(fillMakerTokenAmount); balances[taker][makerToken] = balances[taker][makerToken].add(fillMakerTokenAmount);
balances[taker][takerToken] = balances[taker][takerToken].minus(fillTakerTokenAmount); balances[taker][takerToken] = balances[taker][takerToken].minus(fillTakerTokenAmount);
balances[taker][zrx.address] = balances[taker][zrx.address].minus(takerFee); balances[taker][zrx.address] = balances[taker][zrx.address].minus(takerFee);
balances[feeRecipient][zrx.address] = balances[feeRecipient][zrx.address] = balances[feeRecipient][zrx.address].add(
balances[feeRecipient][zrx.address].add(makerFee.add(takerFee)); makerFee.add(takerFee),
);
}); });
await exWrapper.batchFillOrKillOrdersAsync(orders, taker, {fillTakerTokenAmounts}); await exWrapper.batchFillOrKillOrdersAsync(orders, taker, {
fillTakerTokenAmounts,
});
const newBalances = await dmyBalances.getAsync(); const newBalances = await dmyBalances.getAsync();
expect(newBalances).to.be.deep.equal(balances); expect(newBalances).to.be.deep.equal(balances);
@ -234,57 +239,77 @@ contract('Exchange', (accounts: string[]) => {
await exWrapper.fillOrKillOrderAsync(orders[0], taker); await exWrapper.fillOrKillOrderAsync(orders[0], taker);
return expect(exWrapper.batchFillOrKillOrdersAsync(orders, taker, {fillTakerTokenAmounts})) return expect(
.to.be.rejectedWith(constants.REVERT); exWrapper.batchFillOrKillOrdersAsync(orders, taker, {
fillTakerTokenAmounts,
}),
).to.be.rejectedWith(constants.REVERT);
}); });
}); });
describe('fillOrdersUpTo', () => { describe('fillOrdersUpTo', () => {
it('should stop when the entire fillTakerTokenAmount is filled', async () => { it('should stop when the entire fillTakerTokenAmount is filled', async () => {
const fillTakerTokenAmount = const fillTakerTokenAmount = orders[0].params.takerTokenAmount.plus(
orders[0].params.takerTokenAmount.plus(orders[1].params.takerTokenAmount.div(2)); orders[1].params.takerTokenAmount.div(2),
await exWrapper.fillOrdersUpToAsync(orders, taker, {fillTakerTokenAmount}); );
await exWrapper.fillOrdersUpToAsync(orders, taker, {
fillTakerTokenAmount,
});
const newBalances = await dmyBalances.getAsync(); const newBalances = await dmyBalances.getAsync();
const fillMakerTokenAmount = orders[0].params.makerTokenAmount.add( const fillMakerTokenAmount = orders[0].params.makerTokenAmount.add(
orders[1].params.makerTokenAmount.dividedToIntegerBy(2)); orders[1].params.makerTokenAmount.dividedToIntegerBy(2),
);
const makerFee = orders[0].params.makerFee.add(orders[1].params.makerFee.dividedToIntegerBy(2)); const makerFee = orders[0].params.makerFee.add(orders[1].params.makerFee.dividedToIntegerBy(2));
const takerFee = orders[0].params.takerFee.add(orders[1].params.takerFee.dividedToIntegerBy(2)); const takerFee = orders[0].params.takerFee.add(orders[1].params.takerFee.dividedToIntegerBy(2));
expect(newBalances[maker][orders[0].params.makerToken]) expect(newBalances[maker][orders[0].params.makerToken]).to.be.bignumber.equal(
.to.be.bignumber.equal(balances[maker][orders[0].params.makerToken].minus(fillMakerTokenAmount)); balances[maker][orders[0].params.makerToken].minus(fillMakerTokenAmount),
expect(newBalances[maker][orders[0].params.takerToken]) );
.to.be.bignumber.equal(balances[maker][orders[0].params.takerToken].add(fillTakerTokenAmount)); expect(newBalances[maker][orders[0].params.takerToken]).to.be.bignumber.equal(
expect(newBalances[maker][zrx.address]).to.be.bignumber balances[maker][orders[0].params.takerToken].add(fillTakerTokenAmount),
.equal(balances[maker][zrx.address].minus(makerFee)); );
expect(newBalances[taker][orders[0].params.takerToken]) expect(newBalances[maker][zrx.address]).to.be.bignumber.equal(
.to.be.bignumber.equal(balances[taker][orders[0].params.takerToken].minus(fillTakerTokenAmount)); balances[maker][zrx.address].minus(makerFee),
expect(newBalances[taker][orders[0].params.makerToken]) );
.to.be.bignumber.equal(balances[taker][orders[0].params.makerToken].add(fillMakerTokenAmount)); expect(newBalances[taker][orders[0].params.takerToken]).to.be.bignumber.equal(
expect(newBalances[taker][zrx.address]).to.be.bignumber balances[taker][orders[0].params.takerToken].minus(fillTakerTokenAmount),
.equal(balances[taker][zrx.address].minus(takerFee)); );
expect(newBalances[feeRecipient][zrx.address]) expect(newBalances[taker][orders[0].params.makerToken]).to.be.bignumber.equal(
.to.be.bignumber.equal(balances[feeRecipient][zrx.address].add(makerFee.add(takerFee))); balances[taker][orders[0].params.makerToken].add(fillMakerTokenAmount),
);
expect(newBalances[taker][zrx.address]).to.be.bignumber.equal(
balances[taker][zrx.address].minus(takerFee),
);
expect(newBalances[feeRecipient][zrx.address]).to.be.bignumber.equal(
balances[feeRecipient][zrx.address].add(makerFee.add(takerFee)),
);
}); });
it('should fill all orders if cannot fill entire fillTakerTokenAmount', async () => { it('should fill all orders if cannot fill entire fillTakerTokenAmount', async () => {
const fillTakerTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(100000), 18); const fillTakerTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(100000), 18);
orders.forEach(order => { orders.forEach(order => {
balances[maker][order.params.makerToken] = balances[maker][order.params.makerToken] balances[maker][order.params.makerToken] = balances[maker][order.params.makerToken].minus(
.minus(order.params.makerTokenAmount); order.params.makerTokenAmount,
balances[maker][order.params.takerToken] = balances[maker][order.params.takerToken] );
.add(order.params.takerTokenAmount); balances[maker][order.params.takerToken] = balances[maker][order.params.takerToken].add(
balances[maker][zrx.address] = balances[maker][zrx.address] order.params.takerTokenAmount,
.minus(order.params.makerFee); );
balances[taker][order.params.makerToken] = balances[taker][order.params.makerToken] balances[maker][zrx.address] = balances[maker][zrx.address].minus(order.params.makerFee);
.add(order.params.makerTokenAmount); balances[taker][order.params.makerToken] = balances[taker][order.params.makerToken].add(
balances[taker][order.params.takerToken] = balances[taker][order.params.takerToken] order.params.makerTokenAmount,
.minus(order.params.takerTokenAmount); );
balances[taker][order.params.takerToken] = balances[taker][order.params.takerToken].minus(
order.params.takerTokenAmount,
);
balances[taker][zrx.address] = balances[taker][zrx.address].minus(order.params.takerFee); balances[taker][zrx.address] = balances[taker][zrx.address].minus(order.params.takerFee);
balances[feeRecipient][zrx.address] = balances[feeRecipient][zrx.address] = balances[feeRecipient][zrx.address].add(
balances[feeRecipient][zrx.address].add(order.params.makerFee.add(order.params.takerFee)); order.params.makerFee.add(order.params.takerFee),
);
});
await exWrapper.fillOrdersUpToAsync(orders, taker, {
fillTakerTokenAmount,
}); });
await exWrapper.fillOrdersUpToAsync(orders, taker, {fillTakerTokenAmount});
const newBalances = await dmyBalances.getAsync(); const newBalances = await dmyBalances.getAsync();
expect(newBalances).to.be.deep.equal(balances); expect(newBalances).to.be.deep.equal(balances);
@ -298,8 +323,9 @@ contract('Exchange', (accounts: string[]) => {
]); ]);
return expect( return expect(
exWrapper.fillOrdersUpToAsync( exWrapper.fillOrdersUpToAsync(orders, taker, {
orders, taker, {fillTakerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(1000), 18)}), fillTakerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(1000), 18),
}),
).to.be.rejectedWith(constants.REVERT); ).to.be.rejectedWith(constants.REVERT);
}); });
}); });
@ -307,10 +333,13 @@ contract('Exchange', (accounts: string[]) => {
describe('batchCancelOrders', () => { describe('batchCancelOrders', () => {
it('should be able to cancel multiple orders', async () => { it('should be able to cancel multiple orders', async () => {
const cancelTakerTokenAmounts = _.map(orders, order => order.params.takerTokenAmount); const cancelTakerTokenAmounts = _.map(orders, order => order.params.takerTokenAmount);
await exWrapper.batchCancelOrdersAsync(orders, maker, {cancelTakerTokenAmounts}); await exWrapper.batchCancelOrdersAsync(orders, maker, {
cancelTakerTokenAmounts,
});
await exWrapper.batchFillOrdersAsync( await exWrapper.batchFillOrdersAsync(orders, taker, {
orders, taker, {fillTakerTokenAmounts: cancelTakerTokenAmounts}); fillTakerTokenAmounts: cancelTakerTokenAmounts,
});
const newBalances = await dmyBalances.getAsync(); const newBalances = await dmyBalances.getAsync();
expect(balances).to.be.deep.equal(newBalances); expect(balances).to.be.deep.equal(newBalances);
}); });

View File

@ -45,8 +45,9 @@ contract('MultiSigWalletWithTimeLock', (accounts: string[]) => {
describe('changeTimeLock', () => { describe('changeTimeLock', () => {
it('should throw when not called by wallet', async () => { it('should throw when not called by wallet', async () => {
return expect(multiSig.changeTimeLock(SECONDS_TIME_LOCKED, {from: owners[0]})) return expect(multiSig.changeTimeLock(SECONDS_TIME_LOCKED, { from: owners[0] })).to.be.rejectedWith(
.to.be.rejectedWith(constants.REVERT); constants.REVERT,
);
}); });
it('should throw without enough confirmations', async () => { it('should throw without enough confirmations', async () => {
@ -96,7 +97,9 @@ contract('MultiSigWalletWithTimeLock', (accounts: string[]) => {
const subRes = await multiSigWrapper.submitTransactionAsync(destination, from, dataParams); const subRes = await multiSigWrapper.submitTransactionAsync(destination, from, dataParams);
txId = subRes.logs[0].args.transactionId.toNumber(); txId = subRes.logs[0].args.transactionId.toNumber();
const confRes = await multiSig.confirmTransaction(txId, {from: owners[1]}); const confRes = await multiSig.confirmTransaction(txId, {
from: owners[1],
});
expect(confRes.logs).to.have.length(2); expect(confRes.logs).to.have.length(2);
return expect(multiSig.executeTransaction(txId)).to.be.rejectedWith(constants.REVERT); return expect(multiSig.executeTransaction(txId)).to.be.rejectedWith(constants.REVERT);

View File

@ -20,8 +20,14 @@ contract('MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress', (accounts: s
const SECONDS_TIME_LOCKED = 1000000; const SECONDS_TIME_LOCKED = 1000000;
// initialize fake addresses // initialize fake addresses
const authorizedAddress = `0x${crypto.solSHA3([accounts[0]]).slice(0, 20).toString('hex')}`; const authorizedAddress = `0x${crypto
const unauthorizedAddress = `0x${crypto.solSHA3([accounts[1]]).slice(0, 20).toString('hex')}`; .solSHA3([accounts[0]])
.slice(0, 20)
.toString('hex')}`;
const unauthorizedAddress = `0x${crypto
.solSHA3([accounts[1]])
.slice(0, 20)
.toString('hex')}`;
let tokenTransferProxy: ContractInstance; let tokenTransferProxy: ContractInstance;
let multiSig: ContractInstance; let multiSig: ContractInstance;
@ -32,10 +38,18 @@ contract('MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress', (accounts: s
beforeEach(async () => { beforeEach(async () => {
const initialOwner = accounts[0]; const initialOwner = accounts[0];
tokenTransferProxy = await TokenTransferProxy.new({ from: initialOwner }); tokenTransferProxy = await TokenTransferProxy.new({ from: initialOwner });
await tokenTransferProxy.addAuthorizedAddress(authorizedAddress, {from: initialOwner}); await tokenTransferProxy.addAuthorizedAddress(authorizedAddress, {
from: initialOwner,
});
multiSig = await MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress.new( multiSig = await MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress.new(
owners, requiredApprovals, SECONDS_TIME_LOCKED, tokenTransferProxy.address); owners,
await tokenTransferProxy.transferOwnership(multiSig.address, {from: initialOwner}); requiredApprovals,
SECONDS_TIME_LOCKED,
tokenTransferProxy.address,
);
await tokenTransferProxy.transferOwnership(multiSig.address, {
from: initialOwner,
});
multiSigWrapper = new MultiSigWrapper(multiSig); multiSigWrapper = new MultiSigWrapper(multiSig);
validDestination = tokenTransferProxy.address; validDestination = tokenTransferProxy.address;
}); });
@ -43,8 +57,7 @@ contract('MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress', (accounts: s
describe('isFunctionRemoveAuthorizedAddress', () => { describe('isFunctionRemoveAuthorizedAddress', () => {
it('should throw if data is not for removeAuthorizedAddress', async () => { it('should throw if data is not for removeAuthorizedAddress', async () => {
const data = MultiSigWrapper.encodeFnArgs('addAuthorizedAddress', PROXY_ABI, [owners[0]]); const data = MultiSigWrapper.encodeFnArgs('addAuthorizedAddress', PROXY_ABI, [owners[0]]);
return expect(multiSig.isFunctionRemoveAuthorizedAddress.call(data)) return expect(multiSig.isFunctionRemoveAuthorizedAddress.call(data)).to.be.rejectedWith(constants.REVERT);
.to.be.rejectedWith(constants.REVERT);
}); });
it('should return true if data is for removeAuthorizedAddress', async () => { it('should return true if data is for removeAuthorizedAddress', async () => {

View File

@ -81,16 +81,20 @@ contract('TokenRegistry', (accounts: string[]) => {
await tokenRegWrapper.addTokenAsync(token1, owner); await tokenRegWrapper.addTokenAsync(token1, owner);
const duplicateNameToken = _.assign({}, token2, { name: token1.name }); const duplicateNameToken = _.assign({}, token2, { name: token1.name });
return expect(tokenRegWrapper.addTokenAsync(duplicateNameToken, owner)) return expect(tokenRegWrapper.addTokenAsync(duplicateNameToken, owner)).to.be.rejectedWith(
.to.be.rejectedWith(constants.REVERT); constants.REVERT,
);
}); });
it('should throw if symbol already exists', async () => { it('should throw if symbol already exists', async () => {
await tokenRegWrapper.addTokenAsync(token1, owner); await tokenRegWrapper.addTokenAsync(token1, owner);
const duplicateSymbolToken = _.assign({}, token2, {symbol: token1.symbol}); const duplicateSymbolToken = _.assign({}, token2, {
symbol: token1.symbol,
});
return expect(tokenRegWrapper.addTokenAsync(duplicateSymbolToken, owner)) return expect(tokenRegWrapper.addTokenAsync(duplicateSymbolToken, owner)).to.be.rejectedWith(
.to.be.rejectedWith(constants.REVERT); constants.REVERT,
);
}); });
}); });
@ -115,12 +119,15 @@ contract('TokenRegistry', (accounts: string[]) => {
describe('setTokenName', () => { describe('setTokenName', () => {
it('should throw when not called by owner', async () => { it('should throw when not called by owner', async () => {
return expect(tokenReg.setTokenName(token1.address, token2.name, {from: notOwner})) return expect(
.to.be.rejectedWith(constants.REVERT); tokenReg.setTokenName(token1.address, token2.name, { from: notOwner }),
).to.be.rejectedWith(constants.REVERT);
}); });
it('should change the token name when called by owner', async () => { it('should change the token name when called by owner', async () => {
const res = await tokenReg.setTokenName(token1.address, token2.name, {from: owner}); const res = await tokenReg.setTokenName(token1.address, token2.name, {
from: owner,
});
expect(res.logs).to.have.length(1); expect(res.logs).to.have.length(1);
const [newData, oldData] = await Promise.all([ const [newData, oldData] = await Promise.all([
tokenRegWrapper.getTokenByNameAsync(token2.name), tokenRegWrapper.getTokenByNameAsync(token2.name),
@ -136,20 +143,25 @@ contract('TokenRegistry', (accounts: string[]) => {
it('should throw if the name already exists', async () => { it('should throw if the name already exists', async () => {
await tokenRegWrapper.addTokenAsync(token2, owner); await tokenRegWrapper.addTokenAsync(token2, owner);
return expect(tokenReg.setTokenName(token1.address, token2.name, {from: owner})) return expect(tokenReg.setTokenName(token1.address, token2.name, { from: owner })).to.be.rejectedWith(
.to.be.rejectedWith(constants.REVERT); constants.REVERT,
);
}); });
it('should throw if token does not exist', async () => { it('should throw if token does not exist', async () => {
return expect(tokenReg.setTokenName(nullToken.address, token2.name, {from: owner})) return expect(
.to.be.rejectedWith(constants.REVERT); tokenReg.setTokenName(nullToken.address, token2.name, { from: owner }),
).to.be.rejectedWith(constants.REVERT);
}); });
}); });
describe('setTokenSymbol', () => { describe('setTokenSymbol', () => {
it('should throw when not called by owner', async () => { it('should throw when not called by owner', async () => {
return expect(tokenReg.setTokenSymbol(token1.address, token2.symbol, {from: notOwner})) return expect(
.to.be.rejectedWith(constants.REVERT); tokenReg.setTokenSymbol(token1.address, token2.symbol, {
from: notOwner,
}),
).to.be.rejectedWith(constants.REVERT);
}); });
it('should change the token symbol when called by owner', async () => { it('should change the token symbol when called by owner', async () => {
@ -169,26 +181,35 @@ contract('TokenRegistry', (accounts: string[]) => {
it('should throw if the symbol already exists', async () => { it('should throw if the symbol already exists', async () => {
await tokenRegWrapper.addTokenAsync(token2, owner); await tokenRegWrapper.addTokenAsync(token2, owner);
return expect(tokenReg.setTokenSymbol(token1.address, token2.symbol, {from: owner})) return expect(
.to.be.rejectedWith(constants.REVERT); tokenReg.setTokenSymbol(token1.address, token2.symbol, {
from: owner,
}),
).to.be.rejectedWith(constants.REVERT);
}); });
it('should throw if token does not exist', async () => { it('should throw if token does not exist', async () => {
return expect(tokenReg.setTokenSymbol(nullToken.address, token2.symbol, {from: owner})) return expect(
.to.be.rejectedWith(constants.REVERT); tokenReg.setTokenSymbol(nullToken.address, token2.symbol, {
from: owner,
}),
).to.be.rejectedWith(constants.REVERT);
}); });
}); });
describe('removeToken', () => { describe('removeToken', () => {
it('should throw if not called by owner', async () => { it('should throw if not called by owner', async () => {
const index = 0; const index = 0;
return expect(tokenReg.removeToken(token1.address, index, {from: notOwner})) return expect(tokenReg.removeToken(token1.address, index, { from: notOwner })).to.be.rejectedWith(
.to.be.rejectedWith(constants.REVERT); constants.REVERT,
);
}); });
it('should remove token metadata when called by owner', async () => { it('should remove token metadata when called by owner', async () => {
const index = 0; const index = 0;
const res = await tokenReg.removeToken(token1.address, index, {from: owner}); const res = await tokenReg.removeToken(token1.address, index, {
from: owner,
});
expect(res.logs).to.have.length(1); expect(res.logs).to.have.length(1);
const tokenData = await tokenRegWrapper.getTokenMetaDataAsync(token1.address); const tokenData = await tokenRegWrapper.getTokenMetaDataAsync(token1.address);
expect(tokenData).to.be.deep.equal(nullToken); expect(tokenData).to.be.deep.equal(nullToken);
@ -196,17 +217,18 @@ contract('TokenRegistry', (accounts: string[]) => {
it('should throw if token does not exist', async () => { it('should throw if token does not exist', async () => {
const index = 0; const index = 0;
return expect(tokenReg.removeToken(nullToken.address, index, {from: owner})) return expect(tokenReg.removeToken(nullToken.address, index, { from: owner })).to.be.rejectedWith(
.to.be.rejectedWith(constants.REVERT); constants.REVERT,
);
}); });
it('should throw if token at given index does not match address', async () => { it('should throw if token at given index does not match address', async () => {
await tokenRegWrapper.addTokenAsync(token2, owner); await tokenRegWrapper.addTokenAsync(token2, owner);
const incorrectIndex = 0; const incorrectIndex = 0;
return expect(tokenReg.removeToken(token2.address, incorrectIndex, {from: owner})) return expect(tokenReg.removeToken(token2.address, incorrectIndex, { from: owner })).to.be.rejectedWith(
.to.be.rejectedWith(constants.REVERT); constants.REVERT,
}); );
});
}); });
}); });
}); });

View File

@ -22,12 +22,15 @@ contract('TokenTransferProxy', (accounts: string[]) => {
describe('addAuthorizedAddress', () => { describe('addAuthorizedAddress', () => {
it('should throw if not called by owner', async () => { it('should throw if not called by owner', async () => {
return expect(tokenTransferProxy.addAuthorizedAddress(notOwner, {from: notOwner})) return expect(tokenTransferProxy.addAuthorizedAddress(notOwner, { from: notOwner })).to.be.rejectedWith(
.to.be.rejectedWith(constants.REVERT); constants.REVERT,
);
}); });
it('should allow owner to add an authorized address', async () => { it('should allow owner to add an authorized address', async () => {
await tokenTransferProxy.addAuthorizedAddress(notAuthorized, {from: owner}); await tokenTransferProxy.addAuthorizedAddress(notAuthorized, {
from: owner,
});
authorized = notAuthorized; authorized = notAuthorized;
notAuthorized = null; notAuthorized = null;
const isAuthorized = await tokenTransferProxy.authorized.call(authorized); const isAuthorized = await tokenTransferProxy.authorized.call(authorized);
@ -35,19 +38,25 @@ contract('TokenTransferProxy', (accounts: string[]) => {
}); });
it('should throw if owner attempts to authorize a duplicate address', async () => { it('should throw if owner attempts to authorize a duplicate address', async () => {
return expect(tokenTransferProxy.addAuthorizedAddress(authorized, {from: owner})) return expect(tokenTransferProxy.addAuthorizedAddress(authorized, { from: owner })).to.be.rejectedWith(
.to.be.rejectedWith(constants.REVERT); constants.REVERT,
);
}); });
}); });
describe('removeAuthorizedAddress', () => { describe('removeAuthorizedAddress', () => {
it('should throw if not called by owner', async () => { it('should throw if not called by owner', async () => {
return expect(tokenTransferProxy.removeAuthorizedAddress(authorized, {from: notOwner})) return expect(
.to.be.rejectedWith(constants.REVERT); tokenTransferProxy.removeAuthorizedAddress(authorized, {
from: notOwner,
}),
).to.be.rejectedWith(constants.REVERT);
}); });
it('should allow owner to remove an authorized address', async () => { it('should allow owner to remove an authorized address', async () => {
await tokenTransferProxy.removeAuthorizedAddress(authorized, {from: owner}); await tokenTransferProxy.removeAuthorizedAddress(authorized, {
from: owner,
});
notAuthorized = authorized; notAuthorized = authorized;
authorized = null; authorized = null;
@ -56,8 +65,11 @@ contract('TokenTransferProxy', (accounts: string[]) => {
}); });
it('should throw if owner attempts to remove an address that is not authorized', async () => { it('should throw if owner attempts to remove an address that is not authorized', async () => {
return expect(tokenTransferProxy.removeAuthorizedAddress(notAuthorized, {from: owner})) return expect(
.to.be.rejectedWith(constants.REVERT); tokenTransferProxy.removeAuthorizedAddress(notAuthorized, {
from: owner,
}),
).to.be.rejectedWith(constants.REVERT);
}); });
}); });
@ -65,7 +77,9 @@ contract('TokenTransferProxy', (accounts: string[]) => {
it('should return all authorized addresses', async () => { it('should return all authorized addresses', async () => {
const initial = await tokenTransferProxy.getAuthorizedAddresses(); const initial = await tokenTransferProxy.getAuthorizedAddresses();
expect(initial).to.have.length(1); expect(initial).to.have.length(1);
await tokenTransferProxy.addAuthorizedAddress(notAuthorized, {from: owner}); await tokenTransferProxy.addAuthorizedAddress(notAuthorized, {
from: owner,
});
authorized = notAuthorized; authorized = notAuthorized;
notAuthorized = null; notAuthorized = null;
@ -73,7 +87,9 @@ contract('TokenTransferProxy', (accounts: string[]) => {
expect(afterAdd).to.have.length(2); expect(afterAdd).to.have.length(2);
expect(afterAdd).to.include(authorized); expect(afterAdd).to.include(authorized);
await tokenTransferProxy.removeAuthorizedAddress(authorized, {from: owner}); await tokenTransferProxy.removeAuthorizedAddress(authorized, {
from: owner,
});
notAuthorized = authorized; notAuthorized = authorized;
authorized = null; authorized = null;
const afterRemove = await tokenTransferProxy.getAuthorizedAddresses(); const afterRemove = await tokenTransferProxy.getAuthorizedAddresses();

View File

@ -8,11 +8,7 @@ import {chaiSetup} from '../utils/chai_setup';
chaiSetup.configure(); chaiSetup.configure();
const expect = chai.expect; const expect = chai.expect;
const { const { TokenTransferProxy, DummyToken, TokenRegistry } = new Artifacts(artifacts);
TokenTransferProxy,
DummyToken,
TokenRegistry,
} = new Artifacts(artifacts);
contract('TokenTransferProxy', (accounts: string[]) => { contract('TokenTransferProxy', (accounts: string[]) => {
const INIT_BAL = 100000000; const INIT_BAL = 100000000;
@ -36,32 +32,42 @@ contract('TokenTransferProxy', (accounts: string[]) => {
dmyBalances = new Balances([rep], [accounts[0], accounts[1]]); dmyBalances = new Balances([rep], [accounts[0], accounts[1]]);
await Promise.all([ await Promise.all([
rep.approve(TokenTransferProxy.address, INIT_ALLOW, {from: accounts[0]}), rep.approve(TokenTransferProxy.address, INIT_ALLOW, {
from: accounts[0],
}),
rep.setBalance(accounts[0], INIT_BAL, { from: owner }), rep.setBalance(accounts[0], INIT_BAL, { from: owner }),
rep.approve(TokenTransferProxy.address, INIT_ALLOW, {from: accounts[1]}), rep.approve(TokenTransferProxy.address, INIT_ALLOW, {
from: accounts[1],
}),
rep.setBalance(accounts[1], INIT_BAL, { from: owner }), rep.setBalance(accounts[1], INIT_BAL, { from: owner }),
]); ]);
}); });
describe('transferFrom', () => { describe('transferFrom', () => {
it('should throw when called by an unauthorized address', async () => { it('should throw when called by an unauthorized address', async () => {
expect(tokenTransferProxy.transferFrom(rep.address, accounts[0], accounts[1], 1000, {from: notAuthorized})) expect(
.to.be.rejectedWith(constants.REVERT); tokenTransferProxy.transferFrom(rep.address, accounts[0], accounts[1], 1000, { from: notAuthorized }),
).to.be.rejectedWith(constants.REVERT);
}); });
it('should allow an authorized address to transfer', async () => { it('should allow an authorized address to transfer', async () => {
const balances = await dmyBalances.getAsync(); const balances = await dmyBalances.getAsync();
await tokenTransferProxy.addAuthorizedAddress(notAuthorized, {from: owner}); await tokenTransferProxy.addAuthorizedAddress(notAuthorized, {
from: owner,
});
const transferAmt = 10000; const transferAmt = 10000;
await tokenTransferProxy.transferFrom(rep.address, accounts[0], accounts[1], await tokenTransferProxy.transferFrom(rep.address, accounts[0], accounts[1], transferAmt, {
transferAmt, {from: notAuthorized}); from: notAuthorized,
});
const newBalances = await dmyBalances.getAsync(); const newBalances = await dmyBalances.getAsync();
expect(newBalances[accounts[0]][rep.address]) expect(newBalances[accounts[0]][rep.address]).to.be.bignumber.equal(
.to.be.bignumber.equal(balances[accounts[0]][rep.address].minus(transferAmt)); balances[accounts[0]][rep.address].minus(transferAmt),
expect(newBalances[accounts[1]][rep.address]) );
.to.be.bignumber.equal(balances[accounts[1]][rep.address].add(transferAmt)); expect(newBalances[accounts[1]][rep.address]).to.be.bignumber.equal(
balances[accounts[1]][rep.address].add(transferAmt),
);
}); });
}); });
}); });

View File

@ -48,7 +48,9 @@ contract('UnlimitedAllowanceToken', (accounts: string[]) => {
}); });
it('should return true on a 0 value transfer', async () => { it('should return true on a 0 value transfer', async () => {
const didReturnTrue = await token.transfer.call(spender, 0, {from: owner}); const didReturnTrue = await token.transfer.call(spender, 0, {
from: owner,
});
expect(didReturnTrue).to.be.true(); expect(didReturnTrue).to.be.true();
}); });
}); });

View File

@ -36,8 +36,9 @@ contract('UnlimitedAllowanceTokenV2', (accounts: string[]) => {
it('should throw if owner has insufficient balance', async () => { it('should throw if owner has insufficient balance', async () => {
const ownerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner); const ownerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
const amountToTransfer = ownerBalance.plus(1); const amountToTransfer = ownerBalance.plus(1);
return expect(token.transfer.call(spender, amountToTransfer, {from: owner})) return expect(token.transfer.call(spender, amountToTransfer, { from: owner })).to.be.rejectedWith(
.to.be.rejectedWith(constants.REVERT); constants.REVERT,
);
}); });
it('should transfer balance from sender to receiver', async () => { it('should transfer balance from sender to receiver', async () => {
@ -55,7 +56,9 @@ contract('UnlimitedAllowanceTokenV2', (accounts: string[]) => {
}); });
it('should return true on a 0 value transfer', async () => { it('should return true on a 0 value transfer', async () => {
const didReturnTrue = await token.transfer.call(spender, 0, {from: owner}); const didReturnTrue = await token.transfer.call(spender, 0, {
from: owner,
});
expect(didReturnTrue).to.be.true(); expect(didReturnTrue).to.be.true();
}); });
}); });
@ -65,8 +68,11 @@ contract('UnlimitedAllowanceTokenV2', (accounts: string[]) => {
const ownerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner); const ownerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
const amountToTransfer = ownerBalance.plus(1); const amountToTransfer = ownerBalance.plus(1);
await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, amountToTransfer); await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, amountToTransfer);
return expect(token.transferFrom.call(owner, spender, amountToTransfer, {from: spender})) return expect(
.to.be.rejectedWith(constants.REVERT); token.transferFrom.call(owner, spender, amountToTransfer, {
from: spender,
}),
).to.be.rejectedWith(constants.REVERT);
}); });
it('should throw if spender has insufficient allowance', async () => { it('should throw if spender has insufficient allowance', async () => {
@ -77,8 +83,11 @@ contract('UnlimitedAllowanceTokenV2', (accounts: string[]) => {
const spenderAllowanceIsInsufficient = spenderAllowance.cmp(amountToTransfer) < 0; const spenderAllowanceIsInsufficient = spenderAllowance.cmp(amountToTransfer) < 0;
expect(spenderAllowanceIsInsufficient).to.be.true(); expect(spenderAllowanceIsInsufficient).to.be.true();
return expect(token.transferFrom.call(owner, spender, amountToTransfer, {from: spender})) return expect(
.to.be.rejectedWith(constants.REVERT); token.transferFrom.call(owner, spender, amountToTransfer, {
from: spender,
}),
).to.be.rejectedWith(constants.REVERT);
}); });
it('should return true on a 0 value transfer', async () => { it('should return true on a 0 value transfer', async () => {

View File

@ -85,7 +85,9 @@ contract('ZRXToken', (accounts: string[]) => {
}); });
it('should return true on a 0 value transfer', async () => { it('should return true on a 0 value transfer', async () => {
const didReturnTrue = await zrx.transfer.call(spender, 0, {from: owner}); const didReturnTrue = await zrx.transfer.call(spender, 0, {
from: owner,
});
expect(didReturnTrue).to.be.true(); expect(didReturnTrue).to.be.true();
}); });
}); });

View File

@ -21,7 +21,8 @@ export class Artifacts {
this.DummyTokenV2 = artifacts.require('DummyToken_v2'); this.DummyTokenV2 = artifacts.require('DummyToken_v2');
this.EtherToken = artifacts.require('WETH9'); this.EtherToken = artifacts.require('WETH9');
this.MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress = artifacts.require( this.MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress = artifacts.require(
'MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress'); 'MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress',
);
this.MaliciousToken = artifacts.require('MaliciousToken'); this.MaliciousToken = artifacts.require('MaliciousToken');
} }
} }

View File

@ -18,7 +18,7 @@ export const crypto = {
const isNumber = _.isFinite(arg); const isNumber = _.isFinite(arg);
if (isNumber) { if (isNumber) {
argTypes.push('uint8'); argTypes.push('uint8');
} else if ((arg).isBigNumber) { } else if (arg.isBigNumber) {
argTypes.push('uint256'); argTypes.push('uint256');
args[i] = new BN(arg.toString(10), 10); args[i] = new BN(arg.toString(10), 10);
} else if (ethUtil.isValidAddress(arg)) { } else if (ethUtil.isValidAddress(arg)) {

View File

@ -10,11 +10,14 @@ export class ExchangeWrapper {
constructor(exchangeContractInstance: ContractInstance) { constructor(exchangeContractInstance: ContractInstance) {
this._exchange = exchangeContractInstance; this._exchange = exchangeContractInstance;
} }
public async fillOrderAsync(order: Order, from: string, public async fillOrderAsync(
order: Order,
from: string,
opts: { opts: {
fillTakerTokenAmount?: BigNumber; fillTakerTokenAmount?: BigNumber;
shouldThrowOnInsufficientBalanceOrAllowance?: boolean; shouldThrowOnInsufficientBalanceOrAllowance?: boolean;
} = {}) { } = {},
) {
const shouldThrowOnInsufficientBalanceOrAllowance = !!opts.shouldThrowOnInsufficientBalanceOrAllowance; const shouldThrowOnInsufficientBalanceOrAllowance = !!opts.shouldThrowOnInsufficientBalanceOrAllowance;
const params = order.createFill(shouldThrowOnInsufficientBalanceOrAllowance, opts.fillTakerTokenAmount); const params = order.createFill(shouldThrowOnInsufficientBalanceOrAllowance, opts.fillTakerTokenAmount);
const tx = await this._exchange.fillOrder( const tx = await this._exchange.fillOrder(
@ -30,8 +33,7 @@ export class ExchangeWrapper {
_.each(tx.logs, log => wrapLogBigNumbers(log)); _.each(tx.logs, log => wrapLogBigNumbers(log));
return tx; return tx;
} }
public async cancelOrderAsync(order: Order, from: string, public async cancelOrderAsync(order: Order, from: string, opts: { cancelTakerTokenAmount?: BigNumber } = {}) {
opts: {cancelTakerTokenAmount?: BigNumber} = {}) {
const params = order.createCancel(opts.cancelTakerTokenAmount); const params = order.createCancel(opts.cancelTakerTokenAmount);
const tx = await this._exchange.cancelOrder( const tx = await this._exchange.cancelOrder(
params.orderAddresses, params.orderAddresses,
@ -42,8 +44,7 @@ export class ExchangeWrapper {
_.each(tx.logs, log => wrapLogBigNumbers(log)); _.each(tx.logs, log => wrapLogBigNumbers(log));
return tx; return tx;
} }
public async fillOrKillOrderAsync(order: Order, from: string, public async fillOrKillOrderAsync(order: Order, from: string, opts: { fillTakerTokenAmount?: BigNumber } = {}) {
opts: {fillTakerTokenAmount?: BigNumber} = {}) {
const shouldThrowOnInsufficientBalanceOrAllowance = true; const shouldThrowOnInsufficientBalanceOrAllowance = true;
const params = order.createFill(shouldThrowOnInsufficientBalanceOrAllowance, opts.fillTakerTokenAmount); const params = order.createFill(shouldThrowOnInsufficientBalanceOrAllowance, opts.fillTakerTokenAmount);
const tx = await this._exchange.fillOrKillOrder( const tx = await this._exchange.fillOrKillOrder(
@ -58,14 +59,20 @@ export class ExchangeWrapper {
_.each(tx.logs, log => wrapLogBigNumbers(log)); _.each(tx.logs, log => wrapLogBigNumbers(log));
return tx; return tx;
} }
public async batchFillOrdersAsync(orders: Order[], from: string, public async batchFillOrdersAsync(
orders: Order[],
from: string,
opts: { opts: {
fillTakerTokenAmounts?: BigNumber[]; fillTakerTokenAmounts?: BigNumber[];
shouldThrowOnInsufficientBalanceOrAllowance?: boolean; shouldThrowOnInsufficientBalanceOrAllowance?: boolean;
} = {}) { } = {},
) {
const shouldThrowOnInsufficientBalanceOrAllowance = !!opts.shouldThrowOnInsufficientBalanceOrAllowance; const shouldThrowOnInsufficientBalanceOrAllowance = !!opts.shouldThrowOnInsufficientBalanceOrAllowance;
const params = formatters.createBatchFill( const params = formatters.createBatchFill(
orders, shouldThrowOnInsufficientBalanceOrAllowance, opts.fillTakerTokenAmounts); orders,
shouldThrowOnInsufficientBalanceOrAllowance,
opts.fillTakerTokenAmounts,
);
const tx = await this._exchange.batchFillOrders( const tx = await this._exchange.batchFillOrders(
params.orderAddresses, params.orderAddresses,
params.orderValues, params.orderValues,
@ -79,8 +86,11 @@ export class ExchangeWrapper {
_.each(tx.logs, log => wrapLogBigNumbers(log)); _.each(tx.logs, log => wrapLogBigNumbers(log));
return tx; return tx;
} }
public async batchFillOrKillOrdersAsync(orders: Order[], from: string, public async batchFillOrKillOrdersAsync(
opts: {fillTakerTokenAmounts?: BigNumber[]} = {}) { orders: Order[],
from: string,
opts: { fillTakerTokenAmounts?: BigNumber[] } = {},
) {
const params = formatters.createBatchFill(orders, undefined, opts.fillTakerTokenAmounts); const params = formatters.createBatchFill(orders, undefined, opts.fillTakerTokenAmounts);
const tx = await this._exchange.batchFillOrKillOrders( const tx = await this._exchange.batchFillOrKillOrders(
params.orderAddresses, params.orderAddresses,
@ -94,15 +104,20 @@ export class ExchangeWrapper {
_.each(tx.logs, log => wrapLogBigNumbers(log)); _.each(tx.logs, log => wrapLogBigNumbers(log));
return tx; return tx;
} }
public async fillOrdersUpToAsync(orders: Order[], from: string, public async fillOrdersUpToAsync(
orders: Order[],
from: string,
opts: { opts: {
fillTakerTokenAmount?: BigNumber; fillTakerTokenAmount?: BigNumber;
shouldThrowOnInsufficientBalanceOrAllowance?: boolean; shouldThrowOnInsufficientBalanceOrAllowance?: boolean;
} = {}) { } = {},
) {
const shouldThrowOnInsufficientBalanceOrAllowance = !!opts.shouldThrowOnInsufficientBalanceOrAllowance; const shouldThrowOnInsufficientBalanceOrAllowance = !!opts.shouldThrowOnInsufficientBalanceOrAllowance;
const params = formatters.createFillUpTo(orders, const params = formatters.createFillUpTo(
orders,
shouldThrowOnInsufficientBalanceOrAllowance, shouldThrowOnInsufficientBalanceOrAllowance,
opts.fillTakerTokenAmount); opts.fillTakerTokenAmount,
);
const tx = await this._exchange.fillOrdersUpTo( const tx = await this._exchange.fillOrdersUpTo(
params.orderAddresses, params.orderAddresses,
params.orderValues, params.orderValues,
@ -116,8 +131,11 @@ export class ExchangeWrapper {
_.each(tx.logs, log => wrapLogBigNumbers(log)); _.each(tx.logs, log => wrapLogBigNumbers(log));
return tx; return tx;
} }
public async batchCancelOrdersAsync(orders: Order[], from: string, public async batchCancelOrdersAsync(
opts: {cancelTakerTokenAmounts?: BigNumber[]} = {}) { orders: Order[],
from: string,
opts: { cancelTakerTokenAmounts?: BigNumber[] } = {},
) {
const params = formatters.createBatchCancel(orders, opts.cancelTakerTokenAmounts); const params = formatters.createBatchCancel(orders, opts.cancelTakerTokenAmounts);
const tx = await this._exchange.batchCancelOrders( const tx = await this._exchange.batchCancelOrders(
params.orderAddresses, params.orderAddresses,
@ -144,13 +162,19 @@ export class ExchangeWrapper {
); );
return isValidSignature; return isValidSignature;
} }
public async isRoundingErrorAsync(numerator: BigNumber, denominator: BigNumber, public async isRoundingErrorAsync(
target: BigNumber): Promise<boolean> { numerator: BigNumber,
denominator: BigNumber,
target: BigNumber,
): Promise<boolean> {
const isRoundingError = await this._exchange.isRoundingError(numerator, denominator, target); const isRoundingError = await this._exchange.isRoundingError(numerator, denominator, target);
return isRoundingError; return isRoundingError;
} }
public async getPartialAmountAsync(numerator: BigNumber, denominator: BigNumber, public async getPartialAmountAsync(
target: BigNumber): Promise<BigNumber> { numerator: BigNumber,
denominator: BigNumber,
target: BigNumber,
): Promise<BigNumber> {
const partialAmount = new BigNumber(await this._exchange.getPartialAmount(numerator, denominator, target)); const partialAmount = new BigNumber(await this._exchange.getPartialAmount(numerator, denominator, target));
return partialAmount; return partialAmount;
} }

View File

@ -5,9 +5,11 @@ import {Order} from './order';
import { BatchCancelOrders, BatchFillOrders, FillOrdersUpTo } from './types'; import { BatchCancelOrders, BatchFillOrders, FillOrdersUpTo } from './types';
export const formatters = { export const formatters = {
createBatchFill(orders: Order[], createBatchFill(
orders: Order[],
shouldThrowOnInsufficientBalanceOrAllowance: boolean, shouldThrowOnInsufficientBalanceOrAllowance: boolean,
fillTakerTokenAmounts: BigNumber[] = []) { fillTakerTokenAmounts: BigNumber[] = [],
) {
const batchFill: BatchFillOrders = { const batchFill: BatchFillOrders = {
orderAddresses: [], orderAddresses: [],
orderValues: [], orderValues: [],
@ -18,11 +20,21 @@ export const formatters = {
s: [], s: [],
}; };
_.forEach(orders, order => { _.forEach(orders, order => {
batchFill.orderAddresses.push([order.params.maker, order.params.taker, order.params.makerToken, batchFill.orderAddresses.push([
order.params.takerToken, order.params.feeRecipient]); order.params.maker,
batchFill.orderValues.push([order.params.makerTokenAmount, order.params.takerTokenAmount, order.params.taker,
order.params.makerFee, order.params.takerFee, order.params.makerToken,
order.params.expirationTimestampInSec, order.params.salt]); order.params.takerToken,
order.params.feeRecipient,
]);
batchFill.orderValues.push([
order.params.makerTokenAmount,
order.params.takerTokenAmount,
order.params.makerFee,
order.params.takerFee,
order.params.expirationTimestampInSec,
order.params.salt,
]);
batchFill.v.push(order.params.v); batchFill.v.push(order.params.v);
batchFill.r.push(order.params.r); batchFill.r.push(order.params.r);
batchFill.s.push(order.params.s); batchFill.s.push(order.params.s);
@ -32,9 +44,11 @@ export const formatters = {
}); });
return batchFill; return batchFill;
}, },
createFillUpTo(orders: Order[], createFillUpTo(
orders: Order[],
shouldThrowOnInsufficientBalanceOrAllowance: boolean, shouldThrowOnInsufficientBalanceOrAllowance: boolean,
fillTakerTokenAmount: BigNumber) { fillTakerTokenAmount: BigNumber,
) {
const fillUpTo: FillOrdersUpTo = { const fillUpTo: FillOrdersUpTo = {
orderAddresses: [], orderAddresses: [],
orderValues: [], orderValues: [],
@ -45,11 +59,21 @@ export const formatters = {
s: [], s: [],
}; };
orders.forEach(order => { orders.forEach(order => {
fillUpTo.orderAddresses.push([order.params.maker, order.params.taker, order.params.makerToken, fillUpTo.orderAddresses.push([
order.params.takerToken, order.params.feeRecipient]); order.params.maker,
fillUpTo.orderValues.push([order.params.makerTokenAmount, order.params.takerTokenAmount, order.params.taker,
order.params.makerFee, order.params.takerFee, order.params.makerToken,
order.params.expirationTimestampInSec, order.params.salt]); order.params.takerToken,
order.params.feeRecipient,
]);
fillUpTo.orderValues.push([
order.params.makerTokenAmount,
order.params.takerTokenAmount,
order.params.makerFee,
order.params.takerFee,
order.params.expirationTimestampInSec,
order.params.salt,
]);
fillUpTo.v.push(order.params.v); fillUpTo.v.push(order.params.v);
fillUpTo.r.push(order.params.r); fillUpTo.r.push(order.params.r);
fillUpTo.s.push(order.params.s); fillUpTo.s.push(order.params.s);
@ -63,11 +87,21 @@ export const formatters = {
cancelTakerTokenAmounts, cancelTakerTokenAmounts,
}; };
orders.forEach(order => { orders.forEach(order => {
batchCancel.orderAddresses.push([order.params.maker, order.params.taker, order.params.makerToken, batchCancel.orderAddresses.push([
order.params.takerToken, order.params.feeRecipient]); order.params.maker,
batchCancel.orderValues.push([order.params.makerTokenAmount, order.params.takerTokenAmount, order.params.taker,
order.params.makerFee, order.params.takerFee, order.params.makerToken,
order.params.expirationTimestampInSec, order.params.salt]); order.params.takerToken,
order.params.feeRecipient,
]);
batchCancel.orderValues.push([
order.params.makerTokenAmount,
order.params.takerTokenAmount,
order.params.makerFee,
order.params.takerFee,
order.params.expirationTimestampInSec,
order.params.salt,
]);
if (cancelTakerTokenAmounts.length < orders.length) { if (cancelTakerTokenAmounts.length < orders.length) {
batchCancel.cancelTakerTokenAmounts.push(order.params.takerTokenAmount); batchCancel.cancelTakerTokenAmounts.push(order.params.takerTokenAmount);
} }

View File

@ -24,11 +24,16 @@ export class MultiSigWrapper {
constructor(multiSigContractInstance: ContractInstance) { constructor(multiSigContractInstance: ContractInstance) {
this._multiSig = multiSigContractInstance; this._multiSig = multiSigContractInstance;
} }
public async submitTransactionAsync(destination: string, from: string, public async submitTransactionAsync(
destination: string,
from: string,
dataParams: TransactionDataParams, dataParams: TransactionDataParams,
value: number = 0) { value: number = 0,
) {
const { name, abi, args = [] } = dataParams; const { name, abi, args = [] } = dataParams;
const encoded = MultiSigWrapper.encodeFnArgs(name, abi, args); const encoded = MultiSigWrapper.encodeFnArgs(name, abi, args);
return this._multiSig.submitTransaction(destination, value, encoded, {from}); return this._multiSig.submitTransaction(destination, value, encoded, {
from,
});
} }
} }

View File

@ -11,12 +11,17 @@ export class OrderFactory {
this._defaultOrderParams = defaultOrderParams; this._defaultOrderParams = defaultOrderParams;
} }
public async newSignedOrderAsync(customOrderParams: OptionalOrderParams = {}) { public async newSignedOrderAsync(customOrderParams: OptionalOrderParams = {}) {
const randomExpiration = new BigNumber(Math.floor((Date.now() + (Math.random() * 100000000000)) / 1000)); const randomExpiration = new BigNumber(Math.floor((Date.now() + Math.random() * 100000000000) / 1000));
const orderParams: OrderParams = _.assign({}, { const orderParams: OrderParams = _.assign(
{},
{
expirationTimestampInSec: randomExpiration, expirationTimestampInSec: randomExpiration,
salt: ZeroEx.generatePseudoRandomSalt(), salt: ZeroEx.generatePseudoRandomSalt(),
taker: ZeroEx.NULL_ADDRESS, taker: ZeroEx.NULL_ADDRESS,
}, this._defaultOrderParams, customOrderParams); },
this._defaultOrderParams,
customOrderParams,
);
const order = new Order(orderParams); const order = new Order(orderParams);
await order.signAsync(); await order.signAsync();
return order; return order;

View File

@ -1,6 +1,6 @@
{ {
"name": "@0xproject/dev-utils", "name": "@0xproject/dev-utils",
"version": "0.0.2", "version": "0.0.3",
"description": "0x dev TS utils", "description": "0x dev TS utils",
"main": "lib/index.js", "main": "lib/index.js",
"types": "lib/index.d.ts", "types": "lib/index.d.ts",
@ -19,7 +19,7 @@
}, },
"homepage": "https://github.com/0xProject/0x.js/packages/dev-utils/README.md", "homepage": "https://github.com/0xProject/0x.js/packages/dev-utils/README.md",
"devDependencies": { "devDependencies": {
"@0xproject/tslint-config": "^0.3.0", "@0xproject/tslint-config": "^0.4.0",
"@types/lodash": "^4.14.86", "@types/lodash": "^4.14.86",
"npm-run-all": "^4.1.2", "npm-run-all": "^4.1.2",
"shx": "^0.2.2", "shx": "^0.2.2",

View File

@ -1,6 +1,6 @@
{ {
"name": "@0xproject/json-schemas", "name": "@0xproject/json-schemas",
"version": "0.7.0", "version": "0.7.1",
"description": "0x-related json schemas", "description": "0x-related json schemas",
"main": "lib/src/index.js", "main": "lib/src/index.js",
"types": "lib/src/index.d.ts", "types": "lib/src/index.d.ts",
@ -27,8 +27,8 @@
"lodash.values": "^4.3.0" "lodash.values": "^4.3.0"
}, },
"devDependencies": { "devDependencies": {
"@0xproject/tslint-config": "^0.3.0", "@0xproject/tslint-config": "^0.4.0",
"@0xproject/utils": "^0.1.1", "@0xproject/utils": "^0.1.2",
"@types/lodash.foreach": "^4.5.3", "@types/lodash.foreach": "^4.5.3",
"@types/lodash.values": "^4.3.3", "@types/lodash.values": "^4.3.3",
"@types/mocha": "^2.2.42", "@types/mocha": "^2.2.42",

View File

@ -1,11 +1,7 @@
export const indexFilterValuesSchema = { export const indexFilterValuesSchema = {
id: '/IndexFilterValues', id: '/IndexFilterValues',
additionalProperties: { additionalProperties: {
oneOf: [ oneOf: [{ $ref: '/Number' }, { $ref: '/Address' }, { $ref: '/OrderHashSchema' }],
{$ref: '/Number'},
{$ref: '/Address'},
{$ref: '/OrderHashSchema'},
],
}, },
type: 'object', type: 'object',
}; };

View File

@ -15,8 +15,16 @@ export const orderSchema = {
exchangeContractAddress: { $ref: '/Address' }, exchangeContractAddress: { $ref: '/Address' },
}, },
required: [ required: [
'maker', 'taker', 'makerFee', 'takerFee', 'makerTokenAmount', 'takerTokenAmount', 'maker',
'salt', 'feeRecipient', 'expirationUnixTimestampSec', 'exchangeContractAddress', 'taker',
'makerFee',
'takerFee',
'makerTokenAmount',
'takerTokenAmount',
'salt',
'feeRecipient',
'expirationUnixTimestampSec',
'exchangeContractAddress',
], ],
type: 'object', type: 'object',
}; };

View File

@ -13,7 +13,12 @@ export const relayerApiFeesPayloadSchema = {
salt: { $ref: '/Number' }, salt: { $ref: '/Number' },
}, },
required: [ required: [
'exchangeContractAddress', 'maker', 'taker', 'makerTokenAddress', 'takerTokenAddress', 'exchangeContractAddress',
'expirationUnixTimestampSec', 'salt', 'maker',
'taker',
'makerTokenAddress',
'takerTokenAddress',
'expirationUnixTimestampSec',
'salt',
], ],
}; };

View File

@ -10,22 +10,13 @@ export const txDataSchema = {
from: { $ref: '/Address' }, from: { $ref: '/Address' },
to: { $ref: '/Address' }, to: { $ref: '/Address' },
value: { value: {
oneOf: [ oneOf: [{ $ref: '/Number' }, { $ref: '/JsNumber' }],
{$ref: '/Number'},
{$ref: '/JsNumber'},
],
}, },
gas: { gas: {
oneOf: [ oneOf: [{ $ref: '/Number' }, { $ref: '/JsNumber' }],
{$ref: '/Number'},
{$ref: '/JsNumber'},
],
}, },
gasPrice: { gasPrice: {
oneOf: [ oneOf: [{ $ref: '/Number' }, { $ref: '/JsNumber' }],
{$ref: '/Number'},
{$ref: '/JsNumber'},
],
}, },
data: { data: {
type: 'string', type: 'string',

View File

@ -1,43 +1,15 @@
import { import { addressSchema, numberSchema } from '../schemas/basic_type_schemas';
addressSchema, import { blockParamSchema, blockRangeSchema } from '../schemas/block_range_schema';
numberSchema, import { ecSignatureParameterSchema, ecSignatureSchema } from '../schemas/ec_signature_schema';
} from '../schemas/basic_type_schemas'; import { indexFilterValuesSchema } from '../schemas/index_filter_values_schema';
import { import { orderCancellationRequestsSchema } from '../schemas/order_cancel_schema';
blockParamSchema, import { orderFillOrKillRequestsSchema } from '../schemas/order_fill_or_kill_requests_schema';
blockRangeSchema, import { orderFillRequestsSchema } from '../schemas/order_fill_requests_schema';
} from '../schemas/block_range_schema'; import { orderHashSchema } from '../schemas/order_hash_schema';
import { import { orderSchema, signedOrderSchema } from '../schemas/order_schemas';
ecSignatureParameterSchema, import { relayerApiErrorResponseSchema } from '../schemas/relayer_api_error_response_schema';
ecSignatureSchema, import { relayerApiFeesPayloadSchema } from '../schemas/relayer_api_fees_payload_schema';
} from '../schemas/ec_signature_schema'; import { relayerApiFeesResponseSchema } from '../schemas/relayer_api_fees_response_schema';
import {
indexFilterValuesSchema,
} from '../schemas/index_filter_values_schema';
import {
orderCancellationRequestsSchema,
} from '../schemas/order_cancel_schema';
import {
orderFillOrKillRequestsSchema,
} from '../schemas/order_fill_or_kill_requests_schema';
import {
orderFillRequestsSchema,
} from '../schemas/order_fill_requests_schema';
import {
orderHashSchema,
} from '../schemas/order_hash_schema';
import {
orderSchema,
signedOrderSchema,
} from '../schemas/order_schemas';
import {
relayerApiErrorResponseSchema,
} from '../schemas/relayer_api_error_response_schema';
import {
relayerApiFeesPayloadSchema,
} from '../schemas/relayer_api_fees_payload_schema';
import {
relayerApiFeesResponseSchema,
} from '../schemas/relayer_api_fees_response_schema';
import { import {
relayerApiOrderbookChannelSubscribePayload, relayerApiOrderbookChannelSubscribePayload,
relayerApiOrderbookChannelSubscribeSchema, relayerApiOrderbookChannelSubscribeSchema,
@ -46,26 +18,15 @@ import {
relayerApiOrderbookChannelSnapshotPayload, relayerApiOrderbookChannelSnapshotPayload,
relayerApiOrderbookChannelSnapshotSchema, relayerApiOrderbookChannelSnapshotSchema,
} from '../schemas/relayer_api_orderbook_channel_snapshot_schema'; } from '../schemas/relayer_api_orderbook_channel_snapshot_schema';
import { import { relayerApiOrderbookChannelUpdateSchema } from '../schemas/relayer_api_orderbook_channel_update_response_schema';
relayerApiOrderbookChannelUpdateSchema, import { relayerApiOrderBookResponseSchema } from '../schemas/relayer_api_orderbook_response_schema';
} from '../schemas/relayer_api_orderbook_channel_update_response_schema';
import {
relayerApiOrderBookResponseSchema,
} from '../schemas/relayer_api_orderbook_response_schema';
import { import {
relayerApiTokenPairsResponseSchema, relayerApiTokenPairsResponseSchema,
relayerApiTokenTradeInfoSchema, relayerApiTokenTradeInfoSchema,
} from '../schemas/relayer_api_token_pairs_response_schema'; } from '../schemas/relayer_api_token_pairs_response_schema';
import { import { signedOrdersSchema } from '../schemas/signed_orders_schema';
signedOrdersSchema, import { tokenSchema } from '../schemas/token_schema';
} from '../schemas/signed_orders_schema'; import { jsNumber, txDataSchema } from '../schemas/tx_data_schema';
import {
tokenSchema,
} from '../schemas/token_schema';
import {
jsNumber,
txDataSchema,
} from '../schemas/tx_data_schema';
export const schemas = { export const schemas = {
numberSchema, numberSchema,

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